ole32: Rewrite transacted storage to be more lazy.
[wine/multimedia.git] / dlls / ole32 / storage32.c
bloba9354c9bb33845520e0b8528d297b92e31f0e26f
1 /*
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
27 * NOTES
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.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
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 typedef struct TransactedDirEntry
123 /* If applicable, a reference to the original DirEntry in the transacted
124 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
125 DirRef transactedParentEntry;
127 /* True if this entry is being used. */
128 int inuse;
130 /* True if data is up to date. */
131 int read;
133 /* True if this entry has been modified. */
134 int dirty;
136 /* True if this entry's stream has been modified. */
137 int stream_dirty;
139 /* True if this entry has been deleted in the transacted storage, but the
140 * delete has not yet been committed. */
141 int deleted;
143 /* If this entry's stream has been modified, a reference to where the stream
144 * is stored in the snapshot file. */
145 DirRef stream_entry;
147 /* This directory entry's data, including any changes that have been made. */
148 DirEntry data;
150 /* A reference to the parent of this node. This is only valid while we are
151 * committing changes. */
152 DirRef parent;
154 /* A reference to a newly-created entry in the transacted parent. This is
155 * always equal to transactedParentEntry except when committing changes. */
156 DirRef newTransactedParentEntry;
157 } TransactedDirEntry;
159 /****************************************************************************
160 * Transacted storage object.
162 typedef struct TransactedSnapshotImpl
164 struct StorageBaseImpl base;
167 * Modified streams are temporarily saved to the scratch file.
169 StorageBaseImpl *scratch;
171 /* The directory structure is kept here, so that we can track how these
172 * entries relate to those in the parent storage. */
173 TransactedDirEntry *entries;
174 ULONG entries_size;
175 ULONG firstFreeEntry;
178 * Changes are committed to the transacted parent.
180 StorageBaseImpl *transactedParent;
181 } TransactedSnapshotImpl;
183 /* Generic function to create a transacted wrapper for a direct storage object. */
184 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
186 /* OLESTREAM memory structure to use for Get and Put Routines */
187 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
188 typedef struct
190 DWORD dwOleID;
191 DWORD dwTypeID;
192 DWORD dwOleTypeNameLength;
193 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
194 CHAR *pstrOleObjFileName;
195 DWORD dwOleObjFileNameLength;
196 DWORD dwMetaFileWidth;
197 DWORD dwMetaFileHeight;
198 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
199 DWORD dwDataLength;
200 BYTE *pData;
201 }OLECONVERT_OLESTREAM_DATA;
203 /* CompObj Stream structure */
204 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
205 typedef struct
207 BYTE byUnknown1[12];
208 CLSID clsid;
209 DWORD dwCLSIDNameLength;
210 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
211 DWORD dwOleTypeNameLength;
212 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
213 DWORD dwProgIDNameLength;
214 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
215 BYTE byUnknown2[16];
216 }OLECONVERT_ISTORAGE_COMPOBJ;
219 /* Ole Presentation Stream structure */
220 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
221 typedef struct
223 BYTE byUnknown1[28];
224 DWORD dwExtentX;
225 DWORD dwExtentY;
226 DWORD dwSize;
227 BYTE *pData;
228 }OLECONVERT_ISTORAGE_OLEPRES;
232 /***********************************************************************
233 * Forward declaration of internal functions used by the method DestroyElement
235 static HRESULT deleteStorageContents(
236 StorageBaseImpl *parentStorage,
237 DirRef indexToDelete,
238 DirEntry entryDataToDelete);
240 static HRESULT deleteStreamContents(
241 StorageBaseImpl *parentStorage,
242 DirRef indexToDelete,
243 DirEntry entryDataToDelete);
245 static HRESULT removeFromTree(
246 StorageBaseImpl *This,
247 DirRef parentStorageIndex,
248 DirRef deletedIndex);
250 /***********************************************************************
251 * Declaration of the functions used to manipulate DirEntry
254 static HRESULT insertIntoTree(
255 StorageBaseImpl *This,
256 DirRef parentStorageIndex,
257 DirRef newEntryIndex);
259 static LONG entryNameCmp(
260 const OLECHAR *name1,
261 const OLECHAR *name2);
263 static DirRef findElement(
264 StorageBaseImpl *storage,
265 DirRef storageEntry,
266 const OLECHAR *name,
267 DirEntry *data);
269 static HRESULT findTreeParent(
270 StorageBaseImpl *storage,
271 DirRef storageEntry,
272 const OLECHAR *childName,
273 DirEntry *parentData,
274 DirRef *parentEntry,
275 ULONG *relation);
277 /***********************************************************************
278 * Declaration of miscellaneous functions...
280 static HRESULT validateSTGM(DWORD stgmValue);
282 static DWORD GetShareModeFromSTGM(DWORD stgm);
283 static DWORD GetAccessModeFromSTGM(DWORD stgm);
284 static DWORD GetCreationModeFromSTGM(DWORD stgm);
286 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
289 /****************************************************************************
290 * IEnumSTATSTGImpl definitions.
292 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
293 * This class allows iterating through the content of a storage and to find
294 * specific items inside it.
296 struct IEnumSTATSTGImpl
298 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
299 * since we want to cast this in an IEnumSTATSTG pointer */
301 LONG ref; /* Reference count */
302 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
303 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
305 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
309 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
310 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
312 /************************************************************************
313 ** Block Functions
316 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
318 return (index+1) * This->bigBlockSize;
321 /************************************************************************
322 ** Storage32BaseImpl implementation
324 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
325 ULARGE_INTEGER offset,
326 void* buffer,
327 ULONG size,
328 ULONG* bytesRead)
330 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
333 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
334 ULARGE_INTEGER offset,
335 const void* buffer,
336 const ULONG size,
337 ULONG* bytesWritten)
339 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
342 /************************************************************************
343 * Storage32BaseImpl_QueryInterface (IUnknown)
345 * This method implements the common QueryInterface for all IStorage32
346 * implementations contained in this file.
348 * See Windows documentation for more details on IUnknown methods.
350 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
351 IStorage* iface,
352 REFIID riid,
353 void** ppvObject)
355 StorageBaseImpl *This = (StorageBaseImpl *)iface;
357 if ( (This==0) || (ppvObject==0) )
358 return E_INVALIDARG;
360 *ppvObject = 0;
362 if (IsEqualGUID(&IID_IUnknown, riid) ||
363 IsEqualGUID(&IID_IStorage, riid))
365 *ppvObject = This;
367 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
369 *ppvObject = &This->pssVtbl;
372 if ((*ppvObject)==0)
373 return E_NOINTERFACE;
375 IStorage_AddRef(iface);
377 return S_OK;
380 /************************************************************************
381 * Storage32BaseImpl_AddRef (IUnknown)
383 * This method implements the common AddRef for all IStorage32
384 * implementations contained in this file.
386 * See Windows documentation for more details on IUnknown methods.
388 static ULONG WINAPI StorageBaseImpl_AddRef(
389 IStorage* iface)
391 StorageBaseImpl *This = (StorageBaseImpl *)iface;
392 ULONG ref = InterlockedIncrement(&This->ref);
394 TRACE("(%p) AddRef to %d\n", This, ref);
396 return ref;
399 /************************************************************************
400 * Storage32BaseImpl_Release (IUnknown)
402 * This method implements the common Release for all IStorage32
403 * implementations contained in this file.
405 * See Windows documentation for more details on IUnknown methods.
407 static ULONG WINAPI StorageBaseImpl_Release(
408 IStorage* iface)
410 StorageBaseImpl *This = (StorageBaseImpl *)iface;
412 ULONG ref = InterlockedDecrement(&This->ref);
414 TRACE("(%p) ReleaseRef to %d\n", This, ref);
416 if (ref == 0)
419 * Since we are using a system of base-classes, we want to call the
420 * destructor of the appropriate derived class. To do this, we are
421 * using virtual functions to implement the destructor.
423 StorageBaseImpl_Destroy(This);
426 return ref;
429 /************************************************************************
430 * Storage32BaseImpl_OpenStream (IStorage)
432 * This method will open the specified stream object from the current storage.
434 * See Windows documentation for more details on IStorage methods.
436 static HRESULT WINAPI StorageBaseImpl_OpenStream(
437 IStorage* iface,
438 const OLECHAR* pwcsName, /* [string][in] */
439 void* reserved1, /* [unique][in] */
440 DWORD grfMode, /* [in] */
441 DWORD reserved2, /* [in] */
442 IStream** ppstm) /* [out] */
444 StorageBaseImpl *This = (StorageBaseImpl *)iface;
445 StgStreamImpl* newStream;
446 DirEntry currentEntry;
447 DirRef streamEntryRef;
448 HRESULT res = STG_E_UNKNOWN;
450 TRACE("(%p, %s, %p, %x, %d, %p)\n",
451 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
453 if ( (pwcsName==NULL) || (ppstm==0) )
455 res = E_INVALIDARG;
456 goto end;
459 *ppstm = NULL;
461 if ( FAILED( validateSTGM(grfMode) ) ||
462 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
464 res = STG_E_INVALIDFLAG;
465 goto end;
469 * As documented.
471 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
473 res = STG_E_INVALIDFUNCTION;
474 goto end;
477 if (This->reverted)
479 res = STG_E_REVERTED;
480 goto end;
484 * Check that we're compatible with the parent's storage mode, but
485 * only if we are not in transacted mode
487 if(!(This->openFlags & STGM_TRANSACTED)) {
488 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
490 res = STG_E_INVALIDFLAG;
491 goto end;
496 * Search for the element with the given name
498 streamEntryRef = findElement(
499 This,
500 This->storageDirEntry,
501 pwcsName,
502 &currentEntry);
505 * If it was found, construct the stream object and return a pointer to it.
507 if ( (streamEntryRef!=DIRENTRY_NULL) &&
508 (currentEntry.stgType==STGTY_STREAM) )
510 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
512 /* A single stream cannot be opened a second time. */
513 res = STG_E_ACCESSDENIED;
514 goto end;
517 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
519 if (newStream!=0)
521 newStream->grfMode = grfMode;
522 *ppstm = (IStream*)newStream;
524 IStream_AddRef(*ppstm);
526 res = S_OK;
527 goto end;
530 res = E_OUTOFMEMORY;
531 goto end;
534 res = STG_E_FILENOTFOUND;
536 end:
537 if (res == S_OK)
538 TRACE("<-- IStream %p\n", *ppstm);
539 TRACE("<-- %08x\n", res);
540 return res;
543 /************************************************************************
544 * Storage32BaseImpl_OpenStorage (IStorage)
546 * This method will open a new storage object from the current storage.
548 * See Windows documentation for more details on IStorage methods.
550 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
551 IStorage* iface,
552 const OLECHAR* pwcsName, /* [string][unique][in] */
553 IStorage* pstgPriority, /* [unique][in] */
554 DWORD grfMode, /* [in] */
555 SNB snbExclude, /* [unique][in] */
556 DWORD reserved, /* [in] */
557 IStorage** ppstg) /* [out] */
559 StorageBaseImpl *This = (StorageBaseImpl *)iface;
560 StorageInternalImpl* newStorage;
561 StorageBaseImpl* newTransactedStorage;
562 DirEntry currentEntry;
563 DirRef storageEntryRef;
564 HRESULT res = STG_E_UNKNOWN;
566 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
567 iface, debugstr_w(pwcsName), pstgPriority,
568 grfMode, snbExclude, reserved, ppstg);
570 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
572 res = E_INVALIDARG;
573 goto end;
576 if (This->openFlags & STGM_SIMPLE)
578 res = STG_E_INVALIDFUNCTION;
579 goto end;
582 /* as documented */
583 if (snbExclude != NULL)
585 res = STG_E_INVALIDPARAMETER;
586 goto end;
589 if ( FAILED( validateSTGM(grfMode) ))
591 res = STG_E_INVALIDFLAG;
592 goto end;
596 * As documented.
598 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
599 (grfMode & STGM_DELETEONRELEASE) ||
600 (grfMode & STGM_PRIORITY) )
602 res = STG_E_INVALIDFUNCTION;
603 goto end;
606 if (This->reverted)
607 return STG_E_REVERTED;
610 * Check that we're compatible with the parent's storage mode,
611 * but only if we are not transacted
613 if(!(This->openFlags & STGM_TRANSACTED)) {
614 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
616 res = STG_E_ACCESSDENIED;
617 goto end;
621 *ppstg = NULL;
623 storageEntryRef = findElement(
624 This,
625 This->storageDirEntry,
626 pwcsName,
627 &currentEntry);
629 if ( (storageEntryRef!=DIRENTRY_NULL) &&
630 (currentEntry.stgType==STGTY_STORAGE) )
632 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
634 /* A single storage cannot be opened a second time. */
635 res = STG_E_ACCESSDENIED;
636 goto end;
639 newStorage = StorageInternalImpl_Construct(
640 This,
641 grfMode,
642 storageEntryRef);
644 if (newStorage != 0)
646 if (grfMode & STGM_TRANSACTED)
648 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
650 if (FAILED(res))
652 HeapFree(GetProcessHeap(), 0, newStorage);
653 goto end;
656 *ppstg = (IStorage*)newTransactedStorage;
658 else
660 *ppstg = (IStorage*)newStorage;
663 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
665 res = S_OK;
666 goto end;
669 res = STG_E_INSUFFICIENTMEMORY;
670 goto end;
673 res = STG_E_FILENOTFOUND;
675 end:
676 TRACE("<-- %08x\n", res);
677 return res;
680 /************************************************************************
681 * Storage32BaseImpl_EnumElements (IStorage)
683 * This method will create an enumerator object that can be used to
684 * retrieve information about all the elements in the storage object.
686 * See Windows documentation for more details on IStorage methods.
688 static HRESULT WINAPI StorageBaseImpl_EnumElements(
689 IStorage* iface,
690 DWORD reserved1, /* [in] */
691 void* reserved2, /* [size_is][unique][in] */
692 DWORD reserved3, /* [in] */
693 IEnumSTATSTG** ppenum) /* [out] */
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 IEnumSTATSTGImpl* newEnum;
698 TRACE("(%p, %d, %p, %d, %p)\n",
699 iface, reserved1, reserved2, reserved3, ppenum);
701 if ( (This==0) || (ppenum==0))
702 return E_INVALIDARG;
704 if (This->reverted)
705 return STG_E_REVERTED;
707 newEnum = IEnumSTATSTGImpl_Construct(
708 This,
709 This->storageDirEntry);
711 if (newEnum!=0)
713 *ppenum = (IEnumSTATSTG*)newEnum;
715 IEnumSTATSTG_AddRef(*ppenum);
717 return S_OK;
720 return E_OUTOFMEMORY;
723 /************************************************************************
724 * Storage32BaseImpl_Stat (IStorage)
726 * This method will retrieve information about this storage object.
728 * See Windows documentation for more details on IStorage methods.
730 static HRESULT WINAPI StorageBaseImpl_Stat(
731 IStorage* iface,
732 STATSTG* pstatstg, /* [out] */
733 DWORD grfStatFlag) /* [in] */
735 StorageBaseImpl *This = (StorageBaseImpl *)iface;
736 DirEntry currentEntry;
737 HRESULT res = STG_E_UNKNOWN;
739 TRACE("(%p, %p, %x)\n",
740 iface, pstatstg, grfStatFlag);
742 if ( (This==0) || (pstatstg==0))
744 res = E_INVALIDARG;
745 goto end;
748 if (This->reverted)
750 res = STG_E_REVERTED;
751 goto end;
754 res = StorageBaseImpl_ReadDirEntry(
755 This,
756 This->storageDirEntry,
757 &currentEntry);
759 if (SUCCEEDED(res))
761 StorageUtl_CopyDirEntryToSTATSTG(
762 This,
763 pstatstg,
764 &currentEntry,
765 grfStatFlag);
767 pstatstg->grfMode = This->openFlags;
768 pstatstg->grfStateBits = This->stateBits;
771 end:
772 if (res == S_OK)
774 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);
776 TRACE("<-- %08x\n", res);
777 return res;
780 /************************************************************************
781 * Storage32BaseImpl_RenameElement (IStorage)
783 * This method will rename the specified element.
785 * See Windows documentation for more details on IStorage methods.
787 static HRESULT WINAPI StorageBaseImpl_RenameElement(
788 IStorage* iface,
789 const OLECHAR* pwcsOldName, /* [in] */
790 const OLECHAR* pwcsNewName) /* [in] */
792 StorageBaseImpl *This = (StorageBaseImpl *)iface;
793 DirEntry currentEntry;
794 DirRef currentEntryRef;
796 TRACE("(%p, %s, %s)\n",
797 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
799 if (This->reverted)
800 return STG_E_REVERTED;
802 currentEntryRef = findElement(This,
803 This->storageDirEntry,
804 pwcsNewName,
805 &currentEntry);
807 if (currentEntryRef != DIRENTRY_NULL)
810 * There is already an element with the new name
812 return STG_E_FILEALREADYEXISTS;
816 * Search for the old element name
818 currentEntryRef = findElement(This,
819 This->storageDirEntry,
820 pwcsOldName,
821 &currentEntry);
823 if (currentEntryRef != DIRENTRY_NULL)
825 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
826 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
828 WARN("Element is already open; cannot rename.\n");
829 return STG_E_ACCESSDENIED;
832 /* Remove the element from its current position in the tree */
833 removeFromTree(This, This->storageDirEntry,
834 currentEntryRef);
836 /* Change the name of the element */
837 strcpyW(currentEntry.name, pwcsNewName);
839 /* Delete any sibling links */
840 currentEntry.leftChild = DIRENTRY_NULL;
841 currentEntry.rightChild = DIRENTRY_NULL;
843 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
844 &currentEntry);
846 /* Insert the element in a new position in the tree */
847 insertIntoTree(This, This->storageDirEntry,
848 currentEntryRef);
850 else
853 * There is no element with the old name
855 return STG_E_FILENOTFOUND;
858 return S_OK;
861 /************************************************************************
862 * Storage32BaseImpl_CreateStream (IStorage)
864 * This method will create a stream object within this storage
866 * See Windows documentation for more details on IStorage methods.
868 static HRESULT WINAPI StorageBaseImpl_CreateStream(
869 IStorage* iface,
870 const OLECHAR* pwcsName, /* [string][in] */
871 DWORD grfMode, /* [in] */
872 DWORD reserved1, /* [in] */
873 DWORD reserved2, /* [in] */
874 IStream** ppstm) /* [out] */
876 StorageBaseImpl *This = (StorageBaseImpl *)iface;
877 StgStreamImpl* newStream;
878 DirEntry currentEntry, newStreamEntry;
879 DirRef currentEntryRef, newStreamEntryRef;
880 HRESULT hr;
882 TRACE("(%p, %s, %x, %d, %d, %p)\n",
883 iface, debugstr_w(pwcsName), grfMode,
884 reserved1, reserved2, ppstm);
886 if (ppstm == 0)
887 return STG_E_INVALIDPOINTER;
889 if (pwcsName == 0)
890 return STG_E_INVALIDNAME;
892 if (reserved1 || reserved2)
893 return STG_E_INVALIDPARAMETER;
895 if ( FAILED( validateSTGM(grfMode) ))
896 return STG_E_INVALIDFLAG;
898 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
899 return STG_E_INVALIDFLAG;
901 if (This->reverted)
902 return STG_E_REVERTED;
905 * As documented.
907 if ((grfMode & STGM_DELETEONRELEASE) ||
908 (grfMode & STGM_TRANSACTED))
909 return STG_E_INVALIDFUNCTION;
912 * Don't worry about permissions in transacted mode, as we can always write
913 * changes; we just can't always commit them.
915 if(!(This->openFlags & STGM_TRANSACTED)) {
916 /* Can't create a stream on read-only storage */
917 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
918 return STG_E_ACCESSDENIED;
920 /* Can't create a stream with greater access than the parent. */
921 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
922 return STG_E_ACCESSDENIED;
925 if(This->openFlags & STGM_SIMPLE)
926 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
928 *ppstm = 0;
930 currentEntryRef = findElement(This,
931 This->storageDirEntry,
932 pwcsName,
933 &currentEntry);
935 if (currentEntryRef != DIRENTRY_NULL)
938 * An element with this name already exists
940 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
942 IStorage_DestroyElement(iface, pwcsName);
944 else
945 return STG_E_FILEALREADYEXISTS;
949 * memset the empty entry
951 memset(&newStreamEntry, 0, sizeof(DirEntry));
953 newStreamEntry.sizeOfNameString =
954 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
956 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
957 return STG_E_INVALIDNAME;
959 strcpyW(newStreamEntry.name, pwcsName);
961 newStreamEntry.stgType = STGTY_STREAM;
962 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
963 newStreamEntry.size.u.LowPart = 0;
964 newStreamEntry.size.u.HighPart = 0;
966 newStreamEntry.leftChild = DIRENTRY_NULL;
967 newStreamEntry.rightChild = DIRENTRY_NULL;
968 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
970 /* call CoFileTime to get the current time
971 newStreamEntry.ctime
972 newStreamEntry.mtime
975 /* newStreamEntry.clsid */
978 * Create an entry with the new data
980 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
981 if (FAILED(hr))
982 return hr;
985 * Insert the new entry in the parent storage's tree.
987 hr = insertIntoTree(
988 This,
989 This->storageDirEntry,
990 newStreamEntryRef);
991 if (FAILED(hr))
993 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
994 return hr;
998 * Open the stream to return it.
1000 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1002 if (newStream != 0)
1004 *ppstm = (IStream*)newStream;
1006 IStream_AddRef(*ppstm);
1008 else
1010 return STG_E_INSUFFICIENTMEMORY;
1013 return S_OK;
1016 /************************************************************************
1017 * Storage32BaseImpl_SetClass (IStorage)
1019 * This method will write the specified CLSID in the directory entry of this
1020 * storage.
1022 * See Windows documentation for more details on IStorage methods.
1024 static HRESULT WINAPI StorageBaseImpl_SetClass(
1025 IStorage* iface,
1026 REFCLSID clsid) /* [in] */
1028 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1029 HRESULT hRes;
1030 DirEntry currentEntry;
1032 TRACE("(%p, %p)\n", iface, clsid);
1034 if (This->reverted)
1035 return STG_E_REVERTED;
1037 hRes = StorageBaseImpl_ReadDirEntry(This,
1038 This->storageDirEntry,
1039 &currentEntry);
1040 if (SUCCEEDED(hRes))
1042 currentEntry.clsid = *clsid;
1044 hRes = StorageBaseImpl_WriteDirEntry(This,
1045 This->storageDirEntry,
1046 &currentEntry);
1049 return hRes;
1052 /************************************************************************
1053 ** Storage32Impl implementation
1056 /************************************************************************
1057 * Storage32BaseImpl_CreateStorage (IStorage)
1059 * This method will create the storage object within the provided storage.
1061 * See Windows documentation for more details on IStorage methods.
1063 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1064 IStorage* iface,
1065 const OLECHAR *pwcsName, /* [string][in] */
1066 DWORD grfMode, /* [in] */
1067 DWORD reserved1, /* [in] */
1068 DWORD reserved2, /* [in] */
1069 IStorage **ppstg) /* [out] */
1071 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1073 DirEntry currentEntry;
1074 DirEntry newEntry;
1075 DirRef currentEntryRef;
1076 DirRef newEntryRef;
1077 HRESULT hr;
1079 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1080 iface, debugstr_w(pwcsName), grfMode,
1081 reserved1, reserved2, ppstg);
1083 if (ppstg == 0)
1084 return STG_E_INVALIDPOINTER;
1086 if (This->openFlags & STGM_SIMPLE)
1088 return STG_E_INVALIDFUNCTION;
1091 if (pwcsName == 0)
1092 return STG_E_INVALIDNAME;
1094 *ppstg = NULL;
1096 if ( FAILED( validateSTGM(grfMode) ) ||
1097 (grfMode & STGM_DELETEONRELEASE) )
1099 WARN("bad grfMode: 0x%x\n", grfMode);
1100 return STG_E_INVALIDFLAG;
1103 if (This->reverted)
1104 return STG_E_REVERTED;
1107 * Check that we're compatible with the parent's storage mode
1109 if ( !(This->openFlags & STGM_TRANSACTED) &&
1110 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1112 WARN("access denied\n");
1113 return STG_E_ACCESSDENIED;
1116 currentEntryRef = findElement(This,
1117 This->storageDirEntry,
1118 pwcsName,
1119 &currentEntry);
1121 if (currentEntryRef != DIRENTRY_NULL)
1124 * An element with this name already exists
1126 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1127 ((This->openFlags & STGM_TRANSACTED) ||
1128 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1130 hr = IStorage_DestroyElement(iface, pwcsName);
1131 if (FAILED(hr))
1132 return hr;
1134 else
1136 WARN("file already exists\n");
1137 return STG_E_FILEALREADYEXISTS;
1140 else if (!(This->openFlags & STGM_TRANSACTED) &&
1141 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1143 WARN("read-only storage\n");
1144 return STG_E_ACCESSDENIED;
1147 memset(&newEntry, 0, sizeof(DirEntry));
1149 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1151 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1153 FIXME("name too long\n");
1154 return STG_E_INVALIDNAME;
1157 strcpyW(newEntry.name, pwcsName);
1159 newEntry.stgType = STGTY_STORAGE;
1160 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1161 newEntry.size.u.LowPart = 0;
1162 newEntry.size.u.HighPart = 0;
1164 newEntry.leftChild = DIRENTRY_NULL;
1165 newEntry.rightChild = DIRENTRY_NULL;
1166 newEntry.dirRootEntry = DIRENTRY_NULL;
1168 /* call CoFileTime to get the current time
1169 newEntry.ctime
1170 newEntry.mtime
1173 /* newEntry.clsid */
1176 * Create a new directory entry for the storage
1178 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1179 if (FAILED(hr))
1180 return hr;
1183 * Insert the new directory entry into the parent storage's tree
1185 hr = insertIntoTree(
1186 This,
1187 This->storageDirEntry,
1188 newEntryRef);
1189 if (FAILED(hr))
1191 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1192 return hr;
1196 * Open it to get a pointer to return.
1198 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1200 if( (hr != S_OK) || (*ppstg == NULL))
1202 return hr;
1206 return S_OK;
1210 /***************************************************************************
1212 * Internal Method
1214 * Reserve a directory entry in the file and initialize it.
1216 static HRESULT StorageImpl_CreateDirEntry(
1217 StorageBaseImpl *base,
1218 const DirEntry *newData,
1219 DirRef *index)
1221 StorageImpl *storage = (StorageImpl*)base;
1222 ULONG currentEntryIndex = 0;
1223 ULONG newEntryIndex = DIRENTRY_NULL;
1224 HRESULT hr = S_OK;
1225 BYTE currentData[RAW_DIRENTRY_SIZE];
1226 WORD sizeOfNameString;
1230 hr = StorageImpl_ReadRawDirEntry(storage,
1231 currentEntryIndex,
1232 currentData);
1234 if (SUCCEEDED(hr))
1236 StorageUtl_ReadWord(
1237 currentData,
1238 OFFSET_PS_NAMELENGTH,
1239 &sizeOfNameString);
1241 if (sizeOfNameString == 0)
1244 * The entry exists and is available, we found it.
1246 newEntryIndex = currentEntryIndex;
1249 else
1252 * We exhausted the directory entries, we will create more space below
1254 newEntryIndex = currentEntryIndex;
1256 currentEntryIndex++;
1258 } while (newEntryIndex == DIRENTRY_NULL);
1261 * grow the directory stream
1263 if (FAILED(hr))
1265 BYTE emptyData[RAW_DIRENTRY_SIZE];
1266 ULARGE_INTEGER newSize;
1267 ULONG entryIndex;
1268 ULONG lastEntry = 0;
1269 ULONG blockCount = 0;
1272 * obtain the new count of blocks in the directory stream
1274 blockCount = BlockChainStream_GetCount(
1275 storage->rootBlockChain)+1;
1278 * initialize the size used by the directory stream
1280 newSize.u.HighPart = 0;
1281 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1284 * add a block to the directory stream
1286 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1289 * memset the empty entry in order to initialize the unused newly
1290 * created entries
1292 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1295 * initialize them
1297 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1299 for(
1300 entryIndex = newEntryIndex + 1;
1301 entryIndex < lastEntry;
1302 entryIndex++)
1304 StorageImpl_WriteRawDirEntry(
1305 storage,
1306 entryIndex,
1307 emptyData);
1311 UpdateRawDirEntry(currentData, newData);
1313 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1315 if (SUCCEEDED(hr))
1316 *index = newEntryIndex;
1318 return hr;
1321 /***************************************************************************
1323 * Internal Method
1325 * Mark a directory entry in the file as free.
1327 static HRESULT StorageImpl_DestroyDirEntry(
1328 StorageBaseImpl *base,
1329 DirRef index)
1331 HRESULT hr;
1332 BYTE emptyData[RAW_DIRENTRY_SIZE];
1333 StorageImpl *storage = (StorageImpl*)base;
1335 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1337 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1339 return hr;
1343 /****************************************************************************
1345 * Internal Method
1347 * Case insensitive comparison of DirEntry.name by first considering
1348 * their size.
1350 * Returns <0 when name1 < name2
1351 * >0 when name1 > name2
1352 * 0 when name1 == name2
1354 static LONG entryNameCmp(
1355 const OLECHAR *name1,
1356 const OLECHAR *name2)
1358 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1360 while (diff == 0 && *name1 != 0)
1363 * We compare the string themselves only when they are of the same length
1365 diff = toupperW(*name1++) - toupperW(*name2++);
1368 return diff;
1371 /****************************************************************************
1373 * Internal Method
1375 * Add a directory entry to a storage
1377 static HRESULT insertIntoTree(
1378 StorageBaseImpl *This,
1379 DirRef parentStorageIndex,
1380 DirRef newEntryIndex)
1382 DirEntry currentEntry;
1383 DirEntry newEntry;
1386 * Read the inserted entry
1388 StorageBaseImpl_ReadDirEntry(This,
1389 newEntryIndex,
1390 &newEntry);
1393 * Read the storage entry
1395 StorageBaseImpl_ReadDirEntry(This,
1396 parentStorageIndex,
1397 &currentEntry);
1399 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1402 * The root storage contains some element, therefore, start the research
1403 * for the appropriate location.
1405 BOOL found = 0;
1406 DirRef current, next, previous, currentEntryId;
1409 * Keep a reference to the root of the storage's element tree
1411 currentEntryId = currentEntry.dirRootEntry;
1414 * Read
1416 StorageBaseImpl_ReadDirEntry(This,
1417 currentEntry.dirRootEntry,
1418 &currentEntry);
1420 previous = currentEntry.leftChild;
1421 next = currentEntry.rightChild;
1422 current = currentEntryId;
1424 while (found == 0)
1426 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1428 if (diff < 0)
1430 if (previous != DIRENTRY_NULL)
1432 StorageBaseImpl_ReadDirEntry(This,
1433 previous,
1434 &currentEntry);
1435 current = previous;
1437 else
1439 currentEntry.leftChild = newEntryIndex;
1440 StorageBaseImpl_WriteDirEntry(This,
1441 current,
1442 &currentEntry);
1443 found = 1;
1446 else if (diff > 0)
1448 if (next != DIRENTRY_NULL)
1450 StorageBaseImpl_ReadDirEntry(This,
1451 next,
1452 &currentEntry);
1453 current = next;
1455 else
1457 currentEntry.rightChild = newEntryIndex;
1458 StorageBaseImpl_WriteDirEntry(This,
1459 current,
1460 &currentEntry);
1461 found = 1;
1464 else
1467 * Trying to insert an item with the same name in the
1468 * subtree structure.
1470 return STG_E_FILEALREADYEXISTS;
1473 previous = currentEntry.leftChild;
1474 next = currentEntry.rightChild;
1477 else
1480 * The storage is empty, make the new entry the root of its element tree
1482 currentEntry.dirRootEntry = newEntryIndex;
1483 StorageBaseImpl_WriteDirEntry(This,
1484 parentStorageIndex,
1485 &currentEntry);
1488 return S_OK;
1491 /****************************************************************************
1493 * Internal Method
1495 * Find and read the element of a storage with the given name.
1497 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1498 const OLECHAR *name, DirEntry *data)
1500 DirRef currentEntry;
1502 /* Read the storage entry to find the root of the tree. */
1503 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1505 currentEntry = data->dirRootEntry;
1507 while (currentEntry != DIRENTRY_NULL)
1509 LONG cmp;
1511 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1513 cmp = entryNameCmp(name, data->name);
1515 if (cmp == 0)
1516 /* found it */
1517 break;
1519 else if (cmp < 0)
1520 currentEntry = data->leftChild;
1522 else if (cmp > 0)
1523 currentEntry = data->rightChild;
1526 return currentEntry;
1529 /****************************************************************************
1531 * Internal Method
1533 * Find and read the binary tree parent of the element with the given name.
1535 * If there is no such element, find a place where it could be inserted and
1536 * return STG_E_FILENOTFOUND.
1538 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1539 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1540 ULONG *relation)
1542 DirRef childEntry;
1543 DirEntry childData;
1545 /* Read the storage entry to find the root of the tree. */
1546 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1548 *parentEntry = storageEntry;
1549 *relation = DIRENTRY_RELATION_DIR;
1551 childEntry = parentData->dirRootEntry;
1553 while (childEntry != DIRENTRY_NULL)
1555 LONG cmp;
1557 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1559 cmp = entryNameCmp(childName, childData.name);
1561 if (cmp == 0)
1562 /* found it */
1563 break;
1565 else if (cmp < 0)
1567 *parentData = childData;
1568 *parentEntry = childEntry;
1569 *relation = DIRENTRY_RELATION_PREVIOUS;
1571 childEntry = parentData->leftChild;
1574 else if (cmp > 0)
1576 *parentData = childData;
1577 *parentEntry = childEntry;
1578 *relation = DIRENTRY_RELATION_NEXT;
1580 childEntry = parentData->rightChild;
1584 if (childEntry == DIRENTRY_NULL)
1585 return STG_E_FILENOTFOUND;
1586 else
1587 return S_OK;
1591 /*************************************************************************
1592 * CopyTo (IStorage)
1594 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1595 IStorage* iface,
1596 DWORD ciidExclude, /* [in] */
1597 const IID* rgiidExclude, /* [size_is][unique][in] */
1598 SNB snbExclude, /* [unique][in] */
1599 IStorage* pstgDest) /* [unique][in] */
1601 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1603 IEnumSTATSTG *elements = 0;
1604 STATSTG curElement, strStat;
1605 HRESULT hr;
1606 IStorage *pstgTmp, *pstgChild;
1607 IStream *pstrTmp, *pstrChild;
1608 DirRef srcEntryRef;
1609 DirEntry srcEntry;
1610 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1611 int i;
1613 TRACE("(%p, %d, %p, %p, %p)\n",
1614 iface, ciidExclude, rgiidExclude,
1615 snbExclude, pstgDest);
1617 if ( pstgDest == 0 )
1618 return STG_E_INVALIDPOINTER;
1621 * Enumerate the elements
1623 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1625 if ( hr != S_OK )
1626 return hr;
1629 * set the class ID
1631 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1632 IStorage_SetClass( pstgDest, &curElement.clsid );
1634 for(i = 0; i < ciidExclude; ++i)
1636 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1637 skip_storage = TRUE;
1638 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1639 skip_stream = TRUE;
1640 else
1641 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1647 * Obtain the next element
1649 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1651 if ( hr == S_FALSE )
1653 hr = S_OK; /* done, every element has been copied */
1654 break;
1657 if ( snbExclude )
1659 WCHAR **snb = snbExclude;
1660 skip = FALSE;
1661 while ( *snb != NULL && !skip )
1663 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1664 skip = TRUE;
1665 ++snb;
1669 if ( skip )
1670 goto cleanup;
1672 if (curElement.type == STGTY_STORAGE)
1674 if(skip_storage)
1675 goto cleanup;
1678 * open child source storage
1680 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1681 STGM_READ|STGM_SHARE_EXCLUSIVE,
1682 NULL, 0, &pstgChild );
1684 if (hr != S_OK)
1685 goto cleanup;
1688 * create a new storage in destination storage
1690 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1691 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1692 0, 0,
1693 &pstgTmp );
1695 * if it already exist, don't create a new one use this one
1697 if (hr == STG_E_FILEALREADYEXISTS)
1699 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1700 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1701 NULL, 0, &pstgTmp );
1704 if (hr == S_OK)
1707 * do the copy recursively
1709 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1710 NULL, pstgTmp );
1712 IStorage_Release( pstgTmp );
1715 IStorage_Release( pstgChild );
1717 else if (curElement.type == STGTY_STREAM)
1719 if(skip_stream)
1720 goto cleanup;
1723 * create a new stream in destination storage. If the stream already
1724 * exist, it will be deleted and a new one will be created.
1726 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1727 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1728 0, 0, &pstrTmp );
1730 if (hr != S_OK)
1731 goto cleanup;
1734 * open child stream storage. This operation must succeed even if the
1735 * stream is already open, so we use internal functions to do it.
1737 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1738 &srcEntry);
1739 if (!srcEntryRef)
1741 ERR("source stream not found\n");
1742 hr = STG_E_DOCFILECORRUPT;
1745 if (hr == S_OK)
1747 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1748 if (pstrChild)
1749 IStream_AddRef(pstrChild);
1750 else
1751 hr = E_OUTOFMEMORY;
1754 if (hr == S_OK)
1757 * Get the size of the source stream
1759 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1762 * Set the size of the destination stream.
1764 IStream_SetSize(pstrTmp, strStat.cbSize);
1767 * do the copy
1769 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1770 NULL, NULL );
1772 IStream_Release( pstrChild );
1775 IStream_Release( pstrTmp );
1777 else
1779 WARN("unknown element type: %d\n", curElement.type);
1782 cleanup:
1783 CoTaskMemFree(curElement.pwcsName);
1784 } while (hr == S_OK);
1787 * Clean-up
1789 IEnumSTATSTG_Release(elements);
1791 return hr;
1794 /*************************************************************************
1795 * MoveElementTo (IStorage)
1797 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1798 IStorage* iface,
1799 const OLECHAR *pwcsName, /* [string][in] */
1800 IStorage *pstgDest, /* [unique][in] */
1801 const OLECHAR *pwcsNewName,/* [string][in] */
1802 DWORD grfFlags) /* [in] */
1804 FIXME("(%p %s %p %s %u): stub\n", iface,
1805 debugstr_w(pwcsName), pstgDest,
1806 debugstr_w(pwcsNewName), grfFlags);
1807 return E_NOTIMPL;
1810 /*************************************************************************
1811 * Commit (IStorage)
1813 * Ensures that any changes made to a storage object open in transacted mode
1814 * are reflected in the parent storage
1816 * NOTES
1817 * Wine doesn't implement transacted mode, which seems to be a basic
1818 * optimization, so we can ignore this stub for now.
1820 static HRESULT WINAPI StorageImpl_Commit(
1821 IStorage* iface,
1822 DWORD grfCommitFlags)/* [in] */
1824 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1825 return S_OK;
1828 /*************************************************************************
1829 * Revert (IStorage)
1831 * Discard all changes that have been made since the last commit operation
1833 static HRESULT WINAPI StorageImpl_Revert(
1834 IStorage* iface)
1836 TRACE("(%p)\n", iface);
1837 return S_OK;
1840 /*************************************************************************
1841 * DestroyElement (IStorage)
1843 * Strategy: This implementation is built this way for simplicity not for speed.
1844 * I always delete the topmost element of the enumeration and adjust
1845 * the deleted element pointer all the time. This takes longer to
1846 * do but allow to reinvoke DestroyElement whenever we encounter a
1847 * storage object. The optimisation resides in the usage of another
1848 * enumeration strategy that would give all the leaves of a storage
1849 * first. (postfix order)
1851 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1852 IStorage* iface,
1853 const OLECHAR *pwcsName)/* [string][in] */
1855 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1857 HRESULT hr = S_OK;
1858 DirEntry entryToDelete;
1859 DirRef entryToDeleteRef;
1861 TRACE("(%p, %s)\n",
1862 iface, debugstr_w(pwcsName));
1864 if (pwcsName==NULL)
1865 return STG_E_INVALIDPOINTER;
1867 if (This->reverted)
1868 return STG_E_REVERTED;
1870 if ( !(This->openFlags & STGM_TRANSACTED) &&
1871 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1872 return STG_E_ACCESSDENIED;
1874 entryToDeleteRef = findElement(
1875 This,
1876 This->storageDirEntry,
1877 pwcsName,
1878 &entryToDelete);
1880 if ( entryToDeleteRef == DIRENTRY_NULL )
1882 return STG_E_FILENOTFOUND;
1885 if ( entryToDelete.stgType == STGTY_STORAGE )
1887 hr = deleteStorageContents(
1888 This,
1889 entryToDeleteRef,
1890 entryToDelete);
1892 else if ( entryToDelete.stgType == STGTY_STREAM )
1894 hr = deleteStreamContents(
1895 This,
1896 entryToDeleteRef,
1897 entryToDelete);
1900 if (hr!=S_OK)
1901 return hr;
1904 * Remove the entry from its parent storage
1906 hr = removeFromTree(
1907 This,
1908 This->storageDirEntry,
1909 entryToDeleteRef);
1912 * Invalidate the entry
1914 if (SUCCEEDED(hr))
1915 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1917 return hr;
1921 /******************************************************************************
1922 * Internal stream list handlers
1925 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1927 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1928 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1931 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1933 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1934 list_remove(&(strm->StrmListEntry));
1937 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1939 StgStreamImpl *strm;
1941 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1943 if (strm->dirEntry == streamEntry)
1945 return TRUE;
1949 return FALSE;
1952 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1954 StorageInternalImpl *childstg;
1956 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1958 if (childstg->base.storageDirEntry == storageEntry)
1960 return TRUE;
1964 return FALSE;
1967 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1969 struct list *cur, *cur2;
1970 StgStreamImpl *strm=NULL;
1971 StorageInternalImpl *childstg=NULL;
1973 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1974 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1975 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1976 strm->parentStorage = NULL;
1977 list_remove(cur);
1980 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1981 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1982 StorageBaseImpl_Invalidate( &childstg->base );
1985 if (stg->transactedChild)
1987 StorageBaseImpl_Invalidate(stg->transactedChild);
1989 stg->transactedChild = NULL;
1994 /*********************************************************************
1996 * Internal Method
1998 * Delete the contents of a storage entry.
2001 static HRESULT deleteStorageContents(
2002 StorageBaseImpl *parentStorage,
2003 DirRef indexToDelete,
2004 DirEntry entryDataToDelete)
2006 IEnumSTATSTG *elements = 0;
2007 IStorage *childStorage = 0;
2008 STATSTG currentElement;
2009 HRESULT hr;
2010 HRESULT destroyHr = S_OK;
2011 StorageInternalImpl *stg, *stg2;
2013 /* Invalidate any open storage objects. */
2014 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2016 if (stg->base.storageDirEntry == indexToDelete)
2018 StorageBaseImpl_Invalidate(&stg->base);
2023 * Open the storage and enumerate it
2025 hr = StorageBaseImpl_OpenStorage(
2026 (IStorage*)parentStorage,
2027 entryDataToDelete.name,
2029 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2032 &childStorage);
2034 if (hr != S_OK)
2036 return hr;
2040 * Enumerate the elements
2042 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2047 * Obtain the next element
2049 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2050 if (hr==S_OK)
2052 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2054 CoTaskMemFree(currentElement.pwcsName);
2058 * We need to Reset the enumeration every time because we delete elements
2059 * and the enumeration could be invalid
2061 IEnumSTATSTG_Reset(elements);
2063 } while ((hr == S_OK) && (destroyHr == S_OK));
2065 IStorage_Release(childStorage);
2066 IEnumSTATSTG_Release(elements);
2068 return destroyHr;
2071 /*********************************************************************
2073 * Internal Method
2075 * Perform the deletion of a stream's data
2078 static HRESULT deleteStreamContents(
2079 StorageBaseImpl *parentStorage,
2080 DirRef indexToDelete,
2081 DirEntry entryDataToDelete)
2083 IStream *pis;
2084 HRESULT hr;
2085 ULARGE_INTEGER size;
2086 StgStreamImpl *strm, *strm2;
2088 /* Invalidate any open stream objects. */
2089 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2091 if (strm->dirEntry == indexToDelete)
2093 TRACE("Stream deleted %p\n", strm);
2094 strm->parentStorage = NULL;
2095 list_remove(&strm->StrmListEntry);
2099 size.u.HighPart = 0;
2100 size.u.LowPart = 0;
2102 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2103 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2105 if (hr!=S_OK)
2107 return(hr);
2111 * Zap the stream
2113 hr = IStream_SetSize(pis, size);
2115 if(hr != S_OK)
2117 return hr;
2121 * Release the stream object.
2123 IStream_Release(pis);
2125 return S_OK;
2128 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2130 switch (relation)
2132 case DIRENTRY_RELATION_PREVIOUS:
2133 entry->leftChild = new_target;
2134 break;
2135 case DIRENTRY_RELATION_NEXT:
2136 entry->rightChild = new_target;
2137 break;
2138 case DIRENTRY_RELATION_DIR:
2139 entry->dirRootEntry = new_target;
2140 break;
2141 default:
2142 assert(0);
2146 /*************************************************************************
2148 * Internal Method
2150 * This method removes a directory entry from its parent storage tree without
2151 * freeing any resources attached to it.
2153 static HRESULT removeFromTree(
2154 StorageBaseImpl *This,
2155 DirRef parentStorageIndex,
2156 DirRef deletedIndex)
2158 HRESULT hr = S_OK;
2159 DirEntry entryToDelete;
2160 DirEntry parentEntry;
2161 DirRef parentEntryRef;
2162 ULONG typeOfRelation;
2164 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2166 if (hr != S_OK)
2167 return hr;
2170 * Find the element that links to the one we want to delete.
2172 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2173 &parentEntry, &parentEntryRef, &typeOfRelation);
2175 if (hr != S_OK)
2176 return hr;
2178 if (entryToDelete.leftChild != DIRENTRY_NULL)
2181 * Replace the deleted entry with its left child
2183 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2185 hr = StorageBaseImpl_WriteDirEntry(
2186 This,
2187 parentEntryRef,
2188 &parentEntry);
2189 if(FAILED(hr))
2191 return hr;
2194 if (entryToDelete.rightChild != DIRENTRY_NULL)
2197 * We need to reinsert the right child somewhere. We already know it and
2198 * its children are greater than everything in the left tree, so we
2199 * insert it at the rightmost point in the left tree.
2201 DirRef newRightChildParent = entryToDelete.leftChild;
2202 DirEntry newRightChildParentEntry;
2206 hr = StorageBaseImpl_ReadDirEntry(
2207 This,
2208 newRightChildParent,
2209 &newRightChildParentEntry);
2210 if (FAILED(hr))
2212 return hr;
2215 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2216 newRightChildParent = newRightChildParentEntry.rightChild;
2217 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2219 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2221 hr = StorageBaseImpl_WriteDirEntry(
2222 This,
2223 newRightChildParent,
2224 &newRightChildParentEntry);
2225 if (FAILED(hr))
2227 return hr;
2231 else
2234 * Replace the deleted entry with its right child
2236 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2238 hr = StorageBaseImpl_WriteDirEntry(
2239 This,
2240 parentEntryRef,
2241 &parentEntry);
2242 if(FAILED(hr))
2244 return hr;
2248 return hr;
2252 /******************************************************************************
2253 * SetElementTimes (IStorage)
2255 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2256 IStorage* iface,
2257 const OLECHAR *pwcsName,/* [string][in] */
2258 const FILETIME *pctime, /* [in] */
2259 const FILETIME *patime, /* [in] */
2260 const FILETIME *pmtime) /* [in] */
2262 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2263 return S_OK;
2266 /******************************************************************************
2267 * SetStateBits (IStorage)
2269 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2270 IStorage* iface,
2271 DWORD grfStateBits,/* [in] */
2272 DWORD grfMask) /* [in] */
2274 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2276 if (This->reverted)
2277 return STG_E_REVERTED;
2279 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2280 return S_OK;
2283 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2284 DirRef index, const DirEntry *data)
2286 StorageImpl *This = (StorageImpl*)base;
2287 return StorageImpl_WriteDirEntry(This, index, data);
2290 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2291 DirRef index, DirEntry *data)
2293 StorageImpl *This = (StorageImpl*)base;
2294 return StorageImpl_ReadDirEntry(This, index, data);
2297 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2299 int i;
2301 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2303 if (!This->blockChainCache[i])
2305 return &This->blockChainCache[i];
2309 i = This->blockChainToEvict;
2311 BlockChainStream_Destroy(This->blockChainCache[i]);
2312 This->blockChainCache[i] = NULL;
2314 This->blockChainToEvict++;
2315 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2316 This->blockChainToEvict = 0;
2318 return &This->blockChainCache[i];
2321 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2322 DirRef index)
2324 int i, free_index=-1;
2326 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2328 if (!This->blockChainCache[i])
2330 if (free_index == -1) free_index = i;
2332 else if (This->blockChainCache[i]->ownerDirEntry == index)
2334 return &This->blockChainCache[i];
2338 if (free_index == -1)
2340 free_index = This->blockChainToEvict;
2342 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2343 This->blockChainCache[free_index] = NULL;
2345 This->blockChainToEvict++;
2346 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2347 This->blockChainToEvict = 0;
2350 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2351 return &This->blockChainCache[free_index];
2354 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2355 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2357 StorageImpl *This = (StorageImpl*)base;
2358 DirEntry data;
2359 HRESULT hr;
2360 ULONG bytesToRead;
2362 hr = StorageImpl_ReadDirEntry(This, index, &data);
2363 if (FAILED(hr)) return hr;
2365 if (data.size.QuadPart == 0)
2367 *bytesRead = 0;
2368 return S_OK;
2371 if (offset.QuadPart + size > data.size.QuadPart)
2373 bytesToRead = data.size.QuadPart - offset.QuadPart;
2375 else
2377 bytesToRead = size;
2380 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2382 SmallBlockChainStream *stream;
2384 stream = SmallBlockChainStream_Construct(This, NULL, index);
2385 if (!stream) return E_OUTOFMEMORY;
2387 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2389 SmallBlockChainStream_Destroy(stream);
2391 return hr;
2393 else
2395 BlockChainStream *stream = NULL;
2397 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2398 if (!stream) return E_OUTOFMEMORY;
2400 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2402 return hr;
2406 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2407 ULARGE_INTEGER newsize)
2409 StorageImpl *This = (StorageImpl*)base;
2410 DirEntry data;
2411 HRESULT hr;
2412 SmallBlockChainStream *smallblock=NULL;
2413 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2415 hr = StorageImpl_ReadDirEntry(This, index, &data);
2416 if (FAILED(hr)) return hr;
2418 /* In simple mode keep the stream size above the small block limit */
2419 if (This->base.openFlags & STGM_SIMPLE)
2420 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2422 if (data.size.QuadPart == newsize.QuadPart)
2423 return S_OK;
2425 /* Create a block chain object of the appropriate type */
2426 if (data.size.QuadPart == 0)
2428 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2430 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2431 if (!smallblock) return E_OUTOFMEMORY;
2433 else
2435 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2436 bigblock = *pbigblock;
2437 if (!bigblock) return E_OUTOFMEMORY;
2440 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2442 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2443 if (!smallblock) return E_OUTOFMEMORY;
2445 else
2447 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2448 bigblock = *pbigblock;
2449 if (!bigblock) return E_OUTOFMEMORY;
2452 /* Change the block chain type if necessary. */
2453 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2455 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2456 if (!bigblock)
2458 SmallBlockChainStream_Destroy(smallblock);
2459 return E_FAIL;
2462 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2463 *pbigblock = bigblock;
2465 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2467 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2468 if (!smallblock)
2469 return E_FAIL;
2472 /* Set the size of the block chain. */
2473 if (smallblock)
2475 SmallBlockChainStream_SetSize(smallblock, newsize);
2476 SmallBlockChainStream_Destroy(smallblock);
2478 else
2480 BlockChainStream_SetSize(bigblock, newsize);
2483 /* Set the size in the directory entry. */
2484 hr = StorageImpl_ReadDirEntry(This, index, &data);
2485 if (SUCCEEDED(hr))
2487 data.size = newsize;
2489 hr = StorageImpl_WriteDirEntry(This, index, &data);
2491 return hr;
2494 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2495 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2497 StorageImpl *This = (StorageImpl*)base;
2498 DirEntry data;
2499 HRESULT hr;
2500 ULARGE_INTEGER newSize;
2502 hr = StorageImpl_ReadDirEntry(This, index, &data);
2503 if (FAILED(hr)) return hr;
2505 /* Grow the stream if necessary */
2506 newSize.QuadPart = 0;
2507 newSize.QuadPart = offset.QuadPart + size;
2509 if (newSize.QuadPart > data.size.QuadPart)
2511 hr = StorageImpl_StreamSetSize(base, index, newSize);
2512 if (FAILED(hr))
2513 return hr;
2515 hr = StorageImpl_ReadDirEntry(This, index, &data);
2516 if (FAILED(hr)) return hr;
2519 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2521 SmallBlockChainStream *stream;
2523 stream = SmallBlockChainStream_Construct(This, NULL, index);
2524 if (!stream) return E_OUTOFMEMORY;
2526 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2528 SmallBlockChainStream_Destroy(stream);
2530 return hr;
2532 else
2534 BlockChainStream *stream;
2536 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2537 if (!stream) return E_OUTOFMEMORY;
2539 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2541 return hr;
2546 * Virtual function table for the IStorage32Impl class.
2548 static const IStorageVtbl Storage32Impl_Vtbl =
2550 StorageBaseImpl_QueryInterface,
2551 StorageBaseImpl_AddRef,
2552 StorageBaseImpl_Release,
2553 StorageBaseImpl_CreateStream,
2554 StorageBaseImpl_OpenStream,
2555 StorageBaseImpl_CreateStorage,
2556 StorageBaseImpl_OpenStorage,
2557 StorageBaseImpl_CopyTo,
2558 StorageBaseImpl_MoveElementTo,
2559 StorageImpl_Commit,
2560 StorageImpl_Revert,
2561 StorageBaseImpl_EnumElements,
2562 StorageBaseImpl_DestroyElement,
2563 StorageBaseImpl_RenameElement,
2564 StorageBaseImpl_SetElementTimes,
2565 StorageBaseImpl_SetClass,
2566 StorageBaseImpl_SetStateBits,
2567 StorageBaseImpl_Stat
2570 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2572 StorageImpl_Destroy,
2573 StorageImpl_Invalidate,
2574 StorageImpl_CreateDirEntry,
2575 StorageImpl_BaseWriteDirEntry,
2576 StorageImpl_BaseReadDirEntry,
2577 StorageImpl_DestroyDirEntry,
2578 StorageImpl_StreamReadAt,
2579 StorageImpl_StreamWriteAt,
2580 StorageImpl_StreamSetSize
2583 static HRESULT StorageImpl_Construct(
2584 HANDLE hFile,
2585 LPCOLESTR pwcsName,
2586 ILockBytes* pLkbyt,
2587 DWORD openFlags,
2588 BOOL fileBased,
2589 BOOL create,
2590 ULONG sector_size,
2591 StorageImpl** result)
2593 StorageImpl* This;
2594 HRESULT hr = S_OK;
2595 DirEntry currentEntry;
2596 DirRef currentEntryRef;
2597 WCHAR fullpath[MAX_PATH];
2599 if ( FAILED( validateSTGM(openFlags) ))
2600 return STG_E_INVALIDFLAG;
2602 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2603 if (!This)
2604 return E_OUTOFMEMORY;
2606 memset(This, 0, sizeof(StorageImpl));
2608 list_init(&This->base.strmHead);
2610 list_init(&This->base.storageHead);
2612 This->base.lpVtbl = &Storage32Impl_Vtbl;
2613 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2614 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2615 This->base.openFlags = (openFlags & ~STGM_CREATE);
2616 This->base.ref = 1;
2617 This->base.create = create;
2619 This->base.reverted = 0;
2621 This->hFile = hFile;
2623 if(pwcsName) {
2624 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2626 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2628 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2629 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2630 if (!This->pwcsName)
2632 hr = STG_E_INSUFFICIENTMEMORY;
2633 goto end;
2635 strcpyW(This->pwcsName, fullpath);
2636 This->base.filename = This->pwcsName;
2640 * Initialize the big block cache.
2642 This->bigBlockSize = sector_size;
2643 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2644 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2645 pLkbyt,
2646 openFlags,
2647 fileBased);
2649 if (This->bigBlockFile == 0)
2651 hr = E_FAIL;
2652 goto end;
2655 if (create)
2657 ULARGE_INTEGER size;
2658 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2661 * Initialize all header variables:
2662 * - The big block depot consists of one block and it is at block 0
2663 * - The directory table starts at block 1
2664 * - There is no small block depot
2666 memset( This->bigBlockDepotStart,
2667 BLOCK_UNUSED,
2668 sizeof(This->bigBlockDepotStart));
2670 This->bigBlockDepotCount = 1;
2671 This->bigBlockDepotStart[0] = 0;
2672 This->rootStartBlock = 1;
2673 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2674 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2675 if (sector_size == 4096)
2676 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2677 else
2678 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2679 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2680 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2681 This->extBigBlockDepotCount = 0;
2683 StorageImpl_SaveFileHeader(This);
2686 * Add one block for the big block depot and one block for the directory table
2688 size.u.HighPart = 0;
2689 size.u.LowPart = This->bigBlockSize * 3;
2690 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2693 * Initialize the big block depot
2695 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2696 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2697 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2698 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2700 else
2703 * Load the header for the file.
2705 hr = StorageImpl_LoadFileHeader(This);
2707 if (FAILED(hr))
2709 goto end;
2714 * There is no block depot cached yet.
2716 This->indexBlockDepotCached = 0xFFFFFFFF;
2719 * Start searching for free blocks with block 0.
2721 This->prevFreeBlock = 0;
2723 This->firstFreeSmallBlock = 0;
2726 * Create the block chain abstractions.
2728 if(!(This->rootBlockChain =
2729 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2731 hr = STG_E_READFAULT;
2732 goto end;
2735 if(!(This->smallBlockDepotChain =
2736 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2737 DIRENTRY_NULL)))
2739 hr = STG_E_READFAULT;
2740 goto end;
2744 * Write the root storage entry (memory only)
2746 if (create)
2748 DirEntry rootEntry;
2750 * Initialize the directory table
2752 memset(&rootEntry, 0, sizeof(rootEntry));
2753 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2754 sizeof(rootEntry.name)/sizeof(WCHAR) );
2755 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2756 rootEntry.stgType = STGTY_ROOT;
2757 rootEntry.leftChild = DIRENTRY_NULL;
2758 rootEntry.rightChild = DIRENTRY_NULL;
2759 rootEntry.dirRootEntry = DIRENTRY_NULL;
2760 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2761 rootEntry.size.u.HighPart = 0;
2762 rootEntry.size.u.LowPart = 0;
2764 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2768 * Find the ID of the root storage.
2770 currentEntryRef = 0;
2774 hr = StorageImpl_ReadDirEntry(
2775 This,
2776 currentEntryRef,
2777 &currentEntry);
2779 if (SUCCEEDED(hr))
2781 if ( (currentEntry.sizeOfNameString != 0 ) &&
2782 (currentEntry.stgType == STGTY_ROOT) )
2784 This->base.storageDirEntry = currentEntryRef;
2788 currentEntryRef++;
2790 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2792 if (FAILED(hr))
2794 hr = STG_E_READFAULT;
2795 goto end;
2799 * Create the block chain abstraction for the small block root chain.
2801 if(!(This->smallBlockRootChain =
2802 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2804 hr = STG_E_READFAULT;
2807 end:
2808 if (FAILED(hr))
2810 IStorage_Release((IStorage*)This);
2811 *result = NULL;
2813 else
2814 *result = This;
2816 return hr;
2819 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2821 StorageImpl *This = (StorageImpl*) iface;
2823 StorageBaseImpl_DeleteAll(&This->base);
2825 This->base.reverted = 1;
2828 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2830 StorageImpl *This = (StorageImpl*) iface;
2831 int i;
2832 TRACE("(%p)\n", This);
2834 StorageImpl_Invalidate(iface);
2836 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2838 BlockChainStream_Destroy(This->smallBlockRootChain);
2839 BlockChainStream_Destroy(This->rootBlockChain);
2840 BlockChainStream_Destroy(This->smallBlockDepotChain);
2842 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2843 BlockChainStream_Destroy(This->blockChainCache[i]);
2845 if (This->bigBlockFile)
2846 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2847 HeapFree(GetProcessHeap(), 0, This);
2850 /******************************************************************************
2851 * Storage32Impl_GetNextFreeBigBlock
2853 * Returns the index of the next free big block.
2854 * If the big block depot is filled, this method will enlarge it.
2857 static ULONG StorageImpl_GetNextFreeBigBlock(
2858 StorageImpl* This)
2860 ULONG depotBlockIndexPos;
2861 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2862 BOOL success;
2863 ULONG depotBlockOffset;
2864 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2865 ULONG nextBlockIndex = BLOCK_SPECIAL;
2866 int depotIndex = 0;
2867 ULONG freeBlock = BLOCK_UNUSED;
2868 ULARGE_INTEGER neededSize;
2870 depotIndex = This->prevFreeBlock / blocksPerDepot;
2871 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2874 * Scan the entire big block depot until we find a block marked free
2876 while (nextBlockIndex != BLOCK_UNUSED)
2878 if (depotIndex < COUNT_BBDEPOTINHEADER)
2880 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2883 * Grow the primary depot.
2885 if (depotBlockIndexPos == BLOCK_UNUSED)
2887 depotBlockIndexPos = depotIndex*blocksPerDepot;
2890 * Add a block depot.
2892 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2893 This->bigBlockDepotCount++;
2894 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2897 * Flag it as a block depot.
2899 StorageImpl_SetNextBlockInChain(This,
2900 depotBlockIndexPos,
2901 BLOCK_SPECIAL);
2903 /* Save new header information.
2905 StorageImpl_SaveFileHeader(This);
2908 else
2910 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2912 if (depotBlockIndexPos == BLOCK_UNUSED)
2915 * Grow the extended depot.
2917 ULONG extIndex = BLOCK_UNUSED;
2918 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2919 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2921 if (extBlockOffset == 0)
2923 /* We need an extended block.
2925 extIndex = Storage32Impl_AddExtBlockDepot(This);
2926 This->extBigBlockDepotCount++;
2927 depotBlockIndexPos = extIndex + 1;
2929 else
2930 depotBlockIndexPos = depotIndex * blocksPerDepot;
2933 * Add a block depot and mark it in the extended block.
2935 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2936 This->bigBlockDepotCount++;
2937 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2939 /* Flag the block depot.
2941 StorageImpl_SetNextBlockInChain(This,
2942 depotBlockIndexPos,
2943 BLOCK_SPECIAL);
2945 /* If necessary, flag the extended depot block.
2947 if (extIndex != BLOCK_UNUSED)
2948 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2950 /* Save header information.
2952 StorageImpl_SaveFileHeader(This);
2956 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2958 if (success)
2960 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2961 ( nextBlockIndex != BLOCK_UNUSED))
2963 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2965 if (nextBlockIndex == BLOCK_UNUSED)
2967 freeBlock = (depotIndex * blocksPerDepot) +
2968 (depotBlockOffset/sizeof(ULONG));
2971 depotBlockOffset += sizeof(ULONG);
2975 depotIndex++;
2976 depotBlockOffset = 0;
2980 * make sure that the block physically exists before using it
2982 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
2983 BIGBLOCKFILE_Expand(This->bigBlockFile, neededSize);
2985 This->prevFreeBlock = freeBlock;
2987 return freeBlock;
2990 /******************************************************************************
2991 * Storage32Impl_AddBlockDepot
2993 * This will create a depot block, essentially it is a block initialized
2994 * to BLOCK_UNUSEDs.
2996 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2998 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3001 * Initialize blocks as free
3003 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3004 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3007 /******************************************************************************
3008 * Storage32Impl_GetExtDepotBlock
3010 * Returns the index of the block that corresponds to the specified depot
3011 * index. This method is only for depot indexes equal or greater than
3012 * COUNT_BBDEPOTINHEADER.
3014 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3016 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3017 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3018 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3019 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3020 ULONG blockIndex = BLOCK_UNUSED;
3021 ULONG extBlockIndex = This->extBigBlockDepotStart;
3023 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3025 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3026 return BLOCK_UNUSED;
3028 while (extBlockCount > 0)
3030 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3031 extBlockCount--;
3034 if (extBlockIndex != BLOCK_UNUSED)
3035 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3036 extBlockOffset * sizeof(ULONG), &blockIndex);
3038 return blockIndex;
3041 /******************************************************************************
3042 * Storage32Impl_SetExtDepotBlock
3044 * Associates the specified block index to the specified depot index.
3045 * This method is only for depot indexes equal or greater than
3046 * COUNT_BBDEPOTINHEADER.
3048 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3050 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3051 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3052 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3053 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3054 ULONG extBlockIndex = This->extBigBlockDepotStart;
3056 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3058 while (extBlockCount > 0)
3060 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3061 extBlockCount--;
3064 if (extBlockIndex != BLOCK_UNUSED)
3066 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3067 extBlockOffset * sizeof(ULONG),
3068 blockIndex);
3072 /******************************************************************************
3073 * Storage32Impl_AddExtBlockDepot
3075 * Creates an extended depot block.
3077 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3079 ULONG numExtBlocks = This->extBigBlockDepotCount;
3080 ULONG nextExtBlock = This->extBigBlockDepotStart;
3081 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3082 ULONG index = BLOCK_UNUSED;
3083 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3084 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3085 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3087 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3088 blocksPerDepotBlock;
3090 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3093 * The first extended block.
3095 This->extBigBlockDepotStart = index;
3097 else
3099 unsigned int i;
3101 * Follow the chain to the last one.
3103 for (i = 0; i < (numExtBlocks - 1); i++)
3105 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3109 * Add the new extended block to the chain.
3111 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3112 index);
3116 * Initialize this block.
3118 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3119 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3121 return index;
3124 /******************************************************************************
3125 * Storage32Impl_FreeBigBlock
3127 * This method will flag the specified block as free in the big block depot.
3129 static void StorageImpl_FreeBigBlock(
3130 StorageImpl* This,
3131 ULONG blockIndex)
3133 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3135 if (blockIndex < This->prevFreeBlock)
3136 This->prevFreeBlock = blockIndex;
3139 /************************************************************************
3140 * Storage32Impl_GetNextBlockInChain
3142 * This method will retrieve the block index of the next big block in
3143 * in the chain.
3145 * Params: This - Pointer to the Storage object.
3146 * blockIndex - Index of the block to retrieve the chain
3147 * for.
3148 * nextBlockIndex - receives the return value.
3150 * Returns: This method returns the index of the next block in the chain.
3151 * It will return the constants:
3152 * BLOCK_SPECIAL - If the block given was not part of a
3153 * chain.
3154 * BLOCK_END_OF_CHAIN - If the block given was the last in
3155 * a chain.
3156 * BLOCK_UNUSED - If the block given was not past of a chain
3157 * and is available.
3158 * BLOCK_EXTBBDEPOT - This block is part of the extended
3159 * big block depot.
3161 * See Windows documentation for more details on IStorage methods.
3163 static HRESULT StorageImpl_GetNextBlockInChain(
3164 StorageImpl* This,
3165 ULONG blockIndex,
3166 ULONG* nextBlockIndex)
3168 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3169 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3170 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3171 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3172 BOOL success;
3173 ULONG depotBlockIndexPos;
3174 int index, num_blocks;
3176 *nextBlockIndex = BLOCK_SPECIAL;
3178 if(depotBlockCount >= This->bigBlockDepotCount)
3180 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3181 This->bigBlockDepotCount);
3182 return STG_E_READFAULT;
3186 * Cache the currently accessed depot block.
3188 if (depotBlockCount != This->indexBlockDepotCached)
3190 This->indexBlockDepotCached = depotBlockCount;
3192 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3194 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3196 else
3199 * We have to look in the extended depot.
3201 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3204 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3206 if (!success)
3207 return STG_E_READFAULT;
3209 num_blocks = This->bigBlockSize / 4;
3211 for (index = 0; index < num_blocks; index++)
3213 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3214 This->blockDepotCached[index] = *nextBlockIndex;
3218 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3220 return S_OK;
3223 /******************************************************************************
3224 * Storage32Impl_GetNextExtendedBlock
3226 * Given an extended block this method will return the next extended block.
3228 * NOTES:
3229 * The last ULONG of an extended block is the block index of the next
3230 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3231 * depot.
3233 * Return values:
3234 * - The index of the next extended block
3235 * - BLOCK_UNUSED: there is no next extended block.
3236 * - Any other return values denotes failure.
3238 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3240 ULONG nextBlockIndex = BLOCK_SPECIAL;
3241 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3243 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3244 &nextBlockIndex);
3246 return nextBlockIndex;
3249 /******************************************************************************
3250 * Storage32Impl_SetNextBlockInChain
3252 * This method will write the index of the specified block's next block
3253 * in the big block depot.
3255 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3256 * do the following
3258 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3259 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3260 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3263 static void StorageImpl_SetNextBlockInChain(
3264 StorageImpl* This,
3265 ULONG blockIndex,
3266 ULONG nextBlock)
3268 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3269 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3270 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3271 ULONG depotBlockIndexPos;
3273 assert(depotBlockCount < This->bigBlockDepotCount);
3274 assert(blockIndex != nextBlock);
3276 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3278 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3280 else
3283 * We have to look in the extended depot.
3285 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3288 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3289 nextBlock);
3291 * Update the cached block depot, if necessary.
3293 if (depotBlockCount == This->indexBlockDepotCached)
3295 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3299 /******************************************************************************
3300 * Storage32Impl_LoadFileHeader
3302 * This method will read in the file header
3304 static HRESULT StorageImpl_LoadFileHeader(
3305 StorageImpl* This)
3307 HRESULT hr;
3308 BYTE headerBigBlock[HEADER_SIZE];
3309 int index;
3310 ULARGE_INTEGER offset;
3311 DWORD bytes_read;
3313 TRACE("\n");
3315 * Get a pointer to the big block of data containing the header.
3317 offset.u.HighPart = 0;
3318 offset.u.LowPart = 0;
3319 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3320 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3321 hr = STG_E_FILENOTFOUND;
3324 * Extract the information from the header.
3326 if (SUCCEEDED(hr))
3329 * Check for the "magic number" signature and return an error if it is not
3330 * found.
3332 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3334 return STG_E_OLDFORMAT;
3337 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3339 return STG_E_INVALIDHEADER;
3342 StorageUtl_ReadWord(
3343 headerBigBlock,
3344 OFFSET_BIGBLOCKSIZEBITS,
3345 &This->bigBlockSizeBits);
3347 StorageUtl_ReadWord(
3348 headerBigBlock,
3349 OFFSET_SMALLBLOCKSIZEBITS,
3350 &This->smallBlockSizeBits);
3352 StorageUtl_ReadDWord(
3353 headerBigBlock,
3354 OFFSET_BBDEPOTCOUNT,
3355 &This->bigBlockDepotCount);
3357 StorageUtl_ReadDWord(
3358 headerBigBlock,
3359 OFFSET_ROOTSTARTBLOCK,
3360 &This->rootStartBlock);
3362 StorageUtl_ReadDWord(
3363 headerBigBlock,
3364 OFFSET_SMALLBLOCKLIMIT,
3365 &This->smallBlockLimit);
3367 StorageUtl_ReadDWord(
3368 headerBigBlock,
3369 OFFSET_SBDEPOTSTART,
3370 &This->smallBlockDepotStart);
3372 StorageUtl_ReadDWord(
3373 headerBigBlock,
3374 OFFSET_EXTBBDEPOTSTART,
3375 &This->extBigBlockDepotStart);
3377 StorageUtl_ReadDWord(
3378 headerBigBlock,
3379 OFFSET_EXTBBDEPOTCOUNT,
3380 &This->extBigBlockDepotCount);
3382 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3384 StorageUtl_ReadDWord(
3385 headerBigBlock,
3386 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3387 &(This->bigBlockDepotStart[index]));
3391 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3393 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3394 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3397 * Right now, the code is making some assumptions about the size of the
3398 * blocks, just make sure they are what we're expecting.
3400 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3401 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3402 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3404 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3405 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3406 hr = STG_E_INVALIDHEADER;
3408 else
3409 hr = S_OK;
3412 return hr;
3415 /******************************************************************************
3416 * Storage32Impl_SaveFileHeader
3418 * This method will save to the file the header
3420 static void StorageImpl_SaveFileHeader(
3421 StorageImpl* This)
3423 BYTE headerBigBlock[HEADER_SIZE];
3424 int index;
3425 HRESULT hr;
3426 ULARGE_INTEGER offset;
3427 DWORD bytes_read, bytes_written;
3430 * Get a pointer to the big block of data containing the header.
3432 offset.u.HighPart = 0;
3433 offset.u.LowPart = 0;
3434 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3435 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3436 hr = STG_E_FILENOTFOUND;
3439 * If the block read failed, the file is probably new.
3441 if (FAILED(hr))
3444 * Initialize for all unknown fields.
3446 memset(headerBigBlock, 0, HEADER_SIZE);
3449 * Initialize the magic number.
3451 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3454 * And a bunch of things we don't know what they mean
3456 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3457 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3458 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3462 * Write the information to the header.
3464 StorageUtl_WriteWord(
3465 headerBigBlock,
3466 OFFSET_BIGBLOCKSIZEBITS,
3467 This->bigBlockSizeBits);
3469 StorageUtl_WriteWord(
3470 headerBigBlock,
3471 OFFSET_SMALLBLOCKSIZEBITS,
3472 This->smallBlockSizeBits);
3474 StorageUtl_WriteDWord(
3475 headerBigBlock,
3476 OFFSET_BBDEPOTCOUNT,
3477 This->bigBlockDepotCount);
3479 StorageUtl_WriteDWord(
3480 headerBigBlock,
3481 OFFSET_ROOTSTARTBLOCK,
3482 This->rootStartBlock);
3484 StorageUtl_WriteDWord(
3485 headerBigBlock,
3486 OFFSET_SMALLBLOCKLIMIT,
3487 This->smallBlockLimit);
3489 StorageUtl_WriteDWord(
3490 headerBigBlock,
3491 OFFSET_SBDEPOTSTART,
3492 This->smallBlockDepotStart);
3494 StorageUtl_WriteDWord(
3495 headerBigBlock,
3496 OFFSET_SBDEPOTCOUNT,
3497 This->smallBlockDepotChain ?
3498 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3500 StorageUtl_WriteDWord(
3501 headerBigBlock,
3502 OFFSET_EXTBBDEPOTSTART,
3503 This->extBigBlockDepotStart);
3505 StorageUtl_WriteDWord(
3506 headerBigBlock,
3507 OFFSET_EXTBBDEPOTCOUNT,
3508 This->extBigBlockDepotCount);
3510 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3512 StorageUtl_WriteDWord(
3513 headerBigBlock,
3514 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3515 (This->bigBlockDepotStart[index]));
3519 * Write the big block back to the file.
3521 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3524 /******************************************************************************
3525 * StorageImpl_ReadRawDirEntry
3527 * This method will read the raw data from a directory entry in the file.
3529 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3531 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3533 ULARGE_INTEGER offset;
3534 HRESULT hr;
3535 ULONG bytesRead;
3537 offset.u.HighPart = 0;
3538 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3540 hr = BlockChainStream_ReadAt(
3541 This->rootBlockChain,
3542 offset,
3543 RAW_DIRENTRY_SIZE,
3544 buffer,
3545 &bytesRead);
3547 return hr;
3550 /******************************************************************************
3551 * StorageImpl_WriteRawDirEntry
3553 * This method will write the raw data from a directory entry in the file.
3555 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3557 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3559 ULARGE_INTEGER offset;
3560 HRESULT hr;
3561 ULONG bytesRead;
3563 offset.u.HighPart = 0;
3564 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3566 hr = BlockChainStream_WriteAt(
3567 This->rootBlockChain,
3568 offset,
3569 RAW_DIRENTRY_SIZE,
3570 buffer,
3571 &bytesRead);
3573 return hr;
3576 /******************************************************************************
3577 * UpdateRawDirEntry
3579 * Update raw directory entry data from the fields in newData.
3581 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3583 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3585 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3587 memcpy(
3588 buffer + OFFSET_PS_NAME,
3589 newData->name,
3590 DIRENTRY_NAME_BUFFER_LEN );
3592 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3594 StorageUtl_WriteWord(
3595 buffer,
3596 OFFSET_PS_NAMELENGTH,
3597 newData->sizeOfNameString);
3599 StorageUtl_WriteDWord(
3600 buffer,
3601 OFFSET_PS_LEFTCHILD,
3602 newData->leftChild);
3604 StorageUtl_WriteDWord(
3605 buffer,
3606 OFFSET_PS_RIGHTCHILD,
3607 newData->rightChild);
3609 StorageUtl_WriteDWord(
3610 buffer,
3611 OFFSET_PS_DIRROOT,
3612 newData->dirRootEntry);
3614 StorageUtl_WriteGUID(
3615 buffer,
3616 OFFSET_PS_GUID,
3617 &newData->clsid);
3619 StorageUtl_WriteDWord(
3620 buffer,
3621 OFFSET_PS_CTIMELOW,
3622 newData->ctime.dwLowDateTime);
3624 StorageUtl_WriteDWord(
3625 buffer,
3626 OFFSET_PS_CTIMEHIGH,
3627 newData->ctime.dwHighDateTime);
3629 StorageUtl_WriteDWord(
3630 buffer,
3631 OFFSET_PS_MTIMELOW,
3632 newData->mtime.dwLowDateTime);
3634 StorageUtl_WriteDWord(
3635 buffer,
3636 OFFSET_PS_MTIMEHIGH,
3637 newData->ctime.dwHighDateTime);
3639 StorageUtl_WriteDWord(
3640 buffer,
3641 OFFSET_PS_STARTBLOCK,
3642 newData->startingBlock);
3644 StorageUtl_WriteDWord(
3645 buffer,
3646 OFFSET_PS_SIZE,
3647 newData->size.u.LowPart);
3650 /******************************************************************************
3651 * Storage32Impl_ReadDirEntry
3653 * This method will read the specified directory entry.
3655 HRESULT StorageImpl_ReadDirEntry(
3656 StorageImpl* This,
3657 DirRef index,
3658 DirEntry* buffer)
3660 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3661 HRESULT readRes;
3663 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3665 if (SUCCEEDED(readRes))
3667 memset(buffer->name, 0, sizeof(buffer->name));
3668 memcpy(
3669 buffer->name,
3670 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3671 DIRENTRY_NAME_BUFFER_LEN );
3672 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3674 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3676 StorageUtl_ReadWord(
3677 currentEntry,
3678 OFFSET_PS_NAMELENGTH,
3679 &buffer->sizeOfNameString);
3681 StorageUtl_ReadDWord(
3682 currentEntry,
3683 OFFSET_PS_LEFTCHILD,
3684 &buffer->leftChild);
3686 StorageUtl_ReadDWord(
3687 currentEntry,
3688 OFFSET_PS_RIGHTCHILD,
3689 &buffer->rightChild);
3691 StorageUtl_ReadDWord(
3692 currentEntry,
3693 OFFSET_PS_DIRROOT,
3694 &buffer->dirRootEntry);
3696 StorageUtl_ReadGUID(
3697 currentEntry,
3698 OFFSET_PS_GUID,
3699 &buffer->clsid);
3701 StorageUtl_ReadDWord(
3702 currentEntry,
3703 OFFSET_PS_CTIMELOW,
3704 &buffer->ctime.dwLowDateTime);
3706 StorageUtl_ReadDWord(
3707 currentEntry,
3708 OFFSET_PS_CTIMEHIGH,
3709 &buffer->ctime.dwHighDateTime);
3711 StorageUtl_ReadDWord(
3712 currentEntry,
3713 OFFSET_PS_MTIMELOW,
3714 &buffer->mtime.dwLowDateTime);
3716 StorageUtl_ReadDWord(
3717 currentEntry,
3718 OFFSET_PS_MTIMEHIGH,
3719 &buffer->mtime.dwHighDateTime);
3721 StorageUtl_ReadDWord(
3722 currentEntry,
3723 OFFSET_PS_STARTBLOCK,
3724 &buffer->startingBlock);
3726 StorageUtl_ReadDWord(
3727 currentEntry,
3728 OFFSET_PS_SIZE,
3729 &buffer->size.u.LowPart);
3731 buffer->size.u.HighPart = 0;
3734 return readRes;
3737 /*********************************************************************
3738 * Write the specified directory entry to the file
3740 HRESULT StorageImpl_WriteDirEntry(
3741 StorageImpl* This,
3742 DirRef index,
3743 const DirEntry* buffer)
3745 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3746 HRESULT writeRes;
3748 UpdateRawDirEntry(currentEntry, buffer);
3750 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3751 return writeRes;
3754 static BOOL StorageImpl_ReadBigBlock(
3755 StorageImpl* This,
3756 ULONG blockIndex,
3757 void* buffer)
3759 ULARGE_INTEGER ulOffset;
3760 DWORD read;
3762 ulOffset.u.HighPart = 0;
3763 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3765 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3766 return (read == This->bigBlockSize);
3769 static BOOL StorageImpl_ReadDWordFromBigBlock(
3770 StorageImpl* This,
3771 ULONG blockIndex,
3772 ULONG offset,
3773 DWORD* value)
3775 ULARGE_INTEGER ulOffset;
3776 DWORD read;
3777 DWORD tmp;
3779 ulOffset.u.HighPart = 0;
3780 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3781 ulOffset.u.LowPart += offset;
3783 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3784 *value = lendian32toh(tmp);
3785 return (read == sizeof(DWORD));
3788 static BOOL StorageImpl_WriteBigBlock(
3789 StorageImpl* This,
3790 ULONG blockIndex,
3791 const void* buffer)
3793 ULARGE_INTEGER ulOffset;
3794 DWORD wrote;
3796 ulOffset.u.HighPart = 0;
3797 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3799 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3800 return (wrote == This->bigBlockSize);
3803 static BOOL StorageImpl_WriteDWordToBigBlock(
3804 StorageImpl* This,
3805 ULONG blockIndex,
3806 ULONG offset,
3807 DWORD value)
3809 ULARGE_INTEGER ulOffset;
3810 DWORD wrote;
3812 ulOffset.u.HighPart = 0;
3813 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3814 ulOffset.u.LowPart += offset;
3816 value = htole32(value);
3817 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3818 return (wrote == sizeof(DWORD));
3821 /******************************************************************************
3822 * Storage32Impl_SmallBlocksToBigBlocks
3824 * This method will convert a small block chain to a big block chain.
3825 * The small block chain will be destroyed.
3827 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3828 StorageImpl* This,
3829 SmallBlockChainStream** ppsbChain)
3831 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3832 ULARGE_INTEGER size, offset;
3833 ULONG cbRead, cbWritten;
3834 ULARGE_INTEGER cbTotalRead;
3835 DirRef streamEntryRef;
3836 HRESULT resWrite = S_OK;
3837 HRESULT resRead;
3838 DirEntry streamEntry;
3839 BYTE *buffer;
3840 BlockChainStream *bbTempChain = NULL;
3841 BlockChainStream *bigBlockChain = NULL;
3844 * Create a temporary big block chain that doesn't have
3845 * an associated directory entry. This temporary chain will be
3846 * used to copy data from small blocks to big blocks.
3848 bbTempChain = BlockChainStream_Construct(This,
3849 &bbHeadOfChain,
3850 DIRENTRY_NULL);
3851 if(!bbTempChain) return NULL;
3853 * Grow the big block chain.
3855 size = SmallBlockChainStream_GetSize(*ppsbChain);
3856 BlockChainStream_SetSize(bbTempChain, size);
3859 * Copy the contents of the small block chain to the big block chain
3860 * by small block size increments.
3862 offset.u.LowPart = 0;
3863 offset.u.HighPart = 0;
3864 cbTotalRead.QuadPart = 0;
3866 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3869 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3870 offset,
3871 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3872 buffer,
3873 &cbRead);
3874 if (FAILED(resRead))
3875 break;
3877 if (cbRead > 0)
3879 cbTotalRead.QuadPart += cbRead;
3881 resWrite = BlockChainStream_WriteAt(bbTempChain,
3882 offset,
3883 cbRead,
3884 buffer,
3885 &cbWritten);
3887 if (FAILED(resWrite))
3888 break;
3890 offset.u.LowPart += cbRead;
3892 } while (cbTotalRead.QuadPart < size.QuadPart);
3893 HeapFree(GetProcessHeap(),0,buffer);
3895 size.u.HighPart = 0;
3896 size.u.LowPart = 0;
3898 if (FAILED(resRead) || FAILED(resWrite))
3900 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3901 BlockChainStream_SetSize(bbTempChain, size);
3902 BlockChainStream_Destroy(bbTempChain);
3903 return NULL;
3907 * Destroy the small block chain.
3909 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3910 SmallBlockChainStream_SetSize(*ppsbChain, size);
3911 SmallBlockChainStream_Destroy(*ppsbChain);
3912 *ppsbChain = 0;
3915 * Change the directory entry. This chain is now a big block chain
3916 * and it doesn't reside in the small blocks chain anymore.
3918 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3920 streamEntry.startingBlock = bbHeadOfChain;
3922 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3925 * Destroy the temporary entryless big block chain.
3926 * Create a new big block chain associated with this entry.
3928 BlockChainStream_Destroy(bbTempChain);
3929 bigBlockChain = BlockChainStream_Construct(This,
3930 NULL,
3931 streamEntryRef);
3933 return bigBlockChain;
3936 /******************************************************************************
3937 * Storage32Impl_BigBlocksToSmallBlocks
3939 * This method will convert a big block chain to a small block chain.
3940 * The big block chain will be destroyed on success.
3942 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3943 StorageImpl* This,
3944 BlockChainStream** ppbbChain)
3946 ULARGE_INTEGER size, offset, cbTotalRead;
3947 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3948 DirRef streamEntryRef;
3949 HRESULT resWrite = S_OK, resRead;
3950 DirEntry streamEntry;
3951 BYTE* buffer;
3952 SmallBlockChainStream* sbTempChain;
3954 TRACE("%p %p\n", This, ppbbChain);
3956 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3957 DIRENTRY_NULL);
3959 if(!sbTempChain)
3960 return NULL;
3962 size = BlockChainStream_GetSize(*ppbbChain);
3963 SmallBlockChainStream_SetSize(sbTempChain, size);
3965 offset.u.HighPart = 0;
3966 offset.u.LowPart = 0;
3967 cbTotalRead.QuadPart = 0;
3968 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3971 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3972 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3973 buffer, &cbRead);
3975 if(FAILED(resRead))
3976 break;
3978 if(cbRead > 0)
3980 cbTotalRead.QuadPart += cbRead;
3982 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3983 cbRead, buffer, &cbWritten);
3985 if(FAILED(resWrite))
3986 break;
3988 offset.u.LowPart += cbRead;
3990 }while(cbTotalRead.QuadPart < size.QuadPart);
3991 HeapFree(GetProcessHeap(), 0, buffer);
3993 size.u.HighPart = 0;
3994 size.u.LowPart = 0;
3996 if(FAILED(resRead) || FAILED(resWrite))
3998 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3999 SmallBlockChainStream_SetSize(sbTempChain, size);
4000 SmallBlockChainStream_Destroy(sbTempChain);
4001 return NULL;
4004 /* destroy the original big block chain */
4005 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4006 BlockChainStream_SetSize(*ppbbChain, size);
4007 BlockChainStream_Destroy(*ppbbChain);
4008 *ppbbChain = NULL;
4010 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4011 streamEntry.startingBlock = sbHeadOfChain;
4012 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4014 SmallBlockChainStream_Destroy(sbTempChain);
4015 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4018 static HRESULT StorageBaseImpl_CopyStream(
4019 StorageBaseImpl *dst, DirRef dst_entry,
4020 StorageBaseImpl *src, DirRef src_entry)
4022 HRESULT hr;
4023 BYTE data[4096];
4024 DirEntry srcdata;
4025 ULARGE_INTEGER bytes_copied;
4026 ULONG bytestocopy, bytesread, byteswritten;
4028 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4030 if (SUCCEEDED(hr))
4032 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4034 bytes_copied.QuadPart = 0;
4035 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4037 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4039 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4040 data, &bytesread);
4041 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4043 if (SUCCEEDED(hr))
4044 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4045 data, &byteswritten);
4046 if (SUCCEEDED(hr) && byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4048 bytes_copied.QuadPart += byteswritten;
4052 return hr;
4055 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4057 DirRef result=This->firstFreeEntry;
4059 while (result < This->entries_size && This->entries[result].inuse)
4060 result++;
4062 if (result == This->entries_size)
4064 ULONG new_size = This->entries_size * 2;
4065 TransactedDirEntry *new_entries;
4067 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4068 if (!new_entries) return DIRENTRY_NULL;
4070 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4071 HeapFree(GetProcessHeap(), 0, This->entries);
4073 This->entries = new_entries;
4074 This->entries_size = new_size;
4077 This->entries[result].inuse = 1;
4079 This->firstFreeEntry = result+1;
4081 return result;
4084 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4085 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4087 DirRef stubEntryRef;
4088 TransactedDirEntry *entry;
4090 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4092 if (stubEntryRef != DIRENTRY_NULL)
4094 entry = &This->entries[stubEntryRef];
4096 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4098 entry->read = 0;
4101 return stubEntryRef;
4104 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4105 TransactedSnapshotImpl *This, DirRef entry)
4107 HRESULT hr=S_OK;
4109 if (!This->entries[entry].read)
4111 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4112 This->entries[entry].transactedParentEntry,
4113 &This->entries[entry].data);
4115 if (SUCCEEDED(hr) && This->entries[entry].data.leftChild != DIRENTRY_NULL)
4117 This->entries[entry].data.leftChild =
4118 TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].data.leftChild);
4120 if (This->entries[entry].data.leftChild == DIRENTRY_NULL)
4121 hr = E_OUTOFMEMORY;
4124 if (SUCCEEDED(hr) && This->entries[entry].data.rightChild != DIRENTRY_NULL)
4126 This->entries[entry].data.rightChild =
4127 TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].data.rightChild);
4129 if (This->entries[entry].data.rightChild == DIRENTRY_NULL)
4130 hr = E_OUTOFMEMORY;
4133 if (SUCCEEDED(hr) && This->entries[entry].data.dirRootEntry != DIRENTRY_NULL)
4135 This->entries[entry].data.dirRootEntry =
4136 TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].data.dirRootEntry);
4138 if (This->entries[entry].data.dirRootEntry == DIRENTRY_NULL)
4139 hr = E_OUTOFMEMORY;
4142 if (SUCCEEDED(hr))
4143 This->entries[entry].read = 1;
4146 return hr;
4149 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4150 TransactedSnapshotImpl *This, DirRef entry)
4152 HRESULT hr = S_OK;
4154 if (!This->entries[entry].stream_dirty)
4156 DirEntry new_entrydata;
4158 memset(&new_entrydata, 0, sizeof(DirEntry));
4159 new_entrydata.name[0] = 'S';
4160 new_entrydata.sizeOfNameString = 1;
4161 new_entrydata.stgType = STGTY_STREAM;
4162 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4163 new_entrydata.leftChild = DIRENTRY_NULL;
4164 new_entrydata.rightChild = DIRENTRY_NULL;
4165 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4167 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4168 &This->entries[entry].stream_entry);
4170 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4172 hr = StorageBaseImpl_CopyStream(
4173 This->scratch, This->entries[entry].stream_entry,
4174 This->transactedParent, This->entries[entry].transactedParentEntry);
4176 if (FAILED(hr))
4177 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4180 if (SUCCEEDED(hr))
4181 This->entries[entry].stream_dirty = 1;
4183 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4185 /* Since this entry is modified, and we aren't using its stream data, we
4186 * no longer care about the original entry. */
4187 DirRef delete_ref;
4188 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4190 if (delete_ref != DIRENTRY_NULL)
4191 This->entries[delete_ref].deleted = 1;
4193 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4197 return hr;
4200 /* Find the first entry in a depth-first traversal. */
4201 static DirRef TransactedSnapshotImpl_FindFirstChild(
4202 TransactedSnapshotImpl* This, DirRef parent)
4204 DirRef cursor, prev;
4205 TransactedDirEntry *entry;
4207 cursor = parent;
4208 entry = &This->entries[cursor];
4209 while (entry->read)
4211 if (entry->data.leftChild != DIRENTRY_NULL)
4213 prev = cursor;
4214 cursor = entry->data.leftChild;
4215 entry = &This->entries[cursor];
4216 entry->parent = prev;
4218 else if (entry->data.rightChild != DIRENTRY_NULL)
4220 prev = cursor;
4221 cursor = entry->data.rightChild;
4222 entry = &This->entries[cursor];
4223 entry->parent = prev;
4225 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4227 prev = cursor;
4228 cursor = entry->data.dirRootEntry;
4229 entry = &This->entries[cursor];
4230 entry->parent = prev;
4232 else
4233 break;
4236 return cursor;
4239 /* Find the next entry in a depth-first traversal. */
4240 static DirRef TransactedSnapshotImpl_FindNextChild(
4241 TransactedSnapshotImpl* This, DirRef current)
4243 DirRef parent;
4244 TransactedDirEntry *parent_entry;
4246 parent = This->entries[current].parent;
4247 parent_entry = &This->entries[parent];
4249 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4251 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4253 This->entries[parent_entry->data.rightChild].parent = parent;
4254 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4257 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4259 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4260 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4264 return parent;
4267 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4268 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4269 TransactedSnapshotImpl* This, DirRef entry)
4271 return entry != DIRENTRY_NULL &&
4272 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4275 /* Destroy the entries created by CopyTree. */
4276 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4277 TransactedSnapshotImpl* This, DirRef stop)
4279 DirRef cursor;
4280 TransactedDirEntry *entry;
4281 ULARGE_INTEGER zero;
4283 zero.QuadPart = 0;
4285 if (!This->entries[This->base.storageDirEntry].read)
4286 return;
4288 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4290 if (cursor == DIRENTRY_NULL)
4291 return;
4293 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4295 while (cursor != DIRENTRY_NULL && cursor != stop)
4297 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4299 entry = &This->entries[cursor];
4301 StorageBaseImpl_StreamSetSize(This->transactedParent,
4302 entry->newTransactedParentEntry, zero);
4304 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4305 entry->newTransactedParentEntry);
4307 entry->newTransactedParentEntry = entry->transactedParentEntry;
4310 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4314 /* Make a copy of our edited tree that we can use in the parent. */
4315 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4317 DirRef cursor;
4318 TransactedDirEntry *entry;
4319 HRESULT hr;
4321 cursor = This->base.storageDirEntry;
4322 entry = &This->entries[cursor];
4323 entry->parent = DIRENTRY_NULL;
4324 entry->newTransactedParentEntry = entry->transactedParentEntry;
4326 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4327 return S_OK;
4329 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4331 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4332 entry = &This->entries[cursor];
4334 while (cursor != DIRENTRY_NULL)
4336 /* Make a copy of this entry in the transacted parent. */
4337 if (!entry->read ||
4338 (!entry->dirty && !entry->stream_dirty &&
4339 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4340 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4341 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4342 entry->newTransactedParentEntry = entry->transactedParentEntry;
4343 else
4345 DirEntry newData;
4347 memcpy(&newData, &entry->data, sizeof(DirEntry));
4349 newData.size.QuadPart = 0;
4350 newData.startingBlock = BLOCK_END_OF_CHAIN;
4352 if (newData.leftChild != DIRENTRY_NULL)
4353 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4355 if (newData.rightChild != DIRENTRY_NULL)
4356 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4358 if (newData.dirRootEntry != DIRENTRY_NULL)
4359 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4361 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4362 &entry->newTransactedParentEntry);
4363 if (FAILED(hr))
4365 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4366 return hr;
4369 hr = StorageBaseImpl_CopyStream(
4370 This->transactedParent, entry->newTransactedParentEntry,
4371 (StorageBaseImpl*)This, cursor);
4372 if (FAILED(hr))
4374 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4375 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4376 return hr;
4380 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4381 entry = &This->entries[cursor];
4384 return hr;
4387 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4388 IStorage* iface,
4389 DWORD grfCommitFlags) /* [in] */
4391 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4392 TransactedDirEntry *root_entry;
4393 DirRef i, dir_root_ref;
4394 DirEntry data;
4395 ULARGE_INTEGER zero;
4396 HRESULT hr;
4398 zero.QuadPart = 0;
4400 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4402 /* Cannot commit a read-only transacted storage */
4403 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4404 return STG_E_ACCESSDENIED;
4406 /* To prevent data loss, we create the new structure in the file before we
4407 * delete the old one, so that in case of errors the old data is intact. We
4408 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4409 * needed in the rare situation where we have just enough free disk space to
4410 * overwrite the existing data. */
4412 root_entry = &This->entries[This->base.storageDirEntry];
4414 if (!root_entry->read)
4415 return S_OK;
4417 hr = TransactedSnapshotImpl_CopyTree(This);
4418 if (FAILED(hr)) return hr;
4420 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4421 dir_root_ref = DIRENTRY_NULL;
4422 else
4423 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4425 /* Update the storage to use the new data in one step. */
4426 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4427 root_entry->transactedParentEntry, &data);
4429 if (SUCCEEDED(hr))
4431 data.dirRootEntry = dir_root_ref;
4432 data.clsid = root_entry->data.clsid;
4433 data.ctime = root_entry->data.ctime;
4434 data.mtime = root_entry->data.mtime;
4436 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4437 root_entry->transactedParentEntry, &data);
4440 if (SUCCEEDED(hr))
4442 /* Destroy the old now-orphaned data. */
4443 for (i=0; i<This->entries_size; i++)
4445 TransactedDirEntry *entry = &This->entries[i];
4446 if (entry->inuse)
4448 if (entry->deleted)
4450 StorageBaseImpl_StreamSetSize(This->transactedParent,
4451 entry->transactedParentEntry, zero);
4452 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4453 entry->transactedParentEntry);
4454 memset(entry, 0, sizeof(TransactedDirEntry));
4455 This->firstFreeEntry = min(i, This->firstFreeEntry);
4457 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4459 if (entry->transactedParentEntry != DIRENTRY_NULL)
4461 StorageBaseImpl_StreamSetSize(This->transactedParent,
4462 entry->transactedParentEntry, zero);
4463 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4464 entry->transactedParentEntry);
4466 if (entry->stream_dirty)
4468 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4469 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4470 entry->stream_dirty = 0;
4472 entry->dirty = 0;
4473 entry->transactedParentEntry = entry->newTransactedParentEntry;
4478 else
4480 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4483 return hr;
4486 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4487 IStorage* iface)
4489 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4490 ULARGE_INTEGER zero;
4491 ULONG i;
4493 TRACE("(%p)\n", iface);
4495 /* Destroy the open objects. */
4496 StorageBaseImpl_DeleteAll(&This->base);
4498 /* Clear out the scratch file. */
4499 zero.QuadPart = 0;
4500 for (i=0; i<This->entries_size; i++)
4502 if (This->entries[i].stream_dirty)
4504 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4505 zero);
4507 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4511 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4513 This->firstFreeEntry = 0;
4514 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4516 return S_OK;
4519 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4521 if (!This->reverted)
4523 TRACE("Storage invalidated (stg=%p)\n", This);
4525 This->reverted = 1;
4527 StorageBaseImpl_DeleteAll(This);
4531 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4533 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4535 TransactedSnapshotImpl_Revert((IStorage*)iface);
4537 IStorage_Release((IStorage*)This->transactedParent);
4539 IStorage_Release((IStorage*)This->scratch);
4541 HeapFree(GetProcessHeap(), 0, This->entries);
4543 HeapFree(GetProcessHeap(), 0, This);
4546 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4547 const DirEntry *newData, DirRef *index)
4549 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4550 DirRef new_ref;
4551 TransactedDirEntry *new_entry;
4553 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4554 if (new_ref == DIRENTRY_NULL)
4555 return E_OUTOFMEMORY;
4557 new_entry = &This->entries[new_ref];
4559 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4560 new_entry->read = 1;
4561 new_entry->dirty = 1;
4562 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4564 *index = new_ref;
4566 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4568 return S_OK;
4571 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4572 DirRef index, const DirEntry *data)
4574 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4575 HRESULT hr;
4577 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4579 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4580 if (FAILED(hr)) return hr;
4582 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4584 if (index != This->base.storageDirEntry)
4586 This->entries[index].dirty = 1;
4588 if (data->size.QuadPart == 0 &&
4589 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4591 /* Since this entry is modified, and we aren't using its stream data, we
4592 * no longer care about the original entry. */
4593 DirRef delete_ref;
4594 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4596 if (delete_ref != DIRENTRY_NULL)
4597 This->entries[delete_ref].deleted = 1;
4599 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4603 return S_OK;
4606 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4607 DirRef index, DirEntry *data)
4609 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4610 HRESULT hr;
4612 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4613 if (FAILED(hr)) return hr;
4615 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4617 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4619 return S_OK;
4622 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4623 DirRef index)
4625 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4627 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4628 This->entries[index].data.size.QuadPart != 0)
4630 /* If we deleted this entry while it has stream data. We must have left the
4631 * data because some other entry is using it, and we need to leave the
4632 * original entry alone. */
4633 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4634 This->firstFreeEntry = min(index, This->firstFreeEntry);
4636 else
4638 This->entries[index].deleted = 1;
4641 return S_OK;
4644 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4645 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4647 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4649 if (This->entries[index].stream_dirty)
4651 return StorageBaseImpl_StreamReadAt(This->scratch,
4652 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4654 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4656 /* This stream doesn't live in the parent, and we haven't allocated storage
4657 * for it yet */
4658 *bytesRead = 0;
4659 return S_OK;
4661 else
4663 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4664 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4668 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4669 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4671 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4672 HRESULT hr;
4674 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4675 if (FAILED(hr)) return hr;
4677 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4678 if (FAILED(hr)) return hr;
4680 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4681 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4683 if (SUCCEEDED(hr) && size != 0)
4684 This->entries[index].data.size.QuadPart = max(
4685 This->entries[index].data.size.QuadPart,
4686 offset.QuadPart + size);
4688 return hr;
4691 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4692 DirRef index, ULARGE_INTEGER newsize)
4694 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4695 HRESULT hr;
4697 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4698 if (FAILED(hr)) return hr;
4700 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4701 return S_OK;
4703 if (newsize.QuadPart == 0)
4705 /* Destroy any parent references or entries in the scratch file. */
4706 if (This->entries[index].stream_dirty)
4708 ULARGE_INTEGER zero;
4709 zero.QuadPart = 0;
4710 StorageBaseImpl_StreamSetSize(This->scratch,
4711 This->entries[index].stream_entry, zero);
4712 StorageBaseImpl_DestroyDirEntry(This->scratch,
4713 This->entries[index].stream_entry);
4714 This->entries[index].stream_dirty = 0;
4716 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4718 DirRef delete_ref;
4719 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4721 if (delete_ref != DIRENTRY_NULL)
4722 This->entries[delete_ref].deleted = 1;
4724 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4727 else
4729 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4730 if (FAILED(hr)) return hr;
4732 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4733 This->entries[index].stream_entry, newsize);
4736 if (SUCCEEDED(hr))
4737 This->entries[index].data.size = newsize;
4739 return hr;
4742 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4744 StorageBaseImpl_QueryInterface,
4745 StorageBaseImpl_AddRef,
4746 StorageBaseImpl_Release,
4747 StorageBaseImpl_CreateStream,
4748 StorageBaseImpl_OpenStream,
4749 StorageBaseImpl_CreateStorage,
4750 StorageBaseImpl_OpenStorage,
4751 StorageBaseImpl_CopyTo,
4752 StorageBaseImpl_MoveElementTo,
4753 TransactedSnapshotImpl_Commit,
4754 TransactedSnapshotImpl_Revert,
4755 StorageBaseImpl_EnumElements,
4756 StorageBaseImpl_DestroyElement,
4757 StorageBaseImpl_RenameElement,
4758 StorageBaseImpl_SetElementTimes,
4759 StorageBaseImpl_SetClass,
4760 StorageBaseImpl_SetStateBits,
4761 StorageBaseImpl_Stat
4764 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4766 TransactedSnapshotImpl_Destroy,
4767 TransactedSnapshotImpl_Invalidate,
4768 TransactedSnapshotImpl_CreateDirEntry,
4769 TransactedSnapshotImpl_WriteDirEntry,
4770 TransactedSnapshotImpl_ReadDirEntry,
4771 TransactedSnapshotImpl_DestroyDirEntry,
4772 TransactedSnapshotImpl_StreamReadAt,
4773 TransactedSnapshotImpl_StreamWriteAt,
4774 TransactedSnapshotImpl_StreamSetSize
4777 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4778 TransactedSnapshotImpl** result)
4780 HRESULT hr;
4782 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4783 if (*result)
4785 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4787 /* This is OK because the property set storage functions use the IStorage functions. */
4788 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4790 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4792 list_init(&(*result)->base.strmHead);
4794 list_init(&(*result)->base.storageHead);
4796 (*result)->base.ref = 1;
4798 (*result)->base.openFlags = parentStorage->openFlags;
4800 (*result)->base.filename = parentStorage->filename;
4802 /* Create a new temporary storage to act as the scratch file. */
4803 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
4804 0, (IStorage**)&(*result)->scratch);
4806 if (SUCCEEDED(hr))
4808 ULONG num_entries = 20;
4810 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
4812 (*result)->entries_size = num_entries;
4814 (*result)->firstFreeEntry = 0;
4816 if ((*result)->entries)
4818 /* parentStorage already has 1 reference, which we take over here. */
4819 (*result)->transactedParent = parentStorage;
4821 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4823 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
4825 else
4827 IStorage_Release((IStorage*)(*result)->scratch);
4829 hr = E_OUTOFMEMORY;
4833 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4835 return hr;
4837 else
4838 return E_OUTOFMEMORY;
4841 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4842 StorageBaseImpl** result)
4844 static int fixme=0;
4846 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4848 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4851 return TransactedSnapshotImpl_Construct(parentStorage,
4852 (TransactedSnapshotImpl**)result);
4855 static HRESULT Storage_Construct(
4856 HANDLE hFile,
4857 LPCOLESTR pwcsName,
4858 ILockBytes* pLkbyt,
4859 DWORD openFlags,
4860 BOOL fileBased,
4861 BOOL create,
4862 ULONG sector_size,
4863 StorageBaseImpl** result)
4865 StorageImpl *newStorage;
4866 StorageBaseImpl *newTransactedStorage;
4867 HRESULT hr;
4869 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
4870 if (FAILED(hr)) goto end;
4872 if (openFlags & STGM_TRANSACTED)
4874 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4875 if (FAILED(hr))
4876 IStorage_Release((IStorage*)newStorage);
4877 else
4878 *result = newTransactedStorage;
4880 else
4881 *result = &newStorage->base;
4883 end:
4884 return hr;
4887 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4889 StorageInternalImpl* This = (StorageInternalImpl*) base;
4891 if (!This->base.reverted)
4893 TRACE("Storage invalidated (stg=%p)\n", This);
4895 This->base.reverted = 1;
4897 This->parentStorage = NULL;
4899 StorageBaseImpl_DeleteAll(&This->base);
4901 list_remove(&This->ParentListEntry);
4905 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4907 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4909 StorageInternalImpl_Invalidate(&This->base);
4911 HeapFree(GetProcessHeap(), 0, This);
4914 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4915 const DirEntry *newData, DirRef *index)
4917 StorageInternalImpl* This = (StorageInternalImpl*) base;
4919 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4920 newData, index);
4923 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4924 DirRef index, const DirEntry *data)
4926 StorageInternalImpl* This = (StorageInternalImpl*) base;
4928 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4929 index, data);
4932 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4933 DirRef index, DirEntry *data)
4935 StorageInternalImpl* This = (StorageInternalImpl*) base;
4937 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4938 index, data);
4941 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4942 DirRef index)
4944 StorageInternalImpl* This = (StorageInternalImpl*) base;
4946 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4947 index);
4950 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4951 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4953 StorageInternalImpl* This = (StorageInternalImpl*) base;
4955 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4956 index, offset, size, buffer, bytesRead);
4959 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4960 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4962 StorageInternalImpl* This = (StorageInternalImpl*) base;
4964 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4965 index, offset, size, buffer, bytesWritten);
4968 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4969 DirRef index, ULARGE_INTEGER newsize)
4971 StorageInternalImpl* This = (StorageInternalImpl*) base;
4973 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4974 index, newsize);
4977 /******************************************************************************
4979 ** Storage32InternalImpl_Commit
4982 static HRESULT WINAPI StorageInternalImpl_Commit(
4983 IStorage* iface,
4984 DWORD grfCommitFlags) /* [in] */
4986 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4987 return S_OK;
4990 /******************************************************************************
4992 ** Storage32InternalImpl_Revert
4995 static HRESULT WINAPI StorageInternalImpl_Revert(
4996 IStorage* iface)
4998 FIXME("(%p): stub\n", iface);
4999 return S_OK;
5002 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5004 IStorage_Release((IStorage*)This->parentStorage);
5005 HeapFree(GetProcessHeap(), 0, This);
5008 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5009 IEnumSTATSTG* iface,
5010 REFIID riid,
5011 void** ppvObject)
5013 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5015 if (ppvObject==0)
5016 return E_INVALIDARG;
5018 *ppvObject = 0;
5020 if (IsEqualGUID(&IID_IUnknown, riid) ||
5021 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5023 *ppvObject = This;
5024 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
5025 return S_OK;
5028 return E_NOINTERFACE;
5031 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5032 IEnumSTATSTG* iface)
5034 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5035 return InterlockedIncrement(&This->ref);
5038 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5039 IEnumSTATSTG* iface)
5041 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5043 ULONG newRef;
5045 newRef = InterlockedDecrement(&This->ref);
5047 if (newRef==0)
5049 IEnumSTATSTGImpl_Destroy(This);
5052 return newRef;
5055 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5056 IEnumSTATSTGImpl* This,
5057 DirRef *ref)
5059 DirRef result = DIRENTRY_NULL;
5060 DirRef searchNode;
5061 DirEntry entry;
5062 HRESULT hr;
5063 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5065 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5066 This->parentStorage->storageDirEntry, &entry);
5067 searchNode = entry.dirRootEntry;
5069 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5071 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5073 if (SUCCEEDED(hr))
5075 LONG diff = entryNameCmp( entry.name, This->name);
5077 if (diff <= 0)
5079 searchNode = entry.rightChild;
5081 else
5083 result = searchNode;
5084 memcpy(result_name, entry.name, sizeof(result_name));
5085 searchNode = entry.leftChild;
5090 if (SUCCEEDED(hr))
5092 *ref = result;
5093 if (result != DIRENTRY_NULL)
5094 memcpy(This->name, result_name, sizeof(result_name));
5097 return hr;
5100 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5101 IEnumSTATSTG* iface,
5102 ULONG celt,
5103 STATSTG* rgelt,
5104 ULONG* pceltFetched)
5106 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5108 DirEntry currentEntry;
5109 STATSTG* currentReturnStruct = rgelt;
5110 ULONG objectFetched = 0;
5111 DirRef currentSearchNode;
5112 HRESULT hr=S_OK;
5114 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5115 return E_INVALIDARG;
5117 if (This->parentStorage->reverted)
5118 return STG_E_REVERTED;
5121 * To avoid the special case, get another pointer to a ULONG value if
5122 * the caller didn't supply one.
5124 if (pceltFetched==0)
5125 pceltFetched = &objectFetched;
5128 * Start the iteration, we will iterate until we hit the end of the
5129 * linked list or until we hit the number of items to iterate through
5131 *pceltFetched = 0;
5133 while ( *pceltFetched < celt )
5135 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5137 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5138 break;
5141 * Read the entry from the storage.
5143 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5144 currentSearchNode,
5145 &currentEntry);
5148 * Copy the information to the return buffer.
5150 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5151 currentReturnStruct,
5152 &currentEntry,
5153 STATFLAG_DEFAULT);
5156 * Step to the next item in the iteration
5158 (*pceltFetched)++;
5159 currentReturnStruct++;
5162 if (SUCCEEDED(hr) && *pceltFetched != celt)
5163 hr = S_FALSE;
5165 return hr;
5169 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5170 IEnumSTATSTG* iface,
5171 ULONG celt)
5173 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5175 ULONG objectFetched = 0;
5176 DirRef currentSearchNode;
5177 HRESULT hr=S_OK;
5179 if (This->parentStorage->reverted)
5180 return STG_E_REVERTED;
5182 while ( (objectFetched < celt) )
5184 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5186 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5187 break;
5189 objectFetched++;
5192 if (SUCCEEDED(hr) && objectFetched != celt)
5193 return S_FALSE;
5195 return hr;
5198 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5199 IEnumSTATSTG* iface)
5201 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5203 if (This->parentStorage->reverted)
5204 return STG_E_REVERTED;
5206 This->name[0] = 0;
5208 return S_OK;
5211 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5212 IEnumSTATSTG* iface,
5213 IEnumSTATSTG** ppenum)
5215 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5217 IEnumSTATSTGImpl* newClone;
5219 if (This->parentStorage->reverted)
5220 return STG_E_REVERTED;
5223 * Perform a sanity check on the parameters.
5225 if (ppenum==0)
5226 return E_INVALIDARG;
5228 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5229 This->storageDirEntry);
5233 * The new clone enumeration must point to the same current node as
5234 * the ole one.
5236 memcpy(newClone->name, This->name, sizeof(newClone->name));
5238 *ppenum = (IEnumSTATSTG*)newClone;
5241 * Don't forget to nail down a reference to the clone before
5242 * returning it.
5244 IEnumSTATSTGImpl_AddRef(*ppenum);
5246 return S_OK;
5250 * Virtual function table for the IEnumSTATSTGImpl class.
5252 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5254 IEnumSTATSTGImpl_QueryInterface,
5255 IEnumSTATSTGImpl_AddRef,
5256 IEnumSTATSTGImpl_Release,
5257 IEnumSTATSTGImpl_Next,
5258 IEnumSTATSTGImpl_Skip,
5259 IEnumSTATSTGImpl_Reset,
5260 IEnumSTATSTGImpl_Clone
5263 /******************************************************************************
5264 ** IEnumSTATSTGImpl implementation
5267 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5268 StorageBaseImpl* parentStorage,
5269 DirRef storageDirEntry)
5271 IEnumSTATSTGImpl* newEnumeration;
5273 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5275 if (newEnumeration!=0)
5278 * Set-up the virtual function table and reference count.
5280 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5281 newEnumeration->ref = 0;
5284 * We want to nail-down the reference to the storage in case the
5285 * enumeration out-lives the storage in the client application.
5287 newEnumeration->parentStorage = parentStorage;
5288 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5290 newEnumeration->storageDirEntry = storageDirEntry;
5293 * Make sure the current node of the iterator is the first one.
5295 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
5298 return newEnumeration;
5302 * Virtual function table for the Storage32InternalImpl class.
5304 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5306 StorageBaseImpl_QueryInterface,
5307 StorageBaseImpl_AddRef,
5308 StorageBaseImpl_Release,
5309 StorageBaseImpl_CreateStream,
5310 StorageBaseImpl_OpenStream,
5311 StorageBaseImpl_CreateStorage,
5312 StorageBaseImpl_OpenStorage,
5313 StorageBaseImpl_CopyTo,
5314 StorageBaseImpl_MoveElementTo,
5315 StorageInternalImpl_Commit,
5316 StorageInternalImpl_Revert,
5317 StorageBaseImpl_EnumElements,
5318 StorageBaseImpl_DestroyElement,
5319 StorageBaseImpl_RenameElement,
5320 StorageBaseImpl_SetElementTimes,
5321 StorageBaseImpl_SetClass,
5322 StorageBaseImpl_SetStateBits,
5323 StorageBaseImpl_Stat
5326 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5328 StorageInternalImpl_Destroy,
5329 StorageInternalImpl_Invalidate,
5330 StorageInternalImpl_CreateDirEntry,
5331 StorageInternalImpl_WriteDirEntry,
5332 StorageInternalImpl_ReadDirEntry,
5333 StorageInternalImpl_DestroyDirEntry,
5334 StorageInternalImpl_StreamReadAt,
5335 StorageInternalImpl_StreamWriteAt,
5336 StorageInternalImpl_StreamSetSize
5339 /******************************************************************************
5340 ** Storage32InternalImpl implementation
5343 static StorageInternalImpl* StorageInternalImpl_Construct(
5344 StorageBaseImpl* parentStorage,
5345 DWORD openFlags,
5346 DirRef storageDirEntry)
5348 StorageInternalImpl* newStorage;
5350 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5352 if (newStorage!=0)
5354 list_init(&newStorage->base.strmHead);
5356 list_init(&newStorage->base.storageHead);
5359 * Initialize the virtual function table.
5361 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5362 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5363 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5364 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5366 newStorage->base.reverted = 0;
5368 newStorage->base.ref = 1;
5370 newStorage->parentStorage = parentStorage;
5373 * Keep a reference to the directory entry of this storage
5375 newStorage->base.storageDirEntry = storageDirEntry;
5377 newStorage->base.create = 0;
5379 return newStorage;
5382 return 0;
5385 /******************************************************************************
5386 ** StorageUtl implementation
5389 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5391 WORD tmp;
5393 memcpy(&tmp, buffer+offset, sizeof(WORD));
5394 *value = lendian16toh(tmp);
5397 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5399 value = htole16(value);
5400 memcpy(buffer+offset, &value, sizeof(WORD));
5403 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5405 DWORD tmp;
5407 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5408 *value = lendian32toh(tmp);
5411 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5413 value = htole32(value);
5414 memcpy(buffer+offset, &value, sizeof(DWORD));
5417 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5418 ULARGE_INTEGER* value)
5420 #ifdef WORDS_BIGENDIAN
5421 ULARGE_INTEGER tmp;
5423 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5424 value->u.LowPart = htole32(tmp.u.HighPart);
5425 value->u.HighPart = htole32(tmp.u.LowPart);
5426 #else
5427 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5428 #endif
5431 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5432 const ULARGE_INTEGER *value)
5434 #ifdef WORDS_BIGENDIAN
5435 ULARGE_INTEGER tmp;
5437 tmp.u.LowPart = htole32(value->u.HighPart);
5438 tmp.u.HighPart = htole32(value->u.LowPart);
5439 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5440 #else
5441 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5442 #endif
5445 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5447 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5448 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5449 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5451 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5454 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5456 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5457 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5458 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5460 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5463 void StorageUtl_CopyDirEntryToSTATSTG(
5464 StorageBaseImpl* storage,
5465 STATSTG* destination,
5466 const DirEntry* source,
5467 int statFlags)
5469 LPCWSTR entryName;
5471 if (source->stgType == STGTY_ROOT)
5473 /* replace the name of root entry (often "Root Entry") by the file name */
5474 entryName = storage->filename;
5476 else
5478 entryName = source->name;
5482 * The copy of the string occurs only when the flag is not set
5484 if( ((statFlags & STATFLAG_NONAME) != 0) ||
5485 (entryName == NULL) ||
5486 (entryName[0] == 0) )
5488 destination->pwcsName = 0;
5490 else
5492 destination->pwcsName =
5493 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
5495 strcpyW(destination->pwcsName, entryName);
5498 switch (source->stgType)
5500 case STGTY_STORAGE:
5501 case STGTY_ROOT:
5502 destination->type = STGTY_STORAGE;
5503 break;
5504 case STGTY_STREAM:
5505 destination->type = STGTY_STREAM;
5506 break;
5507 default:
5508 destination->type = STGTY_STREAM;
5509 break;
5512 destination->cbSize = source->size;
5514 currentReturnStruct->mtime = {0}; TODO
5515 currentReturnStruct->ctime = {0};
5516 currentReturnStruct->atime = {0};
5518 destination->grfMode = 0;
5519 destination->grfLocksSupported = 0;
5520 destination->clsid = source->clsid;
5521 destination->grfStateBits = 0;
5522 destination->reserved = 0;
5525 /******************************************************************************
5526 ** BlockChainStream implementation
5529 /* Read and save the index of all blocks in this stream. */
5530 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5532 ULONG next_sector, next_offset;
5533 HRESULT hr;
5534 struct BlockChainRun *last_run;
5536 if (This->indexCacheLen == 0)
5538 last_run = NULL;
5539 next_offset = 0;
5540 next_sector = BlockChainStream_GetHeadOfChain(This);
5542 else
5544 last_run = &This->indexCache[This->indexCacheLen-1];
5545 next_offset = last_run->lastOffset+1;
5546 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5547 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5548 &next_sector);
5549 if (FAILED(hr)) return hr;
5552 while (next_sector != BLOCK_END_OF_CHAIN)
5554 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5556 /* Add the current block to the cache. */
5557 if (This->indexCacheSize == 0)
5559 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5560 if (!This->indexCache) return E_OUTOFMEMORY;
5561 This->indexCacheSize = 16;
5563 else if (This->indexCacheSize == This->indexCacheLen)
5565 struct BlockChainRun *new_cache;
5566 ULONG new_size;
5568 new_size = This->indexCacheSize * 2;
5569 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5570 if (!new_cache) return E_OUTOFMEMORY;
5571 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5573 HeapFree(GetProcessHeap(), 0, This->indexCache);
5574 This->indexCache = new_cache;
5575 This->indexCacheSize = new_size;
5578 This->indexCacheLen++;
5579 last_run = &This->indexCache[This->indexCacheLen-1];
5580 last_run->firstSector = next_sector;
5581 last_run->firstOffset = next_offset;
5584 last_run->lastOffset = next_offset;
5586 /* Find the next block. */
5587 next_offset++;
5588 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5589 if (FAILED(hr)) return hr;
5592 if (This->indexCacheLen)
5594 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5595 This->numBlocks = last_run->lastOffset+1;
5597 else
5599 This->tailIndex = BLOCK_END_OF_CHAIN;
5600 This->numBlocks = 0;
5603 return S_OK;
5606 /* Locate the nth block in this stream. */
5607 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5609 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5610 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5612 if (offset >= This->numBlocks)
5613 return BLOCK_END_OF_CHAIN;
5615 while (min_run < max_run)
5617 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5618 if (offset < This->indexCache[run_to_check].firstOffset)
5620 max_offset = This->indexCache[run_to_check].firstOffset-1;
5621 max_run = run_to_check-1;
5623 else if (offset > This->indexCache[run_to_check].lastOffset)
5625 min_offset = This->indexCache[run_to_check].lastOffset+1;
5626 min_run = run_to_check+1;
5628 else
5629 /* Block is in this run. */
5630 min_run = max_run = run_to_check;
5633 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5636 BlockChainStream* BlockChainStream_Construct(
5637 StorageImpl* parentStorage,
5638 ULONG* headOfStreamPlaceHolder,
5639 DirRef dirEntry)
5641 BlockChainStream* newStream;
5643 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5645 newStream->parentStorage = parentStorage;
5646 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5647 newStream->ownerDirEntry = dirEntry;
5648 newStream->indexCache = NULL;
5649 newStream->indexCacheLen = 0;
5650 newStream->indexCacheSize = 0;
5652 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5654 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5655 HeapFree(GetProcessHeap(), 0, newStream);
5656 return NULL;
5659 return newStream;
5662 void BlockChainStream_Destroy(BlockChainStream* This)
5664 if (This)
5665 HeapFree(GetProcessHeap(), 0, This->indexCache);
5666 HeapFree(GetProcessHeap(), 0, This);
5669 /******************************************************************************
5670 * BlockChainStream_GetHeadOfChain
5672 * Returns the head of this stream chain.
5673 * Some special chains don't have directory entries, their heads are kept in
5674 * This->headOfStreamPlaceHolder.
5677 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5679 DirEntry chainEntry;
5680 HRESULT hr;
5682 if (This->headOfStreamPlaceHolder != 0)
5683 return *(This->headOfStreamPlaceHolder);
5685 if (This->ownerDirEntry != DIRENTRY_NULL)
5687 hr = StorageImpl_ReadDirEntry(
5688 This->parentStorage,
5689 This->ownerDirEntry,
5690 &chainEntry);
5692 if (SUCCEEDED(hr))
5694 return chainEntry.startingBlock;
5698 return BLOCK_END_OF_CHAIN;
5701 /******************************************************************************
5702 * BlockChainStream_GetCount
5704 * Returns the number of blocks that comprises this chain.
5705 * This is not the size of the stream as the last block may not be full!
5707 * FIXME: Use the cache to get this information.
5709 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5711 ULONG blockIndex;
5712 ULONG count = 0;
5714 blockIndex = BlockChainStream_GetHeadOfChain(This);
5716 while (blockIndex != BLOCK_END_OF_CHAIN)
5718 count++;
5720 if(FAILED(StorageImpl_GetNextBlockInChain(
5721 This->parentStorage,
5722 blockIndex,
5723 &blockIndex)))
5724 return 0;
5727 return count;
5730 /******************************************************************************
5731 * BlockChainStream_ReadAt
5733 * Reads a specified number of bytes from this chain at the specified offset.
5734 * bytesRead may be NULL.
5735 * Failure will be returned if the specified number of bytes has not been read.
5737 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5738 ULARGE_INTEGER offset,
5739 ULONG size,
5740 void* buffer,
5741 ULONG* bytesRead)
5743 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5744 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5745 ULONG bytesToReadInBuffer;
5746 ULONG blockIndex;
5747 BYTE* bufferWalker;
5749 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5752 * Find the first block in the stream that contains part of the buffer.
5754 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5756 if (blockIndex == BLOCK_END_OF_CHAIN)
5757 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5760 * Start reading the buffer.
5762 *bytesRead = 0;
5763 bufferWalker = buffer;
5765 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5767 ULARGE_INTEGER ulOffset;
5768 DWORD bytesReadAt;
5770 * Calculate how many bytes we can copy from this big block.
5772 bytesToReadInBuffer =
5773 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5775 TRACE("block %i\n",blockIndex);
5776 ulOffset.u.HighPart = 0;
5777 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5778 offsetInBlock;
5780 StorageImpl_ReadAt(This->parentStorage,
5781 ulOffset,
5782 bufferWalker,
5783 bytesToReadInBuffer,
5784 &bytesReadAt);
5786 * Step to the next big block.
5788 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5789 return STG_E_DOCFILECORRUPT;
5791 bufferWalker += bytesReadAt;
5792 size -= bytesReadAt;
5793 *bytesRead += bytesReadAt;
5794 offsetInBlock = 0; /* There is no offset on the next block */
5796 if (bytesToReadInBuffer != bytesReadAt)
5797 break;
5800 return (size == 0) ? S_OK : STG_E_READFAULT;
5803 /******************************************************************************
5804 * BlockChainStream_WriteAt
5806 * Writes the specified number of bytes to this chain at the specified offset.
5807 * Will fail if not all specified number of bytes have been written.
5809 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5810 ULARGE_INTEGER offset,
5811 ULONG size,
5812 const void* buffer,
5813 ULONG* bytesWritten)
5815 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5816 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5817 ULONG bytesToWrite;
5818 ULONG blockIndex;
5819 const BYTE* bufferWalker;
5822 * Find the first block in the stream that contains part of the buffer.
5824 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5826 /* BlockChainStream_SetSize should have already been called to ensure we have
5827 * enough blocks in the chain to write into */
5828 if (blockIndex == BLOCK_END_OF_CHAIN)
5830 ERR("not enough blocks in chain to write data\n");
5831 return STG_E_DOCFILECORRUPT;
5834 *bytesWritten = 0;
5835 bufferWalker = buffer;
5837 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5839 ULARGE_INTEGER ulOffset;
5840 DWORD bytesWrittenAt;
5842 * Calculate how many bytes we can copy from this big block.
5844 bytesToWrite =
5845 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5847 TRACE("block %i\n",blockIndex);
5848 ulOffset.u.HighPart = 0;
5849 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5850 offsetInBlock;
5852 StorageImpl_WriteAt(This->parentStorage,
5853 ulOffset,
5854 bufferWalker,
5855 bytesToWrite,
5856 &bytesWrittenAt);
5859 * Step to the next big block.
5861 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5862 &blockIndex)))
5863 return STG_E_DOCFILECORRUPT;
5865 bufferWalker += bytesWrittenAt;
5866 size -= bytesWrittenAt;
5867 *bytesWritten += bytesWrittenAt;
5868 offsetInBlock = 0; /* There is no offset on the next block */
5870 if (bytesWrittenAt != bytesToWrite)
5871 break;
5874 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5877 /******************************************************************************
5878 * BlockChainStream_Shrink
5880 * Shrinks this chain in the big block depot.
5882 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5883 ULARGE_INTEGER newSize)
5885 ULONG blockIndex;
5886 ULONG numBlocks;
5889 * Figure out how many blocks are needed to contain the new size
5891 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5893 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5894 numBlocks++;
5896 if (numBlocks)
5899 * Go to the new end of chain
5901 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
5903 /* Mark the new end of chain */
5904 StorageImpl_SetNextBlockInChain(
5905 This->parentStorage,
5906 blockIndex,
5907 BLOCK_END_OF_CHAIN);
5909 This->tailIndex = blockIndex;
5911 else
5913 if (This->headOfStreamPlaceHolder != 0)
5915 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
5917 else
5919 DirEntry chainEntry;
5920 assert(This->ownerDirEntry != DIRENTRY_NULL);
5922 StorageImpl_ReadDirEntry(
5923 This->parentStorage,
5924 This->ownerDirEntry,
5925 &chainEntry);
5927 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
5929 StorageImpl_WriteDirEntry(
5930 This->parentStorage,
5931 This->ownerDirEntry,
5932 &chainEntry);
5935 This->tailIndex = BLOCK_END_OF_CHAIN;
5938 This->numBlocks = numBlocks;
5941 * Mark the extra blocks as free
5943 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
5945 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
5946 StorageImpl_FreeBigBlock(This->parentStorage,
5947 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
5948 if (last_run->lastOffset == last_run->firstOffset)
5949 This->indexCacheLen--;
5950 else
5951 last_run->lastOffset--;
5954 return TRUE;
5957 /******************************************************************************
5958 * BlockChainStream_Enlarge
5960 * Grows this chain in the big block depot.
5962 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5963 ULARGE_INTEGER newSize)
5965 ULONG blockIndex, currentBlock;
5966 ULONG newNumBlocks;
5967 ULONG oldNumBlocks = 0;
5969 blockIndex = BlockChainStream_GetHeadOfChain(This);
5972 * Empty chain. Create the head.
5974 if (blockIndex == BLOCK_END_OF_CHAIN)
5976 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5977 StorageImpl_SetNextBlockInChain(This->parentStorage,
5978 blockIndex,
5979 BLOCK_END_OF_CHAIN);
5981 if (This->headOfStreamPlaceHolder != 0)
5983 *(This->headOfStreamPlaceHolder) = blockIndex;
5985 else
5987 DirEntry chainEntry;
5988 assert(This->ownerDirEntry != DIRENTRY_NULL);
5990 StorageImpl_ReadDirEntry(
5991 This->parentStorage,
5992 This->ownerDirEntry,
5993 &chainEntry);
5995 chainEntry.startingBlock = blockIndex;
5997 StorageImpl_WriteDirEntry(
5998 This->parentStorage,
5999 This->ownerDirEntry,
6000 &chainEntry);
6003 This->tailIndex = blockIndex;
6004 This->numBlocks = 1;
6008 * Figure out how many blocks are needed to contain this stream
6010 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6012 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6013 newNumBlocks++;
6016 * Go to the current end of chain
6018 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6020 currentBlock = blockIndex;
6022 while (blockIndex != BLOCK_END_OF_CHAIN)
6024 This->numBlocks++;
6025 currentBlock = blockIndex;
6027 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6028 &blockIndex)))
6029 return FALSE;
6032 This->tailIndex = currentBlock;
6035 currentBlock = This->tailIndex;
6036 oldNumBlocks = This->numBlocks;
6039 * Add new blocks to the chain
6041 if (oldNumBlocks < newNumBlocks)
6043 while (oldNumBlocks < newNumBlocks)
6045 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6047 StorageImpl_SetNextBlockInChain(
6048 This->parentStorage,
6049 currentBlock,
6050 blockIndex);
6052 StorageImpl_SetNextBlockInChain(
6053 This->parentStorage,
6054 blockIndex,
6055 BLOCK_END_OF_CHAIN);
6057 currentBlock = blockIndex;
6058 oldNumBlocks++;
6061 This->tailIndex = blockIndex;
6062 This->numBlocks = newNumBlocks;
6065 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6066 return FALSE;
6068 return TRUE;
6071 /******************************************************************************
6072 * BlockChainStream_SetSize
6074 * Sets the size of this stream. The big block depot will be updated.
6075 * The file will grow if we grow the chain.
6077 * TODO: Free the actual blocks in the file when we shrink the chain.
6078 * Currently, the blocks are still in the file. So the file size
6079 * doesn't shrink even if we shrink streams.
6081 BOOL BlockChainStream_SetSize(
6082 BlockChainStream* This,
6083 ULARGE_INTEGER newSize)
6085 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6087 if (newSize.u.LowPart == size.u.LowPart)
6088 return TRUE;
6090 if (newSize.u.LowPart < size.u.LowPart)
6092 BlockChainStream_Shrink(This, newSize);
6094 else
6096 BlockChainStream_Enlarge(This, newSize);
6099 return TRUE;
6102 /******************************************************************************
6103 * BlockChainStream_GetSize
6105 * Returns the size of this chain.
6106 * Will return the block count if this chain doesn't have a directory entry.
6108 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6110 DirEntry chainEntry;
6112 if(This->headOfStreamPlaceHolder == NULL)
6115 * This chain has a directory entry so use the size value from there.
6117 StorageImpl_ReadDirEntry(
6118 This->parentStorage,
6119 This->ownerDirEntry,
6120 &chainEntry);
6122 return chainEntry.size;
6124 else
6127 * this chain is a chain that does not have a directory entry, figure out the
6128 * size by making the product number of used blocks times the
6129 * size of them
6131 ULARGE_INTEGER result;
6132 result.u.HighPart = 0;
6134 result.u.LowPart =
6135 BlockChainStream_GetCount(This) *
6136 This->parentStorage->bigBlockSize;
6138 return result;
6142 /******************************************************************************
6143 ** SmallBlockChainStream implementation
6146 SmallBlockChainStream* SmallBlockChainStream_Construct(
6147 StorageImpl* parentStorage,
6148 ULONG* headOfStreamPlaceHolder,
6149 DirRef dirEntry)
6151 SmallBlockChainStream* newStream;
6153 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6155 newStream->parentStorage = parentStorage;
6156 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6157 newStream->ownerDirEntry = dirEntry;
6159 return newStream;
6162 void SmallBlockChainStream_Destroy(
6163 SmallBlockChainStream* This)
6165 HeapFree(GetProcessHeap(), 0, This);
6168 /******************************************************************************
6169 * SmallBlockChainStream_GetHeadOfChain
6171 * Returns the head of this chain of small blocks.
6173 static ULONG SmallBlockChainStream_GetHeadOfChain(
6174 SmallBlockChainStream* This)
6176 DirEntry chainEntry;
6177 HRESULT hr;
6179 if (This->headOfStreamPlaceHolder != NULL)
6180 return *(This->headOfStreamPlaceHolder);
6182 if (This->ownerDirEntry)
6184 hr = StorageImpl_ReadDirEntry(
6185 This->parentStorage,
6186 This->ownerDirEntry,
6187 &chainEntry);
6189 if (SUCCEEDED(hr))
6191 return chainEntry.startingBlock;
6196 return BLOCK_END_OF_CHAIN;
6199 /******************************************************************************
6200 * SmallBlockChainStream_GetNextBlockInChain
6202 * Returns the index of the next small block in this chain.
6204 * Return Values:
6205 * - BLOCK_END_OF_CHAIN: end of this chain
6206 * - BLOCK_UNUSED: small block 'blockIndex' is free
6208 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6209 SmallBlockChainStream* This,
6210 ULONG blockIndex,
6211 ULONG* nextBlockInChain)
6213 ULARGE_INTEGER offsetOfBlockInDepot;
6214 DWORD buffer;
6215 ULONG bytesRead;
6216 HRESULT res;
6218 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6220 offsetOfBlockInDepot.u.HighPart = 0;
6221 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6224 * Read those bytes in the buffer from the small block file.
6226 res = BlockChainStream_ReadAt(
6227 This->parentStorage->smallBlockDepotChain,
6228 offsetOfBlockInDepot,
6229 sizeof(DWORD),
6230 &buffer,
6231 &bytesRead);
6233 if (SUCCEEDED(res))
6235 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6236 return S_OK;
6239 return res;
6242 /******************************************************************************
6243 * SmallBlockChainStream_SetNextBlockInChain
6245 * Writes the index of the next block of the specified block in the small
6246 * block depot.
6247 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6248 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6250 static void SmallBlockChainStream_SetNextBlockInChain(
6251 SmallBlockChainStream* This,
6252 ULONG blockIndex,
6253 ULONG nextBlock)
6255 ULARGE_INTEGER offsetOfBlockInDepot;
6256 DWORD buffer;
6257 ULONG bytesWritten;
6259 offsetOfBlockInDepot.u.HighPart = 0;
6260 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6262 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6265 * Read those bytes in the buffer from the small block file.
6267 BlockChainStream_WriteAt(
6268 This->parentStorage->smallBlockDepotChain,
6269 offsetOfBlockInDepot,
6270 sizeof(DWORD),
6271 &buffer,
6272 &bytesWritten);
6275 /******************************************************************************
6276 * SmallBlockChainStream_FreeBlock
6278 * Flag small block 'blockIndex' as free in the small block depot.
6280 static void SmallBlockChainStream_FreeBlock(
6281 SmallBlockChainStream* This,
6282 ULONG blockIndex)
6284 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6287 /******************************************************************************
6288 * SmallBlockChainStream_GetNextFreeBlock
6290 * Returns the index of a free small block. The small block depot will be
6291 * enlarged if necessary. The small block chain will also be enlarged if
6292 * necessary.
6294 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6295 SmallBlockChainStream* This)
6297 ULARGE_INTEGER offsetOfBlockInDepot;
6298 DWORD buffer;
6299 ULONG bytesRead;
6300 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6301 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6302 HRESULT res = S_OK;
6303 ULONG smallBlocksPerBigBlock;
6305 offsetOfBlockInDepot.u.HighPart = 0;
6308 * Scan the small block depot for a free block
6310 while (nextBlockIndex != BLOCK_UNUSED)
6312 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6314 res = BlockChainStream_ReadAt(
6315 This->parentStorage->smallBlockDepotChain,
6316 offsetOfBlockInDepot,
6317 sizeof(DWORD),
6318 &buffer,
6319 &bytesRead);
6322 * If we run out of space for the small block depot, enlarge it
6324 if (SUCCEEDED(res))
6326 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6328 if (nextBlockIndex != BLOCK_UNUSED)
6329 blockIndex++;
6331 else
6333 ULONG count =
6334 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6336 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6337 ULARGE_INTEGER newSize, offset;
6338 ULONG bytesWritten;
6340 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6341 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6344 * Initialize all the small blocks to free
6346 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6347 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6348 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6349 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6351 StorageImpl_SaveFileHeader(This->parentStorage);
6355 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6357 smallBlocksPerBigBlock =
6358 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6361 * Verify if we have to allocate big blocks to contain small blocks
6363 if (blockIndex % smallBlocksPerBigBlock == 0)
6365 DirEntry rootEntry;
6366 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6367 ULARGE_INTEGER old_size, size_required;
6369 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6371 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6373 if (size_required.QuadPart > old_size.QuadPart)
6375 BlockChainStream_SetSize(
6376 This->parentStorage->smallBlockRootChain,
6377 size_required);
6379 StorageImpl_ReadDirEntry(
6380 This->parentStorage,
6381 This->parentStorage->base.storageDirEntry,
6382 &rootEntry);
6384 rootEntry.size = size_required;
6386 StorageImpl_WriteDirEntry(
6387 This->parentStorage,
6388 This->parentStorage->base.storageDirEntry,
6389 &rootEntry);
6393 return blockIndex;
6396 /******************************************************************************
6397 * SmallBlockChainStream_ReadAt
6399 * Reads a specified number of bytes from this chain at the specified offset.
6400 * bytesRead may be NULL.
6401 * Failure will be returned if the specified number of bytes has not been read.
6403 HRESULT SmallBlockChainStream_ReadAt(
6404 SmallBlockChainStream* This,
6405 ULARGE_INTEGER offset,
6406 ULONG size,
6407 void* buffer,
6408 ULONG* bytesRead)
6410 HRESULT rc = S_OK;
6411 ULARGE_INTEGER offsetInBigBlockFile;
6412 ULONG blockNoInSequence =
6413 offset.u.LowPart / This->parentStorage->smallBlockSize;
6415 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6416 ULONG bytesToReadInBuffer;
6417 ULONG blockIndex;
6418 ULONG bytesReadFromBigBlockFile;
6419 BYTE* bufferWalker;
6422 * This should never happen on a small block file.
6424 assert(offset.u.HighPart==0);
6427 * Find the first block in the stream that contains part of the buffer.
6429 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6431 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6433 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6434 if(FAILED(rc))
6435 return rc;
6436 blockNoInSequence--;
6440 * Start reading the buffer.
6442 *bytesRead = 0;
6443 bufferWalker = buffer;
6445 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6448 * Calculate how many bytes we can copy from this small block.
6450 bytesToReadInBuffer =
6451 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6454 * Calculate the offset of the small block in the small block file.
6456 offsetInBigBlockFile.u.HighPart = 0;
6457 offsetInBigBlockFile.u.LowPart =
6458 blockIndex * This->parentStorage->smallBlockSize;
6460 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6463 * Read those bytes in the buffer from the small block file.
6464 * The small block has already been identified so it shouldn't fail
6465 * unless the file is corrupt.
6467 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6468 offsetInBigBlockFile,
6469 bytesToReadInBuffer,
6470 bufferWalker,
6471 &bytesReadFromBigBlockFile);
6473 if (FAILED(rc))
6474 return rc;
6477 * Step to the next big block.
6479 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6480 if(FAILED(rc))
6481 return STG_E_DOCFILECORRUPT;
6483 bufferWalker += bytesReadFromBigBlockFile;
6484 size -= bytesReadFromBigBlockFile;
6485 *bytesRead += bytesReadFromBigBlockFile;
6486 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6489 return (size == 0) ? S_OK : STG_E_READFAULT;
6492 /******************************************************************************
6493 * SmallBlockChainStream_WriteAt
6495 * Writes the specified number of bytes to this chain at the specified offset.
6496 * Will fail if not all specified number of bytes have been written.
6498 HRESULT SmallBlockChainStream_WriteAt(
6499 SmallBlockChainStream* This,
6500 ULARGE_INTEGER offset,
6501 ULONG size,
6502 const void* buffer,
6503 ULONG* bytesWritten)
6505 ULARGE_INTEGER offsetInBigBlockFile;
6506 ULONG blockNoInSequence =
6507 offset.u.LowPart / This->parentStorage->smallBlockSize;
6509 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6510 ULONG bytesToWriteInBuffer;
6511 ULONG blockIndex;
6512 ULONG bytesWrittenToBigBlockFile;
6513 const BYTE* bufferWalker;
6514 HRESULT res;
6517 * This should never happen on a small block file.
6519 assert(offset.u.HighPart==0);
6522 * Find the first block in the stream that contains part of the buffer.
6524 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6526 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6528 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6529 return STG_E_DOCFILECORRUPT;
6530 blockNoInSequence--;
6534 * Start writing the buffer.
6536 *bytesWritten = 0;
6537 bufferWalker = buffer;
6538 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6541 * Calculate how many bytes we can copy to this small block.
6543 bytesToWriteInBuffer =
6544 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6547 * Calculate the offset of the small block in the small block file.
6549 offsetInBigBlockFile.u.HighPart = 0;
6550 offsetInBigBlockFile.u.LowPart =
6551 blockIndex * This->parentStorage->smallBlockSize;
6553 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6556 * Write those bytes in the buffer to the small block file.
6558 res = BlockChainStream_WriteAt(
6559 This->parentStorage->smallBlockRootChain,
6560 offsetInBigBlockFile,
6561 bytesToWriteInBuffer,
6562 bufferWalker,
6563 &bytesWrittenToBigBlockFile);
6564 if (FAILED(res))
6565 return res;
6568 * Step to the next big block.
6570 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6571 &blockIndex)))
6572 return FALSE;
6573 bufferWalker += bytesWrittenToBigBlockFile;
6574 size -= bytesWrittenToBigBlockFile;
6575 *bytesWritten += bytesWrittenToBigBlockFile;
6576 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6579 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6582 /******************************************************************************
6583 * SmallBlockChainStream_Shrink
6585 * Shrinks this chain in the small block depot.
6587 static BOOL SmallBlockChainStream_Shrink(
6588 SmallBlockChainStream* This,
6589 ULARGE_INTEGER newSize)
6591 ULONG blockIndex, extraBlock;
6592 ULONG numBlocks;
6593 ULONG count = 0;
6595 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6597 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6598 numBlocks++;
6600 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6603 * Go to the new end of chain
6605 while (count < numBlocks)
6607 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6608 &blockIndex)))
6609 return FALSE;
6610 count++;
6614 * If the count is 0, we have a special case, the head of the chain was
6615 * just freed.
6617 if (count == 0)
6619 DirEntry chainEntry;
6621 StorageImpl_ReadDirEntry(This->parentStorage,
6622 This->ownerDirEntry,
6623 &chainEntry);
6625 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6627 StorageImpl_WriteDirEntry(This->parentStorage,
6628 This->ownerDirEntry,
6629 &chainEntry);
6632 * We start freeing the chain at the head block.
6634 extraBlock = blockIndex;
6636 else
6638 /* Get the next block before marking the new end */
6639 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6640 &extraBlock)))
6641 return FALSE;
6643 /* Mark the new end of chain */
6644 SmallBlockChainStream_SetNextBlockInChain(
6645 This,
6646 blockIndex,
6647 BLOCK_END_OF_CHAIN);
6651 * Mark the extra blocks as free
6653 while (extraBlock != BLOCK_END_OF_CHAIN)
6655 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6656 &blockIndex)))
6657 return FALSE;
6658 SmallBlockChainStream_FreeBlock(This, extraBlock);
6659 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
6660 extraBlock = blockIndex;
6663 return TRUE;
6666 /******************************************************************************
6667 * SmallBlockChainStream_Enlarge
6669 * Grows this chain in the small block depot.
6671 static BOOL SmallBlockChainStream_Enlarge(
6672 SmallBlockChainStream* This,
6673 ULARGE_INTEGER newSize)
6675 ULONG blockIndex, currentBlock;
6676 ULONG newNumBlocks;
6677 ULONG oldNumBlocks = 0;
6679 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6682 * Empty chain. Create the head.
6684 if (blockIndex == BLOCK_END_OF_CHAIN)
6686 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6687 SmallBlockChainStream_SetNextBlockInChain(
6688 This,
6689 blockIndex,
6690 BLOCK_END_OF_CHAIN);
6692 if (This->headOfStreamPlaceHolder != NULL)
6694 *(This->headOfStreamPlaceHolder) = blockIndex;
6696 else
6698 DirEntry chainEntry;
6700 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6701 &chainEntry);
6703 chainEntry.startingBlock = blockIndex;
6705 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6706 &chainEntry);
6710 currentBlock = blockIndex;
6713 * Figure out how many blocks are needed to contain this stream
6715 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6717 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6718 newNumBlocks++;
6721 * Go to the current end of chain
6723 while (blockIndex != BLOCK_END_OF_CHAIN)
6725 oldNumBlocks++;
6726 currentBlock = blockIndex;
6727 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6728 return FALSE;
6732 * Add new blocks to the chain
6734 while (oldNumBlocks < newNumBlocks)
6736 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6737 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6739 SmallBlockChainStream_SetNextBlockInChain(
6740 This,
6741 blockIndex,
6742 BLOCK_END_OF_CHAIN);
6744 currentBlock = blockIndex;
6745 oldNumBlocks++;
6748 return TRUE;
6751 /******************************************************************************
6752 * SmallBlockChainStream_SetSize
6754 * Sets the size of this stream.
6755 * The file will grow if we grow the chain.
6757 * TODO: Free the actual blocks in the file when we shrink the chain.
6758 * Currently, the blocks are still in the file. So the file size
6759 * doesn't shrink even if we shrink streams.
6761 BOOL SmallBlockChainStream_SetSize(
6762 SmallBlockChainStream* This,
6763 ULARGE_INTEGER newSize)
6765 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6767 if (newSize.u.LowPart == size.u.LowPart)
6768 return TRUE;
6770 if (newSize.u.LowPart < size.u.LowPart)
6772 SmallBlockChainStream_Shrink(This, newSize);
6774 else
6776 SmallBlockChainStream_Enlarge(This, newSize);
6779 return TRUE;
6782 /******************************************************************************
6783 * SmallBlockChainStream_GetCount
6785 * Returns the number of small blocks that comprises this chain.
6786 * This is not the size of the stream as the last block may not be full!
6789 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6791 ULONG blockIndex;
6792 ULONG count = 0;
6794 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6796 while(blockIndex != BLOCK_END_OF_CHAIN)
6798 count++;
6800 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6801 blockIndex, &blockIndex)))
6802 return 0;
6805 return count;
6808 /******************************************************************************
6809 * SmallBlockChainStream_GetSize
6811 * Returns the size of this chain.
6813 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6815 DirEntry chainEntry;
6817 if(This->headOfStreamPlaceHolder != NULL)
6819 ULARGE_INTEGER result;
6820 result.u.HighPart = 0;
6822 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6823 This->parentStorage->smallBlockSize;
6825 return result;
6828 StorageImpl_ReadDirEntry(
6829 This->parentStorage,
6830 This->ownerDirEntry,
6831 &chainEntry);
6833 return chainEntry.size;
6836 static HRESULT create_storagefile(
6837 LPCOLESTR pwcsName,
6838 DWORD grfMode,
6839 DWORD grfAttrs,
6840 STGOPTIONS* pStgOptions,
6841 REFIID riid,
6842 void** ppstgOpen)
6844 StorageBaseImpl* newStorage = 0;
6845 HANDLE hFile = INVALID_HANDLE_VALUE;
6846 HRESULT hr = STG_E_INVALIDFLAG;
6847 DWORD shareMode;
6848 DWORD accessMode;
6849 DWORD creationMode;
6850 DWORD fileAttributes;
6851 WCHAR tempFileName[MAX_PATH];
6853 if (ppstgOpen == 0)
6854 return STG_E_INVALIDPOINTER;
6856 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
6857 return STG_E_INVALIDPARAMETER;
6859 /* if no share mode given then DENY_NONE is the default */
6860 if (STGM_SHARE_MODE(grfMode) == 0)
6861 grfMode |= STGM_SHARE_DENY_NONE;
6863 if ( FAILED( validateSTGM(grfMode) ))
6864 goto end;
6866 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6867 switch(STGM_ACCESS_MODE(grfMode))
6869 case STGM_WRITE:
6870 case STGM_READWRITE:
6871 break;
6872 default:
6873 goto end;
6876 /* in direct mode, can only use SHARE_EXCLUSIVE */
6877 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6878 goto end;
6880 /* but in transacted mode, any share mode is valid */
6883 * Generate a unique name.
6885 if (pwcsName == 0)
6887 WCHAR tempPath[MAX_PATH];
6888 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6890 memset(tempPath, 0, sizeof(tempPath));
6891 memset(tempFileName, 0, sizeof(tempFileName));
6893 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6894 tempPath[0] = '.';
6896 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6897 pwcsName = tempFileName;
6898 else
6900 hr = STG_E_INSUFFICIENTMEMORY;
6901 goto end;
6904 creationMode = TRUNCATE_EXISTING;
6906 else
6908 creationMode = GetCreationModeFromSTGM(grfMode);
6912 * Interpret the STGM value grfMode
6914 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6915 accessMode = GetAccessModeFromSTGM(grfMode);
6917 if (grfMode & STGM_DELETEONRELEASE)
6918 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6919 else
6920 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6922 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6924 static int fixme;
6925 if (!fixme++)
6926 FIXME("Storage share mode not implemented.\n");
6929 *ppstgOpen = 0;
6931 hFile = CreateFileW(pwcsName,
6932 accessMode,
6933 shareMode,
6934 NULL,
6935 creationMode,
6936 fileAttributes,
6939 if (hFile == INVALID_HANDLE_VALUE)
6941 if(GetLastError() == ERROR_FILE_EXISTS)
6942 hr = STG_E_FILEALREADYEXISTS;
6943 else
6944 hr = E_FAIL;
6945 goto end;
6949 * Allocate and initialize the new IStorage32object.
6951 hr = Storage_Construct(
6952 hFile,
6953 pwcsName,
6954 NULL,
6955 grfMode,
6956 TRUE,
6957 TRUE,
6958 pStgOptions->ulSectorSize,
6959 &newStorage);
6961 if (FAILED(hr))
6963 goto end;
6966 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
6968 IStorage_Release((IStorage*)newStorage);
6970 end:
6971 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6973 return hr;
6976 /******************************************************************************
6977 * StgCreateDocfile [OLE32.@]
6978 * Creates a new compound file storage object
6980 * PARAMS
6981 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6982 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6983 * reserved [ ?] unused?, usually 0
6984 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6986 * RETURNS
6987 * S_OK if the file was successfully created
6988 * some STG_E_ value if error
6989 * NOTES
6990 * if pwcsName is NULL, create file with new unique name
6991 * the function can returns
6992 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6993 * (unrealized now)
6995 HRESULT WINAPI StgCreateDocfile(
6996 LPCOLESTR pwcsName,
6997 DWORD grfMode,
6998 DWORD reserved,
6999 IStorage **ppstgOpen)
7001 STGOPTIONS stgoptions = {1, 0, 512};
7003 TRACE("(%s, %x, %d, %p)\n",
7004 debugstr_w(pwcsName), grfMode,
7005 reserved, ppstgOpen);
7007 if (ppstgOpen == 0)
7008 return STG_E_INVALIDPOINTER;
7009 if (reserved != 0)
7010 return STG_E_INVALIDPARAMETER;
7012 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7015 /******************************************************************************
7016 * StgCreateStorageEx [OLE32.@]
7018 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7020 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7021 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7023 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7025 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7026 return STG_E_INVALIDPARAMETER;
7029 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7031 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7032 return STG_E_INVALIDPARAMETER;
7035 if (stgfmt == STGFMT_FILE)
7037 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7038 return STG_E_INVALIDPARAMETER;
7041 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7043 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7047 ERR("Invalid stgfmt argument\n");
7048 return STG_E_INVALIDPARAMETER;
7051 /******************************************************************************
7052 * StgCreatePropSetStg [OLE32.@]
7054 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7055 IPropertySetStorage **ppPropSetStg)
7057 HRESULT hr;
7059 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7060 if (reserved)
7061 hr = STG_E_INVALIDPARAMETER;
7062 else
7063 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7064 (void**)ppPropSetStg);
7065 return hr;
7068 /******************************************************************************
7069 * StgOpenStorageEx [OLE32.@]
7071 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7073 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7074 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7076 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7078 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7079 return STG_E_INVALIDPARAMETER;
7082 switch (stgfmt)
7084 case STGFMT_FILE:
7085 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7086 return STG_E_INVALIDPARAMETER;
7088 case STGFMT_STORAGE:
7089 break;
7091 case STGFMT_DOCFILE:
7092 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7094 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7095 return STG_E_INVALIDPARAMETER;
7097 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7098 break;
7100 case STGFMT_ANY:
7101 WARN("STGFMT_ANY assuming storage\n");
7102 break;
7104 default:
7105 return STG_E_INVALIDPARAMETER;
7108 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7112 /******************************************************************************
7113 * StgOpenStorage [OLE32.@]
7115 HRESULT WINAPI StgOpenStorage(
7116 const OLECHAR *pwcsName,
7117 IStorage *pstgPriority,
7118 DWORD grfMode,
7119 SNB snbExclude,
7120 DWORD reserved,
7121 IStorage **ppstgOpen)
7123 StorageBaseImpl* newStorage = 0;
7124 HRESULT hr = S_OK;
7125 HANDLE hFile = 0;
7126 DWORD shareMode;
7127 DWORD accessMode;
7129 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7130 debugstr_w(pwcsName), pstgPriority, grfMode,
7131 snbExclude, reserved, ppstgOpen);
7133 if (pwcsName == 0)
7135 hr = STG_E_INVALIDNAME;
7136 goto end;
7139 if (ppstgOpen == 0)
7141 hr = STG_E_INVALIDPOINTER;
7142 goto end;
7145 if (reserved)
7147 hr = STG_E_INVALIDPARAMETER;
7148 goto end;
7151 if (grfMode & STGM_PRIORITY)
7153 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7154 return STG_E_INVALIDFLAG;
7155 if (grfMode & STGM_DELETEONRELEASE)
7156 return STG_E_INVALIDFUNCTION;
7157 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7158 return STG_E_INVALIDFLAG;
7159 grfMode &= ~0xf0; /* remove the existing sharing mode */
7160 grfMode |= STGM_SHARE_DENY_NONE;
7162 /* STGM_PRIORITY stops other IStorage objects on the same file from
7163 * committing until the STGM_PRIORITY IStorage is closed. it also
7164 * stops non-transacted mode StgOpenStorage calls with write access from
7165 * succeeding. obviously, both of these cannot be achieved through just
7166 * file share flags */
7167 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7171 * Validate the sharing mode
7173 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7174 switch(STGM_SHARE_MODE(grfMode))
7176 case STGM_SHARE_EXCLUSIVE:
7177 case STGM_SHARE_DENY_WRITE:
7178 break;
7179 default:
7180 hr = STG_E_INVALIDFLAG;
7181 goto end;
7184 if ( FAILED( validateSTGM(grfMode) ) ||
7185 (grfMode&STGM_CREATE))
7187 hr = STG_E_INVALIDFLAG;
7188 goto end;
7191 /* shared reading requires transacted mode */
7192 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7193 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7194 !(grfMode&STGM_TRANSACTED) )
7196 hr = STG_E_INVALIDFLAG;
7197 goto end;
7201 * Interpret the STGM value grfMode
7203 shareMode = GetShareModeFromSTGM(grfMode);
7204 accessMode = GetAccessModeFromSTGM(grfMode);
7206 *ppstgOpen = 0;
7208 hFile = CreateFileW( pwcsName,
7209 accessMode,
7210 shareMode,
7211 NULL,
7212 OPEN_EXISTING,
7213 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7216 if (hFile==INVALID_HANDLE_VALUE)
7218 DWORD last_error = GetLastError();
7220 hr = E_FAIL;
7222 switch (last_error)
7224 case ERROR_FILE_NOT_FOUND:
7225 hr = STG_E_FILENOTFOUND;
7226 break;
7228 case ERROR_PATH_NOT_FOUND:
7229 hr = STG_E_PATHNOTFOUND;
7230 break;
7232 case ERROR_ACCESS_DENIED:
7233 case ERROR_WRITE_PROTECT:
7234 hr = STG_E_ACCESSDENIED;
7235 break;
7237 case ERROR_SHARING_VIOLATION:
7238 hr = STG_E_SHAREVIOLATION;
7239 break;
7241 default:
7242 hr = E_FAIL;
7245 goto end;
7249 * Refuse to open the file if it's too small to be a structured storage file
7250 * FIXME: verify the file when reading instead of here
7252 if (GetFileSize(hFile, NULL) < 0x100)
7254 CloseHandle(hFile);
7255 hr = STG_E_FILEALREADYEXISTS;
7256 goto end;
7260 * Allocate and initialize the new IStorage32object.
7262 hr = Storage_Construct(
7263 hFile,
7264 pwcsName,
7265 NULL,
7266 grfMode,
7267 TRUE,
7268 FALSE,
7269 512,
7270 &newStorage);
7272 if (FAILED(hr))
7275 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7277 if(hr == STG_E_INVALIDHEADER)
7278 hr = STG_E_FILEALREADYEXISTS;
7279 goto end;
7283 * Get an "out" pointer for the caller.
7285 *ppstgOpen = (IStorage*)newStorage;
7287 end:
7288 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7289 return hr;
7292 /******************************************************************************
7293 * StgCreateDocfileOnILockBytes [OLE32.@]
7295 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7296 ILockBytes *plkbyt,
7297 DWORD grfMode,
7298 DWORD reserved,
7299 IStorage** ppstgOpen)
7301 StorageBaseImpl* newStorage = 0;
7302 HRESULT hr = S_OK;
7304 if ((ppstgOpen == 0) || (plkbyt == 0))
7305 return STG_E_INVALIDPOINTER;
7308 * Allocate and initialize the new IStorage object.
7310 hr = Storage_Construct(
7313 plkbyt,
7314 grfMode,
7315 FALSE,
7316 TRUE,
7317 512,
7318 &newStorage);
7320 if (FAILED(hr))
7322 return hr;
7326 * Get an "out" pointer for the caller.
7328 *ppstgOpen = (IStorage*)newStorage;
7330 return hr;
7333 /******************************************************************************
7334 * StgOpenStorageOnILockBytes [OLE32.@]
7336 HRESULT WINAPI StgOpenStorageOnILockBytes(
7337 ILockBytes *plkbyt,
7338 IStorage *pstgPriority,
7339 DWORD grfMode,
7340 SNB snbExclude,
7341 DWORD reserved,
7342 IStorage **ppstgOpen)
7344 StorageBaseImpl* newStorage = 0;
7345 HRESULT hr = S_OK;
7347 if ((plkbyt == 0) || (ppstgOpen == 0))
7348 return STG_E_INVALIDPOINTER;
7350 if ( FAILED( validateSTGM(grfMode) ))
7351 return STG_E_INVALIDFLAG;
7353 *ppstgOpen = 0;
7356 * Allocate and initialize the new IStorage object.
7358 hr = Storage_Construct(
7361 plkbyt,
7362 grfMode,
7363 FALSE,
7364 FALSE,
7365 512,
7366 &newStorage);
7368 if (FAILED(hr))
7370 return hr;
7374 * Get an "out" pointer for the caller.
7376 *ppstgOpen = (IStorage*)newStorage;
7378 return hr;
7381 /******************************************************************************
7382 * StgSetTimes [ole32.@]
7383 * StgSetTimes [OLE32.@]
7387 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7388 FILETIME const *patime, FILETIME const *pmtime)
7390 IStorage *stg = NULL;
7391 HRESULT r;
7393 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7395 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7396 0, 0, &stg);
7397 if( SUCCEEDED(r) )
7399 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7400 IStorage_Release(stg);
7403 return r;
7406 /******************************************************************************
7407 * StgIsStorageILockBytes [OLE32.@]
7409 * Determines if the ILockBytes contains a storage object.
7411 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7413 BYTE sig[8];
7414 ULARGE_INTEGER offset;
7416 offset.u.HighPart = 0;
7417 offset.u.LowPart = 0;
7419 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7421 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7422 return S_OK;
7424 return S_FALSE;
7427 /******************************************************************************
7428 * WriteClassStg [OLE32.@]
7430 * This method will store the specified CLSID in the specified storage object
7432 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7434 HRESULT hRes;
7436 if(!pStg)
7437 return E_INVALIDARG;
7439 if(!rclsid)
7440 return STG_E_INVALIDPOINTER;
7442 hRes = IStorage_SetClass(pStg, rclsid);
7444 return hRes;
7447 /***********************************************************************
7448 * ReadClassStg (OLE32.@)
7450 * This method reads the CLSID previously written to a storage object with
7451 * the WriteClassStg.
7453 * PARAMS
7454 * pstg [I] IStorage pointer
7455 * pclsid [O] Pointer to where the CLSID is written
7457 * RETURNS
7458 * Success: S_OK.
7459 * Failure: HRESULT code.
7461 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7463 STATSTG pstatstg;
7464 HRESULT hRes;
7466 TRACE("(%p, %p)\n", pstg, pclsid);
7468 if(!pstg || !pclsid)
7469 return E_INVALIDARG;
7472 * read a STATSTG structure (contains the clsid) from the storage
7474 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7476 if(SUCCEEDED(hRes))
7477 *pclsid=pstatstg.clsid;
7479 return hRes;
7482 /***********************************************************************
7483 * OleLoadFromStream (OLE32.@)
7485 * This function loads an object from stream
7487 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7489 CLSID clsid;
7490 HRESULT res;
7491 LPPERSISTSTREAM xstm;
7493 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7495 res=ReadClassStm(pStm,&clsid);
7496 if (FAILED(res))
7497 return res;
7498 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7499 if (FAILED(res))
7500 return res;
7501 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7502 if (FAILED(res)) {
7503 IUnknown_Release((IUnknown*)*ppvObj);
7504 return res;
7506 res=IPersistStream_Load(xstm,pStm);
7507 IPersistStream_Release(xstm);
7508 /* FIXME: all refcounts ok at this point? I think they should be:
7509 * pStm : unchanged
7510 * ppvObj : 1
7511 * xstm : 0 (released)
7513 return res;
7516 /***********************************************************************
7517 * OleSaveToStream (OLE32.@)
7519 * This function saves an object with the IPersistStream interface on it
7520 * to the specified stream.
7522 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7525 CLSID clsid;
7526 HRESULT res;
7528 TRACE("(%p,%p)\n",pPStm,pStm);
7530 res=IPersistStream_GetClassID(pPStm,&clsid);
7532 if (SUCCEEDED(res)){
7534 res=WriteClassStm(pStm,&clsid);
7536 if (SUCCEEDED(res))
7538 res=IPersistStream_Save(pPStm,pStm,TRUE);
7541 TRACE("Finished Save\n");
7542 return res;
7545 /****************************************************************************
7546 * This method validate a STGM parameter that can contain the values below
7548 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7549 * The stgm values contained in 0xffff0000 are bitmasks.
7551 * STGM_DIRECT 0x00000000
7552 * STGM_TRANSACTED 0x00010000
7553 * STGM_SIMPLE 0x08000000
7555 * STGM_READ 0x00000000
7556 * STGM_WRITE 0x00000001
7557 * STGM_READWRITE 0x00000002
7559 * STGM_SHARE_DENY_NONE 0x00000040
7560 * STGM_SHARE_DENY_READ 0x00000030
7561 * STGM_SHARE_DENY_WRITE 0x00000020
7562 * STGM_SHARE_EXCLUSIVE 0x00000010
7564 * STGM_PRIORITY 0x00040000
7565 * STGM_DELETEONRELEASE 0x04000000
7567 * STGM_CREATE 0x00001000
7568 * STGM_CONVERT 0x00020000
7569 * STGM_FAILIFTHERE 0x00000000
7571 * STGM_NOSCRATCH 0x00100000
7572 * STGM_NOSNAPSHOT 0x00200000
7574 static HRESULT validateSTGM(DWORD stgm)
7576 DWORD access = STGM_ACCESS_MODE(stgm);
7577 DWORD share = STGM_SHARE_MODE(stgm);
7578 DWORD create = STGM_CREATE_MODE(stgm);
7580 if (stgm&~STGM_KNOWN_FLAGS)
7582 ERR("unknown flags %08x\n", stgm);
7583 return E_FAIL;
7586 switch (access)
7588 case STGM_READ:
7589 case STGM_WRITE:
7590 case STGM_READWRITE:
7591 break;
7592 default:
7593 return E_FAIL;
7596 switch (share)
7598 case STGM_SHARE_DENY_NONE:
7599 case STGM_SHARE_DENY_READ:
7600 case STGM_SHARE_DENY_WRITE:
7601 case STGM_SHARE_EXCLUSIVE:
7602 break;
7603 default:
7604 return E_FAIL;
7607 switch (create)
7609 case STGM_CREATE:
7610 case STGM_FAILIFTHERE:
7611 break;
7612 default:
7613 return E_FAIL;
7617 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7619 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7620 return E_FAIL;
7623 * STGM_CREATE | STGM_CONVERT
7624 * if both are false, STGM_FAILIFTHERE is set to TRUE
7626 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7627 return E_FAIL;
7630 * STGM_NOSCRATCH requires STGM_TRANSACTED
7632 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7633 return E_FAIL;
7636 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7637 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7639 if ( (stgm & STGM_NOSNAPSHOT) &&
7640 (!(stgm & STGM_TRANSACTED) ||
7641 share == STGM_SHARE_EXCLUSIVE ||
7642 share == STGM_SHARE_DENY_WRITE) )
7643 return E_FAIL;
7645 return S_OK;
7648 /****************************************************************************
7649 * GetShareModeFromSTGM
7651 * This method will return a share mode flag from a STGM value.
7652 * The STGM value is assumed valid.
7654 static DWORD GetShareModeFromSTGM(DWORD stgm)
7656 switch (STGM_SHARE_MODE(stgm))
7658 case STGM_SHARE_DENY_NONE:
7659 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7660 case STGM_SHARE_DENY_READ:
7661 return FILE_SHARE_WRITE;
7662 case STGM_SHARE_DENY_WRITE:
7663 return FILE_SHARE_READ;
7664 case STGM_SHARE_EXCLUSIVE:
7665 return 0;
7667 ERR("Invalid share mode!\n");
7668 assert(0);
7669 return 0;
7672 /****************************************************************************
7673 * GetAccessModeFromSTGM
7675 * This method will return an access mode flag from a STGM value.
7676 * The STGM value is assumed valid.
7678 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7680 switch (STGM_ACCESS_MODE(stgm))
7682 case STGM_READ:
7683 return GENERIC_READ;
7684 case STGM_WRITE:
7685 case STGM_READWRITE:
7686 return GENERIC_READ | GENERIC_WRITE;
7688 ERR("Invalid access mode!\n");
7689 assert(0);
7690 return 0;
7693 /****************************************************************************
7694 * GetCreationModeFromSTGM
7696 * This method will return a creation mode flag from a STGM value.
7697 * The STGM value is assumed valid.
7699 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7701 switch(STGM_CREATE_MODE(stgm))
7703 case STGM_CREATE:
7704 return CREATE_ALWAYS;
7705 case STGM_CONVERT:
7706 FIXME("STGM_CONVERT not implemented!\n");
7707 return CREATE_NEW;
7708 case STGM_FAILIFTHERE:
7709 return CREATE_NEW;
7711 ERR("Invalid create mode!\n");
7712 assert(0);
7713 return 0;
7717 /*************************************************************************
7718 * OLECONVERT_LoadOLE10 [Internal]
7720 * Loads the OLE10 STREAM to memory
7722 * PARAMS
7723 * pOleStream [I] The OLESTREAM
7724 * pData [I] Data Structure for the OLESTREAM Data
7726 * RETURNS
7727 * Success: S_OK
7728 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7729 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7731 * NOTES
7732 * This function is used by OleConvertOLESTREAMToIStorage only.
7734 * Memory allocated for pData must be freed by the caller
7736 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7738 DWORD dwSize;
7739 HRESULT hRes = S_OK;
7740 int nTryCnt=0;
7741 int max_try = 6;
7743 pData->pData = NULL;
7744 pData->pstrOleObjFileName = NULL;
7746 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7748 /* Get the OleID */
7749 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7750 if(dwSize != sizeof(pData->dwOleID))
7752 hRes = CONVERT10_E_OLESTREAM_GET;
7754 else if(pData->dwOleID != OLESTREAM_ID)
7756 hRes = CONVERT10_E_OLESTREAM_FMT;
7758 else
7760 hRes = S_OK;
7761 break;
7765 if(hRes == S_OK)
7767 /* Get the TypeID... more info needed for this field */
7768 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7769 if(dwSize != sizeof(pData->dwTypeID))
7771 hRes = CONVERT10_E_OLESTREAM_GET;
7774 if(hRes == S_OK)
7776 if(pData->dwTypeID != 0)
7778 /* Get the length of the OleTypeName */
7779 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7780 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7782 hRes = CONVERT10_E_OLESTREAM_GET;
7785 if(hRes == S_OK)
7787 if(pData->dwOleTypeNameLength > 0)
7789 /* Get the OleTypeName */
7790 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7791 if(dwSize != pData->dwOleTypeNameLength)
7793 hRes = CONVERT10_E_OLESTREAM_GET;
7797 if(bStrem1)
7799 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7800 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7802 hRes = CONVERT10_E_OLESTREAM_GET;
7804 if(hRes == S_OK)
7806 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7807 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7808 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7809 if(pData->pstrOleObjFileName)
7811 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7812 if(dwSize != pData->dwOleObjFileNameLength)
7814 hRes = CONVERT10_E_OLESTREAM_GET;
7817 else
7818 hRes = CONVERT10_E_OLESTREAM_GET;
7821 else
7823 /* Get the Width of the Metafile */
7824 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7825 if(dwSize != sizeof(pData->dwMetaFileWidth))
7827 hRes = CONVERT10_E_OLESTREAM_GET;
7829 if(hRes == S_OK)
7831 /* Get the Height of the Metafile */
7832 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7833 if(dwSize != sizeof(pData->dwMetaFileHeight))
7835 hRes = CONVERT10_E_OLESTREAM_GET;
7839 if(hRes == S_OK)
7841 /* Get the Length of the Data */
7842 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7843 if(dwSize != sizeof(pData->dwDataLength))
7845 hRes = CONVERT10_E_OLESTREAM_GET;
7849 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7851 if(!bStrem1) /* if it is a second OLE stream data */
7853 pData->dwDataLength -= 8;
7854 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7855 if(dwSize != sizeof(pData->strUnknown))
7857 hRes = CONVERT10_E_OLESTREAM_GET;
7861 if(hRes == S_OK)
7863 if(pData->dwDataLength > 0)
7865 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7867 /* Get Data (ex. IStorage, Metafile, or BMP) */
7868 if(pData->pData)
7870 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7871 if(dwSize != pData->dwDataLength)
7873 hRes = CONVERT10_E_OLESTREAM_GET;
7876 else
7878 hRes = CONVERT10_E_OLESTREAM_GET;
7884 return hRes;
7887 /*************************************************************************
7888 * OLECONVERT_SaveOLE10 [Internal]
7890 * Saves the OLE10 STREAM From memory
7892 * PARAMS
7893 * pData [I] Data Structure for the OLESTREAM Data
7894 * pOleStream [I] The OLESTREAM to save
7896 * RETURNS
7897 * Success: S_OK
7898 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7900 * NOTES
7901 * This function is used by OleConvertIStorageToOLESTREAM only.
7904 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7906 DWORD dwSize;
7907 HRESULT hRes = S_OK;
7910 /* Set the OleID */
7911 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7912 if(dwSize != sizeof(pData->dwOleID))
7914 hRes = CONVERT10_E_OLESTREAM_PUT;
7917 if(hRes == S_OK)
7919 /* Set the TypeID */
7920 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7921 if(dwSize != sizeof(pData->dwTypeID))
7923 hRes = CONVERT10_E_OLESTREAM_PUT;
7927 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7929 /* Set the Length of the OleTypeName */
7930 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7931 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7933 hRes = CONVERT10_E_OLESTREAM_PUT;
7936 if(hRes == S_OK)
7938 if(pData->dwOleTypeNameLength > 0)
7940 /* Set the OleTypeName */
7941 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7942 if(dwSize != pData->dwOleTypeNameLength)
7944 hRes = CONVERT10_E_OLESTREAM_PUT;
7949 if(hRes == S_OK)
7951 /* Set the width of the Metafile */
7952 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7953 if(dwSize != sizeof(pData->dwMetaFileWidth))
7955 hRes = CONVERT10_E_OLESTREAM_PUT;
7959 if(hRes == S_OK)
7961 /* Set the height of the Metafile */
7962 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7963 if(dwSize != sizeof(pData->dwMetaFileHeight))
7965 hRes = CONVERT10_E_OLESTREAM_PUT;
7969 if(hRes == S_OK)
7971 /* Set the length of the Data */
7972 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7973 if(dwSize != sizeof(pData->dwDataLength))
7975 hRes = CONVERT10_E_OLESTREAM_PUT;
7979 if(hRes == S_OK)
7981 if(pData->dwDataLength > 0)
7983 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7984 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7985 if(dwSize != pData->dwDataLength)
7987 hRes = CONVERT10_E_OLESTREAM_PUT;
7992 return hRes;
7995 /*************************************************************************
7996 * OLECONVERT_GetOLE20FromOLE10[Internal]
7998 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7999 * opens it, and copies the content to the dest IStorage for
8000 * OleConvertOLESTREAMToIStorage
8003 * PARAMS
8004 * pDestStorage [I] The IStorage to copy the data to
8005 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8006 * nBufferLength [I] The size of the buffer
8008 * RETURNS
8009 * Nothing
8011 * NOTES
8015 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8017 HRESULT hRes;
8018 HANDLE hFile;
8019 IStorage *pTempStorage;
8020 DWORD dwNumOfBytesWritten;
8021 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8022 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8024 /* Create a temp File */
8025 GetTempPathW(MAX_PATH, wstrTempDir);
8026 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8027 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8029 if(hFile != INVALID_HANDLE_VALUE)
8031 /* Write IStorage Data to File */
8032 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8033 CloseHandle(hFile);
8035 /* Open and copy temp storage to the Dest Storage */
8036 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8037 if(hRes == S_OK)
8039 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8040 IStorage_Release(pTempStorage);
8042 DeleteFileW(wstrTempFile);
8047 /*************************************************************************
8048 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8050 * Saves the OLE10 STREAM From memory
8052 * PARAMS
8053 * pStorage [I] The Src IStorage to copy
8054 * pData [I] The Dest Memory to write to.
8056 * RETURNS
8057 * The size in bytes allocated for pData
8059 * NOTES
8060 * Memory allocated for pData must be freed by the caller
8062 * Used by OleConvertIStorageToOLESTREAM only.
8065 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8067 HANDLE hFile;
8068 HRESULT hRes;
8069 DWORD nDataLength = 0;
8070 IStorage *pTempStorage;
8071 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8072 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8074 *pData = NULL;
8076 /* Create temp Storage */
8077 GetTempPathW(MAX_PATH, wstrTempDir);
8078 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8079 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8081 if(hRes == S_OK)
8083 /* Copy Src Storage to the Temp Storage */
8084 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8085 IStorage_Release(pTempStorage);
8087 /* Open Temp Storage as a file and copy to memory */
8088 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8089 if(hFile != INVALID_HANDLE_VALUE)
8091 nDataLength = GetFileSize(hFile, NULL);
8092 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8093 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8094 CloseHandle(hFile);
8096 DeleteFileW(wstrTempFile);
8098 return nDataLength;
8101 /*************************************************************************
8102 * OLECONVERT_CreateOleStream [Internal]
8104 * Creates the "\001OLE" stream in the IStorage if necessary.
8106 * PARAMS
8107 * pStorage [I] Dest storage to create the stream in
8109 * RETURNS
8110 * Nothing
8112 * NOTES
8113 * This function is used by OleConvertOLESTREAMToIStorage only.
8115 * This stream is still unknown, MS Word seems to have extra data
8116 * but since the data is stored in the OLESTREAM there should be
8117 * no need to recreate the stream. If the stream is manually
8118 * deleted it will create it with this default data.
8121 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8123 HRESULT hRes;
8124 IStream *pStream;
8125 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8126 BYTE pOleStreamHeader [] =
8128 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8130 0x00, 0x00, 0x00, 0x00
8133 /* Create stream if not present */
8134 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8135 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8137 if(hRes == S_OK)
8139 /* Write default Data */
8140 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8141 IStream_Release(pStream);
8145 /* write a string to a stream, preceded by its length */
8146 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8148 HRESULT r;
8149 LPSTR str;
8150 DWORD len = 0;
8152 if( string )
8153 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8154 r = IStream_Write( stm, &len, sizeof(len), NULL);
8155 if( FAILED( r ) )
8156 return r;
8157 if(len == 0)
8158 return r;
8159 str = CoTaskMemAlloc( len );
8160 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8161 r = IStream_Write( stm, str, len, NULL);
8162 CoTaskMemFree( str );
8163 return r;
8166 /* read a string preceded by its length from a stream */
8167 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8169 HRESULT r;
8170 DWORD len, count = 0;
8171 LPSTR str;
8172 LPWSTR wstr;
8174 r = IStream_Read( stm, &len, sizeof(len), &count );
8175 if( FAILED( r ) )
8176 return r;
8177 if( count != sizeof(len) )
8178 return E_OUTOFMEMORY;
8180 TRACE("%d bytes\n",len);
8182 str = CoTaskMemAlloc( len );
8183 if( !str )
8184 return E_OUTOFMEMORY;
8185 count = 0;
8186 r = IStream_Read( stm, str, len, &count );
8187 if( FAILED( r ) )
8188 return r;
8189 if( count != len )
8191 CoTaskMemFree( str );
8192 return E_OUTOFMEMORY;
8195 TRACE("Read string %s\n",debugstr_an(str,len));
8197 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8198 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8199 if( wstr )
8200 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8201 CoTaskMemFree( str );
8203 *string = wstr;
8205 return r;
8209 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8210 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8212 IStream *pstm;
8213 HRESULT r = S_OK;
8214 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8216 static const BYTE unknown1[12] =
8217 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8218 0xFF, 0xFF, 0xFF, 0xFF};
8219 static const BYTE unknown2[16] =
8220 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8221 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8223 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8224 debugstr_w(lpszUserType), debugstr_w(szClipName),
8225 debugstr_w(szProgIDName));
8227 /* Create a CompObj stream */
8228 r = IStorage_CreateStream(pstg, szwStreamName,
8229 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8230 if( FAILED (r) )
8231 return r;
8233 /* Write CompObj Structure to stream */
8234 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8236 if( SUCCEEDED( r ) )
8237 r = WriteClassStm( pstm, clsid );
8239 if( SUCCEEDED( r ) )
8240 r = STREAM_WriteString( pstm, lpszUserType );
8241 if( SUCCEEDED( r ) )
8242 r = STREAM_WriteString( pstm, szClipName );
8243 if( SUCCEEDED( r ) )
8244 r = STREAM_WriteString( pstm, szProgIDName );
8245 if( SUCCEEDED( r ) )
8246 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8248 IStream_Release( pstm );
8250 return r;
8253 /***********************************************************************
8254 * WriteFmtUserTypeStg (OLE32.@)
8256 HRESULT WINAPI WriteFmtUserTypeStg(
8257 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8259 HRESULT r;
8260 WCHAR szwClipName[0x40];
8261 CLSID clsid = CLSID_NULL;
8262 LPWSTR wstrProgID = NULL;
8263 DWORD n;
8265 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8267 /* get the clipboard format name */
8268 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8269 szwClipName[n]=0;
8271 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8273 /* FIXME: There's room to save a CLSID and its ProgID, but
8274 the CLSID is not looked up in the registry and in all the
8275 tests I wrote it was CLSID_NULL. Where does it come from?
8278 /* get the real program ID. This may fail, but that's fine */
8279 ProgIDFromCLSID(&clsid, &wstrProgID);
8281 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8283 r = STORAGE_WriteCompObj( pstg, &clsid,
8284 lpszUserType, szwClipName, wstrProgID );
8286 CoTaskMemFree(wstrProgID);
8288 return r;
8292 /******************************************************************************
8293 * ReadFmtUserTypeStg [OLE32.@]
8295 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8297 HRESULT r;
8298 IStream *stm = 0;
8299 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8300 unsigned char unknown1[12];
8301 unsigned char unknown2[16];
8302 DWORD count;
8303 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8304 CLSID clsid;
8306 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8308 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8309 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8310 if( FAILED ( r ) )
8312 WARN("Failed to open stream r = %08x\n", r);
8313 return r;
8316 /* read the various parts of the structure */
8317 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8318 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8319 goto end;
8320 r = ReadClassStm( stm, &clsid );
8321 if( FAILED( r ) )
8322 goto end;
8324 r = STREAM_ReadString( stm, &szCLSIDName );
8325 if( FAILED( r ) )
8326 goto end;
8328 r = STREAM_ReadString( stm, &szOleTypeName );
8329 if( FAILED( r ) )
8330 goto end;
8332 r = STREAM_ReadString( stm, &szProgIDName );
8333 if( FAILED( r ) )
8334 goto end;
8336 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8337 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8338 goto end;
8340 /* ok, success... now we just need to store what we found */
8341 if( pcf )
8342 *pcf = RegisterClipboardFormatW( szOleTypeName );
8343 CoTaskMemFree( szOleTypeName );
8345 if( lplpszUserType )
8346 *lplpszUserType = szCLSIDName;
8347 CoTaskMemFree( szProgIDName );
8349 end:
8350 IStream_Release( stm );
8352 return r;
8356 /*************************************************************************
8357 * OLECONVERT_CreateCompObjStream [Internal]
8359 * Creates a "\001CompObj" is the destination IStorage if necessary.
8361 * PARAMS
8362 * pStorage [I] The dest IStorage to create the CompObj Stream
8363 * if necessary.
8364 * strOleTypeName [I] The ProgID
8366 * RETURNS
8367 * Success: S_OK
8368 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8370 * NOTES
8371 * This function is used by OleConvertOLESTREAMToIStorage only.
8373 * The stream data is stored in the OLESTREAM and there should be
8374 * no need to recreate the stream. If the stream is manually
8375 * deleted it will attempt to create it by querying the registry.
8379 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8381 IStream *pStream;
8382 HRESULT hStorageRes, hRes = S_OK;
8383 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8384 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8385 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8387 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8388 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8390 /* Initialize the CompObj structure */
8391 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8392 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8393 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8396 /* Create a CompObj stream if it doesn't exist */
8397 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8398 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8399 if(hStorageRes == S_OK)
8401 /* copy the OleTypeName to the compobj struct */
8402 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8403 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8405 /* copy the OleTypeName to the compobj struct */
8406 /* Note: in the test made, these were Identical */
8407 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8408 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8410 /* Get the CLSID */
8411 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8412 bufferW, OLESTREAM_MAX_STR_LEN );
8413 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8415 if(hRes == S_OK)
8417 HKEY hKey;
8418 LONG hErr;
8419 /* Get the CLSID Default Name from the Registry */
8420 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8421 if(hErr == ERROR_SUCCESS)
8423 char strTemp[OLESTREAM_MAX_STR_LEN];
8424 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8425 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8426 if(hErr == ERROR_SUCCESS)
8428 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8430 RegCloseKey(hKey);
8434 /* Write CompObj Structure to stream */
8435 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8437 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8439 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8440 if(IStorageCompObj.dwCLSIDNameLength > 0)
8442 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8444 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8445 if(IStorageCompObj.dwOleTypeNameLength > 0)
8447 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8449 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8450 if(IStorageCompObj.dwProgIDNameLength > 0)
8452 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8454 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8455 IStream_Release(pStream);
8457 return hRes;
8461 /*************************************************************************
8462 * OLECONVERT_CreateOlePresStream[Internal]
8464 * Creates the "\002OlePres000" Stream with the Metafile data
8466 * PARAMS
8467 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8468 * dwExtentX [I] Width of the Metafile
8469 * dwExtentY [I] Height of the Metafile
8470 * pData [I] Metafile data
8471 * dwDataLength [I] Size of the Metafile data
8473 * RETURNS
8474 * Success: S_OK
8475 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8477 * NOTES
8478 * This function is used by OleConvertOLESTREAMToIStorage only.
8481 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8483 HRESULT hRes;
8484 IStream *pStream;
8485 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8486 BYTE pOlePresStreamHeader [] =
8488 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8489 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8490 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8491 0x00, 0x00, 0x00, 0x00
8494 BYTE pOlePresStreamHeaderEmpty [] =
8496 0x00, 0x00, 0x00, 0x00,
8497 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8498 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8499 0x00, 0x00, 0x00, 0x00
8502 /* Create the OlePres000 Stream */
8503 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8504 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8506 if(hRes == S_OK)
8508 DWORD nHeaderSize;
8509 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8511 memset(&OlePres, 0, sizeof(OlePres));
8512 /* Do we have any metafile data to save */
8513 if(dwDataLength > 0)
8515 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8516 nHeaderSize = sizeof(pOlePresStreamHeader);
8518 else
8520 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8521 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8523 /* Set width and height of the metafile */
8524 OlePres.dwExtentX = dwExtentX;
8525 OlePres.dwExtentY = -dwExtentY;
8527 /* Set Data and Length */
8528 if(dwDataLength > sizeof(METAFILEPICT16))
8530 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8531 OlePres.pData = &(pData[8]);
8533 /* Save OlePres000 Data to Stream */
8534 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8535 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8536 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8537 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8538 if(OlePres.dwSize > 0)
8540 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8542 IStream_Release(pStream);
8546 /*************************************************************************
8547 * OLECONVERT_CreateOle10NativeStream [Internal]
8549 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8551 * PARAMS
8552 * pStorage [I] Dest storage to create the stream in
8553 * pData [I] Ole10 Native Data (ex. bmp)
8554 * dwDataLength [I] Size of the Ole10 Native Data
8556 * RETURNS
8557 * Nothing
8559 * NOTES
8560 * This function is used by OleConvertOLESTREAMToIStorage only.
8562 * Might need to verify the data and return appropriate error message
8565 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8567 HRESULT hRes;
8568 IStream *pStream;
8569 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8571 /* Create the Ole10Native Stream */
8572 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8573 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8575 if(hRes == S_OK)
8577 /* Write info to stream */
8578 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8579 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8580 IStream_Release(pStream);
8585 /*************************************************************************
8586 * OLECONVERT_GetOLE10ProgID [Internal]
8588 * Finds the ProgID (or OleTypeID) from the IStorage
8590 * PARAMS
8591 * pStorage [I] The Src IStorage to get the ProgID
8592 * strProgID [I] the ProgID string to get
8593 * dwSize [I] the size of the string
8595 * RETURNS
8596 * Success: S_OK
8597 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8599 * NOTES
8600 * This function is used by OleConvertIStorageToOLESTREAM only.
8604 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8606 HRESULT hRes;
8607 IStream *pStream;
8608 LARGE_INTEGER iSeekPos;
8609 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8610 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8612 /* Open the CompObj Stream */
8613 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8614 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8615 if(hRes == S_OK)
8618 /*Get the OleType from the CompObj Stream */
8619 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8620 iSeekPos.u.HighPart = 0;
8622 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8623 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8624 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8625 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8626 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8627 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8628 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8630 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8631 if(*dwSize > 0)
8633 IStream_Read(pStream, strProgID, *dwSize, NULL);
8635 IStream_Release(pStream);
8637 else
8639 STATSTG stat;
8640 LPOLESTR wstrProgID;
8642 /* Get the OleType from the registry */
8643 REFCLSID clsid = &(stat.clsid);
8644 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8645 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8646 if(hRes == S_OK)
8648 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8652 return hRes;
8655 /*************************************************************************
8656 * OLECONVERT_GetOle10PresData [Internal]
8658 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8660 * PARAMS
8661 * pStorage [I] Src IStroage
8662 * pOleStream [I] Dest OleStream Mem Struct
8664 * RETURNS
8665 * Nothing
8667 * NOTES
8668 * This function is used by OleConvertIStorageToOLESTREAM only.
8670 * Memory allocated for pData must be freed by the caller
8674 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8677 HRESULT hRes;
8678 IStream *pStream;
8679 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8681 /* Initialize Default data for OLESTREAM */
8682 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8683 pOleStreamData[0].dwTypeID = 2;
8684 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8685 pOleStreamData[1].dwTypeID = 0;
8686 pOleStreamData[0].dwMetaFileWidth = 0;
8687 pOleStreamData[0].dwMetaFileHeight = 0;
8688 pOleStreamData[0].pData = NULL;
8689 pOleStreamData[1].pData = NULL;
8691 /* Open Ole10Native Stream */
8692 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8693 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8694 if(hRes == S_OK)
8697 /* Read Size and Data */
8698 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8699 if(pOleStreamData->dwDataLength > 0)
8701 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8702 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8704 IStream_Release(pStream);
8710 /*************************************************************************
8711 * OLECONVERT_GetOle20PresData[Internal]
8713 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8715 * PARAMS
8716 * pStorage [I] Src IStroage
8717 * pOleStreamData [I] Dest OleStream Mem Struct
8719 * RETURNS
8720 * Nothing
8722 * NOTES
8723 * This function is used by OleConvertIStorageToOLESTREAM only.
8725 * Memory allocated for pData must be freed by the caller
8727 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8729 HRESULT hRes;
8730 IStream *pStream;
8731 OLECONVERT_ISTORAGE_OLEPRES olePress;
8732 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8734 /* Initialize Default data for OLESTREAM */
8735 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8736 pOleStreamData[0].dwTypeID = 2;
8737 pOleStreamData[0].dwMetaFileWidth = 0;
8738 pOleStreamData[0].dwMetaFileHeight = 0;
8739 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8740 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8741 pOleStreamData[1].dwTypeID = 0;
8742 pOleStreamData[1].dwOleTypeNameLength = 0;
8743 pOleStreamData[1].strOleTypeName[0] = 0;
8744 pOleStreamData[1].dwMetaFileWidth = 0;
8745 pOleStreamData[1].dwMetaFileHeight = 0;
8746 pOleStreamData[1].pData = NULL;
8747 pOleStreamData[1].dwDataLength = 0;
8750 /* Open OlePress000 stream */
8751 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8752 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8753 if(hRes == S_OK)
8755 LARGE_INTEGER iSeekPos;
8756 METAFILEPICT16 MetaFilePict;
8757 static const char strMetafilePictName[] = "METAFILEPICT";
8759 /* Set the TypeID for a Metafile */
8760 pOleStreamData[1].dwTypeID = 5;
8762 /* Set the OleTypeName to Metafile */
8763 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8764 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8766 iSeekPos.u.HighPart = 0;
8767 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8769 /* Get Presentation Data */
8770 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8771 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8772 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8773 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8775 /*Set width and Height */
8776 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8777 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8778 if(olePress.dwSize > 0)
8780 /* Set Length */
8781 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8783 /* Set MetaFilePict struct */
8784 MetaFilePict.mm = 8;
8785 MetaFilePict.xExt = olePress.dwExtentX;
8786 MetaFilePict.yExt = olePress.dwExtentY;
8787 MetaFilePict.hMF = 0;
8789 /* Get Metafile Data */
8790 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8791 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8792 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8794 IStream_Release(pStream);
8798 /*************************************************************************
8799 * OleConvertOLESTREAMToIStorage [OLE32.@]
8801 * Read info on MSDN
8803 * TODO
8804 * DVTARGETDEVICE parameter is not handled
8805 * Still unsure of some mem fields for OLE 10 Stream
8806 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8807 * and "\001OLE" streams
8810 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8811 LPOLESTREAM pOleStream,
8812 LPSTORAGE pstg,
8813 const DVTARGETDEVICE* ptd)
8815 int i;
8816 HRESULT hRes=S_OK;
8817 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8819 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8821 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8823 if(ptd != NULL)
8825 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8828 if(pstg == NULL || pOleStream == NULL)
8830 hRes = E_INVALIDARG;
8833 if(hRes == S_OK)
8835 /* Load the OLESTREAM to Memory */
8836 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8839 if(hRes == S_OK)
8841 /* Load the OLESTREAM to Memory (part 2)*/
8842 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8845 if(hRes == S_OK)
8848 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8850 /* Do we have the IStorage Data in the OLESTREAM */
8851 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8853 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8854 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8856 else
8858 /* It must be an original OLE 1.0 source */
8859 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8862 else
8864 /* It must be an original OLE 1.0 source */
8865 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8868 /* Create CompObj Stream if necessary */
8869 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8870 if(hRes == S_OK)
8872 /*Create the Ole Stream if necessary */
8873 OLECONVERT_CreateOleStream(pstg);
8878 /* Free allocated memory */
8879 for(i=0; i < 2; i++)
8881 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8882 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8883 pOleStreamData[i].pstrOleObjFileName = NULL;
8885 return hRes;
8888 /*************************************************************************
8889 * OleConvertIStorageToOLESTREAM [OLE32.@]
8891 * Read info on MSDN
8893 * Read info on MSDN
8895 * TODO
8896 * Still unsure of some mem fields for OLE 10 Stream
8897 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8898 * and "\001OLE" streams.
8901 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8902 LPSTORAGE pstg,
8903 LPOLESTREAM pOleStream)
8905 int i;
8906 HRESULT hRes = S_OK;
8907 IStream *pStream;
8908 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8909 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8911 TRACE("%p %p\n", pstg, pOleStream);
8913 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8915 if(pstg == NULL || pOleStream == NULL)
8917 hRes = E_INVALIDARG;
8919 if(hRes == S_OK)
8921 /* Get the ProgID */
8922 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8923 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8925 if(hRes == S_OK)
8927 /* Was it originally Ole10 */
8928 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8929 if(hRes == S_OK)
8931 IStream_Release(pStream);
8932 /* Get Presentation Data for Ole10Native */
8933 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8935 else
8937 /* Get Presentation Data (OLE20) */
8938 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8941 /* Save OLESTREAM */
8942 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8943 if(hRes == S_OK)
8945 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8950 /* Free allocated memory */
8951 for(i=0; i < 2; i++)
8953 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8956 return hRes;
8959 /***********************************************************************
8960 * GetConvertStg (OLE32.@)
8962 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8963 FIXME("unimplemented stub!\n");
8964 return E_FAIL;
8967 /******************************************************************************
8968 * StgIsStorageFile [OLE32.@]
8969 * Verify if the file contains a storage object
8971 * PARAMS
8972 * fn [ I] Filename
8974 * RETURNS
8975 * S_OK if file has magic bytes as a storage object
8976 * S_FALSE if file is not storage
8978 HRESULT WINAPI
8979 StgIsStorageFile(LPCOLESTR fn)
8981 HANDLE hf;
8982 BYTE magic[8];
8983 DWORD bytes_read;
8985 TRACE("%s\n", debugstr_w(fn));
8986 hf = CreateFileW(fn, GENERIC_READ,
8987 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8988 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8990 if (hf == INVALID_HANDLE_VALUE)
8991 return STG_E_FILENOTFOUND;
8993 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8995 WARN(" unable to read file\n");
8996 CloseHandle(hf);
8997 return S_FALSE;
9000 CloseHandle(hf);
9002 if (bytes_read != 8) {
9003 TRACE(" too short\n");
9004 return S_FALSE;
9007 if (!memcmp(magic,STORAGE_magic,8)) {
9008 TRACE(" -> YES\n");
9009 return S_OK;
9012 TRACE(" -> Invalid header.\n");
9013 return S_FALSE;
9016 /***********************************************************************
9017 * WriteClassStm (OLE32.@)
9019 * Writes a CLSID to a stream.
9021 * PARAMS
9022 * pStm [I] Stream to write to.
9023 * rclsid [I] CLSID to write.
9025 * RETURNS
9026 * Success: S_OK.
9027 * Failure: HRESULT code.
9029 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9031 TRACE("(%p,%p)\n",pStm,rclsid);
9033 if (!pStm || !rclsid)
9034 return E_INVALIDARG;
9036 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9039 /***********************************************************************
9040 * ReadClassStm (OLE32.@)
9042 * Reads a CLSID from a stream.
9044 * PARAMS
9045 * pStm [I] Stream to read from.
9046 * rclsid [O] CLSID to read.
9048 * RETURNS
9049 * Success: S_OK.
9050 * Failure: HRESULT code.
9052 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9054 ULONG nbByte;
9055 HRESULT res;
9057 TRACE("(%p,%p)\n",pStm,pclsid);
9059 if (!pStm || !pclsid)
9060 return E_INVALIDARG;
9062 /* clear the output args */
9063 *pclsid = CLSID_NULL;
9065 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9067 if (FAILED(res))
9068 return res;
9070 if (nbByte != sizeof(CLSID))
9071 return STG_E_READFAULT;
9072 else
9073 return S_OK;