push 6e61d6ca5bcaf95ac09a664b4ba4f88238c927be
[wine/hacks.git] / dlls / ole32 / storage32.c
blob837365bdb9d60847945b19e0b3f4e1074975031d
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 /****************************************************************************
122 * Transacted storage object that reads/writes a snapshot file.
124 typedef struct TransactedSnapshotImpl
126 struct StorageBaseImpl base;
129 * Changes are temporarily saved to the snapshot.
131 StorageBaseImpl *snapshot;
134 * Changes are committed to the transacted parent.
136 StorageBaseImpl *transactedParent;
137 } TransactedSnapshotImpl;
139 /* Generic function to create a transacted wrapper for a direct storage object. */
140 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
142 /* OLESTREAM memory structure to use for Get and Put Routines */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
144 typedef struct
146 DWORD dwOleID;
147 DWORD dwTypeID;
148 DWORD dwOleTypeNameLength;
149 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
150 CHAR *pstrOleObjFileName;
151 DWORD dwOleObjFileNameLength;
152 DWORD dwMetaFileWidth;
153 DWORD dwMetaFileHeight;
154 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
155 DWORD dwDataLength;
156 BYTE *pData;
157 }OLECONVERT_OLESTREAM_DATA;
159 /* CompObj Stream structure */
160 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
161 typedef struct
163 BYTE byUnknown1[12];
164 CLSID clsid;
165 DWORD dwCLSIDNameLength;
166 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
167 DWORD dwOleTypeNameLength;
168 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
169 DWORD dwProgIDNameLength;
170 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
171 BYTE byUnknown2[16];
172 }OLECONVERT_ISTORAGE_COMPOBJ;
175 /* Ole Presentation Stream structure */
176 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
177 typedef struct
179 BYTE byUnknown1[28];
180 DWORD dwExtentX;
181 DWORD dwExtentY;
182 DWORD dwSize;
183 BYTE *pData;
184 }OLECONVERT_ISTORAGE_OLEPRES;
188 /***********************************************************************
189 * Forward declaration of internal functions used by the method DestroyElement
191 static HRESULT deleteStorageContents(
192 StorageBaseImpl *parentStorage,
193 DirRef indexToDelete,
194 DirEntry entryDataToDelete);
196 static HRESULT deleteStreamContents(
197 StorageBaseImpl *parentStorage,
198 DirRef indexToDelete,
199 DirEntry entryDataToDelete);
201 static HRESULT removeFromTree(
202 StorageBaseImpl *This,
203 DirRef parentStorageIndex,
204 DirRef deletedIndex);
206 /***********************************************************************
207 * Declaration of the functions used to manipulate DirEntry
210 static HRESULT insertIntoTree(
211 StorageBaseImpl *This,
212 DirRef parentStorageIndex,
213 DirRef newEntryIndex);
215 static LONG entryNameCmp(
216 const OLECHAR *name1,
217 const OLECHAR *name2);
219 static DirRef findElement(
220 StorageBaseImpl *storage,
221 DirRef storageEntry,
222 const OLECHAR *name,
223 DirEntry *data);
225 static HRESULT findTreeParent(
226 StorageBaseImpl *storage,
227 DirRef storageEntry,
228 const OLECHAR *childName,
229 DirEntry *parentData,
230 DirRef *parentEntry,
231 ULONG *relation);
233 /***********************************************************************
234 * Declaration of miscellaneous functions...
236 static HRESULT validateSTGM(DWORD stgmValue);
238 static DWORD GetShareModeFromSTGM(DWORD stgm);
239 static DWORD GetAccessModeFromSTGM(DWORD stgm);
240 static DWORD GetCreationModeFromSTGM(DWORD stgm);
242 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
245 /****************************************************************************
246 * IEnumSTATSTGImpl definitions.
248 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
249 * This class allows iterating through the content of a storage and to find
250 * specific items inside it.
252 struct IEnumSTATSTGImpl
254 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
255 * since we want to cast this in an IEnumSTATSTG pointer */
257 LONG ref; /* Reference count */
258 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
259 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
261 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
265 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
266 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
268 /************************************************************************
269 ** Block Functions
272 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
274 if (index == 0xffffffff)
275 index = 0;
276 else
277 index ++;
279 return index * BIG_BLOCK_SIZE;
282 /************************************************************************
283 ** Storage32BaseImpl implementation
285 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
286 ULARGE_INTEGER offset,
287 void* buffer,
288 ULONG size,
289 ULONG* bytesRead)
291 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
294 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
295 ULARGE_INTEGER offset,
296 const void* buffer,
297 const ULONG size,
298 ULONG* bytesWritten)
300 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
303 /************************************************************************
304 * Storage32BaseImpl_QueryInterface (IUnknown)
306 * This method implements the common QueryInterface for all IStorage32
307 * implementations contained in this file.
309 * See Windows documentation for more details on IUnknown methods.
311 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
312 IStorage* iface,
313 REFIID riid,
314 void** ppvObject)
316 StorageBaseImpl *This = (StorageBaseImpl *)iface;
318 if ( (This==0) || (ppvObject==0) )
319 return E_INVALIDARG;
321 *ppvObject = 0;
323 if (IsEqualGUID(&IID_IUnknown, riid) ||
324 IsEqualGUID(&IID_IStorage, riid))
326 *ppvObject = This;
328 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
330 *ppvObject = &This->pssVtbl;
333 if ((*ppvObject)==0)
334 return E_NOINTERFACE;
336 IStorage_AddRef(iface);
338 return S_OK;
341 /************************************************************************
342 * Storage32BaseImpl_AddRef (IUnknown)
344 * This method implements the common AddRef for all IStorage32
345 * implementations contained in this file.
347 * See Windows documentation for more details on IUnknown methods.
349 static ULONG WINAPI StorageBaseImpl_AddRef(
350 IStorage* iface)
352 StorageBaseImpl *This = (StorageBaseImpl *)iface;
353 ULONG ref = InterlockedIncrement(&This->ref);
355 TRACE("(%p) AddRef to %d\n", This, ref);
357 return ref;
360 /************************************************************************
361 * Storage32BaseImpl_Release (IUnknown)
363 * This method implements the common Release for all IStorage32
364 * implementations contained in this file.
366 * See Windows documentation for more details on IUnknown methods.
368 static ULONG WINAPI StorageBaseImpl_Release(
369 IStorage* iface)
371 StorageBaseImpl *This = (StorageBaseImpl *)iface;
373 ULONG ref = InterlockedDecrement(&This->ref);
375 TRACE("(%p) ReleaseRef to %d\n", This, ref);
377 if (ref == 0)
380 * Since we are using a system of base-classes, we want to call the
381 * destructor of the appropriate derived class. To do this, we are
382 * using virtual functions to implement the destructor.
384 StorageBaseImpl_Destroy(This);
387 return ref;
390 /************************************************************************
391 * Storage32BaseImpl_OpenStream (IStorage)
393 * This method will open the specified stream object from the current storage.
395 * See Windows documentation for more details on IStorage methods.
397 static HRESULT WINAPI StorageBaseImpl_OpenStream(
398 IStorage* iface,
399 const OLECHAR* pwcsName, /* [string][in] */
400 void* reserved1, /* [unique][in] */
401 DWORD grfMode, /* [in] */
402 DWORD reserved2, /* [in] */
403 IStream** ppstm) /* [out] */
405 StorageBaseImpl *This = (StorageBaseImpl *)iface;
406 StgStreamImpl* newStream;
407 DirEntry currentEntry;
408 DirRef streamEntryRef;
409 HRESULT res = STG_E_UNKNOWN;
411 TRACE("(%p, %s, %p, %x, %d, %p)\n",
412 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
414 if ( (pwcsName==NULL) || (ppstm==0) )
416 res = E_INVALIDARG;
417 goto end;
420 *ppstm = NULL;
422 if ( FAILED( validateSTGM(grfMode) ) ||
423 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
425 res = STG_E_INVALIDFLAG;
426 goto end;
430 * As documented.
432 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
434 res = STG_E_INVALIDFUNCTION;
435 goto end;
438 if (This->reverted)
440 res = STG_E_REVERTED;
441 goto end;
445 * Check that we're compatible with the parent's storage mode, but
446 * only if we are not in transacted mode
448 if(!(This->openFlags & STGM_TRANSACTED)) {
449 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
451 res = STG_E_INVALIDFLAG;
452 goto end;
457 * Search for the element with the given name
459 streamEntryRef = findElement(
460 This,
461 This->storageDirEntry,
462 pwcsName,
463 &currentEntry);
466 * If it was found, construct the stream object and return a pointer to it.
468 if ( (streamEntryRef!=DIRENTRY_NULL) &&
469 (currentEntry.stgType==STGTY_STREAM) )
471 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
473 /* A single stream cannot be opened a second time. */
474 res = STG_E_ACCESSDENIED;
475 goto end;
478 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
480 if (newStream!=0)
482 newStream->grfMode = grfMode;
483 *ppstm = (IStream*)newStream;
485 IStream_AddRef(*ppstm);
487 res = S_OK;
488 goto end;
491 res = E_OUTOFMEMORY;
492 goto end;
495 res = STG_E_FILENOTFOUND;
497 end:
498 if (res == S_OK)
499 TRACE("<-- IStream %p\n", *ppstm);
500 TRACE("<-- %08x\n", res);
501 return res;
504 /************************************************************************
505 * Storage32BaseImpl_OpenStorage (IStorage)
507 * This method will open a new storage object from the current storage.
509 * See Windows documentation for more details on IStorage methods.
511 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
512 IStorage* iface,
513 const OLECHAR* pwcsName, /* [string][unique][in] */
514 IStorage* pstgPriority, /* [unique][in] */
515 DWORD grfMode, /* [in] */
516 SNB snbExclude, /* [unique][in] */
517 DWORD reserved, /* [in] */
518 IStorage** ppstg) /* [out] */
520 StorageBaseImpl *This = (StorageBaseImpl *)iface;
521 StorageInternalImpl* newStorage;
522 StorageBaseImpl* newTransactedStorage;
523 DirEntry currentEntry;
524 DirRef storageEntryRef;
525 HRESULT res = STG_E_UNKNOWN;
527 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
528 iface, debugstr_w(pwcsName), pstgPriority,
529 grfMode, snbExclude, reserved, ppstg);
531 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
533 res = E_INVALIDARG;
534 goto end;
537 if (This->openFlags & STGM_SIMPLE)
539 res = STG_E_INVALIDFUNCTION;
540 goto end;
543 /* as documented */
544 if (snbExclude != NULL)
546 res = STG_E_INVALIDPARAMETER;
547 goto end;
550 if ( FAILED( validateSTGM(grfMode) ))
552 res = STG_E_INVALIDFLAG;
553 goto end;
557 * As documented.
559 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
560 (grfMode & STGM_DELETEONRELEASE) ||
561 (grfMode & STGM_PRIORITY) )
563 res = STG_E_INVALIDFUNCTION;
564 goto end;
567 if (This->reverted)
568 return STG_E_REVERTED;
571 * Check that we're compatible with the parent's storage mode,
572 * but only if we are not transacted
574 if(!(This->openFlags & STGM_TRANSACTED)) {
575 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
577 res = STG_E_ACCESSDENIED;
578 goto end;
582 *ppstg = NULL;
584 storageEntryRef = findElement(
585 This,
586 This->storageDirEntry,
587 pwcsName,
588 &currentEntry);
590 if ( (storageEntryRef!=DIRENTRY_NULL) &&
591 (currentEntry.stgType==STGTY_STORAGE) )
593 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
595 /* A single storage cannot be opened a second time. */
596 res = STG_E_ACCESSDENIED;
597 goto end;
600 newStorage = StorageInternalImpl_Construct(
601 This,
602 grfMode,
603 storageEntryRef);
605 if (newStorage != 0)
607 if (grfMode & STGM_TRANSACTED)
609 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
611 if (FAILED(res))
613 HeapFree(GetProcessHeap(), 0, newStorage);
614 goto end;
617 *ppstg = (IStorage*)newTransactedStorage;
619 else
621 *ppstg = (IStorage*)newStorage;
624 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
626 res = S_OK;
627 goto end;
630 res = STG_E_INSUFFICIENTMEMORY;
631 goto end;
634 res = STG_E_FILENOTFOUND;
636 end:
637 TRACE("<-- %08x\n", res);
638 return res;
641 /************************************************************************
642 * Storage32BaseImpl_EnumElements (IStorage)
644 * This method will create an enumerator object that can be used to
645 * retrieve information about all the elements in the storage object.
647 * See Windows documentation for more details on IStorage methods.
649 static HRESULT WINAPI StorageBaseImpl_EnumElements(
650 IStorage* iface,
651 DWORD reserved1, /* [in] */
652 void* reserved2, /* [size_is][unique][in] */
653 DWORD reserved3, /* [in] */
654 IEnumSTATSTG** ppenum) /* [out] */
656 StorageBaseImpl *This = (StorageBaseImpl *)iface;
657 IEnumSTATSTGImpl* newEnum;
659 TRACE("(%p, %d, %p, %d, %p)\n",
660 iface, reserved1, reserved2, reserved3, ppenum);
662 if ( (This==0) || (ppenum==0))
663 return E_INVALIDARG;
665 if (This->reverted)
666 return STG_E_REVERTED;
668 newEnum = IEnumSTATSTGImpl_Construct(
669 This,
670 This->storageDirEntry);
672 if (newEnum!=0)
674 *ppenum = (IEnumSTATSTG*)newEnum;
676 IEnumSTATSTG_AddRef(*ppenum);
678 return S_OK;
681 return E_OUTOFMEMORY;
684 /************************************************************************
685 * Storage32BaseImpl_Stat (IStorage)
687 * This method will retrieve information about this storage object.
689 * See Windows documentation for more details on IStorage methods.
691 static HRESULT WINAPI StorageBaseImpl_Stat(
692 IStorage* iface,
693 STATSTG* pstatstg, /* [out] */
694 DWORD grfStatFlag) /* [in] */
696 StorageBaseImpl *This = (StorageBaseImpl *)iface;
697 DirEntry currentEntry;
698 HRESULT res = STG_E_UNKNOWN;
700 TRACE("(%p, %p, %x)\n",
701 iface, pstatstg, grfStatFlag);
703 if ( (This==0) || (pstatstg==0))
705 res = E_INVALIDARG;
706 goto end;
709 if (This->reverted)
711 res = STG_E_REVERTED;
712 goto end;
715 res = StorageBaseImpl_ReadDirEntry(
716 This,
717 This->storageDirEntry,
718 &currentEntry);
720 if (SUCCEEDED(res))
722 StorageUtl_CopyDirEntryToSTATSTG(
723 This,
724 pstatstg,
725 &currentEntry,
726 grfStatFlag);
728 pstatstg->grfMode = This->openFlags;
729 pstatstg->grfStateBits = This->stateBits;
732 end:
733 if (res == S_OK)
735 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);
737 TRACE("<-- %08x\n", res);
738 return res;
741 /************************************************************************
742 * Storage32BaseImpl_RenameElement (IStorage)
744 * This method will rename the specified element.
746 * See Windows documentation for more details on IStorage methods.
748 static HRESULT WINAPI StorageBaseImpl_RenameElement(
749 IStorage* iface,
750 const OLECHAR* pwcsOldName, /* [in] */
751 const OLECHAR* pwcsNewName) /* [in] */
753 StorageBaseImpl *This = (StorageBaseImpl *)iface;
754 DirEntry currentEntry;
755 DirRef currentEntryRef;
757 TRACE("(%p, %s, %s)\n",
758 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
760 if (This->reverted)
761 return STG_E_REVERTED;
763 currentEntryRef = findElement(This,
764 This->storageDirEntry,
765 pwcsNewName,
766 &currentEntry);
768 if (currentEntryRef != DIRENTRY_NULL)
771 * There is already an element with the new name
773 return STG_E_FILEALREADYEXISTS;
777 * Search for the old element name
779 currentEntryRef = findElement(This,
780 This->storageDirEntry,
781 pwcsOldName,
782 &currentEntry);
784 if (currentEntryRef != DIRENTRY_NULL)
786 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
787 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
789 WARN("Element is already open; cannot rename.\n");
790 return STG_E_ACCESSDENIED;
793 /* Remove the element from its current position in the tree */
794 removeFromTree(This, This->storageDirEntry,
795 currentEntryRef);
797 /* Change the name of the element */
798 strcpyW(currentEntry.name, pwcsNewName);
800 /* Delete any sibling links */
801 currentEntry.leftChild = DIRENTRY_NULL;
802 currentEntry.rightChild = DIRENTRY_NULL;
804 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
805 &currentEntry);
807 /* Insert the element in a new position in the tree */
808 insertIntoTree(This, This->storageDirEntry,
809 currentEntryRef);
811 else
814 * There is no element with the old name
816 return STG_E_FILENOTFOUND;
819 return S_OK;
822 /************************************************************************
823 * Storage32BaseImpl_CreateStream (IStorage)
825 * This method will create a stream object within this storage
827 * See Windows documentation for more details on IStorage methods.
829 static HRESULT WINAPI StorageBaseImpl_CreateStream(
830 IStorage* iface,
831 const OLECHAR* pwcsName, /* [string][in] */
832 DWORD grfMode, /* [in] */
833 DWORD reserved1, /* [in] */
834 DWORD reserved2, /* [in] */
835 IStream** ppstm) /* [out] */
837 StorageBaseImpl *This = (StorageBaseImpl *)iface;
838 StgStreamImpl* newStream;
839 DirEntry currentEntry, newStreamEntry;
840 DirRef currentEntryRef, newStreamEntryRef;
841 HRESULT hr;
843 TRACE("(%p, %s, %x, %d, %d, %p)\n",
844 iface, debugstr_w(pwcsName), grfMode,
845 reserved1, reserved2, ppstm);
847 if (ppstm == 0)
848 return STG_E_INVALIDPOINTER;
850 if (pwcsName == 0)
851 return STG_E_INVALIDNAME;
853 if (reserved1 || reserved2)
854 return STG_E_INVALIDPARAMETER;
856 if ( FAILED( validateSTGM(grfMode) ))
857 return STG_E_INVALIDFLAG;
859 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
860 return STG_E_INVALIDFLAG;
862 if (This->reverted)
863 return STG_E_REVERTED;
866 * As documented.
868 if ((grfMode & STGM_DELETEONRELEASE) ||
869 (grfMode & STGM_TRANSACTED))
870 return STG_E_INVALIDFUNCTION;
873 * Don't worry about permissions in transacted mode, as we can always write
874 * changes; we just can't always commit them.
876 if(!(This->openFlags & STGM_TRANSACTED)) {
877 /* Can't create a stream on read-only storage */
878 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
879 return STG_E_ACCESSDENIED;
881 /* Can't create a stream with greater access than the parent. */
882 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
883 return STG_E_ACCESSDENIED;
886 if(This->openFlags & STGM_SIMPLE)
887 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
889 *ppstm = 0;
891 currentEntryRef = findElement(This,
892 This->storageDirEntry,
893 pwcsName,
894 &currentEntry);
896 if (currentEntryRef != DIRENTRY_NULL)
899 * An element with this name already exists
901 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
903 IStorage_DestroyElement(iface, pwcsName);
905 else
906 return STG_E_FILEALREADYEXISTS;
910 * memset the empty entry
912 memset(&newStreamEntry, 0, sizeof(DirEntry));
914 newStreamEntry.sizeOfNameString =
915 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
917 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
918 return STG_E_INVALIDNAME;
920 strcpyW(newStreamEntry.name, pwcsName);
922 newStreamEntry.stgType = STGTY_STREAM;
923 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
924 newStreamEntry.size.u.LowPart = 0;
925 newStreamEntry.size.u.HighPart = 0;
927 newStreamEntry.leftChild = DIRENTRY_NULL;
928 newStreamEntry.rightChild = DIRENTRY_NULL;
929 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
931 /* call CoFileTime to get the current time
932 newStreamEntry.ctime
933 newStreamEntry.mtime
936 /* newStreamEntry.clsid */
939 * Create an entry with the new data
941 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
942 if (FAILED(hr))
943 return hr;
946 * Insert the new entry in the parent storage's tree.
948 hr = insertIntoTree(
949 This,
950 This->storageDirEntry,
951 newStreamEntryRef);
952 if (FAILED(hr))
954 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
955 return hr;
959 * Open the stream to return it.
961 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
963 if (newStream != 0)
965 *ppstm = (IStream*)newStream;
967 IStream_AddRef(*ppstm);
969 else
971 return STG_E_INSUFFICIENTMEMORY;
974 return S_OK;
977 /************************************************************************
978 * Storage32BaseImpl_SetClass (IStorage)
980 * This method will write the specified CLSID in the directory entry of this
981 * storage.
983 * See Windows documentation for more details on IStorage methods.
985 static HRESULT WINAPI StorageBaseImpl_SetClass(
986 IStorage* iface,
987 REFCLSID clsid) /* [in] */
989 StorageBaseImpl *This = (StorageBaseImpl *)iface;
990 HRESULT hRes;
991 DirEntry currentEntry;
993 TRACE("(%p, %p)\n", iface, clsid);
995 if (This->reverted)
996 return STG_E_REVERTED;
998 hRes = StorageBaseImpl_ReadDirEntry(This,
999 This->storageDirEntry,
1000 &currentEntry);
1001 if (SUCCEEDED(hRes))
1003 currentEntry.clsid = *clsid;
1005 hRes = StorageBaseImpl_WriteDirEntry(This,
1006 This->storageDirEntry,
1007 &currentEntry);
1010 return hRes;
1013 /************************************************************************
1014 ** Storage32Impl implementation
1017 /************************************************************************
1018 * Storage32BaseImpl_CreateStorage (IStorage)
1020 * This method will create the storage object within the provided storage.
1022 * See Windows documentation for more details on IStorage methods.
1024 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1025 IStorage* iface,
1026 const OLECHAR *pwcsName, /* [string][in] */
1027 DWORD grfMode, /* [in] */
1028 DWORD reserved1, /* [in] */
1029 DWORD reserved2, /* [in] */
1030 IStorage **ppstg) /* [out] */
1032 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1034 DirEntry currentEntry;
1035 DirEntry newEntry;
1036 DirRef currentEntryRef;
1037 DirRef newEntryRef;
1038 HRESULT hr;
1040 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1041 iface, debugstr_w(pwcsName), grfMode,
1042 reserved1, reserved2, ppstg);
1044 if (ppstg == 0)
1045 return STG_E_INVALIDPOINTER;
1047 if (This->openFlags & STGM_SIMPLE)
1049 return STG_E_INVALIDFUNCTION;
1052 if (pwcsName == 0)
1053 return STG_E_INVALIDNAME;
1055 *ppstg = NULL;
1057 if ( FAILED( validateSTGM(grfMode) ) ||
1058 (grfMode & STGM_DELETEONRELEASE) )
1060 WARN("bad grfMode: 0x%x\n", grfMode);
1061 return STG_E_INVALIDFLAG;
1064 if (This->reverted)
1065 return STG_E_REVERTED;
1068 * Check that we're compatible with the parent's storage mode
1070 if ( !(This->openFlags & STGM_TRANSACTED) &&
1071 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1073 WARN("access denied\n");
1074 return STG_E_ACCESSDENIED;
1077 currentEntryRef = findElement(This,
1078 This->storageDirEntry,
1079 pwcsName,
1080 &currentEntry);
1082 if (currentEntryRef != DIRENTRY_NULL)
1085 * An element with this name already exists
1087 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1088 ((This->openFlags & STGM_TRANSACTED) ||
1089 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1091 hr = IStorage_DestroyElement(iface, pwcsName);
1092 if (FAILED(hr))
1093 return hr;
1095 else
1097 WARN("file already exists\n");
1098 return STG_E_FILEALREADYEXISTS;
1101 else if (!(This->openFlags & STGM_TRANSACTED) &&
1102 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1104 WARN("read-only storage\n");
1105 return STG_E_ACCESSDENIED;
1108 memset(&newEntry, 0, sizeof(DirEntry));
1110 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1112 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1114 FIXME("name too long\n");
1115 return STG_E_INVALIDNAME;
1118 strcpyW(newEntry.name, pwcsName);
1120 newEntry.stgType = STGTY_STORAGE;
1121 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1122 newEntry.size.u.LowPart = 0;
1123 newEntry.size.u.HighPart = 0;
1125 newEntry.leftChild = DIRENTRY_NULL;
1126 newEntry.rightChild = DIRENTRY_NULL;
1127 newEntry.dirRootEntry = DIRENTRY_NULL;
1129 /* call CoFileTime to get the current time
1130 newEntry.ctime
1131 newEntry.mtime
1134 /* newEntry.clsid */
1137 * Create a new directory entry for the storage
1139 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1140 if (FAILED(hr))
1141 return hr;
1144 * Insert the new directory entry into the parent storage's tree
1146 hr = insertIntoTree(
1147 This,
1148 This->storageDirEntry,
1149 newEntryRef);
1150 if (FAILED(hr))
1152 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1153 return hr;
1157 * Open it to get a pointer to return.
1159 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1161 if( (hr != S_OK) || (*ppstg == NULL))
1163 return hr;
1167 return S_OK;
1171 /***************************************************************************
1173 * Internal Method
1175 * Reserve a directory entry in the file and initialize it.
1177 static HRESULT StorageImpl_CreateDirEntry(
1178 StorageBaseImpl *base,
1179 const DirEntry *newData,
1180 DirRef *index)
1182 StorageImpl *storage = (StorageImpl*)base;
1183 ULONG currentEntryIndex = 0;
1184 ULONG newEntryIndex = DIRENTRY_NULL;
1185 HRESULT hr = S_OK;
1186 BYTE currentData[RAW_DIRENTRY_SIZE];
1187 WORD sizeOfNameString;
1191 hr = StorageImpl_ReadRawDirEntry(storage,
1192 currentEntryIndex,
1193 currentData);
1195 if (SUCCEEDED(hr))
1197 StorageUtl_ReadWord(
1198 currentData,
1199 OFFSET_PS_NAMELENGTH,
1200 &sizeOfNameString);
1202 if (sizeOfNameString == 0)
1205 * The entry exists and is available, we found it.
1207 newEntryIndex = currentEntryIndex;
1210 else
1213 * We exhausted the directory entries, we will create more space below
1215 newEntryIndex = currentEntryIndex;
1217 currentEntryIndex++;
1219 } while (newEntryIndex == DIRENTRY_NULL);
1222 * grow the directory stream
1224 if (FAILED(hr))
1226 BYTE emptyData[RAW_DIRENTRY_SIZE];
1227 ULARGE_INTEGER newSize;
1228 ULONG entryIndex;
1229 ULONG lastEntry = 0;
1230 ULONG blockCount = 0;
1233 * obtain the new count of blocks in the directory stream
1235 blockCount = BlockChainStream_GetCount(
1236 storage->rootBlockChain)+1;
1239 * initialize the size used by the directory stream
1241 newSize.u.HighPart = 0;
1242 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1245 * add a block to the directory stream
1247 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1250 * memset the empty entry in order to initialize the unused newly
1251 * created entries
1253 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1256 * initialize them
1258 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1260 for(
1261 entryIndex = newEntryIndex + 1;
1262 entryIndex < lastEntry;
1263 entryIndex++)
1265 StorageImpl_WriteRawDirEntry(
1266 storage,
1267 entryIndex,
1268 emptyData);
1272 UpdateRawDirEntry(currentData, newData);
1274 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1276 if (SUCCEEDED(hr))
1277 *index = newEntryIndex;
1279 return hr;
1282 /***************************************************************************
1284 * Internal Method
1286 * Mark a directory entry in the file as free.
1288 static HRESULT StorageImpl_DestroyDirEntry(
1289 StorageBaseImpl *base,
1290 DirRef index)
1292 HRESULT hr;
1293 BYTE emptyData[RAW_DIRENTRY_SIZE];
1294 StorageImpl *storage = (StorageImpl*)base;
1296 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1298 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1300 return hr;
1304 /***************************************************************************
1306 * Internal Method
1308 * Destroy an entry, its attached data, and all entries reachable from it.
1310 static HRESULT DestroyReachableEntries(
1311 StorageBaseImpl *base,
1312 DirRef index)
1314 HRESULT hr = S_OK;
1315 DirEntry data;
1316 ULARGE_INTEGER zero;
1318 zero.QuadPart = 0;
1320 if (index != DIRENTRY_NULL)
1322 hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1324 if (SUCCEEDED(hr))
1325 hr = DestroyReachableEntries(base, data.dirRootEntry);
1327 if (SUCCEEDED(hr))
1328 hr = DestroyReachableEntries(base, data.leftChild);
1330 if (SUCCEEDED(hr))
1331 hr = DestroyReachableEntries(base, data.rightChild);
1333 if (SUCCEEDED(hr))
1334 hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1336 if (SUCCEEDED(hr))
1337 hr = StorageBaseImpl_DestroyDirEntry(base, index);
1340 return hr;
1344 /****************************************************************************
1346 * Internal Method
1348 * Case insensitive comparison of DirEntry.name by first considering
1349 * their size.
1351 * Returns <0 when name1 < name2
1352 * >0 when name1 > name2
1353 * 0 when name1 == name2
1355 static LONG entryNameCmp(
1356 const OLECHAR *name1,
1357 const OLECHAR *name2)
1359 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1361 while (diff == 0 && *name1 != 0)
1364 * We compare the string themselves only when they are of the same length
1366 diff = toupperW(*name1++) - toupperW(*name2++);
1369 return diff;
1372 /****************************************************************************
1374 * Internal Method
1376 * Add a directory entry to a storage
1378 static HRESULT insertIntoTree(
1379 StorageBaseImpl *This,
1380 DirRef parentStorageIndex,
1381 DirRef newEntryIndex)
1383 DirEntry currentEntry;
1384 DirEntry newEntry;
1387 * Read the inserted entry
1389 StorageBaseImpl_ReadDirEntry(This,
1390 newEntryIndex,
1391 &newEntry);
1394 * Read the storage entry
1396 StorageBaseImpl_ReadDirEntry(This,
1397 parentStorageIndex,
1398 &currentEntry);
1400 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1403 * The root storage contains some element, therefore, start the research
1404 * for the appropriate location.
1406 BOOL found = 0;
1407 DirRef current, next, previous, currentEntryId;
1410 * Keep a reference to the root of the storage's element tree
1412 currentEntryId = currentEntry.dirRootEntry;
1415 * Read
1417 StorageBaseImpl_ReadDirEntry(This,
1418 currentEntry.dirRootEntry,
1419 &currentEntry);
1421 previous = currentEntry.leftChild;
1422 next = currentEntry.rightChild;
1423 current = currentEntryId;
1425 while (found == 0)
1427 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1429 if (diff < 0)
1431 if (previous != DIRENTRY_NULL)
1433 StorageBaseImpl_ReadDirEntry(This,
1434 previous,
1435 &currentEntry);
1436 current = previous;
1438 else
1440 currentEntry.leftChild = newEntryIndex;
1441 StorageBaseImpl_WriteDirEntry(This,
1442 current,
1443 &currentEntry);
1444 found = 1;
1447 else if (diff > 0)
1449 if (next != DIRENTRY_NULL)
1451 StorageBaseImpl_ReadDirEntry(This,
1452 next,
1453 &currentEntry);
1454 current = next;
1456 else
1458 currentEntry.rightChild = newEntryIndex;
1459 StorageBaseImpl_WriteDirEntry(This,
1460 current,
1461 &currentEntry);
1462 found = 1;
1465 else
1468 * Trying to insert an item with the same name in the
1469 * subtree structure.
1471 return STG_E_FILEALREADYEXISTS;
1474 previous = currentEntry.leftChild;
1475 next = currentEntry.rightChild;
1478 else
1481 * The storage is empty, make the new entry the root of its element tree
1483 currentEntry.dirRootEntry = newEntryIndex;
1484 StorageBaseImpl_WriteDirEntry(This,
1485 parentStorageIndex,
1486 &currentEntry);
1489 return S_OK;
1492 /****************************************************************************
1494 * Internal Method
1496 * Find and read the element of a storage with the given name.
1498 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1499 const OLECHAR *name, DirEntry *data)
1501 DirRef currentEntry;
1503 /* Read the storage entry to find the root of the tree. */
1504 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1506 currentEntry = data->dirRootEntry;
1508 while (currentEntry != DIRENTRY_NULL)
1510 LONG cmp;
1512 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1514 cmp = entryNameCmp(name, data->name);
1516 if (cmp == 0)
1517 /* found it */
1518 break;
1520 else if (cmp < 0)
1521 currentEntry = data->leftChild;
1523 else if (cmp > 0)
1524 currentEntry = data->rightChild;
1527 return currentEntry;
1530 /****************************************************************************
1532 * Internal Method
1534 * Find and read the binary tree parent of the element with the given name.
1536 * If there is no such element, find a place where it could be inserted and
1537 * return STG_E_FILENOTFOUND.
1539 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1540 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1541 ULONG *relation)
1543 DirRef childEntry;
1544 DirEntry childData;
1546 /* Read the storage entry to find the root of the tree. */
1547 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1549 *parentEntry = storageEntry;
1550 *relation = DIRENTRY_RELATION_DIR;
1552 childEntry = parentData->dirRootEntry;
1554 while (childEntry != DIRENTRY_NULL)
1556 LONG cmp;
1558 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1560 cmp = entryNameCmp(childName, childData.name);
1562 if (cmp == 0)
1563 /* found it */
1564 break;
1566 else if (cmp < 0)
1568 *parentData = childData;
1569 *parentEntry = childEntry;
1570 *relation = DIRENTRY_RELATION_PREVIOUS;
1572 childEntry = parentData->leftChild;
1575 else if (cmp > 0)
1577 *parentData = childData;
1578 *parentEntry = childEntry;
1579 *relation = DIRENTRY_RELATION_NEXT;
1581 childEntry = parentData->rightChild;
1585 if (childEntry == DIRENTRY_NULL)
1586 return STG_E_FILENOTFOUND;
1587 else
1588 return S_OK;
1592 /*************************************************************************
1593 * CopyTo (IStorage)
1595 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1596 IStorage* iface,
1597 DWORD ciidExclude, /* [in] */
1598 const IID* rgiidExclude, /* [size_is][unique][in] */
1599 SNB snbExclude, /* [unique][in] */
1600 IStorage* pstgDest) /* [unique][in] */
1602 IEnumSTATSTG *elements = 0;
1603 STATSTG curElement, strStat;
1604 HRESULT hr;
1605 IStorage *pstgTmp, *pstgChild;
1606 IStream *pstrTmp, *pstrChild;
1607 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1608 int i;
1610 TRACE("(%p, %d, %p, %p, %p)\n",
1611 iface, ciidExclude, rgiidExclude,
1612 snbExclude, pstgDest);
1614 if ( pstgDest == 0 )
1615 return STG_E_INVALIDPOINTER;
1618 * Enumerate the elements
1620 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1622 if ( hr != S_OK )
1623 return hr;
1626 * set the class ID
1628 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1629 IStorage_SetClass( pstgDest, &curElement.clsid );
1631 for(i = 0; i < ciidExclude; ++i)
1633 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1634 skip_storage = TRUE;
1635 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1636 skip_stream = TRUE;
1637 else
1638 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1644 * Obtain the next element
1646 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1648 if ( hr == S_FALSE )
1650 hr = S_OK; /* done, every element has been copied */
1651 break;
1654 if ( snbExclude )
1656 WCHAR **snb = snbExclude;
1657 skip = FALSE;
1658 while ( *snb != NULL && !skip )
1660 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1661 skip = TRUE;
1662 ++snb;
1666 if ( skip )
1667 goto cleanup;
1669 if (curElement.type == STGTY_STORAGE)
1671 if(skip_storage)
1672 goto cleanup;
1675 * open child source storage
1677 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1678 STGM_READ|STGM_SHARE_EXCLUSIVE,
1679 NULL, 0, &pstgChild );
1681 if (hr != S_OK)
1682 goto cleanup;
1685 * create a new storage in destination storage
1687 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1688 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1689 0, 0,
1690 &pstgTmp );
1692 * if it already exist, don't create a new one use this one
1694 if (hr == STG_E_FILEALREADYEXISTS)
1696 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1697 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1698 NULL, 0, &pstgTmp );
1701 if (hr == S_OK)
1704 * do the copy recursively
1706 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1707 NULL, pstgTmp );
1709 IStorage_Release( pstgTmp );
1712 IStorage_Release( pstgChild );
1714 else if (curElement.type == STGTY_STREAM)
1716 if(skip_stream)
1717 goto cleanup;
1720 * create a new stream in destination storage. If the stream already
1721 * exist, it will be deleted and a new one will be created.
1723 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1724 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1725 0, 0, &pstrTmp );
1727 if (hr != S_OK)
1728 goto cleanup;
1731 * open child stream storage
1733 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1734 STGM_READ|STGM_SHARE_EXCLUSIVE,
1735 0, &pstrChild );
1737 if (hr == S_OK)
1740 * Get the size of the source stream
1742 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1745 * Set the size of the destination stream.
1747 IStream_SetSize(pstrTmp, strStat.cbSize);
1750 * do the copy
1752 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1753 NULL, NULL );
1755 IStream_Release( pstrChild );
1758 IStream_Release( pstrTmp );
1760 else
1762 WARN("unknown element type: %d\n", curElement.type);
1765 cleanup:
1766 CoTaskMemFree(curElement.pwcsName);
1767 } while (hr == S_OK);
1770 * Clean-up
1772 IEnumSTATSTG_Release(elements);
1774 return hr;
1777 /*************************************************************************
1778 * MoveElementTo (IStorage)
1780 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1781 IStorage* iface,
1782 const OLECHAR *pwcsName, /* [string][in] */
1783 IStorage *pstgDest, /* [unique][in] */
1784 const OLECHAR *pwcsNewName,/* [string][in] */
1785 DWORD grfFlags) /* [in] */
1787 FIXME("(%p %s %p %s %u): stub\n", iface,
1788 debugstr_w(pwcsName), pstgDest,
1789 debugstr_w(pwcsNewName), grfFlags);
1790 return E_NOTIMPL;
1793 /*************************************************************************
1794 * Commit (IStorage)
1796 * Ensures that any changes made to a storage object open in transacted mode
1797 * are reflected in the parent storage
1799 * NOTES
1800 * Wine doesn't implement transacted mode, which seems to be a basic
1801 * optimization, so we can ignore this stub for now.
1803 static HRESULT WINAPI StorageImpl_Commit(
1804 IStorage* iface,
1805 DWORD grfCommitFlags)/* [in] */
1807 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1808 return S_OK;
1811 /*************************************************************************
1812 * Revert (IStorage)
1814 * Discard all changes that have been made since the last commit operation
1816 static HRESULT WINAPI StorageImpl_Revert(
1817 IStorage* iface)
1819 TRACE("(%p)\n", iface);
1820 return S_OK;
1823 /*************************************************************************
1824 * DestroyElement (IStorage)
1826 * Strategy: This implementation is built this way for simplicity not for speed.
1827 * I always delete the topmost element of the enumeration and adjust
1828 * the deleted element pointer all the time. This takes longer to
1829 * do but allow to reinvoke DestroyElement whenever we encounter a
1830 * storage object. The optimisation resides in the usage of another
1831 * enumeration strategy that would give all the leaves of a storage
1832 * first. (postfix order)
1834 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1835 IStorage* iface,
1836 const OLECHAR *pwcsName)/* [string][in] */
1838 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1840 HRESULT hr = S_OK;
1841 DirEntry entryToDelete;
1842 DirRef entryToDeleteRef;
1844 TRACE("(%p, %s)\n",
1845 iface, debugstr_w(pwcsName));
1847 if (pwcsName==NULL)
1848 return STG_E_INVALIDPOINTER;
1850 if (This->reverted)
1851 return STG_E_REVERTED;
1853 if ( !(This->openFlags & STGM_TRANSACTED) &&
1854 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1855 return STG_E_ACCESSDENIED;
1857 entryToDeleteRef = findElement(
1858 This,
1859 This->storageDirEntry,
1860 pwcsName,
1861 &entryToDelete);
1863 if ( entryToDeleteRef == DIRENTRY_NULL )
1865 return STG_E_FILENOTFOUND;
1868 if ( entryToDelete.stgType == STGTY_STORAGE )
1870 hr = deleteStorageContents(
1871 This,
1872 entryToDeleteRef,
1873 entryToDelete);
1875 else if ( entryToDelete.stgType == STGTY_STREAM )
1877 hr = deleteStreamContents(
1878 This,
1879 entryToDeleteRef,
1880 entryToDelete);
1883 if (hr!=S_OK)
1884 return hr;
1887 * Remove the entry from its parent storage
1889 hr = removeFromTree(
1890 This,
1891 This->storageDirEntry,
1892 entryToDeleteRef);
1895 * Invalidate the entry
1897 if (SUCCEEDED(hr))
1898 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1900 return hr;
1904 /******************************************************************************
1905 * Internal stream list handlers
1908 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1910 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1911 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1914 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1916 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1917 list_remove(&(strm->StrmListEntry));
1920 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1922 StgStreamImpl *strm;
1924 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1926 if (strm->dirEntry == streamEntry)
1928 return TRUE;
1932 return FALSE;
1935 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1937 StorageInternalImpl *childstg;
1939 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1941 if (childstg->base.storageDirEntry == storageEntry)
1943 return TRUE;
1947 return FALSE;
1950 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1952 struct list *cur, *cur2;
1953 StgStreamImpl *strm=NULL;
1954 StorageInternalImpl *childstg=NULL;
1956 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1957 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1958 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1959 strm->parentStorage = NULL;
1960 list_remove(cur);
1963 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1964 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1965 StorageBaseImpl_Invalidate( &childstg->base );
1968 if (stg->transactedChild)
1970 StorageBaseImpl_Invalidate(stg->transactedChild);
1972 stg->transactedChild = NULL;
1977 /*********************************************************************
1979 * Internal Method
1981 * Delete the contents of a storage entry.
1984 static HRESULT deleteStorageContents(
1985 StorageBaseImpl *parentStorage,
1986 DirRef indexToDelete,
1987 DirEntry entryDataToDelete)
1989 IEnumSTATSTG *elements = 0;
1990 IStorage *childStorage = 0;
1991 STATSTG currentElement;
1992 HRESULT hr;
1993 HRESULT destroyHr = S_OK;
1994 StorageInternalImpl *stg, *stg2;
1996 /* Invalidate any open storage objects. */
1997 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1999 if (stg->base.storageDirEntry == indexToDelete)
2001 StorageBaseImpl_Invalidate(&stg->base);
2006 * Open the storage and enumerate it
2008 hr = StorageBaseImpl_OpenStorage(
2009 (IStorage*)parentStorage,
2010 entryDataToDelete.name,
2012 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2015 &childStorage);
2017 if (hr != S_OK)
2019 return hr;
2023 * Enumerate the elements
2025 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2030 * Obtain the next element
2032 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2033 if (hr==S_OK)
2035 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2037 CoTaskMemFree(currentElement.pwcsName);
2041 * We need to Reset the enumeration every time because we delete elements
2042 * and the enumeration could be invalid
2044 IEnumSTATSTG_Reset(elements);
2046 } while ((hr == S_OK) && (destroyHr == S_OK));
2048 IStorage_Release(childStorage);
2049 IEnumSTATSTG_Release(elements);
2051 return destroyHr;
2054 /*********************************************************************
2056 * Internal Method
2058 * Perform the deletion of a stream's data
2061 static HRESULT deleteStreamContents(
2062 StorageBaseImpl *parentStorage,
2063 DirRef indexToDelete,
2064 DirEntry entryDataToDelete)
2066 IStream *pis;
2067 HRESULT hr;
2068 ULARGE_INTEGER size;
2069 StgStreamImpl *strm, *strm2;
2071 /* Invalidate any open stream objects. */
2072 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2074 if (strm->dirEntry == indexToDelete)
2076 TRACE("Stream deleted %p\n", strm);
2077 strm->parentStorage = NULL;
2078 list_remove(&strm->StrmListEntry);
2082 size.u.HighPart = 0;
2083 size.u.LowPart = 0;
2085 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2086 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2088 if (hr!=S_OK)
2090 return(hr);
2094 * Zap the stream
2096 hr = IStream_SetSize(pis, size);
2098 if(hr != S_OK)
2100 return hr;
2104 * Release the stream object.
2106 IStream_Release(pis);
2108 return S_OK;
2111 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2113 switch (relation)
2115 case DIRENTRY_RELATION_PREVIOUS:
2116 entry->leftChild = new_target;
2117 break;
2118 case DIRENTRY_RELATION_NEXT:
2119 entry->rightChild = new_target;
2120 break;
2121 case DIRENTRY_RELATION_DIR:
2122 entry->dirRootEntry = new_target;
2123 break;
2124 default:
2125 assert(0);
2129 /*************************************************************************
2131 * Internal Method
2133 * This method removes a directory entry from its parent storage tree without
2134 * freeing any resources attached to it.
2136 static HRESULT removeFromTree(
2137 StorageBaseImpl *This,
2138 DirRef parentStorageIndex,
2139 DirRef deletedIndex)
2141 HRESULT hr = S_OK;
2142 DirEntry entryToDelete;
2143 DirEntry parentEntry;
2144 DirRef parentEntryRef;
2145 ULONG typeOfRelation;
2147 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2149 if (hr != S_OK)
2150 return hr;
2153 * Find the element that links to the one we want to delete.
2155 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2156 &parentEntry, &parentEntryRef, &typeOfRelation);
2158 if (hr != S_OK)
2159 return hr;
2161 if (entryToDelete.leftChild != DIRENTRY_NULL)
2164 * Replace the deleted entry with its left child
2166 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2168 hr = StorageBaseImpl_WriteDirEntry(
2169 This,
2170 parentEntryRef,
2171 &parentEntry);
2172 if(FAILED(hr))
2174 return hr;
2177 if (entryToDelete.rightChild != DIRENTRY_NULL)
2180 * We need to reinsert the right child somewhere. We already know it and
2181 * its children are greater than everything in the left tree, so we
2182 * insert it at the rightmost point in the left tree.
2184 DirRef newRightChildParent = entryToDelete.leftChild;
2185 DirEntry newRightChildParentEntry;
2189 hr = StorageBaseImpl_ReadDirEntry(
2190 This,
2191 newRightChildParent,
2192 &newRightChildParentEntry);
2193 if (FAILED(hr))
2195 return hr;
2198 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2199 newRightChildParent = newRightChildParentEntry.rightChild;
2200 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2202 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2204 hr = StorageBaseImpl_WriteDirEntry(
2205 This,
2206 newRightChildParent,
2207 &newRightChildParentEntry);
2208 if (FAILED(hr))
2210 return hr;
2214 else
2217 * Replace the deleted entry with its right child
2219 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2221 hr = StorageBaseImpl_WriteDirEntry(
2222 This,
2223 parentEntryRef,
2224 &parentEntry);
2225 if(FAILED(hr))
2227 return hr;
2231 return hr;
2235 /******************************************************************************
2236 * SetElementTimes (IStorage)
2238 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2239 IStorage* iface,
2240 const OLECHAR *pwcsName,/* [string][in] */
2241 const FILETIME *pctime, /* [in] */
2242 const FILETIME *patime, /* [in] */
2243 const FILETIME *pmtime) /* [in] */
2245 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2246 return S_OK;
2249 /******************************************************************************
2250 * SetStateBits (IStorage)
2252 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2253 IStorage* iface,
2254 DWORD grfStateBits,/* [in] */
2255 DWORD grfMask) /* [in] */
2257 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2259 if (This->reverted)
2260 return STG_E_REVERTED;
2262 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2263 return S_OK;
2266 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2267 DirRef index, const DirEntry *data)
2269 StorageImpl *This = (StorageImpl*)base;
2270 return StorageImpl_WriteDirEntry(This, index, data);
2273 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2274 DirRef index, DirEntry *data)
2276 StorageImpl *This = (StorageImpl*)base;
2277 return StorageImpl_ReadDirEntry(This, index, data);
2280 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2282 int i;
2284 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2286 if (!This->blockChainCache[i])
2288 return &This->blockChainCache[i];
2292 i = This->blockChainToEvict;
2294 BlockChainStream_Destroy(This->blockChainCache[i]);
2295 This->blockChainCache[i] = NULL;
2297 This->blockChainToEvict++;
2298 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2299 This->blockChainToEvict = 0;
2301 return &This->blockChainCache[i];
2304 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2305 DirRef index)
2307 int i, free_index=-1;
2309 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2311 if (!This->blockChainCache[i])
2313 if (free_index == -1) free_index = i;
2315 else if (This->blockChainCache[i]->ownerDirEntry == index)
2317 return &This->blockChainCache[i];
2321 if (free_index == -1)
2323 free_index = This->blockChainToEvict;
2325 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2326 This->blockChainCache[free_index] = NULL;
2328 This->blockChainToEvict++;
2329 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2330 This->blockChainToEvict = 0;
2333 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2334 return &This->blockChainCache[free_index];
2337 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2338 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2340 StorageImpl *This = (StorageImpl*)base;
2341 DirEntry data;
2342 HRESULT hr;
2343 ULONG bytesToRead;
2345 hr = StorageImpl_ReadDirEntry(This, index, &data);
2346 if (FAILED(hr)) return hr;
2348 if (data.size.QuadPart == 0)
2350 *bytesRead = 0;
2351 return S_OK;
2354 if (offset.QuadPart + size > data.size.QuadPart)
2356 bytesToRead = data.size.QuadPart - offset.QuadPart;
2358 else
2360 bytesToRead = size;
2363 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2365 SmallBlockChainStream *stream;
2367 stream = SmallBlockChainStream_Construct(This, NULL, index);
2368 if (!stream) return E_OUTOFMEMORY;
2370 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2372 SmallBlockChainStream_Destroy(stream);
2374 return hr;
2376 else
2378 BlockChainStream *stream = NULL;
2380 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2381 if (!stream) return E_OUTOFMEMORY;
2383 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2385 return hr;
2389 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2390 ULARGE_INTEGER newsize)
2392 StorageImpl *This = (StorageImpl*)base;
2393 DirEntry data;
2394 HRESULT hr;
2395 SmallBlockChainStream *smallblock=NULL;
2396 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2398 hr = StorageImpl_ReadDirEntry(This, index, &data);
2399 if (FAILED(hr)) return hr;
2401 /* In simple mode keep the stream size above the small block limit */
2402 if (This->base.openFlags & STGM_SIMPLE)
2403 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2405 if (data.size.QuadPart == newsize.QuadPart)
2406 return S_OK;
2408 /* Create a block chain object of the appropriate type */
2409 if (data.size.QuadPart == 0)
2411 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2413 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2414 if (!smallblock) return E_OUTOFMEMORY;
2416 else
2418 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2419 bigblock = *pbigblock;
2420 if (!bigblock) return E_OUTOFMEMORY;
2423 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2425 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2426 if (!smallblock) return E_OUTOFMEMORY;
2428 else
2430 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2431 bigblock = *pbigblock;
2432 if (!bigblock) return E_OUTOFMEMORY;
2435 /* Change the block chain type if necessary. */
2436 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2438 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2439 if (!bigblock)
2441 SmallBlockChainStream_Destroy(smallblock);
2442 return E_FAIL;
2445 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2446 *pbigblock = bigblock;
2448 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2450 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2451 if (!smallblock)
2452 return E_FAIL;
2455 /* Set the size of the block chain. */
2456 if (smallblock)
2458 SmallBlockChainStream_SetSize(smallblock, newsize);
2459 SmallBlockChainStream_Destroy(smallblock);
2461 else
2463 BlockChainStream_SetSize(bigblock, newsize);
2466 /* Set the size in the directory entry. */
2467 hr = StorageImpl_ReadDirEntry(This, index, &data);
2468 if (SUCCEEDED(hr))
2470 data.size = newsize;
2472 hr = StorageImpl_WriteDirEntry(This, index, &data);
2474 return hr;
2477 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2478 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2480 StorageImpl *This = (StorageImpl*)base;
2481 DirEntry data;
2482 HRESULT hr;
2483 ULARGE_INTEGER newSize;
2485 hr = StorageImpl_ReadDirEntry(This, index, &data);
2486 if (FAILED(hr)) return hr;
2488 /* Grow the stream if necessary */
2489 newSize.QuadPart = 0;
2490 newSize.QuadPart = offset.QuadPart + size;
2492 if (newSize.QuadPart > data.size.QuadPart)
2494 hr = StorageImpl_StreamSetSize(base, index, newSize);
2495 if (FAILED(hr))
2496 return hr;
2498 hr = StorageImpl_ReadDirEntry(This, index, &data);
2499 if (FAILED(hr)) return hr;
2502 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2504 SmallBlockChainStream *stream;
2506 stream = SmallBlockChainStream_Construct(This, NULL, index);
2507 if (!stream) return E_OUTOFMEMORY;
2509 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2511 SmallBlockChainStream_Destroy(stream);
2513 return hr;
2515 else
2517 BlockChainStream *stream;
2519 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2520 if (!stream) return E_OUTOFMEMORY;
2522 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2524 return hr;
2529 * Virtual function table for the IStorage32Impl class.
2531 static const IStorageVtbl Storage32Impl_Vtbl =
2533 StorageBaseImpl_QueryInterface,
2534 StorageBaseImpl_AddRef,
2535 StorageBaseImpl_Release,
2536 StorageBaseImpl_CreateStream,
2537 StorageBaseImpl_OpenStream,
2538 StorageBaseImpl_CreateStorage,
2539 StorageBaseImpl_OpenStorage,
2540 StorageBaseImpl_CopyTo,
2541 StorageBaseImpl_MoveElementTo,
2542 StorageImpl_Commit,
2543 StorageImpl_Revert,
2544 StorageBaseImpl_EnumElements,
2545 StorageBaseImpl_DestroyElement,
2546 StorageBaseImpl_RenameElement,
2547 StorageBaseImpl_SetElementTimes,
2548 StorageBaseImpl_SetClass,
2549 StorageBaseImpl_SetStateBits,
2550 StorageBaseImpl_Stat
2553 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2555 StorageImpl_Destroy,
2556 StorageImpl_Invalidate,
2557 StorageImpl_CreateDirEntry,
2558 StorageImpl_BaseWriteDirEntry,
2559 StorageImpl_BaseReadDirEntry,
2560 StorageImpl_DestroyDirEntry,
2561 StorageImpl_StreamReadAt,
2562 StorageImpl_StreamWriteAt,
2563 StorageImpl_StreamSetSize
2566 static HRESULT StorageImpl_Construct(
2567 HANDLE hFile,
2568 LPCOLESTR pwcsName,
2569 ILockBytes* pLkbyt,
2570 DWORD openFlags,
2571 BOOL fileBased,
2572 BOOL create,
2573 StorageImpl** result)
2575 StorageImpl* This;
2576 HRESULT hr = S_OK;
2577 DirEntry currentEntry;
2578 DirRef currentEntryRef;
2579 WCHAR fullpath[MAX_PATH];
2581 if ( FAILED( validateSTGM(openFlags) ))
2582 return STG_E_INVALIDFLAG;
2584 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2585 if (!This)
2586 return E_OUTOFMEMORY;
2588 memset(This, 0, sizeof(StorageImpl));
2590 list_init(&This->base.strmHead);
2592 list_init(&This->base.storageHead);
2594 This->base.lpVtbl = &Storage32Impl_Vtbl;
2595 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2596 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2597 This->base.openFlags = (openFlags & ~STGM_CREATE);
2598 This->base.ref = 1;
2599 This->base.create = create;
2601 This->base.reverted = 0;
2603 This->hFile = hFile;
2605 if(pwcsName) {
2606 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2608 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2610 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2611 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2612 if (!This->pwcsName)
2614 hr = STG_E_INSUFFICIENTMEMORY;
2615 goto end;
2617 strcpyW(This->pwcsName, fullpath);
2618 This->base.filename = This->pwcsName;
2622 * Initialize the big block cache.
2624 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2625 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2626 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2627 pLkbyt,
2628 openFlags,
2629 This->bigBlockSize,
2630 fileBased);
2632 if (This->bigBlockFile == 0)
2634 hr = E_FAIL;
2635 goto end;
2638 if (create)
2640 ULARGE_INTEGER size;
2641 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2644 * Initialize all header variables:
2645 * - The big block depot consists of one block and it is at block 0
2646 * - The directory table starts at block 1
2647 * - There is no small block depot
2649 memset( This->bigBlockDepotStart,
2650 BLOCK_UNUSED,
2651 sizeof(This->bigBlockDepotStart));
2653 This->bigBlockDepotCount = 1;
2654 This->bigBlockDepotStart[0] = 0;
2655 This->rootStartBlock = 1;
2656 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2657 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2658 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2659 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2660 This->extBigBlockDepotCount = 0;
2662 StorageImpl_SaveFileHeader(This);
2665 * Add one block for the big block depot and one block for the directory table
2667 size.u.HighPart = 0;
2668 size.u.LowPart = This->bigBlockSize * 3;
2669 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2672 * Initialize the big block depot
2674 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2675 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2676 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2677 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2679 else
2682 * Load the header for the file.
2684 hr = StorageImpl_LoadFileHeader(This);
2686 if (FAILED(hr))
2688 goto end;
2693 * There is no block depot cached yet.
2695 This->indexBlockDepotCached = 0xFFFFFFFF;
2698 * Start searching for free blocks with block 0.
2700 This->prevFreeBlock = 0;
2703 * Create the block chain abstractions.
2705 if(!(This->rootBlockChain =
2706 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2708 hr = STG_E_READFAULT;
2709 goto end;
2712 if(!(This->smallBlockDepotChain =
2713 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2714 DIRENTRY_NULL)))
2716 hr = STG_E_READFAULT;
2717 goto end;
2721 * Write the root storage entry (memory only)
2723 if (create)
2725 DirEntry rootEntry;
2727 * Initialize the directory table
2729 memset(&rootEntry, 0, sizeof(rootEntry));
2730 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2731 sizeof(rootEntry.name)/sizeof(WCHAR) );
2732 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2733 rootEntry.stgType = STGTY_ROOT;
2734 rootEntry.leftChild = DIRENTRY_NULL;
2735 rootEntry.rightChild = DIRENTRY_NULL;
2736 rootEntry.dirRootEntry = DIRENTRY_NULL;
2737 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2738 rootEntry.size.u.HighPart = 0;
2739 rootEntry.size.u.LowPart = 0;
2741 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2745 * Find the ID of the root storage.
2747 currentEntryRef = 0;
2751 hr = StorageImpl_ReadDirEntry(
2752 This,
2753 currentEntryRef,
2754 &currentEntry);
2756 if (SUCCEEDED(hr))
2758 if ( (currentEntry.sizeOfNameString != 0 ) &&
2759 (currentEntry.stgType == STGTY_ROOT) )
2761 This->base.storageDirEntry = currentEntryRef;
2765 currentEntryRef++;
2767 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2769 if (FAILED(hr))
2771 hr = STG_E_READFAULT;
2772 goto end;
2776 * Create the block chain abstraction for the small block root chain.
2778 if(!(This->smallBlockRootChain =
2779 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2781 hr = STG_E_READFAULT;
2784 end:
2785 if (FAILED(hr))
2787 IStorage_Release((IStorage*)This);
2788 *result = NULL;
2790 else
2791 *result = This;
2793 return hr;
2796 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2798 StorageImpl *This = (StorageImpl*) iface;
2800 StorageBaseImpl_DeleteAll(&This->base);
2802 This->base.reverted = 1;
2805 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2807 StorageImpl *This = (StorageImpl*) iface;
2808 int i;
2809 TRACE("(%p)\n", This);
2811 StorageImpl_Invalidate(iface);
2813 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2815 BlockChainStream_Destroy(This->smallBlockRootChain);
2816 BlockChainStream_Destroy(This->rootBlockChain);
2817 BlockChainStream_Destroy(This->smallBlockDepotChain);
2819 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2820 BlockChainStream_Destroy(This->blockChainCache[i]);
2822 if (This->bigBlockFile)
2823 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2824 HeapFree(GetProcessHeap(), 0, This);
2827 /******************************************************************************
2828 * Storage32Impl_GetNextFreeBigBlock
2830 * Returns the index of the next free big block.
2831 * If the big block depot is filled, this method will enlarge it.
2834 static ULONG StorageImpl_GetNextFreeBigBlock(
2835 StorageImpl* This)
2837 ULONG depotBlockIndexPos;
2838 BYTE depotBuffer[BIG_BLOCK_SIZE];
2839 BOOL success;
2840 ULONG depotBlockOffset;
2841 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2842 ULONG nextBlockIndex = BLOCK_SPECIAL;
2843 int depotIndex = 0;
2844 ULONG freeBlock = BLOCK_UNUSED;
2846 depotIndex = This->prevFreeBlock / blocksPerDepot;
2847 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2850 * Scan the entire big block depot until we find a block marked free
2852 while (nextBlockIndex != BLOCK_UNUSED)
2854 if (depotIndex < COUNT_BBDEPOTINHEADER)
2856 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2859 * Grow the primary depot.
2861 if (depotBlockIndexPos == BLOCK_UNUSED)
2863 depotBlockIndexPos = depotIndex*blocksPerDepot;
2866 * Add a block depot.
2868 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2869 This->bigBlockDepotCount++;
2870 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2873 * Flag it as a block depot.
2875 StorageImpl_SetNextBlockInChain(This,
2876 depotBlockIndexPos,
2877 BLOCK_SPECIAL);
2879 /* Save new header information.
2881 StorageImpl_SaveFileHeader(This);
2884 else
2886 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2888 if (depotBlockIndexPos == BLOCK_UNUSED)
2891 * Grow the extended depot.
2893 ULONG extIndex = BLOCK_UNUSED;
2894 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2895 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2897 if (extBlockOffset == 0)
2899 /* We need an extended block.
2901 extIndex = Storage32Impl_AddExtBlockDepot(This);
2902 This->extBigBlockDepotCount++;
2903 depotBlockIndexPos = extIndex + 1;
2905 else
2906 depotBlockIndexPos = depotIndex * blocksPerDepot;
2909 * Add a block depot and mark it in the extended block.
2911 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2912 This->bigBlockDepotCount++;
2913 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2915 /* Flag the block depot.
2917 StorageImpl_SetNextBlockInChain(This,
2918 depotBlockIndexPos,
2919 BLOCK_SPECIAL);
2921 /* If necessary, flag the extended depot block.
2923 if (extIndex != BLOCK_UNUSED)
2924 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2926 /* Save header information.
2928 StorageImpl_SaveFileHeader(This);
2932 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2934 if (success)
2936 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2937 ( nextBlockIndex != BLOCK_UNUSED))
2939 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2941 if (nextBlockIndex == BLOCK_UNUSED)
2943 freeBlock = (depotIndex * blocksPerDepot) +
2944 (depotBlockOffset/sizeof(ULONG));
2947 depotBlockOffset += sizeof(ULONG);
2951 depotIndex++;
2952 depotBlockOffset = 0;
2956 * make sure that the block physically exists before using it
2958 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2960 This->prevFreeBlock = freeBlock;
2962 return freeBlock;
2965 /******************************************************************************
2966 * Storage32Impl_AddBlockDepot
2968 * This will create a depot block, essentially it is a block initialized
2969 * to BLOCK_UNUSEDs.
2971 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2973 BYTE blockBuffer[BIG_BLOCK_SIZE];
2976 * Initialize blocks as free
2978 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2979 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2982 /******************************************************************************
2983 * Storage32Impl_GetExtDepotBlock
2985 * Returns the index of the block that corresponds to the specified depot
2986 * index. This method is only for depot indexes equal or greater than
2987 * COUNT_BBDEPOTINHEADER.
2989 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2991 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2992 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2993 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2994 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2995 ULONG blockIndex = BLOCK_UNUSED;
2996 ULONG extBlockIndex = This->extBigBlockDepotStart;
2998 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3000 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3001 return BLOCK_UNUSED;
3003 while (extBlockCount > 0)
3005 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3006 extBlockCount--;
3009 if (extBlockIndex != BLOCK_UNUSED)
3010 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3011 extBlockOffset * sizeof(ULONG), &blockIndex);
3013 return blockIndex;
3016 /******************************************************************************
3017 * Storage32Impl_SetExtDepotBlock
3019 * Associates the specified block index to the specified depot index.
3020 * This method is only for depot indexes equal or greater than
3021 * COUNT_BBDEPOTINHEADER.
3023 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3025 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3026 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3027 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3028 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3029 ULONG extBlockIndex = This->extBigBlockDepotStart;
3031 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3033 while (extBlockCount > 0)
3035 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3036 extBlockCount--;
3039 if (extBlockIndex != BLOCK_UNUSED)
3041 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3042 extBlockOffset * sizeof(ULONG),
3043 blockIndex);
3047 /******************************************************************************
3048 * Storage32Impl_AddExtBlockDepot
3050 * Creates an extended depot block.
3052 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3054 ULONG numExtBlocks = This->extBigBlockDepotCount;
3055 ULONG nextExtBlock = This->extBigBlockDepotStart;
3056 BYTE depotBuffer[BIG_BLOCK_SIZE];
3057 ULONG index = BLOCK_UNUSED;
3058 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3059 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3060 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3062 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3063 blocksPerDepotBlock;
3065 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3068 * The first extended block.
3070 This->extBigBlockDepotStart = index;
3072 else
3074 unsigned int i;
3076 * Follow the chain to the last one.
3078 for (i = 0; i < (numExtBlocks - 1); i++)
3080 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3084 * Add the new extended block to the chain.
3086 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3087 index);
3091 * Initialize this block.
3093 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3094 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3096 return index;
3099 /******************************************************************************
3100 * Storage32Impl_FreeBigBlock
3102 * This method will flag the specified block as free in the big block depot.
3104 static void StorageImpl_FreeBigBlock(
3105 StorageImpl* This,
3106 ULONG blockIndex)
3108 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3110 if (blockIndex < This->prevFreeBlock)
3111 This->prevFreeBlock = blockIndex;
3114 /************************************************************************
3115 * Storage32Impl_GetNextBlockInChain
3117 * This method will retrieve the block index of the next big block in
3118 * in the chain.
3120 * Params: This - Pointer to the Storage object.
3121 * blockIndex - Index of the block to retrieve the chain
3122 * for.
3123 * nextBlockIndex - receives the return value.
3125 * Returns: This method returns the index of the next block in the chain.
3126 * It will return the constants:
3127 * BLOCK_SPECIAL - If the block given was not part of a
3128 * chain.
3129 * BLOCK_END_OF_CHAIN - If the block given was the last in
3130 * a chain.
3131 * BLOCK_UNUSED - If the block given was not past of a chain
3132 * and is available.
3133 * BLOCK_EXTBBDEPOT - This block is part of the extended
3134 * big block depot.
3136 * See Windows documentation for more details on IStorage methods.
3138 static HRESULT StorageImpl_GetNextBlockInChain(
3139 StorageImpl* This,
3140 ULONG blockIndex,
3141 ULONG* nextBlockIndex)
3143 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3144 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3145 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3146 BYTE depotBuffer[BIG_BLOCK_SIZE];
3147 BOOL success;
3148 ULONG depotBlockIndexPos;
3149 int index;
3151 *nextBlockIndex = BLOCK_SPECIAL;
3153 if(depotBlockCount >= This->bigBlockDepotCount)
3155 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3156 This->bigBlockDepotCount);
3157 return STG_E_READFAULT;
3161 * Cache the currently accessed depot block.
3163 if (depotBlockCount != This->indexBlockDepotCached)
3165 This->indexBlockDepotCached = depotBlockCount;
3167 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3169 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3171 else
3174 * We have to look in the extended depot.
3176 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3179 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3181 if (!success)
3182 return STG_E_READFAULT;
3184 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
3186 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3187 This->blockDepotCached[index] = *nextBlockIndex;
3191 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3193 return S_OK;
3196 /******************************************************************************
3197 * Storage32Impl_GetNextExtendedBlock
3199 * Given an extended block this method will return the next extended block.
3201 * NOTES:
3202 * The last ULONG of an extended block is the block index of the next
3203 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3204 * depot.
3206 * Return values:
3207 * - The index of the next extended block
3208 * - BLOCK_UNUSED: there is no next extended block.
3209 * - Any other return values denotes failure.
3211 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3213 ULONG nextBlockIndex = BLOCK_SPECIAL;
3214 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3216 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3217 &nextBlockIndex);
3219 return nextBlockIndex;
3222 /******************************************************************************
3223 * Storage32Impl_SetNextBlockInChain
3225 * This method will write the index of the specified block's next block
3226 * in the big block depot.
3228 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3229 * do the following
3231 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3232 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3233 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3236 static void StorageImpl_SetNextBlockInChain(
3237 StorageImpl* This,
3238 ULONG blockIndex,
3239 ULONG nextBlock)
3241 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3242 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3243 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3244 ULONG depotBlockIndexPos;
3246 assert(depotBlockCount < This->bigBlockDepotCount);
3247 assert(blockIndex != nextBlock);
3249 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3251 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3253 else
3256 * We have to look in the extended depot.
3258 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3261 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3262 nextBlock);
3264 * Update the cached block depot, if necessary.
3266 if (depotBlockCount == This->indexBlockDepotCached)
3268 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3272 /******************************************************************************
3273 * Storage32Impl_LoadFileHeader
3275 * This method will read in the file header, i.e. big block index -1.
3277 static HRESULT StorageImpl_LoadFileHeader(
3278 StorageImpl* This)
3280 HRESULT hr = STG_E_FILENOTFOUND;
3281 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3282 BOOL success;
3283 int index;
3285 TRACE("\n");
3287 * Get a pointer to the big block of data containing the header.
3289 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3292 * Extract the information from the header.
3294 if (success)
3297 * Check for the "magic number" signature and return an error if it is not
3298 * found.
3300 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3302 return STG_E_OLDFORMAT;
3305 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3307 return STG_E_INVALIDHEADER;
3310 StorageUtl_ReadWord(
3311 headerBigBlock,
3312 OFFSET_BIGBLOCKSIZEBITS,
3313 &This->bigBlockSizeBits);
3315 StorageUtl_ReadWord(
3316 headerBigBlock,
3317 OFFSET_SMALLBLOCKSIZEBITS,
3318 &This->smallBlockSizeBits);
3320 StorageUtl_ReadDWord(
3321 headerBigBlock,
3322 OFFSET_BBDEPOTCOUNT,
3323 &This->bigBlockDepotCount);
3325 StorageUtl_ReadDWord(
3326 headerBigBlock,
3327 OFFSET_ROOTSTARTBLOCK,
3328 &This->rootStartBlock);
3330 StorageUtl_ReadDWord(
3331 headerBigBlock,
3332 OFFSET_SBDEPOTSTART,
3333 &This->smallBlockDepotStart);
3335 StorageUtl_ReadDWord(
3336 headerBigBlock,
3337 OFFSET_EXTBBDEPOTSTART,
3338 &This->extBigBlockDepotStart);
3340 StorageUtl_ReadDWord(
3341 headerBigBlock,
3342 OFFSET_EXTBBDEPOTCOUNT,
3343 &This->extBigBlockDepotCount);
3345 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3347 StorageUtl_ReadDWord(
3348 headerBigBlock,
3349 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3350 &(This->bigBlockDepotStart[index]));
3354 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3356 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3357 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3360 * Right now, the code is making some assumptions about the size of the
3361 * blocks, just make sure they are what we're expecting.
3363 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3364 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3366 WARN("Broken OLE storage file\n");
3367 hr = STG_E_INVALIDHEADER;
3369 else
3370 hr = S_OK;
3373 return hr;
3376 /******************************************************************************
3377 * Storage32Impl_SaveFileHeader
3379 * This method will save to the file the header, i.e. big block -1.
3381 static void StorageImpl_SaveFileHeader(
3382 StorageImpl* This)
3384 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3385 int index;
3386 BOOL success;
3389 * Get a pointer to the big block of data containing the header.
3391 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3394 * If the block read failed, the file is probably new.
3396 if (!success)
3399 * Initialize for all unknown fields.
3401 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3404 * Initialize the magic number.
3406 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3409 * And a bunch of things we don't know what they mean
3411 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3412 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3413 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3414 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3418 * Write the information to the header.
3420 StorageUtl_WriteWord(
3421 headerBigBlock,
3422 OFFSET_BIGBLOCKSIZEBITS,
3423 This->bigBlockSizeBits);
3425 StorageUtl_WriteWord(
3426 headerBigBlock,
3427 OFFSET_SMALLBLOCKSIZEBITS,
3428 This->smallBlockSizeBits);
3430 StorageUtl_WriteDWord(
3431 headerBigBlock,
3432 OFFSET_BBDEPOTCOUNT,
3433 This->bigBlockDepotCount);
3435 StorageUtl_WriteDWord(
3436 headerBigBlock,
3437 OFFSET_ROOTSTARTBLOCK,
3438 This->rootStartBlock);
3440 StorageUtl_WriteDWord(
3441 headerBigBlock,
3442 OFFSET_SBDEPOTSTART,
3443 This->smallBlockDepotStart);
3445 StorageUtl_WriteDWord(
3446 headerBigBlock,
3447 OFFSET_SBDEPOTCOUNT,
3448 This->smallBlockDepotChain ?
3449 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3451 StorageUtl_WriteDWord(
3452 headerBigBlock,
3453 OFFSET_EXTBBDEPOTSTART,
3454 This->extBigBlockDepotStart);
3456 StorageUtl_WriteDWord(
3457 headerBigBlock,
3458 OFFSET_EXTBBDEPOTCOUNT,
3459 This->extBigBlockDepotCount);
3461 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3463 StorageUtl_WriteDWord(
3464 headerBigBlock,
3465 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3466 (This->bigBlockDepotStart[index]));
3470 * Write the big block back to the file.
3472 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3475 /******************************************************************************
3476 * StorageImpl_ReadRawDirEntry
3478 * This method will read the raw data from a directory entry in the file.
3480 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3482 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3484 ULARGE_INTEGER offset;
3485 HRESULT hr;
3486 ULONG bytesRead;
3488 offset.u.HighPart = 0;
3489 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3491 hr = BlockChainStream_ReadAt(
3492 This->rootBlockChain,
3493 offset,
3494 RAW_DIRENTRY_SIZE,
3495 buffer,
3496 &bytesRead);
3498 return hr;
3501 /******************************************************************************
3502 * StorageImpl_WriteRawDirEntry
3504 * This method will write the raw data from a directory entry in the file.
3506 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3508 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3510 ULARGE_INTEGER offset;
3511 HRESULT hr;
3512 ULONG bytesRead;
3514 offset.u.HighPart = 0;
3515 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3517 hr = BlockChainStream_WriteAt(
3518 This->rootBlockChain,
3519 offset,
3520 RAW_DIRENTRY_SIZE,
3521 buffer,
3522 &bytesRead);
3524 return hr;
3527 /******************************************************************************
3528 * UpdateRawDirEntry
3530 * Update raw directory entry data from the fields in newData.
3532 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3534 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3536 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3538 memcpy(
3539 buffer + OFFSET_PS_NAME,
3540 newData->name,
3541 DIRENTRY_NAME_BUFFER_LEN );
3543 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3545 StorageUtl_WriteWord(
3546 buffer,
3547 OFFSET_PS_NAMELENGTH,
3548 newData->sizeOfNameString);
3550 StorageUtl_WriteDWord(
3551 buffer,
3552 OFFSET_PS_LEFTCHILD,
3553 newData->leftChild);
3555 StorageUtl_WriteDWord(
3556 buffer,
3557 OFFSET_PS_RIGHTCHILD,
3558 newData->rightChild);
3560 StorageUtl_WriteDWord(
3561 buffer,
3562 OFFSET_PS_DIRROOT,
3563 newData->dirRootEntry);
3565 StorageUtl_WriteGUID(
3566 buffer,
3567 OFFSET_PS_GUID,
3568 &newData->clsid);
3570 StorageUtl_WriteDWord(
3571 buffer,
3572 OFFSET_PS_CTIMELOW,
3573 newData->ctime.dwLowDateTime);
3575 StorageUtl_WriteDWord(
3576 buffer,
3577 OFFSET_PS_CTIMEHIGH,
3578 newData->ctime.dwHighDateTime);
3580 StorageUtl_WriteDWord(
3581 buffer,
3582 OFFSET_PS_MTIMELOW,
3583 newData->mtime.dwLowDateTime);
3585 StorageUtl_WriteDWord(
3586 buffer,
3587 OFFSET_PS_MTIMEHIGH,
3588 newData->ctime.dwHighDateTime);
3590 StorageUtl_WriteDWord(
3591 buffer,
3592 OFFSET_PS_STARTBLOCK,
3593 newData->startingBlock);
3595 StorageUtl_WriteDWord(
3596 buffer,
3597 OFFSET_PS_SIZE,
3598 newData->size.u.LowPart);
3601 /******************************************************************************
3602 * Storage32Impl_ReadDirEntry
3604 * This method will read the specified directory entry.
3606 HRESULT StorageImpl_ReadDirEntry(
3607 StorageImpl* This,
3608 DirRef index,
3609 DirEntry* buffer)
3611 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3612 HRESULT readRes;
3614 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3616 if (SUCCEEDED(readRes))
3618 memset(buffer->name, 0, sizeof(buffer->name));
3619 memcpy(
3620 buffer->name,
3621 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3622 DIRENTRY_NAME_BUFFER_LEN );
3623 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3625 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3627 StorageUtl_ReadWord(
3628 currentEntry,
3629 OFFSET_PS_NAMELENGTH,
3630 &buffer->sizeOfNameString);
3632 StorageUtl_ReadDWord(
3633 currentEntry,
3634 OFFSET_PS_LEFTCHILD,
3635 &buffer->leftChild);
3637 StorageUtl_ReadDWord(
3638 currentEntry,
3639 OFFSET_PS_RIGHTCHILD,
3640 &buffer->rightChild);
3642 StorageUtl_ReadDWord(
3643 currentEntry,
3644 OFFSET_PS_DIRROOT,
3645 &buffer->dirRootEntry);
3647 StorageUtl_ReadGUID(
3648 currentEntry,
3649 OFFSET_PS_GUID,
3650 &buffer->clsid);
3652 StorageUtl_ReadDWord(
3653 currentEntry,
3654 OFFSET_PS_CTIMELOW,
3655 &buffer->ctime.dwLowDateTime);
3657 StorageUtl_ReadDWord(
3658 currentEntry,
3659 OFFSET_PS_CTIMEHIGH,
3660 &buffer->ctime.dwHighDateTime);
3662 StorageUtl_ReadDWord(
3663 currentEntry,
3664 OFFSET_PS_MTIMELOW,
3665 &buffer->mtime.dwLowDateTime);
3667 StorageUtl_ReadDWord(
3668 currentEntry,
3669 OFFSET_PS_MTIMEHIGH,
3670 &buffer->mtime.dwHighDateTime);
3672 StorageUtl_ReadDWord(
3673 currentEntry,
3674 OFFSET_PS_STARTBLOCK,
3675 &buffer->startingBlock);
3677 StorageUtl_ReadDWord(
3678 currentEntry,
3679 OFFSET_PS_SIZE,
3680 &buffer->size.u.LowPart);
3682 buffer->size.u.HighPart = 0;
3685 return readRes;
3688 /*********************************************************************
3689 * Write the specified directory entry to the file
3691 HRESULT StorageImpl_WriteDirEntry(
3692 StorageImpl* This,
3693 DirRef index,
3694 const DirEntry* buffer)
3696 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3697 HRESULT writeRes;
3699 UpdateRawDirEntry(currentEntry, buffer);
3701 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3702 return writeRes;
3705 static BOOL StorageImpl_ReadBigBlock(
3706 StorageImpl* This,
3707 ULONG blockIndex,
3708 void* buffer)
3710 ULARGE_INTEGER ulOffset;
3711 DWORD read;
3713 ulOffset.u.HighPart = 0;
3714 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3716 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3717 return (read == This->bigBlockSize);
3720 static BOOL StorageImpl_ReadDWordFromBigBlock(
3721 StorageImpl* This,
3722 ULONG blockIndex,
3723 ULONG offset,
3724 DWORD* value)
3726 ULARGE_INTEGER ulOffset;
3727 DWORD read;
3728 DWORD tmp;
3730 ulOffset.u.HighPart = 0;
3731 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3732 ulOffset.u.LowPart += offset;
3734 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3735 *value = lendian32toh(tmp);
3736 return (read == sizeof(DWORD));
3739 static BOOL StorageImpl_WriteBigBlock(
3740 StorageImpl* This,
3741 ULONG blockIndex,
3742 const void* buffer)
3744 ULARGE_INTEGER ulOffset;
3745 DWORD wrote;
3747 ulOffset.u.HighPart = 0;
3748 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3750 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3751 return (wrote == This->bigBlockSize);
3754 static BOOL StorageImpl_WriteDWordToBigBlock(
3755 StorageImpl* This,
3756 ULONG blockIndex,
3757 ULONG offset,
3758 DWORD value)
3760 ULARGE_INTEGER ulOffset;
3761 DWORD wrote;
3763 ulOffset.u.HighPart = 0;
3764 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3765 ulOffset.u.LowPart += offset;
3767 value = htole32(value);
3768 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3769 return (wrote == sizeof(DWORD));
3772 /******************************************************************************
3773 * Storage32Impl_SmallBlocksToBigBlocks
3775 * This method will convert a small block chain to a big block chain.
3776 * The small block chain will be destroyed.
3778 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3779 StorageImpl* This,
3780 SmallBlockChainStream** ppsbChain)
3782 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3783 ULARGE_INTEGER size, offset;
3784 ULONG cbRead, cbWritten;
3785 ULARGE_INTEGER cbTotalRead;
3786 DirRef streamEntryRef;
3787 HRESULT resWrite = S_OK;
3788 HRESULT resRead;
3789 DirEntry streamEntry;
3790 BYTE *buffer;
3791 BlockChainStream *bbTempChain = NULL;
3792 BlockChainStream *bigBlockChain = NULL;
3795 * Create a temporary big block chain that doesn't have
3796 * an associated directory entry. This temporary chain will be
3797 * used to copy data from small blocks to big blocks.
3799 bbTempChain = BlockChainStream_Construct(This,
3800 &bbHeadOfChain,
3801 DIRENTRY_NULL);
3802 if(!bbTempChain) return NULL;
3804 * Grow the big block chain.
3806 size = SmallBlockChainStream_GetSize(*ppsbChain);
3807 BlockChainStream_SetSize(bbTempChain, size);
3810 * Copy the contents of the small block chain to the big block chain
3811 * by small block size increments.
3813 offset.u.LowPart = 0;
3814 offset.u.HighPart = 0;
3815 cbTotalRead.QuadPart = 0;
3817 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3820 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3821 offset,
3822 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3823 buffer,
3824 &cbRead);
3825 if (FAILED(resRead))
3826 break;
3828 if (cbRead > 0)
3830 cbTotalRead.QuadPart += cbRead;
3832 resWrite = BlockChainStream_WriteAt(bbTempChain,
3833 offset,
3834 cbRead,
3835 buffer,
3836 &cbWritten);
3838 if (FAILED(resWrite))
3839 break;
3841 offset.u.LowPart += cbRead;
3843 } while (cbTotalRead.QuadPart < size.QuadPart);
3844 HeapFree(GetProcessHeap(),0,buffer);
3846 size.u.HighPart = 0;
3847 size.u.LowPart = 0;
3849 if (FAILED(resRead) || FAILED(resWrite))
3851 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3852 BlockChainStream_SetSize(bbTempChain, size);
3853 BlockChainStream_Destroy(bbTempChain);
3854 return NULL;
3858 * Destroy the small block chain.
3860 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3861 SmallBlockChainStream_SetSize(*ppsbChain, size);
3862 SmallBlockChainStream_Destroy(*ppsbChain);
3863 *ppsbChain = 0;
3866 * Change the directory entry. This chain is now a big block chain
3867 * and it doesn't reside in the small blocks chain anymore.
3869 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3871 streamEntry.startingBlock = bbHeadOfChain;
3873 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3876 * Destroy the temporary entryless big block chain.
3877 * Create a new big block chain associated with this entry.
3879 BlockChainStream_Destroy(bbTempChain);
3880 bigBlockChain = BlockChainStream_Construct(This,
3881 NULL,
3882 streamEntryRef);
3884 return bigBlockChain;
3887 /******************************************************************************
3888 * Storage32Impl_BigBlocksToSmallBlocks
3890 * This method will convert a big block chain to a small block chain.
3891 * The big block chain will be destroyed on success.
3893 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3894 StorageImpl* This,
3895 BlockChainStream** ppbbChain)
3897 ULARGE_INTEGER size, offset, cbTotalRead;
3898 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3899 DirRef streamEntryRef;
3900 HRESULT resWrite = S_OK, resRead;
3901 DirEntry streamEntry;
3902 BYTE* buffer;
3903 SmallBlockChainStream* sbTempChain;
3905 TRACE("%p %p\n", This, ppbbChain);
3907 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3908 DIRENTRY_NULL);
3910 if(!sbTempChain)
3911 return NULL;
3913 size = BlockChainStream_GetSize(*ppbbChain);
3914 SmallBlockChainStream_SetSize(sbTempChain, size);
3916 offset.u.HighPart = 0;
3917 offset.u.LowPart = 0;
3918 cbTotalRead.QuadPart = 0;
3919 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3922 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3923 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3924 buffer, &cbRead);
3926 if(FAILED(resRead))
3927 break;
3929 if(cbRead > 0)
3931 cbTotalRead.QuadPart += cbRead;
3933 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3934 cbRead, buffer, &cbWritten);
3936 if(FAILED(resWrite))
3937 break;
3939 offset.u.LowPart += cbRead;
3941 }while(cbTotalRead.QuadPart < size.QuadPart);
3942 HeapFree(GetProcessHeap(), 0, buffer);
3944 size.u.HighPart = 0;
3945 size.u.LowPart = 0;
3947 if(FAILED(resRead) || FAILED(resWrite))
3949 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3950 SmallBlockChainStream_SetSize(sbTempChain, size);
3951 SmallBlockChainStream_Destroy(sbTempChain);
3952 return NULL;
3955 /* destroy the original big block chain */
3956 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3957 BlockChainStream_SetSize(*ppbbChain, size);
3958 BlockChainStream_Destroy(*ppbbChain);
3959 *ppbbChain = NULL;
3961 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3962 streamEntry.startingBlock = sbHeadOfChain;
3963 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3965 SmallBlockChainStream_Destroy(sbTempChain);
3966 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3969 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
3971 HRESULT hr;
3972 DirEntry parentData, snapshotData;
3974 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
3975 0, (IStorage**)snapshot);
3977 if (SUCCEEDED(hr))
3979 hr = StorageBaseImpl_ReadDirEntry(original,
3980 original->storageDirEntry, &parentData);
3982 if (SUCCEEDED(hr))
3983 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
3984 (*snapshot)->storageDirEntry, &snapshotData);
3986 if (SUCCEEDED(hr))
3988 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
3989 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
3990 snapshotData.stgType = parentData.stgType;
3991 snapshotData.clsid = parentData.clsid;
3992 snapshotData.ctime = parentData.ctime;
3993 snapshotData.mtime = parentData.mtime;
3994 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
3995 (*snapshot)->storageDirEntry, &snapshotData);
3998 if (SUCCEEDED(hr))
3999 hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
4000 (IStorage*)(*snapshot));
4002 if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
4005 return hr;
4008 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4009 IStorage* iface,
4010 DWORD grfCommitFlags) /* [in] */
4012 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4013 HRESULT hr;
4014 DirEntry data, tempStorageData, snapshotRootData;
4015 DirRef tempStorageEntry, oldDirRoot;
4016 StorageInternalImpl *tempStorage;
4018 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4020 /* Cannot commit a read-only transacted storage */
4021 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4022 return STG_E_ACCESSDENIED;
4024 /* To prevent data loss, we create the new structure in the file before we
4025 * delete the old one, so that in case of errors the old data is intact. We
4026 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4027 * needed in the rare situation where we have just enough free disk space to
4028 * overwrite the existing data. */
4030 /* Create an orphaned storage in the parent for the new directory structure. */
4031 memset(&data, 0, sizeof(data));
4032 data.name[0] = 'D';
4033 data.sizeOfNameString = 1;
4034 data.stgType = STGTY_STORAGE;
4035 data.leftChild = DIRENTRY_NULL;
4036 data.rightChild = DIRENTRY_NULL;
4037 data.dirRootEntry = DIRENTRY_NULL;
4038 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
4040 if (FAILED(hr)) return hr;
4042 tempStorage = StorageInternalImpl_Construct(This->transactedParent,
4043 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
4044 if (tempStorage)
4046 hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
4047 (IStorage*)tempStorage);
4049 list_init(&tempStorage->ParentListEntry);
4051 IStorage_Release((IStorage*) tempStorage);
4053 else
4054 hr = E_OUTOFMEMORY;
4056 if (FAILED(hr))
4058 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4059 return hr;
4062 /* Update the storage to use the new data in one step. */
4063 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4064 This->transactedParent->storageDirEntry, &data);
4066 if (SUCCEEDED(hr))
4068 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4069 tempStorageEntry, &tempStorageData);
4072 if (SUCCEEDED(hr))
4074 hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4075 This->snapshot->storageDirEntry, &snapshotRootData);
4078 if (SUCCEEDED(hr))
4080 oldDirRoot = data.dirRootEntry;
4081 data.dirRootEntry = tempStorageData.dirRootEntry;
4082 data.clsid = snapshotRootData.clsid;
4083 data.ctime = snapshotRootData.ctime;
4084 data.mtime = snapshotRootData.mtime;
4086 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4087 This->transactedParent->storageDirEntry, &data);
4090 if (SUCCEEDED(hr))
4092 /* Destroy the old now-orphaned data. */
4093 DestroyReachableEntries(This->transactedParent, oldDirRoot);
4094 StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4096 else
4098 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4101 return hr;
4104 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4105 IStorage* iface)
4107 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4108 StorageBaseImpl *newSnapshot;
4109 HRESULT hr;
4111 TRACE("(%p)\n", iface);
4113 /* Create a new copy of the parent data. */
4114 hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4115 if (FAILED(hr)) return hr;
4117 /* Destroy the open objects. */
4118 StorageBaseImpl_DeleteAll(&This->base);
4120 /* Replace our current snapshot. */
4121 IStorage_Release((IStorage*)This->snapshot);
4122 This->snapshot = newSnapshot;
4124 return S_OK;
4127 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4129 if (!This->reverted)
4131 TRACE("Storage invalidated (stg=%p)\n", This);
4133 This->reverted = 1;
4135 StorageBaseImpl_DeleteAll(This);
4139 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4141 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4143 TransactedSnapshotImpl_Invalidate(iface);
4145 IStorage_Release((IStorage*)This->transactedParent);
4147 IStorage_Release((IStorage*)This->snapshot);
4149 HeapFree(GetProcessHeap(), 0, This);
4152 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4153 const DirEntry *newData, DirRef *index)
4155 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4157 return StorageBaseImpl_CreateDirEntry(This->snapshot,
4158 newData, index);
4161 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4162 DirRef index, const DirEntry *data)
4164 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4166 return StorageBaseImpl_WriteDirEntry(This->snapshot,
4167 index, data);
4170 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4171 DirRef index, DirEntry *data)
4173 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4175 return StorageBaseImpl_ReadDirEntry(This->snapshot,
4176 index, data);
4179 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4180 DirRef index)
4182 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4184 return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4185 index);
4188 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4189 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4191 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4193 return StorageBaseImpl_StreamReadAt(This->snapshot,
4194 index, offset, size, buffer, bytesRead);
4197 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4198 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4200 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4202 return StorageBaseImpl_StreamWriteAt(This->snapshot,
4203 index, offset, size, buffer, bytesWritten);
4206 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4207 DirRef index, ULARGE_INTEGER newsize)
4209 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4211 return StorageBaseImpl_StreamSetSize(This->snapshot,
4212 index, newsize);
4215 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4217 StorageBaseImpl_QueryInterface,
4218 StorageBaseImpl_AddRef,
4219 StorageBaseImpl_Release,
4220 StorageBaseImpl_CreateStream,
4221 StorageBaseImpl_OpenStream,
4222 StorageBaseImpl_CreateStorage,
4223 StorageBaseImpl_OpenStorage,
4224 StorageBaseImpl_CopyTo,
4225 StorageBaseImpl_MoveElementTo,
4226 TransactedSnapshotImpl_Commit,
4227 TransactedSnapshotImpl_Revert,
4228 StorageBaseImpl_EnumElements,
4229 StorageBaseImpl_DestroyElement,
4230 StorageBaseImpl_RenameElement,
4231 StorageBaseImpl_SetElementTimes,
4232 StorageBaseImpl_SetClass,
4233 StorageBaseImpl_SetStateBits,
4234 StorageBaseImpl_Stat
4237 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4239 TransactedSnapshotImpl_Destroy,
4240 TransactedSnapshotImpl_Invalidate,
4241 TransactedSnapshotImpl_CreateDirEntry,
4242 TransactedSnapshotImpl_WriteDirEntry,
4243 TransactedSnapshotImpl_ReadDirEntry,
4244 TransactedSnapshotImpl_DestroyDirEntry,
4245 TransactedSnapshotImpl_StreamReadAt,
4246 TransactedSnapshotImpl_StreamWriteAt,
4247 TransactedSnapshotImpl_StreamSetSize
4250 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4251 TransactedSnapshotImpl** result)
4253 HRESULT hr;
4255 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4256 if (*result)
4258 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4260 /* This is OK because the property set storage functions use the IStorage functions. */
4261 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4263 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4265 list_init(&(*result)->base.strmHead);
4267 list_init(&(*result)->base.storageHead);
4269 (*result)->base.ref = 1;
4271 (*result)->base.openFlags = parentStorage->openFlags;
4273 (*result)->base.filename = parentStorage->filename;
4275 /* Create a new temporary storage to act as the snapshot */
4276 hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4278 if (SUCCEEDED(hr))
4280 (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4282 /* parentStorage already has 1 reference, which we take over here. */
4283 (*result)->transactedParent = parentStorage;
4285 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4288 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4290 return hr;
4292 else
4293 return E_OUTOFMEMORY;
4296 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4297 StorageBaseImpl** result)
4299 static int fixme=0;
4301 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4303 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4306 return TransactedSnapshotImpl_Construct(parentStorage,
4307 (TransactedSnapshotImpl**)result);
4310 static HRESULT Storage_Construct(
4311 HANDLE hFile,
4312 LPCOLESTR pwcsName,
4313 ILockBytes* pLkbyt,
4314 DWORD openFlags,
4315 BOOL fileBased,
4316 BOOL create,
4317 StorageBaseImpl** result)
4319 StorageImpl *newStorage;
4320 StorageBaseImpl *newTransactedStorage;
4321 HRESULT hr;
4323 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, &newStorage);
4324 if (FAILED(hr)) goto end;
4326 if (openFlags & STGM_TRANSACTED)
4328 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4329 if (FAILED(hr))
4330 IStorage_Release((IStorage*)newStorage);
4331 else
4332 *result = newTransactedStorage;
4334 else
4335 *result = &newStorage->base;
4337 end:
4338 return hr;
4341 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4343 StorageInternalImpl* This = (StorageInternalImpl*) base;
4345 if (!This->base.reverted)
4347 TRACE("Storage invalidated (stg=%p)\n", This);
4349 This->base.reverted = 1;
4351 This->parentStorage = NULL;
4353 StorageBaseImpl_DeleteAll(&This->base);
4355 list_remove(&This->ParentListEntry);
4359 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4361 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4363 StorageInternalImpl_Invalidate(&This->base);
4365 HeapFree(GetProcessHeap(), 0, This);
4368 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4369 const DirEntry *newData, DirRef *index)
4371 StorageInternalImpl* This = (StorageInternalImpl*) base;
4373 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4374 newData, index);
4377 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4378 DirRef index, const DirEntry *data)
4380 StorageInternalImpl* This = (StorageInternalImpl*) base;
4382 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4383 index, data);
4386 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4387 DirRef index, DirEntry *data)
4389 StorageInternalImpl* This = (StorageInternalImpl*) base;
4391 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4392 index, data);
4395 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4396 DirRef index)
4398 StorageInternalImpl* This = (StorageInternalImpl*) base;
4400 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4401 index);
4404 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4405 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4407 StorageInternalImpl* This = (StorageInternalImpl*) base;
4409 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4410 index, offset, size, buffer, bytesRead);
4413 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4414 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4416 StorageInternalImpl* This = (StorageInternalImpl*) base;
4418 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4419 index, offset, size, buffer, bytesWritten);
4422 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4423 DirRef index, ULARGE_INTEGER newsize)
4425 StorageInternalImpl* This = (StorageInternalImpl*) base;
4427 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4428 index, newsize);
4431 /******************************************************************************
4433 ** Storage32InternalImpl_Commit
4436 static HRESULT WINAPI StorageInternalImpl_Commit(
4437 IStorage* iface,
4438 DWORD grfCommitFlags) /* [in] */
4440 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4441 return S_OK;
4444 /******************************************************************************
4446 ** Storage32InternalImpl_Revert
4449 static HRESULT WINAPI StorageInternalImpl_Revert(
4450 IStorage* iface)
4452 FIXME("(%p): stub\n", iface);
4453 return S_OK;
4456 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4458 IStorage_Release((IStorage*)This->parentStorage);
4459 HeapFree(GetProcessHeap(), 0, This);
4462 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4463 IEnumSTATSTG* iface,
4464 REFIID riid,
4465 void** ppvObject)
4467 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4469 if (ppvObject==0)
4470 return E_INVALIDARG;
4472 *ppvObject = 0;
4474 if (IsEqualGUID(&IID_IUnknown, riid) ||
4475 IsEqualGUID(&IID_IEnumSTATSTG, riid))
4477 *ppvObject = This;
4478 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4479 return S_OK;
4482 return E_NOINTERFACE;
4485 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
4486 IEnumSTATSTG* iface)
4488 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4489 return InterlockedIncrement(&This->ref);
4492 static ULONG WINAPI IEnumSTATSTGImpl_Release(
4493 IEnumSTATSTG* iface)
4495 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4497 ULONG newRef;
4499 newRef = InterlockedDecrement(&This->ref);
4501 if (newRef==0)
4503 IEnumSTATSTGImpl_Destroy(This);
4506 return newRef;
4509 static HRESULT IEnumSTATSTGImpl_GetNextRef(
4510 IEnumSTATSTGImpl* This,
4511 DirRef *ref)
4513 DirRef result = DIRENTRY_NULL;
4514 DirRef searchNode;
4515 DirEntry entry;
4516 HRESULT hr;
4517 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
4519 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
4520 This->parentStorage->storageDirEntry, &entry);
4521 searchNode = entry.dirRootEntry;
4523 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
4525 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
4527 if (SUCCEEDED(hr))
4529 LONG diff = entryNameCmp( entry.name, This->name);
4531 if (diff <= 0)
4533 searchNode = entry.rightChild;
4535 else
4537 result = searchNode;
4538 memcpy(result_name, entry.name, sizeof(result_name));
4539 searchNode = entry.leftChild;
4544 if (SUCCEEDED(hr))
4546 *ref = result;
4547 if (result != DIRENTRY_NULL)
4548 memcpy(This->name, result_name, sizeof(result_name));
4551 return hr;
4554 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4555 IEnumSTATSTG* iface,
4556 ULONG celt,
4557 STATSTG* rgelt,
4558 ULONG* pceltFetched)
4560 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4562 DirEntry currentEntry;
4563 STATSTG* currentReturnStruct = rgelt;
4564 ULONG objectFetched = 0;
4565 DirRef currentSearchNode;
4566 HRESULT hr=S_OK;
4568 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4569 return E_INVALIDARG;
4571 if (This->parentStorage->reverted)
4572 return STG_E_REVERTED;
4575 * To avoid the special case, get another pointer to a ULONG value if
4576 * the caller didn't supply one.
4578 if (pceltFetched==0)
4579 pceltFetched = &objectFetched;
4582 * Start the iteration, we will iterate until we hit the end of the
4583 * linked list or until we hit the number of items to iterate through
4585 *pceltFetched = 0;
4587 while ( *pceltFetched < celt )
4589 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
4591 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4592 break;
4595 * Read the entry from the storage.
4597 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4598 currentSearchNode,
4599 &currentEntry);
4602 * Copy the information to the return buffer.
4604 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4605 currentReturnStruct,
4606 &currentEntry,
4607 STATFLAG_DEFAULT);
4610 * Step to the next item in the iteration
4612 (*pceltFetched)++;
4613 currentReturnStruct++;
4616 if (SUCCEEDED(hr) && *pceltFetched != celt)
4617 hr = S_FALSE;
4619 return hr;
4623 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4624 IEnumSTATSTG* iface,
4625 ULONG celt)
4627 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4629 ULONG objectFetched = 0;
4630 DirRef currentSearchNode;
4631 HRESULT hr=S_OK;
4633 if (This->parentStorage->reverted)
4634 return STG_E_REVERTED;
4636 while ( (objectFetched < celt) )
4638 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
4640 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4641 break;
4643 objectFetched++;
4646 if (SUCCEEDED(hr) && objectFetched != celt)
4647 return S_FALSE;
4649 return hr;
4652 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4653 IEnumSTATSTG* iface)
4655 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4657 if (This->parentStorage->reverted)
4658 return STG_E_REVERTED;
4660 This->name[0] = 0;
4662 return S_OK;
4665 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4666 IEnumSTATSTG* iface,
4667 IEnumSTATSTG** ppenum)
4669 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4671 IEnumSTATSTGImpl* newClone;
4673 if (This->parentStorage->reverted)
4674 return STG_E_REVERTED;
4677 * Perform a sanity check on the parameters.
4679 if (ppenum==0)
4680 return E_INVALIDARG;
4682 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4683 This->storageDirEntry);
4687 * The new clone enumeration must point to the same current node as
4688 * the ole one.
4690 memcpy(newClone->name, This->name, sizeof(newClone->name));
4692 *ppenum = (IEnumSTATSTG*)newClone;
4695 * Don't forget to nail down a reference to the clone before
4696 * returning it.
4698 IEnumSTATSTGImpl_AddRef(*ppenum);
4700 return S_OK;
4704 * Virtual function table for the IEnumSTATSTGImpl class.
4706 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4708 IEnumSTATSTGImpl_QueryInterface,
4709 IEnumSTATSTGImpl_AddRef,
4710 IEnumSTATSTGImpl_Release,
4711 IEnumSTATSTGImpl_Next,
4712 IEnumSTATSTGImpl_Skip,
4713 IEnumSTATSTGImpl_Reset,
4714 IEnumSTATSTGImpl_Clone
4717 /******************************************************************************
4718 ** IEnumSTATSTGImpl implementation
4721 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4722 StorageBaseImpl* parentStorage,
4723 DirRef storageDirEntry)
4725 IEnumSTATSTGImpl* newEnumeration;
4727 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4729 if (newEnumeration!=0)
4732 * Set-up the virtual function table and reference count.
4734 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4735 newEnumeration->ref = 0;
4738 * We want to nail-down the reference to the storage in case the
4739 * enumeration out-lives the storage in the client application.
4741 newEnumeration->parentStorage = parentStorage;
4742 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4744 newEnumeration->storageDirEntry = storageDirEntry;
4747 * Make sure the current node of the iterator is the first one.
4749 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4752 return newEnumeration;
4756 * Virtual function table for the Storage32InternalImpl class.
4758 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4760 StorageBaseImpl_QueryInterface,
4761 StorageBaseImpl_AddRef,
4762 StorageBaseImpl_Release,
4763 StorageBaseImpl_CreateStream,
4764 StorageBaseImpl_OpenStream,
4765 StorageBaseImpl_CreateStorage,
4766 StorageBaseImpl_OpenStorage,
4767 StorageBaseImpl_CopyTo,
4768 StorageBaseImpl_MoveElementTo,
4769 StorageInternalImpl_Commit,
4770 StorageInternalImpl_Revert,
4771 StorageBaseImpl_EnumElements,
4772 StorageBaseImpl_DestroyElement,
4773 StorageBaseImpl_RenameElement,
4774 StorageBaseImpl_SetElementTimes,
4775 StorageBaseImpl_SetClass,
4776 StorageBaseImpl_SetStateBits,
4777 StorageBaseImpl_Stat
4780 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4782 StorageInternalImpl_Destroy,
4783 StorageInternalImpl_Invalidate,
4784 StorageInternalImpl_CreateDirEntry,
4785 StorageInternalImpl_WriteDirEntry,
4786 StorageInternalImpl_ReadDirEntry,
4787 StorageInternalImpl_DestroyDirEntry,
4788 StorageInternalImpl_StreamReadAt,
4789 StorageInternalImpl_StreamWriteAt,
4790 StorageInternalImpl_StreamSetSize
4793 /******************************************************************************
4794 ** Storage32InternalImpl implementation
4797 static StorageInternalImpl* StorageInternalImpl_Construct(
4798 StorageBaseImpl* parentStorage,
4799 DWORD openFlags,
4800 DirRef storageDirEntry)
4802 StorageInternalImpl* newStorage;
4804 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4806 if (newStorage!=0)
4808 list_init(&newStorage->base.strmHead);
4810 list_init(&newStorage->base.storageHead);
4813 * Initialize the virtual function table.
4815 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4816 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4817 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4819 newStorage->base.reverted = 0;
4821 newStorage->base.ref = 1;
4823 newStorage->parentStorage = parentStorage;
4826 * Keep a reference to the directory entry of this storage
4828 newStorage->base.storageDirEntry = storageDirEntry;
4830 newStorage->base.create = 0;
4832 return newStorage;
4835 return 0;
4838 /******************************************************************************
4839 ** StorageUtl implementation
4842 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4844 WORD tmp;
4846 memcpy(&tmp, buffer+offset, sizeof(WORD));
4847 *value = lendian16toh(tmp);
4850 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4852 value = htole16(value);
4853 memcpy(buffer+offset, &value, sizeof(WORD));
4856 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4858 DWORD tmp;
4860 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4861 *value = lendian32toh(tmp);
4864 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4866 value = htole32(value);
4867 memcpy(buffer+offset, &value, sizeof(DWORD));
4870 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4871 ULARGE_INTEGER* value)
4873 #ifdef WORDS_BIGENDIAN
4874 ULARGE_INTEGER tmp;
4876 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4877 value->u.LowPart = htole32(tmp.u.HighPart);
4878 value->u.HighPart = htole32(tmp.u.LowPart);
4879 #else
4880 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4881 #endif
4884 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4885 const ULARGE_INTEGER *value)
4887 #ifdef WORDS_BIGENDIAN
4888 ULARGE_INTEGER tmp;
4890 tmp.u.LowPart = htole32(value->u.HighPart);
4891 tmp.u.HighPart = htole32(value->u.LowPart);
4892 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4893 #else
4894 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4895 #endif
4898 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4900 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4901 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4902 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4904 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4907 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4909 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4910 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4911 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4913 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4916 void StorageUtl_CopyDirEntryToSTATSTG(
4917 StorageBaseImpl* storage,
4918 STATSTG* destination,
4919 const DirEntry* source,
4920 int statFlags)
4922 LPCWSTR entryName;
4924 if (source->stgType == STGTY_ROOT)
4926 /* replace the name of root entry (often "Root Entry") by the file name */
4927 entryName = storage->filename;
4929 else
4931 entryName = source->name;
4935 * The copy of the string occurs only when the flag is not set
4937 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4938 (entryName == NULL) ||
4939 (entryName[0] == 0) )
4941 destination->pwcsName = 0;
4943 else
4945 destination->pwcsName =
4946 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4948 strcpyW(destination->pwcsName, entryName);
4951 switch (source->stgType)
4953 case STGTY_STORAGE:
4954 case STGTY_ROOT:
4955 destination->type = STGTY_STORAGE;
4956 break;
4957 case STGTY_STREAM:
4958 destination->type = STGTY_STREAM;
4959 break;
4960 default:
4961 destination->type = STGTY_STREAM;
4962 break;
4965 destination->cbSize = source->size;
4967 currentReturnStruct->mtime = {0}; TODO
4968 currentReturnStruct->ctime = {0};
4969 currentReturnStruct->atime = {0};
4971 destination->grfMode = 0;
4972 destination->grfLocksSupported = 0;
4973 destination->clsid = source->clsid;
4974 destination->grfStateBits = 0;
4975 destination->reserved = 0;
4978 /******************************************************************************
4979 ** BlockChainStream implementation
4982 BlockChainStream* BlockChainStream_Construct(
4983 StorageImpl* parentStorage,
4984 ULONG* headOfStreamPlaceHolder,
4985 DirRef dirEntry)
4987 BlockChainStream* newStream;
4988 ULONG blockIndex;
4990 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4992 newStream->parentStorage = parentStorage;
4993 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4994 newStream->ownerDirEntry = dirEntry;
4995 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4996 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4997 newStream->numBlocks = 0;
4999 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
5001 while (blockIndex != BLOCK_END_OF_CHAIN)
5003 newStream->numBlocks++;
5004 newStream->tailIndex = blockIndex;
5006 if(FAILED(StorageImpl_GetNextBlockInChain(
5007 parentStorage,
5008 blockIndex,
5009 &blockIndex)))
5011 HeapFree(GetProcessHeap(), 0, newStream);
5012 return NULL;
5016 return newStream;
5019 void BlockChainStream_Destroy(BlockChainStream* This)
5021 HeapFree(GetProcessHeap(), 0, This);
5024 /******************************************************************************
5025 * BlockChainStream_GetHeadOfChain
5027 * Returns the head of this stream chain.
5028 * Some special chains don't have directory entries, their heads are kept in
5029 * This->headOfStreamPlaceHolder.
5032 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5034 DirEntry chainEntry;
5035 HRESULT hr;
5037 if (This->headOfStreamPlaceHolder != 0)
5038 return *(This->headOfStreamPlaceHolder);
5040 if (This->ownerDirEntry != DIRENTRY_NULL)
5042 hr = StorageImpl_ReadDirEntry(
5043 This->parentStorage,
5044 This->ownerDirEntry,
5045 &chainEntry);
5047 if (SUCCEEDED(hr))
5049 return chainEntry.startingBlock;
5053 return BLOCK_END_OF_CHAIN;
5056 /******************************************************************************
5057 * BlockChainStream_GetCount
5059 * Returns the number of blocks that comprises this chain.
5060 * This is not the size of the stream as the last block may not be full!
5063 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5065 ULONG blockIndex;
5066 ULONG count = 0;
5068 blockIndex = BlockChainStream_GetHeadOfChain(This);
5070 while (blockIndex != BLOCK_END_OF_CHAIN)
5072 count++;
5074 if(FAILED(StorageImpl_GetNextBlockInChain(
5075 This->parentStorage,
5076 blockIndex,
5077 &blockIndex)))
5078 return 0;
5081 return count;
5084 /******************************************************************************
5085 * BlockChainStream_ReadAt
5087 * Reads a specified number of bytes from this chain at the specified offset.
5088 * bytesRead may be NULL.
5089 * Failure will be returned if the specified number of bytes has not been read.
5091 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5092 ULARGE_INTEGER offset,
5093 ULONG size,
5094 void* buffer,
5095 ULONG* bytesRead)
5097 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5098 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5099 ULONG bytesToReadInBuffer;
5100 ULONG blockIndex;
5101 BYTE* bufferWalker;
5103 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5106 * Find the first block in the stream that contains part of the buffer.
5108 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5109 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5110 (blockNoInSequence < This->lastBlockNoInSequence) )
5112 blockIndex = BlockChainStream_GetHeadOfChain(This);
5113 This->lastBlockNoInSequence = blockNoInSequence;
5115 else
5117 ULONG temp = blockNoInSequence;
5119 blockIndex = This->lastBlockNoInSequenceIndex;
5120 blockNoInSequence -= This->lastBlockNoInSequence;
5121 This->lastBlockNoInSequence = temp;
5124 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5126 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5127 return STG_E_DOCFILECORRUPT;
5128 blockNoInSequence--;
5131 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5132 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5134 This->lastBlockNoInSequenceIndex = blockIndex;
5137 * Start reading the buffer.
5139 *bytesRead = 0;
5140 bufferWalker = buffer;
5142 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5144 ULARGE_INTEGER ulOffset;
5145 DWORD bytesReadAt;
5147 * Calculate how many bytes we can copy from this big block.
5149 bytesToReadInBuffer =
5150 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5152 TRACE("block %i\n",blockIndex);
5153 ulOffset.u.HighPart = 0;
5154 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5155 offsetInBlock;
5157 StorageImpl_ReadAt(This->parentStorage,
5158 ulOffset,
5159 bufferWalker,
5160 bytesToReadInBuffer,
5161 &bytesReadAt);
5163 * Step to the next big block.
5165 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5166 return STG_E_DOCFILECORRUPT;
5168 bufferWalker += bytesReadAt;
5169 size -= bytesReadAt;
5170 *bytesRead += bytesReadAt;
5171 offsetInBlock = 0; /* There is no offset on the next block */
5173 if (bytesToReadInBuffer != bytesReadAt)
5174 break;
5177 return (size == 0) ? S_OK : STG_E_READFAULT;
5180 /******************************************************************************
5181 * BlockChainStream_WriteAt
5183 * Writes the specified number of bytes to this chain at the specified offset.
5184 * Will fail if not all specified number of bytes have been written.
5186 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5187 ULARGE_INTEGER offset,
5188 ULONG size,
5189 const void* buffer,
5190 ULONG* bytesWritten)
5192 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5193 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5194 ULONG bytesToWrite;
5195 ULONG blockIndex;
5196 const BYTE* bufferWalker;
5199 * Find the first block in the stream that contains part of the buffer.
5201 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5202 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5203 (blockNoInSequence < This->lastBlockNoInSequence) )
5205 blockIndex = BlockChainStream_GetHeadOfChain(This);
5206 This->lastBlockNoInSequence = blockNoInSequence;
5208 else
5210 ULONG temp = blockNoInSequence;
5212 blockIndex = This->lastBlockNoInSequenceIndex;
5213 blockNoInSequence -= This->lastBlockNoInSequence;
5214 This->lastBlockNoInSequence = temp;
5217 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5219 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5220 &blockIndex)))
5221 return STG_E_DOCFILECORRUPT;
5222 blockNoInSequence--;
5225 This->lastBlockNoInSequenceIndex = blockIndex;
5227 /* BlockChainStream_SetSize should have already been called to ensure we have
5228 * enough blocks in the chain to write into */
5229 if (blockIndex == BLOCK_END_OF_CHAIN)
5231 ERR("not enough blocks in chain to write data\n");
5232 return STG_E_DOCFILECORRUPT;
5235 *bytesWritten = 0;
5236 bufferWalker = buffer;
5238 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5240 ULARGE_INTEGER ulOffset;
5241 DWORD bytesWrittenAt;
5243 * Calculate how many bytes we can copy from this big block.
5245 bytesToWrite =
5246 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5248 TRACE("block %i\n",blockIndex);
5249 ulOffset.u.HighPart = 0;
5250 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5251 offsetInBlock;
5253 StorageImpl_WriteAt(This->parentStorage,
5254 ulOffset,
5255 bufferWalker,
5256 bytesToWrite,
5257 &bytesWrittenAt);
5260 * Step to the next big block.
5262 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5263 &blockIndex)))
5264 return STG_E_DOCFILECORRUPT;
5266 bufferWalker += bytesWrittenAt;
5267 size -= bytesWrittenAt;
5268 *bytesWritten += bytesWrittenAt;
5269 offsetInBlock = 0; /* There is no offset on the next block */
5271 if (bytesWrittenAt != bytesToWrite)
5272 break;
5275 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5278 /******************************************************************************
5279 * BlockChainStream_Shrink
5281 * Shrinks this chain in the big block depot.
5283 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5284 ULARGE_INTEGER newSize)
5286 ULONG blockIndex, extraBlock;
5287 ULONG numBlocks;
5288 ULONG count = 1;
5291 * Reset the last accessed block cache.
5293 This->lastBlockNoInSequence = 0xFFFFFFFF;
5294 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5297 * Figure out how many blocks are needed to contain the new size
5299 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5301 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5302 numBlocks++;
5304 blockIndex = BlockChainStream_GetHeadOfChain(This);
5307 * Go to the new end of chain
5309 while (count < numBlocks)
5311 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5312 &blockIndex)))
5313 return FALSE;
5314 count++;
5317 /* Get the next block before marking the new end */
5318 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5319 &extraBlock)))
5320 return FALSE;
5322 /* Mark the new end of chain */
5323 StorageImpl_SetNextBlockInChain(
5324 This->parentStorage,
5325 blockIndex,
5326 BLOCK_END_OF_CHAIN);
5328 This->tailIndex = blockIndex;
5329 This->numBlocks = numBlocks;
5332 * Mark the extra blocks as free
5334 while (extraBlock != BLOCK_END_OF_CHAIN)
5336 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5337 &blockIndex)))
5338 return FALSE;
5339 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5340 extraBlock = blockIndex;
5343 return TRUE;
5346 /******************************************************************************
5347 * BlockChainStream_Enlarge
5349 * Grows this chain in the big block depot.
5351 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5352 ULARGE_INTEGER newSize)
5354 ULONG blockIndex, currentBlock;
5355 ULONG newNumBlocks;
5356 ULONG oldNumBlocks = 0;
5358 blockIndex = BlockChainStream_GetHeadOfChain(This);
5361 * Empty chain. Create the head.
5363 if (blockIndex == BLOCK_END_OF_CHAIN)
5365 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5366 StorageImpl_SetNextBlockInChain(This->parentStorage,
5367 blockIndex,
5368 BLOCK_END_OF_CHAIN);
5370 if (This->headOfStreamPlaceHolder != 0)
5372 *(This->headOfStreamPlaceHolder) = blockIndex;
5374 else
5376 DirEntry chainEntry;
5377 assert(This->ownerDirEntry != DIRENTRY_NULL);
5379 StorageImpl_ReadDirEntry(
5380 This->parentStorage,
5381 This->ownerDirEntry,
5382 &chainEntry);
5384 chainEntry.startingBlock = blockIndex;
5386 StorageImpl_WriteDirEntry(
5387 This->parentStorage,
5388 This->ownerDirEntry,
5389 &chainEntry);
5392 This->tailIndex = blockIndex;
5393 This->numBlocks = 1;
5397 * Figure out how many blocks are needed to contain this stream
5399 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5401 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5402 newNumBlocks++;
5405 * Go to the current end of chain
5407 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5409 currentBlock = blockIndex;
5411 while (blockIndex != BLOCK_END_OF_CHAIN)
5413 This->numBlocks++;
5414 currentBlock = blockIndex;
5416 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5417 &blockIndex)))
5418 return FALSE;
5421 This->tailIndex = currentBlock;
5424 currentBlock = This->tailIndex;
5425 oldNumBlocks = This->numBlocks;
5428 * Add new blocks to the chain
5430 if (oldNumBlocks < newNumBlocks)
5432 while (oldNumBlocks < newNumBlocks)
5434 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5436 StorageImpl_SetNextBlockInChain(
5437 This->parentStorage,
5438 currentBlock,
5439 blockIndex);
5441 StorageImpl_SetNextBlockInChain(
5442 This->parentStorage,
5443 blockIndex,
5444 BLOCK_END_OF_CHAIN);
5446 currentBlock = blockIndex;
5447 oldNumBlocks++;
5450 This->tailIndex = blockIndex;
5451 This->numBlocks = newNumBlocks;
5454 return TRUE;
5457 /******************************************************************************
5458 * BlockChainStream_SetSize
5460 * Sets the size of this stream. The big block depot will be updated.
5461 * The file will grow if we grow the chain.
5463 * TODO: Free the actual blocks in the file when we shrink the chain.
5464 * Currently, the blocks are still in the file. So the file size
5465 * doesn't shrink even if we shrink streams.
5467 BOOL BlockChainStream_SetSize(
5468 BlockChainStream* This,
5469 ULARGE_INTEGER newSize)
5471 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5473 if (newSize.u.LowPart == size.u.LowPart)
5474 return TRUE;
5476 if (newSize.u.LowPart < size.u.LowPart)
5478 BlockChainStream_Shrink(This, newSize);
5480 else
5482 BlockChainStream_Enlarge(This, newSize);
5485 return TRUE;
5488 /******************************************************************************
5489 * BlockChainStream_GetSize
5491 * Returns the size of this chain.
5492 * Will return the block count if this chain doesn't have a directory entry.
5494 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5496 DirEntry chainEntry;
5498 if(This->headOfStreamPlaceHolder == NULL)
5501 * This chain has a directory entry so use the size value from there.
5503 StorageImpl_ReadDirEntry(
5504 This->parentStorage,
5505 This->ownerDirEntry,
5506 &chainEntry);
5508 return chainEntry.size;
5510 else
5513 * this chain is a chain that does not have a directory entry, figure out the
5514 * size by making the product number of used blocks times the
5515 * size of them
5517 ULARGE_INTEGER result;
5518 result.u.HighPart = 0;
5520 result.u.LowPart =
5521 BlockChainStream_GetCount(This) *
5522 This->parentStorage->bigBlockSize;
5524 return result;
5528 /******************************************************************************
5529 ** SmallBlockChainStream implementation
5532 SmallBlockChainStream* SmallBlockChainStream_Construct(
5533 StorageImpl* parentStorage,
5534 ULONG* headOfStreamPlaceHolder,
5535 DirRef dirEntry)
5537 SmallBlockChainStream* newStream;
5539 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5541 newStream->parentStorage = parentStorage;
5542 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5543 newStream->ownerDirEntry = dirEntry;
5545 return newStream;
5548 void SmallBlockChainStream_Destroy(
5549 SmallBlockChainStream* This)
5551 HeapFree(GetProcessHeap(), 0, This);
5554 /******************************************************************************
5555 * SmallBlockChainStream_GetHeadOfChain
5557 * Returns the head of this chain of small blocks.
5559 static ULONG SmallBlockChainStream_GetHeadOfChain(
5560 SmallBlockChainStream* This)
5562 DirEntry chainEntry;
5563 HRESULT hr;
5565 if (This->headOfStreamPlaceHolder != NULL)
5566 return *(This->headOfStreamPlaceHolder);
5568 if (This->ownerDirEntry)
5570 hr = StorageImpl_ReadDirEntry(
5571 This->parentStorage,
5572 This->ownerDirEntry,
5573 &chainEntry);
5575 if (SUCCEEDED(hr))
5577 return chainEntry.startingBlock;
5582 return BLOCK_END_OF_CHAIN;
5585 /******************************************************************************
5586 * SmallBlockChainStream_GetNextBlockInChain
5588 * Returns the index of the next small block in this chain.
5590 * Return Values:
5591 * - BLOCK_END_OF_CHAIN: end of this chain
5592 * - BLOCK_UNUSED: small block 'blockIndex' is free
5594 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5595 SmallBlockChainStream* This,
5596 ULONG blockIndex,
5597 ULONG* nextBlockInChain)
5599 ULARGE_INTEGER offsetOfBlockInDepot;
5600 DWORD buffer;
5601 ULONG bytesRead;
5602 HRESULT res;
5604 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5606 offsetOfBlockInDepot.u.HighPart = 0;
5607 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5610 * Read those bytes in the buffer from the small block file.
5612 res = BlockChainStream_ReadAt(
5613 This->parentStorage->smallBlockDepotChain,
5614 offsetOfBlockInDepot,
5615 sizeof(DWORD),
5616 &buffer,
5617 &bytesRead);
5619 if (SUCCEEDED(res))
5621 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5622 return S_OK;
5625 return res;
5628 /******************************************************************************
5629 * SmallBlockChainStream_SetNextBlockInChain
5631 * Writes the index of the next block of the specified block in the small
5632 * block depot.
5633 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5634 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5636 static void SmallBlockChainStream_SetNextBlockInChain(
5637 SmallBlockChainStream* This,
5638 ULONG blockIndex,
5639 ULONG nextBlock)
5641 ULARGE_INTEGER offsetOfBlockInDepot;
5642 DWORD buffer;
5643 ULONG bytesWritten;
5645 offsetOfBlockInDepot.u.HighPart = 0;
5646 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5648 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5651 * Read those bytes in the buffer from the small block file.
5653 BlockChainStream_WriteAt(
5654 This->parentStorage->smallBlockDepotChain,
5655 offsetOfBlockInDepot,
5656 sizeof(DWORD),
5657 &buffer,
5658 &bytesWritten);
5661 /******************************************************************************
5662 * SmallBlockChainStream_FreeBlock
5664 * Flag small block 'blockIndex' as free in the small block depot.
5666 static void SmallBlockChainStream_FreeBlock(
5667 SmallBlockChainStream* This,
5668 ULONG blockIndex)
5670 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5673 /******************************************************************************
5674 * SmallBlockChainStream_GetNextFreeBlock
5676 * Returns the index of a free small block. The small block depot will be
5677 * enlarged if necessary. The small block chain will also be enlarged if
5678 * necessary.
5680 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5681 SmallBlockChainStream* This)
5683 ULARGE_INTEGER offsetOfBlockInDepot;
5684 DWORD buffer;
5685 ULONG bytesRead;
5686 ULONG blockIndex = 0;
5687 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5688 HRESULT res = S_OK;
5689 ULONG smallBlocksPerBigBlock;
5691 offsetOfBlockInDepot.u.HighPart = 0;
5694 * Scan the small block depot for a free block
5696 while (nextBlockIndex != BLOCK_UNUSED)
5698 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5700 res = BlockChainStream_ReadAt(
5701 This->parentStorage->smallBlockDepotChain,
5702 offsetOfBlockInDepot,
5703 sizeof(DWORD),
5704 &buffer,
5705 &bytesRead);
5708 * If we run out of space for the small block depot, enlarge it
5710 if (SUCCEEDED(res))
5712 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5714 if (nextBlockIndex != BLOCK_UNUSED)
5715 blockIndex++;
5717 else
5719 ULONG count =
5720 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5722 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5723 ULONG nextBlock, newsbdIndex;
5724 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5726 nextBlock = sbdIndex;
5727 while (nextBlock != BLOCK_END_OF_CHAIN)
5729 sbdIndex = nextBlock;
5730 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5733 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5734 if (sbdIndex != BLOCK_END_OF_CHAIN)
5735 StorageImpl_SetNextBlockInChain(
5736 This->parentStorage,
5737 sbdIndex,
5738 newsbdIndex);
5740 StorageImpl_SetNextBlockInChain(
5741 This->parentStorage,
5742 newsbdIndex,
5743 BLOCK_END_OF_CHAIN);
5746 * Initialize all the small blocks to free
5748 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5749 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5751 if (count == 0)
5754 * We have just created the small block depot.
5756 DirEntry rootEntry;
5757 ULONG sbStartIndex;
5760 * Save it in the header
5762 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5763 StorageImpl_SaveFileHeader(This->parentStorage);
5766 * And allocate the first big block that will contain small blocks
5768 sbStartIndex =
5769 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5771 StorageImpl_SetNextBlockInChain(
5772 This->parentStorage,
5773 sbStartIndex,
5774 BLOCK_END_OF_CHAIN);
5776 StorageImpl_ReadDirEntry(
5777 This->parentStorage,
5778 This->parentStorage->base.storageDirEntry,
5779 &rootEntry);
5781 rootEntry.startingBlock = sbStartIndex;
5782 rootEntry.size.u.HighPart = 0;
5783 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5785 StorageImpl_WriteDirEntry(
5786 This->parentStorage,
5787 This->parentStorage->base.storageDirEntry,
5788 &rootEntry);
5790 else
5791 StorageImpl_SaveFileHeader(This->parentStorage);
5795 smallBlocksPerBigBlock =
5796 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5799 * Verify if we have to allocate big blocks to contain small blocks
5801 if (blockIndex % smallBlocksPerBigBlock == 0)
5803 DirEntry rootEntry;
5804 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5806 StorageImpl_ReadDirEntry(
5807 This->parentStorage,
5808 This->parentStorage->base.storageDirEntry,
5809 &rootEntry);
5811 if (rootEntry.size.u.LowPart <
5812 (blocksRequired * This->parentStorage->bigBlockSize))
5814 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5816 BlockChainStream_SetSize(
5817 This->parentStorage->smallBlockRootChain,
5818 rootEntry.size);
5820 StorageImpl_WriteDirEntry(
5821 This->parentStorage,
5822 This->parentStorage->base.storageDirEntry,
5823 &rootEntry);
5827 return blockIndex;
5830 /******************************************************************************
5831 * SmallBlockChainStream_ReadAt
5833 * Reads a specified number of bytes from this chain at the specified offset.
5834 * bytesRead may be NULL.
5835 * Failure will be returned if the specified number of bytes has not been read.
5837 HRESULT SmallBlockChainStream_ReadAt(
5838 SmallBlockChainStream* This,
5839 ULARGE_INTEGER offset,
5840 ULONG size,
5841 void* buffer,
5842 ULONG* bytesRead)
5844 HRESULT rc = S_OK;
5845 ULARGE_INTEGER offsetInBigBlockFile;
5846 ULONG blockNoInSequence =
5847 offset.u.LowPart / This->parentStorage->smallBlockSize;
5849 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5850 ULONG bytesToReadInBuffer;
5851 ULONG blockIndex;
5852 ULONG bytesReadFromBigBlockFile;
5853 BYTE* bufferWalker;
5856 * This should never happen on a small block file.
5858 assert(offset.u.HighPart==0);
5861 * Find the first block in the stream that contains part of the buffer.
5863 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5865 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5867 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5868 if(FAILED(rc))
5869 return rc;
5870 blockNoInSequence--;
5874 * Start reading the buffer.
5876 *bytesRead = 0;
5877 bufferWalker = buffer;
5879 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5882 * Calculate how many bytes we can copy from this small block.
5884 bytesToReadInBuffer =
5885 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5888 * Calculate the offset of the small block in the small block file.
5890 offsetInBigBlockFile.u.HighPart = 0;
5891 offsetInBigBlockFile.u.LowPart =
5892 blockIndex * This->parentStorage->smallBlockSize;
5894 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5897 * Read those bytes in the buffer from the small block file.
5898 * The small block has already been identified so it shouldn't fail
5899 * unless the file is corrupt.
5901 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5902 offsetInBigBlockFile,
5903 bytesToReadInBuffer,
5904 bufferWalker,
5905 &bytesReadFromBigBlockFile);
5907 if (FAILED(rc))
5908 return rc;
5911 * Step to the next big block.
5913 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5914 if(FAILED(rc))
5915 return STG_E_DOCFILECORRUPT;
5917 bufferWalker += bytesReadFromBigBlockFile;
5918 size -= bytesReadFromBigBlockFile;
5919 *bytesRead += bytesReadFromBigBlockFile;
5920 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5923 return (size == 0) ? S_OK : STG_E_READFAULT;
5926 /******************************************************************************
5927 * SmallBlockChainStream_WriteAt
5929 * Writes the specified number of bytes to this chain at the specified offset.
5930 * Will fail if not all specified number of bytes have been written.
5932 HRESULT SmallBlockChainStream_WriteAt(
5933 SmallBlockChainStream* This,
5934 ULARGE_INTEGER offset,
5935 ULONG size,
5936 const void* buffer,
5937 ULONG* bytesWritten)
5939 ULARGE_INTEGER offsetInBigBlockFile;
5940 ULONG blockNoInSequence =
5941 offset.u.LowPart / This->parentStorage->smallBlockSize;
5943 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5944 ULONG bytesToWriteInBuffer;
5945 ULONG blockIndex;
5946 ULONG bytesWrittenToBigBlockFile;
5947 const BYTE* bufferWalker;
5948 HRESULT res;
5951 * This should never happen on a small block file.
5953 assert(offset.u.HighPart==0);
5956 * Find the first block in the stream that contains part of the buffer.
5958 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5960 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5962 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5963 return STG_E_DOCFILECORRUPT;
5964 blockNoInSequence--;
5968 * Start writing the buffer.
5970 *bytesWritten = 0;
5971 bufferWalker = buffer;
5972 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5975 * Calculate how many bytes we can copy to this small block.
5977 bytesToWriteInBuffer =
5978 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5981 * Calculate the offset of the small block in the small block file.
5983 offsetInBigBlockFile.u.HighPart = 0;
5984 offsetInBigBlockFile.u.LowPart =
5985 blockIndex * This->parentStorage->smallBlockSize;
5987 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5990 * Write those bytes in the buffer to the small block file.
5992 res = BlockChainStream_WriteAt(
5993 This->parentStorage->smallBlockRootChain,
5994 offsetInBigBlockFile,
5995 bytesToWriteInBuffer,
5996 bufferWalker,
5997 &bytesWrittenToBigBlockFile);
5998 if (FAILED(res))
5999 return res;
6002 * Step to the next big block.
6004 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6005 &blockIndex)))
6006 return FALSE;
6007 bufferWalker += bytesWrittenToBigBlockFile;
6008 size -= bytesWrittenToBigBlockFile;
6009 *bytesWritten += bytesWrittenToBigBlockFile;
6010 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6013 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6016 /******************************************************************************
6017 * SmallBlockChainStream_Shrink
6019 * Shrinks this chain in the small block depot.
6021 static BOOL SmallBlockChainStream_Shrink(
6022 SmallBlockChainStream* This,
6023 ULARGE_INTEGER newSize)
6025 ULONG blockIndex, extraBlock;
6026 ULONG numBlocks;
6027 ULONG count = 0;
6029 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6031 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6032 numBlocks++;
6034 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6037 * Go to the new end of chain
6039 while (count < numBlocks)
6041 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6042 &blockIndex)))
6043 return FALSE;
6044 count++;
6048 * If the count is 0, we have a special case, the head of the chain was
6049 * just freed.
6051 if (count == 0)
6053 DirEntry chainEntry;
6055 StorageImpl_ReadDirEntry(This->parentStorage,
6056 This->ownerDirEntry,
6057 &chainEntry);
6059 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6061 StorageImpl_WriteDirEntry(This->parentStorage,
6062 This->ownerDirEntry,
6063 &chainEntry);
6066 * We start freeing the chain at the head block.
6068 extraBlock = blockIndex;
6070 else
6072 /* Get the next block before marking the new end */
6073 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6074 &extraBlock)))
6075 return FALSE;
6077 /* Mark the new end of chain */
6078 SmallBlockChainStream_SetNextBlockInChain(
6079 This,
6080 blockIndex,
6081 BLOCK_END_OF_CHAIN);
6085 * Mark the extra blocks as free
6087 while (extraBlock != BLOCK_END_OF_CHAIN)
6089 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6090 &blockIndex)))
6091 return FALSE;
6092 SmallBlockChainStream_FreeBlock(This, extraBlock);
6093 extraBlock = blockIndex;
6096 return TRUE;
6099 /******************************************************************************
6100 * SmallBlockChainStream_Enlarge
6102 * Grows this chain in the small block depot.
6104 static BOOL SmallBlockChainStream_Enlarge(
6105 SmallBlockChainStream* This,
6106 ULARGE_INTEGER newSize)
6108 ULONG blockIndex, currentBlock;
6109 ULONG newNumBlocks;
6110 ULONG oldNumBlocks = 0;
6112 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6115 * Empty chain. Create the head.
6117 if (blockIndex == BLOCK_END_OF_CHAIN)
6119 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6120 SmallBlockChainStream_SetNextBlockInChain(
6121 This,
6122 blockIndex,
6123 BLOCK_END_OF_CHAIN);
6125 if (This->headOfStreamPlaceHolder != NULL)
6127 *(This->headOfStreamPlaceHolder) = blockIndex;
6129 else
6131 DirEntry chainEntry;
6133 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6134 &chainEntry);
6136 chainEntry.startingBlock = blockIndex;
6138 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6139 &chainEntry);
6143 currentBlock = blockIndex;
6146 * Figure out how many blocks are needed to contain this stream
6148 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6150 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6151 newNumBlocks++;
6154 * Go to the current end of chain
6156 while (blockIndex != BLOCK_END_OF_CHAIN)
6158 oldNumBlocks++;
6159 currentBlock = blockIndex;
6160 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6161 return FALSE;
6165 * Add new blocks to the chain
6167 while (oldNumBlocks < newNumBlocks)
6169 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6170 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6172 SmallBlockChainStream_SetNextBlockInChain(
6173 This,
6174 blockIndex,
6175 BLOCK_END_OF_CHAIN);
6177 currentBlock = blockIndex;
6178 oldNumBlocks++;
6181 return TRUE;
6184 /******************************************************************************
6185 * SmallBlockChainStream_SetSize
6187 * Sets the size of this stream.
6188 * The file will grow if we grow the chain.
6190 * TODO: Free the actual blocks in the file when we shrink the chain.
6191 * Currently, the blocks are still in the file. So the file size
6192 * doesn't shrink even if we shrink streams.
6194 BOOL SmallBlockChainStream_SetSize(
6195 SmallBlockChainStream* This,
6196 ULARGE_INTEGER newSize)
6198 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6200 if (newSize.u.LowPart == size.u.LowPart)
6201 return TRUE;
6203 if (newSize.u.LowPart < size.u.LowPart)
6205 SmallBlockChainStream_Shrink(This, newSize);
6207 else
6209 SmallBlockChainStream_Enlarge(This, newSize);
6212 return TRUE;
6215 /******************************************************************************
6216 * SmallBlockChainStream_GetCount
6218 * Returns the number of small blocks that comprises this chain.
6219 * This is not the size of the stream as the last block may not be full!
6222 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6224 ULONG blockIndex;
6225 ULONG count = 0;
6227 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6229 while(blockIndex != BLOCK_END_OF_CHAIN)
6231 count++;
6233 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6234 blockIndex, &blockIndex)))
6235 return 0;
6238 return count;
6241 /******************************************************************************
6242 * SmallBlockChainStream_GetSize
6244 * Returns the size of this chain.
6246 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6248 DirEntry chainEntry;
6250 if(This->headOfStreamPlaceHolder != NULL)
6252 ULARGE_INTEGER result;
6253 result.u.HighPart = 0;
6255 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6256 This->parentStorage->smallBlockSize;
6258 return result;
6261 StorageImpl_ReadDirEntry(
6262 This->parentStorage,
6263 This->ownerDirEntry,
6264 &chainEntry);
6266 return chainEntry.size;
6269 /******************************************************************************
6270 * StgCreateDocfile [OLE32.@]
6271 * Creates a new compound file storage object
6273 * PARAMS
6274 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6275 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6276 * reserved [ ?] unused?, usually 0
6277 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6279 * RETURNS
6280 * S_OK if the file was successfully created
6281 * some STG_E_ value if error
6282 * NOTES
6283 * if pwcsName is NULL, create file with new unique name
6284 * the function can returns
6285 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6286 * (unrealized now)
6288 HRESULT WINAPI StgCreateDocfile(
6289 LPCOLESTR pwcsName,
6290 DWORD grfMode,
6291 DWORD reserved,
6292 IStorage **ppstgOpen)
6294 StorageBaseImpl* newStorage = 0;
6295 HANDLE hFile = INVALID_HANDLE_VALUE;
6296 HRESULT hr = STG_E_INVALIDFLAG;
6297 DWORD shareMode;
6298 DWORD accessMode;
6299 DWORD creationMode;
6300 DWORD fileAttributes;
6301 WCHAR tempFileName[MAX_PATH];
6303 TRACE("(%s, %x, %d, %p)\n",
6304 debugstr_w(pwcsName), grfMode,
6305 reserved, ppstgOpen);
6307 if (ppstgOpen == 0)
6308 return STG_E_INVALIDPOINTER;
6309 if (reserved != 0)
6310 return STG_E_INVALIDPARAMETER;
6312 /* if no share mode given then DENY_NONE is the default */
6313 if (STGM_SHARE_MODE(grfMode) == 0)
6314 grfMode |= STGM_SHARE_DENY_NONE;
6316 if ( FAILED( validateSTGM(grfMode) ))
6317 goto end;
6319 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6320 switch(STGM_ACCESS_MODE(grfMode))
6322 case STGM_WRITE:
6323 case STGM_READWRITE:
6324 break;
6325 default:
6326 goto end;
6329 /* in direct mode, can only use SHARE_EXCLUSIVE */
6330 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6331 goto end;
6333 /* but in transacted mode, any share mode is valid */
6336 * Generate a unique name.
6338 if (pwcsName == 0)
6340 WCHAR tempPath[MAX_PATH];
6341 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6343 memset(tempPath, 0, sizeof(tempPath));
6344 memset(tempFileName, 0, sizeof(tempFileName));
6346 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6347 tempPath[0] = '.';
6349 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6350 pwcsName = tempFileName;
6351 else
6353 hr = STG_E_INSUFFICIENTMEMORY;
6354 goto end;
6357 creationMode = TRUNCATE_EXISTING;
6359 else
6361 creationMode = GetCreationModeFromSTGM(grfMode);
6365 * Interpret the STGM value grfMode
6367 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6368 accessMode = GetAccessModeFromSTGM(grfMode);
6370 if (grfMode & STGM_DELETEONRELEASE)
6371 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6372 else
6373 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6375 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6377 static int fixme;
6378 if (!fixme++)
6379 FIXME("Storage share mode not implemented.\n");
6382 *ppstgOpen = 0;
6384 hFile = CreateFileW(pwcsName,
6385 accessMode,
6386 shareMode,
6387 NULL,
6388 creationMode,
6389 fileAttributes,
6392 if (hFile == INVALID_HANDLE_VALUE)
6394 if(GetLastError() == ERROR_FILE_EXISTS)
6395 hr = STG_E_FILEALREADYEXISTS;
6396 else
6397 hr = E_FAIL;
6398 goto end;
6402 * Allocate and initialize the new IStorage32object.
6404 hr = Storage_Construct(
6405 hFile,
6406 pwcsName,
6407 NULL,
6408 grfMode,
6409 TRUE,
6410 TRUE,
6411 &newStorage);
6413 if (FAILED(hr))
6415 goto end;
6419 * Get an "out" pointer for the caller.
6421 *ppstgOpen = (IStorage*)newStorage;
6423 end:
6424 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6426 return hr;
6429 /******************************************************************************
6430 * StgCreateStorageEx [OLE32.@]
6432 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6434 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6435 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6437 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6439 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6440 return STG_E_INVALIDPARAMETER;
6443 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6445 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6446 return STG_E_INVALIDPARAMETER;
6449 if (stgfmt == STGFMT_FILE)
6451 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6452 return STG_E_INVALIDPARAMETER;
6455 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6457 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6458 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
6461 ERR("Invalid stgfmt argument\n");
6462 return STG_E_INVALIDPARAMETER;
6465 /******************************************************************************
6466 * StgCreatePropSetStg [OLE32.@]
6468 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6469 IPropertySetStorage **ppPropSetStg)
6471 HRESULT hr;
6473 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6474 if (reserved)
6475 hr = STG_E_INVALIDPARAMETER;
6476 else
6477 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6478 (void**)ppPropSetStg);
6479 return hr;
6482 /******************************************************************************
6483 * StgOpenStorageEx [OLE32.@]
6485 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6487 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6488 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6490 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6492 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6493 return STG_E_INVALIDPARAMETER;
6496 switch (stgfmt)
6498 case STGFMT_FILE:
6499 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6500 return STG_E_INVALIDPARAMETER;
6502 case STGFMT_STORAGE:
6503 break;
6505 case STGFMT_DOCFILE:
6506 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6508 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6509 return STG_E_INVALIDPARAMETER;
6511 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6512 break;
6514 case STGFMT_ANY:
6515 WARN("STGFMT_ANY assuming storage\n");
6516 break;
6518 default:
6519 return STG_E_INVALIDPARAMETER;
6522 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6526 /******************************************************************************
6527 * StgOpenStorage [OLE32.@]
6529 HRESULT WINAPI StgOpenStorage(
6530 const OLECHAR *pwcsName,
6531 IStorage *pstgPriority,
6532 DWORD grfMode,
6533 SNB snbExclude,
6534 DWORD reserved,
6535 IStorage **ppstgOpen)
6537 StorageBaseImpl* newStorage = 0;
6538 HRESULT hr = S_OK;
6539 HANDLE hFile = 0;
6540 DWORD shareMode;
6541 DWORD accessMode;
6543 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6544 debugstr_w(pwcsName), pstgPriority, grfMode,
6545 snbExclude, reserved, ppstgOpen);
6547 if (pwcsName == 0)
6549 hr = STG_E_INVALIDNAME;
6550 goto end;
6553 if (ppstgOpen == 0)
6555 hr = STG_E_INVALIDPOINTER;
6556 goto end;
6559 if (reserved)
6561 hr = STG_E_INVALIDPARAMETER;
6562 goto end;
6565 if (grfMode & STGM_PRIORITY)
6567 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6568 return STG_E_INVALIDFLAG;
6569 if (grfMode & STGM_DELETEONRELEASE)
6570 return STG_E_INVALIDFUNCTION;
6571 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6572 return STG_E_INVALIDFLAG;
6573 grfMode &= ~0xf0; /* remove the existing sharing mode */
6574 grfMode |= STGM_SHARE_DENY_NONE;
6576 /* STGM_PRIORITY stops other IStorage objects on the same file from
6577 * committing until the STGM_PRIORITY IStorage is closed. it also
6578 * stops non-transacted mode StgOpenStorage calls with write access from
6579 * succeeding. obviously, both of these cannot be achieved through just
6580 * file share flags */
6581 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6585 * Validate the sharing mode
6587 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6588 switch(STGM_SHARE_MODE(grfMode))
6590 case STGM_SHARE_EXCLUSIVE:
6591 case STGM_SHARE_DENY_WRITE:
6592 break;
6593 default:
6594 hr = STG_E_INVALIDFLAG;
6595 goto end;
6598 if ( FAILED( validateSTGM(grfMode) ) ||
6599 (grfMode&STGM_CREATE))
6601 hr = STG_E_INVALIDFLAG;
6602 goto end;
6605 /* shared reading requires transacted mode */
6606 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6607 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6608 !(grfMode&STGM_TRANSACTED) )
6610 hr = STG_E_INVALIDFLAG;
6611 goto end;
6615 * Interpret the STGM value grfMode
6617 shareMode = GetShareModeFromSTGM(grfMode);
6618 accessMode = GetAccessModeFromSTGM(grfMode);
6620 *ppstgOpen = 0;
6622 hFile = CreateFileW( pwcsName,
6623 accessMode,
6624 shareMode,
6625 NULL,
6626 OPEN_EXISTING,
6627 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6630 if (hFile==INVALID_HANDLE_VALUE)
6632 DWORD last_error = GetLastError();
6634 hr = E_FAIL;
6636 switch (last_error)
6638 case ERROR_FILE_NOT_FOUND:
6639 hr = STG_E_FILENOTFOUND;
6640 break;
6642 case ERROR_PATH_NOT_FOUND:
6643 hr = STG_E_PATHNOTFOUND;
6644 break;
6646 case ERROR_ACCESS_DENIED:
6647 case ERROR_WRITE_PROTECT:
6648 hr = STG_E_ACCESSDENIED;
6649 break;
6651 case ERROR_SHARING_VIOLATION:
6652 hr = STG_E_SHAREVIOLATION;
6653 break;
6655 default:
6656 hr = E_FAIL;
6659 goto end;
6663 * Refuse to open the file if it's too small to be a structured storage file
6664 * FIXME: verify the file when reading instead of here
6666 if (GetFileSize(hFile, NULL) < 0x100)
6668 CloseHandle(hFile);
6669 hr = STG_E_FILEALREADYEXISTS;
6670 goto end;
6674 * Allocate and initialize the new IStorage32object.
6676 hr = Storage_Construct(
6677 hFile,
6678 pwcsName,
6679 NULL,
6680 grfMode,
6681 TRUE,
6682 FALSE,
6683 &newStorage);
6685 if (FAILED(hr))
6688 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6690 if(hr == STG_E_INVALIDHEADER)
6691 hr = STG_E_FILEALREADYEXISTS;
6692 goto end;
6696 * Get an "out" pointer for the caller.
6698 *ppstgOpen = (IStorage*)newStorage;
6700 end:
6701 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6702 return hr;
6705 /******************************************************************************
6706 * StgCreateDocfileOnILockBytes [OLE32.@]
6708 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6709 ILockBytes *plkbyt,
6710 DWORD grfMode,
6711 DWORD reserved,
6712 IStorage** ppstgOpen)
6714 StorageBaseImpl* newStorage = 0;
6715 HRESULT hr = S_OK;
6717 if ((ppstgOpen == 0) || (plkbyt == 0))
6718 return STG_E_INVALIDPOINTER;
6721 * Allocate and initialize the new IStorage object.
6723 hr = Storage_Construct(
6726 plkbyt,
6727 grfMode,
6728 FALSE,
6729 TRUE,
6730 &newStorage);
6732 if (FAILED(hr))
6734 return hr;
6738 * Get an "out" pointer for the caller.
6740 *ppstgOpen = (IStorage*)newStorage;
6742 return hr;
6745 /******************************************************************************
6746 * StgOpenStorageOnILockBytes [OLE32.@]
6748 HRESULT WINAPI StgOpenStorageOnILockBytes(
6749 ILockBytes *plkbyt,
6750 IStorage *pstgPriority,
6751 DWORD grfMode,
6752 SNB snbExclude,
6753 DWORD reserved,
6754 IStorage **ppstgOpen)
6756 StorageBaseImpl* newStorage = 0;
6757 HRESULT hr = S_OK;
6759 if ((plkbyt == 0) || (ppstgOpen == 0))
6760 return STG_E_INVALIDPOINTER;
6762 if ( FAILED( validateSTGM(grfMode) ))
6763 return STG_E_INVALIDFLAG;
6765 *ppstgOpen = 0;
6768 * Allocate and initialize the new IStorage object.
6770 hr = Storage_Construct(
6773 plkbyt,
6774 grfMode,
6775 FALSE,
6776 FALSE,
6777 &newStorage);
6779 if (FAILED(hr))
6781 return hr;
6785 * Get an "out" pointer for the caller.
6787 *ppstgOpen = (IStorage*)newStorage;
6789 return hr;
6792 /******************************************************************************
6793 * StgSetTimes [ole32.@]
6794 * StgSetTimes [OLE32.@]
6798 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6799 FILETIME const *patime, FILETIME const *pmtime)
6801 IStorage *stg = NULL;
6802 HRESULT r;
6804 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6806 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6807 0, 0, &stg);
6808 if( SUCCEEDED(r) )
6810 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6811 IStorage_Release(stg);
6814 return r;
6817 /******************************************************************************
6818 * StgIsStorageILockBytes [OLE32.@]
6820 * Determines if the ILockBytes contains a storage object.
6822 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6824 BYTE sig[8];
6825 ULARGE_INTEGER offset;
6827 offset.u.HighPart = 0;
6828 offset.u.LowPart = 0;
6830 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6832 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6833 return S_OK;
6835 return S_FALSE;
6838 /******************************************************************************
6839 * WriteClassStg [OLE32.@]
6841 * This method will store the specified CLSID in the specified storage object
6843 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6845 HRESULT hRes;
6847 if(!pStg)
6848 return E_INVALIDARG;
6850 if(!rclsid)
6851 return STG_E_INVALIDPOINTER;
6853 hRes = IStorage_SetClass(pStg, rclsid);
6855 return hRes;
6858 /***********************************************************************
6859 * ReadClassStg (OLE32.@)
6861 * This method reads the CLSID previously written to a storage object with
6862 * the WriteClassStg.
6864 * PARAMS
6865 * pstg [I] IStorage pointer
6866 * pclsid [O] Pointer to where the CLSID is written
6868 * RETURNS
6869 * Success: S_OK.
6870 * Failure: HRESULT code.
6872 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6874 STATSTG pstatstg;
6875 HRESULT hRes;
6877 TRACE("(%p, %p)\n", pstg, pclsid);
6879 if(!pstg || !pclsid)
6880 return E_INVALIDARG;
6883 * read a STATSTG structure (contains the clsid) from the storage
6885 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6887 if(SUCCEEDED(hRes))
6888 *pclsid=pstatstg.clsid;
6890 return hRes;
6893 /***********************************************************************
6894 * OleLoadFromStream (OLE32.@)
6896 * This function loads an object from stream
6898 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6900 CLSID clsid;
6901 HRESULT res;
6902 LPPERSISTSTREAM xstm;
6904 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6906 res=ReadClassStm(pStm,&clsid);
6907 if (FAILED(res))
6908 return res;
6909 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6910 if (FAILED(res))
6911 return res;
6912 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6913 if (FAILED(res)) {
6914 IUnknown_Release((IUnknown*)*ppvObj);
6915 return res;
6917 res=IPersistStream_Load(xstm,pStm);
6918 IPersistStream_Release(xstm);
6919 /* FIXME: all refcounts ok at this point? I think they should be:
6920 * pStm : unchanged
6921 * ppvObj : 1
6922 * xstm : 0 (released)
6924 return res;
6927 /***********************************************************************
6928 * OleSaveToStream (OLE32.@)
6930 * This function saves an object with the IPersistStream interface on it
6931 * to the specified stream.
6933 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6936 CLSID clsid;
6937 HRESULT res;
6939 TRACE("(%p,%p)\n",pPStm,pStm);
6941 res=IPersistStream_GetClassID(pPStm,&clsid);
6943 if (SUCCEEDED(res)){
6945 res=WriteClassStm(pStm,&clsid);
6947 if (SUCCEEDED(res))
6949 res=IPersistStream_Save(pPStm,pStm,TRUE);
6952 TRACE("Finished Save\n");
6953 return res;
6956 /****************************************************************************
6957 * This method validate a STGM parameter that can contain the values below
6959 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6960 * The stgm values contained in 0xffff0000 are bitmasks.
6962 * STGM_DIRECT 0x00000000
6963 * STGM_TRANSACTED 0x00010000
6964 * STGM_SIMPLE 0x08000000
6966 * STGM_READ 0x00000000
6967 * STGM_WRITE 0x00000001
6968 * STGM_READWRITE 0x00000002
6970 * STGM_SHARE_DENY_NONE 0x00000040
6971 * STGM_SHARE_DENY_READ 0x00000030
6972 * STGM_SHARE_DENY_WRITE 0x00000020
6973 * STGM_SHARE_EXCLUSIVE 0x00000010
6975 * STGM_PRIORITY 0x00040000
6976 * STGM_DELETEONRELEASE 0x04000000
6978 * STGM_CREATE 0x00001000
6979 * STGM_CONVERT 0x00020000
6980 * STGM_FAILIFTHERE 0x00000000
6982 * STGM_NOSCRATCH 0x00100000
6983 * STGM_NOSNAPSHOT 0x00200000
6985 static HRESULT validateSTGM(DWORD stgm)
6987 DWORD access = STGM_ACCESS_MODE(stgm);
6988 DWORD share = STGM_SHARE_MODE(stgm);
6989 DWORD create = STGM_CREATE_MODE(stgm);
6991 if (stgm&~STGM_KNOWN_FLAGS)
6993 ERR("unknown flags %08x\n", stgm);
6994 return E_FAIL;
6997 switch (access)
6999 case STGM_READ:
7000 case STGM_WRITE:
7001 case STGM_READWRITE:
7002 break;
7003 default:
7004 return E_FAIL;
7007 switch (share)
7009 case STGM_SHARE_DENY_NONE:
7010 case STGM_SHARE_DENY_READ:
7011 case STGM_SHARE_DENY_WRITE:
7012 case STGM_SHARE_EXCLUSIVE:
7013 break;
7014 default:
7015 return E_FAIL;
7018 switch (create)
7020 case STGM_CREATE:
7021 case STGM_FAILIFTHERE:
7022 break;
7023 default:
7024 return E_FAIL;
7028 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7030 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7031 return E_FAIL;
7034 * STGM_CREATE | STGM_CONVERT
7035 * if both are false, STGM_FAILIFTHERE is set to TRUE
7037 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7038 return E_FAIL;
7041 * STGM_NOSCRATCH requires STGM_TRANSACTED
7043 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7044 return E_FAIL;
7047 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7048 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7050 if ( (stgm & STGM_NOSNAPSHOT) &&
7051 (!(stgm & STGM_TRANSACTED) ||
7052 share == STGM_SHARE_EXCLUSIVE ||
7053 share == STGM_SHARE_DENY_WRITE) )
7054 return E_FAIL;
7056 return S_OK;
7059 /****************************************************************************
7060 * GetShareModeFromSTGM
7062 * This method will return a share mode flag from a STGM value.
7063 * The STGM value is assumed valid.
7065 static DWORD GetShareModeFromSTGM(DWORD stgm)
7067 switch (STGM_SHARE_MODE(stgm))
7069 case STGM_SHARE_DENY_NONE:
7070 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7071 case STGM_SHARE_DENY_READ:
7072 return FILE_SHARE_WRITE;
7073 case STGM_SHARE_DENY_WRITE:
7074 return FILE_SHARE_READ;
7075 case STGM_SHARE_EXCLUSIVE:
7076 return 0;
7078 ERR("Invalid share mode!\n");
7079 assert(0);
7080 return 0;
7083 /****************************************************************************
7084 * GetAccessModeFromSTGM
7086 * This method will return an access mode flag from a STGM value.
7087 * The STGM value is assumed valid.
7089 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7091 switch (STGM_ACCESS_MODE(stgm))
7093 case STGM_READ:
7094 return GENERIC_READ;
7095 case STGM_WRITE:
7096 case STGM_READWRITE:
7097 return GENERIC_READ | GENERIC_WRITE;
7099 ERR("Invalid access mode!\n");
7100 assert(0);
7101 return 0;
7104 /****************************************************************************
7105 * GetCreationModeFromSTGM
7107 * This method will return a creation mode flag from a STGM value.
7108 * The STGM value is assumed valid.
7110 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7112 switch(STGM_CREATE_MODE(stgm))
7114 case STGM_CREATE:
7115 return CREATE_ALWAYS;
7116 case STGM_CONVERT:
7117 FIXME("STGM_CONVERT not implemented!\n");
7118 return CREATE_NEW;
7119 case STGM_FAILIFTHERE:
7120 return CREATE_NEW;
7122 ERR("Invalid create mode!\n");
7123 assert(0);
7124 return 0;
7128 /*************************************************************************
7129 * OLECONVERT_LoadOLE10 [Internal]
7131 * Loads the OLE10 STREAM to memory
7133 * PARAMS
7134 * pOleStream [I] The OLESTREAM
7135 * pData [I] Data Structure for the OLESTREAM Data
7137 * RETURNS
7138 * Success: S_OK
7139 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7140 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7142 * NOTES
7143 * This function is used by OleConvertOLESTREAMToIStorage only.
7145 * Memory allocated for pData must be freed by the caller
7147 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7149 DWORD dwSize;
7150 HRESULT hRes = S_OK;
7151 int nTryCnt=0;
7152 int max_try = 6;
7154 pData->pData = NULL;
7155 pData->pstrOleObjFileName = NULL;
7157 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7159 /* Get the OleID */
7160 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7161 if(dwSize != sizeof(pData->dwOleID))
7163 hRes = CONVERT10_E_OLESTREAM_GET;
7165 else if(pData->dwOleID != OLESTREAM_ID)
7167 hRes = CONVERT10_E_OLESTREAM_FMT;
7169 else
7171 hRes = S_OK;
7172 break;
7176 if(hRes == S_OK)
7178 /* Get the TypeID... more info needed for this field */
7179 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7180 if(dwSize != sizeof(pData->dwTypeID))
7182 hRes = CONVERT10_E_OLESTREAM_GET;
7185 if(hRes == S_OK)
7187 if(pData->dwTypeID != 0)
7189 /* Get the length of the OleTypeName */
7190 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7191 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7193 hRes = CONVERT10_E_OLESTREAM_GET;
7196 if(hRes == S_OK)
7198 if(pData->dwOleTypeNameLength > 0)
7200 /* Get the OleTypeName */
7201 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7202 if(dwSize != pData->dwOleTypeNameLength)
7204 hRes = CONVERT10_E_OLESTREAM_GET;
7208 if(bStrem1)
7210 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7211 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7213 hRes = CONVERT10_E_OLESTREAM_GET;
7215 if(hRes == S_OK)
7217 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7218 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7219 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7220 if(pData->pstrOleObjFileName)
7222 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7223 if(dwSize != pData->dwOleObjFileNameLength)
7225 hRes = CONVERT10_E_OLESTREAM_GET;
7228 else
7229 hRes = CONVERT10_E_OLESTREAM_GET;
7232 else
7234 /* Get the Width of the Metafile */
7235 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7236 if(dwSize != sizeof(pData->dwMetaFileWidth))
7238 hRes = CONVERT10_E_OLESTREAM_GET;
7240 if(hRes == S_OK)
7242 /* Get the Height of the Metafile */
7243 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7244 if(dwSize != sizeof(pData->dwMetaFileHeight))
7246 hRes = CONVERT10_E_OLESTREAM_GET;
7250 if(hRes == S_OK)
7252 /* Get the Length of the Data */
7253 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7254 if(dwSize != sizeof(pData->dwDataLength))
7256 hRes = CONVERT10_E_OLESTREAM_GET;
7260 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7262 if(!bStrem1) /* if it is a second OLE stream data */
7264 pData->dwDataLength -= 8;
7265 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7266 if(dwSize != sizeof(pData->strUnknown))
7268 hRes = CONVERT10_E_OLESTREAM_GET;
7272 if(hRes == S_OK)
7274 if(pData->dwDataLength > 0)
7276 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7278 /* Get Data (ex. IStorage, Metafile, or BMP) */
7279 if(pData->pData)
7281 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7282 if(dwSize != pData->dwDataLength)
7284 hRes = CONVERT10_E_OLESTREAM_GET;
7287 else
7289 hRes = CONVERT10_E_OLESTREAM_GET;
7295 return hRes;
7298 /*************************************************************************
7299 * OLECONVERT_SaveOLE10 [Internal]
7301 * Saves the OLE10 STREAM From memory
7303 * PARAMS
7304 * pData [I] Data Structure for the OLESTREAM Data
7305 * pOleStream [I] The OLESTREAM to save
7307 * RETURNS
7308 * Success: S_OK
7309 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7311 * NOTES
7312 * This function is used by OleConvertIStorageToOLESTREAM only.
7315 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7317 DWORD dwSize;
7318 HRESULT hRes = S_OK;
7321 /* Set the OleID */
7322 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7323 if(dwSize != sizeof(pData->dwOleID))
7325 hRes = CONVERT10_E_OLESTREAM_PUT;
7328 if(hRes == S_OK)
7330 /* Set the TypeID */
7331 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7332 if(dwSize != sizeof(pData->dwTypeID))
7334 hRes = CONVERT10_E_OLESTREAM_PUT;
7338 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7340 /* Set the Length of the OleTypeName */
7341 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7342 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7344 hRes = CONVERT10_E_OLESTREAM_PUT;
7347 if(hRes == S_OK)
7349 if(pData->dwOleTypeNameLength > 0)
7351 /* Set the OleTypeName */
7352 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7353 if(dwSize != pData->dwOleTypeNameLength)
7355 hRes = CONVERT10_E_OLESTREAM_PUT;
7360 if(hRes == S_OK)
7362 /* Set the width of the Metafile */
7363 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7364 if(dwSize != sizeof(pData->dwMetaFileWidth))
7366 hRes = CONVERT10_E_OLESTREAM_PUT;
7370 if(hRes == S_OK)
7372 /* Set the height of the Metafile */
7373 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7374 if(dwSize != sizeof(pData->dwMetaFileHeight))
7376 hRes = CONVERT10_E_OLESTREAM_PUT;
7380 if(hRes == S_OK)
7382 /* Set the length of the Data */
7383 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7384 if(dwSize != sizeof(pData->dwDataLength))
7386 hRes = CONVERT10_E_OLESTREAM_PUT;
7390 if(hRes == S_OK)
7392 if(pData->dwDataLength > 0)
7394 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7395 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7396 if(dwSize != pData->dwDataLength)
7398 hRes = CONVERT10_E_OLESTREAM_PUT;
7403 return hRes;
7406 /*************************************************************************
7407 * OLECONVERT_GetOLE20FromOLE10[Internal]
7409 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7410 * opens it, and copies the content to the dest IStorage for
7411 * OleConvertOLESTREAMToIStorage
7414 * PARAMS
7415 * pDestStorage [I] The IStorage to copy the data to
7416 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7417 * nBufferLength [I] The size of the buffer
7419 * RETURNS
7420 * Nothing
7422 * NOTES
7426 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7428 HRESULT hRes;
7429 HANDLE hFile;
7430 IStorage *pTempStorage;
7431 DWORD dwNumOfBytesWritten;
7432 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7433 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7435 /* Create a temp File */
7436 GetTempPathW(MAX_PATH, wstrTempDir);
7437 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7438 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7440 if(hFile != INVALID_HANDLE_VALUE)
7442 /* Write IStorage Data to File */
7443 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7444 CloseHandle(hFile);
7446 /* Open and copy temp storage to the Dest Storage */
7447 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7448 if(hRes == S_OK)
7450 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7451 IStorage_Release(pTempStorage);
7453 DeleteFileW(wstrTempFile);
7458 /*************************************************************************
7459 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7461 * Saves the OLE10 STREAM From memory
7463 * PARAMS
7464 * pStorage [I] The Src IStorage to copy
7465 * pData [I] The Dest Memory to write to.
7467 * RETURNS
7468 * The size in bytes allocated for pData
7470 * NOTES
7471 * Memory allocated for pData must be freed by the caller
7473 * Used by OleConvertIStorageToOLESTREAM only.
7476 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7478 HANDLE hFile;
7479 HRESULT hRes;
7480 DWORD nDataLength = 0;
7481 IStorage *pTempStorage;
7482 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7483 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7485 *pData = NULL;
7487 /* Create temp Storage */
7488 GetTempPathW(MAX_PATH, wstrTempDir);
7489 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7490 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7492 if(hRes == S_OK)
7494 /* Copy Src Storage to the Temp Storage */
7495 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7496 IStorage_Release(pTempStorage);
7498 /* Open Temp Storage as a file and copy to memory */
7499 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7500 if(hFile != INVALID_HANDLE_VALUE)
7502 nDataLength = GetFileSize(hFile, NULL);
7503 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7504 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7505 CloseHandle(hFile);
7507 DeleteFileW(wstrTempFile);
7509 return nDataLength;
7512 /*************************************************************************
7513 * OLECONVERT_CreateOleStream [Internal]
7515 * Creates the "\001OLE" stream in the IStorage if necessary.
7517 * PARAMS
7518 * pStorage [I] Dest storage to create the stream in
7520 * RETURNS
7521 * Nothing
7523 * NOTES
7524 * This function is used by OleConvertOLESTREAMToIStorage only.
7526 * This stream is still unknown, MS Word seems to have extra data
7527 * but since the data is stored in the OLESTREAM there should be
7528 * no need to recreate the stream. If the stream is manually
7529 * deleted it will create it with this default data.
7532 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7534 HRESULT hRes;
7535 IStream *pStream;
7536 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7537 BYTE pOleStreamHeader [] =
7539 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7540 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7541 0x00, 0x00, 0x00, 0x00
7544 /* Create stream if not present */
7545 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7546 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7548 if(hRes == S_OK)
7550 /* Write default Data */
7551 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7552 IStream_Release(pStream);
7556 /* write a string to a stream, preceded by its length */
7557 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7559 HRESULT r;
7560 LPSTR str;
7561 DWORD len = 0;
7563 if( string )
7564 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7565 r = IStream_Write( stm, &len, sizeof(len), NULL);
7566 if( FAILED( r ) )
7567 return r;
7568 if(len == 0)
7569 return r;
7570 str = CoTaskMemAlloc( len );
7571 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7572 r = IStream_Write( stm, str, len, NULL);
7573 CoTaskMemFree( str );
7574 return r;
7577 /* read a string preceded by its length from a stream */
7578 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7580 HRESULT r;
7581 DWORD len, count = 0;
7582 LPSTR str;
7583 LPWSTR wstr;
7585 r = IStream_Read( stm, &len, sizeof(len), &count );
7586 if( FAILED( r ) )
7587 return r;
7588 if( count != sizeof(len) )
7589 return E_OUTOFMEMORY;
7591 TRACE("%d bytes\n",len);
7593 str = CoTaskMemAlloc( len );
7594 if( !str )
7595 return E_OUTOFMEMORY;
7596 count = 0;
7597 r = IStream_Read( stm, str, len, &count );
7598 if( FAILED( r ) )
7599 return r;
7600 if( count != len )
7602 CoTaskMemFree( str );
7603 return E_OUTOFMEMORY;
7606 TRACE("Read string %s\n",debugstr_an(str,len));
7608 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7609 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7610 if( wstr )
7611 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7612 CoTaskMemFree( str );
7614 *string = wstr;
7616 return r;
7620 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7621 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7623 IStream *pstm;
7624 HRESULT r = S_OK;
7625 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7627 static const BYTE unknown1[12] =
7628 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7629 0xFF, 0xFF, 0xFF, 0xFF};
7630 static const BYTE unknown2[16] =
7631 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7632 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7634 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7635 debugstr_w(lpszUserType), debugstr_w(szClipName),
7636 debugstr_w(szProgIDName));
7638 /* Create a CompObj stream */
7639 r = IStorage_CreateStream(pstg, szwStreamName,
7640 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7641 if( FAILED (r) )
7642 return r;
7644 /* Write CompObj Structure to stream */
7645 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7647 if( SUCCEEDED( r ) )
7648 r = WriteClassStm( pstm, clsid );
7650 if( SUCCEEDED( r ) )
7651 r = STREAM_WriteString( pstm, lpszUserType );
7652 if( SUCCEEDED( r ) )
7653 r = STREAM_WriteString( pstm, szClipName );
7654 if( SUCCEEDED( r ) )
7655 r = STREAM_WriteString( pstm, szProgIDName );
7656 if( SUCCEEDED( r ) )
7657 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7659 IStream_Release( pstm );
7661 return r;
7664 /***********************************************************************
7665 * WriteFmtUserTypeStg (OLE32.@)
7667 HRESULT WINAPI WriteFmtUserTypeStg(
7668 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7670 HRESULT r;
7671 WCHAR szwClipName[0x40];
7672 CLSID clsid = CLSID_NULL;
7673 LPWSTR wstrProgID = NULL;
7674 DWORD n;
7676 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7678 /* get the clipboard format name */
7679 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7680 szwClipName[n]=0;
7682 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7684 /* FIXME: There's room to save a CLSID and its ProgID, but
7685 the CLSID is not looked up in the registry and in all the
7686 tests I wrote it was CLSID_NULL. Where does it come from?
7689 /* get the real program ID. This may fail, but that's fine */
7690 ProgIDFromCLSID(&clsid, &wstrProgID);
7692 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7694 r = STORAGE_WriteCompObj( pstg, &clsid,
7695 lpszUserType, szwClipName, wstrProgID );
7697 CoTaskMemFree(wstrProgID);
7699 return r;
7703 /******************************************************************************
7704 * ReadFmtUserTypeStg [OLE32.@]
7706 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7708 HRESULT r;
7709 IStream *stm = 0;
7710 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7711 unsigned char unknown1[12];
7712 unsigned char unknown2[16];
7713 DWORD count;
7714 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7715 CLSID clsid;
7717 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7719 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7720 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7721 if( FAILED ( r ) )
7723 WARN("Failed to open stream r = %08x\n", r);
7724 return r;
7727 /* read the various parts of the structure */
7728 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7729 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7730 goto end;
7731 r = ReadClassStm( stm, &clsid );
7732 if( FAILED( r ) )
7733 goto end;
7735 r = STREAM_ReadString( stm, &szCLSIDName );
7736 if( FAILED( r ) )
7737 goto end;
7739 r = STREAM_ReadString( stm, &szOleTypeName );
7740 if( FAILED( r ) )
7741 goto end;
7743 r = STREAM_ReadString( stm, &szProgIDName );
7744 if( FAILED( r ) )
7745 goto end;
7747 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7748 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7749 goto end;
7751 /* ok, success... now we just need to store what we found */
7752 if( pcf )
7753 *pcf = RegisterClipboardFormatW( szOleTypeName );
7754 CoTaskMemFree( szOleTypeName );
7756 if( lplpszUserType )
7757 *lplpszUserType = szCLSIDName;
7758 CoTaskMemFree( szProgIDName );
7760 end:
7761 IStream_Release( stm );
7763 return r;
7767 /*************************************************************************
7768 * OLECONVERT_CreateCompObjStream [Internal]
7770 * Creates a "\001CompObj" is the destination IStorage if necessary.
7772 * PARAMS
7773 * pStorage [I] The dest IStorage to create the CompObj Stream
7774 * if necessary.
7775 * strOleTypeName [I] The ProgID
7777 * RETURNS
7778 * Success: S_OK
7779 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7781 * NOTES
7782 * This function is used by OleConvertOLESTREAMToIStorage only.
7784 * The stream data is stored in the OLESTREAM and there should be
7785 * no need to recreate the stream. If the stream is manually
7786 * deleted it will attempt to create it by querying the registry.
7790 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7792 IStream *pStream;
7793 HRESULT hStorageRes, hRes = S_OK;
7794 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7795 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7796 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7798 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7799 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7801 /* Initialize the CompObj structure */
7802 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7803 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7804 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7807 /* Create a CompObj stream if it doesn't exist */
7808 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7809 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7810 if(hStorageRes == S_OK)
7812 /* copy the OleTypeName to the compobj struct */
7813 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7814 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7816 /* copy the OleTypeName to the compobj struct */
7817 /* Note: in the test made, these were Identical */
7818 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7819 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7821 /* Get the CLSID */
7822 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7823 bufferW, OLESTREAM_MAX_STR_LEN );
7824 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7826 if(hRes == S_OK)
7828 HKEY hKey;
7829 LONG hErr;
7830 /* Get the CLSID Default Name from the Registry */
7831 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7832 if(hErr == ERROR_SUCCESS)
7834 char strTemp[OLESTREAM_MAX_STR_LEN];
7835 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7836 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7837 if(hErr == ERROR_SUCCESS)
7839 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7841 RegCloseKey(hKey);
7845 /* Write CompObj Structure to stream */
7846 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7848 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7850 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7851 if(IStorageCompObj.dwCLSIDNameLength > 0)
7853 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7855 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7856 if(IStorageCompObj.dwOleTypeNameLength > 0)
7858 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7860 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7861 if(IStorageCompObj.dwProgIDNameLength > 0)
7863 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7865 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7866 IStream_Release(pStream);
7868 return hRes;
7872 /*************************************************************************
7873 * OLECONVERT_CreateOlePresStream[Internal]
7875 * Creates the "\002OlePres000" Stream with the Metafile data
7877 * PARAMS
7878 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7879 * dwExtentX [I] Width of the Metafile
7880 * dwExtentY [I] Height of the Metafile
7881 * pData [I] Metafile data
7882 * dwDataLength [I] Size of the Metafile data
7884 * RETURNS
7885 * Success: S_OK
7886 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7888 * NOTES
7889 * This function is used by OleConvertOLESTREAMToIStorage only.
7892 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7894 HRESULT hRes;
7895 IStream *pStream;
7896 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7897 BYTE pOlePresStreamHeader [] =
7899 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7900 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7901 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7902 0x00, 0x00, 0x00, 0x00
7905 BYTE pOlePresStreamHeaderEmpty [] =
7907 0x00, 0x00, 0x00, 0x00,
7908 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7909 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7910 0x00, 0x00, 0x00, 0x00
7913 /* Create the OlePres000 Stream */
7914 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7915 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7917 if(hRes == S_OK)
7919 DWORD nHeaderSize;
7920 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7922 memset(&OlePres, 0, sizeof(OlePres));
7923 /* Do we have any metafile data to save */
7924 if(dwDataLength > 0)
7926 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7927 nHeaderSize = sizeof(pOlePresStreamHeader);
7929 else
7931 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7932 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7934 /* Set width and height of the metafile */
7935 OlePres.dwExtentX = dwExtentX;
7936 OlePres.dwExtentY = -dwExtentY;
7938 /* Set Data and Length */
7939 if(dwDataLength > sizeof(METAFILEPICT16))
7941 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7942 OlePres.pData = &(pData[8]);
7944 /* Save OlePres000 Data to Stream */
7945 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7946 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7947 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7948 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7949 if(OlePres.dwSize > 0)
7951 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7953 IStream_Release(pStream);
7957 /*************************************************************************
7958 * OLECONVERT_CreateOle10NativeStream [Internal]
7960 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7962 * PARAMS
7963 * pStorage [I] Dest storage to create the stream in
7964 * pData [I] Ole10 Native Data (ex. bmp)
7965 * dwDataLength [I] Size of the Ole10 Native Data
7967 * RETURNS
7968 * Nothing
7970 * NOTES
7971 * This function is used by OleConvertOLESTREAMToIStorage only.
7973 * Might need to verify the data and return appropriate error message
7976 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7978 HRESULT hRes;
7979 IStream *pStream;
7980 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7982 /* Create the Ole10Native Stream */
7983 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7984 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7986 if(hRes == S_OK)
7988 /* Write info to stream */
7989 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7990 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7991 IStream_Release(pStream);
7996 /*************************************************************************
7997 * OLECONVERT_GetOLE10ProgID [Internal]
7999 * Finds the ProgID (or OleTypeID) from the IStorage
8001 * PARAMS
8002 * pStorage [I] The Src IStorage to get the ProgID
8003 * strProgID [I] the ProgID string to get
8004 * dwSize [I] the size of the string
8006 * RETURNS
8007 * Success: S_OK
8008 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8010 * NOTES
8011 * This function is used by OleConvertIStorageToOLESTREAM only.
8015 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8017 HRESULT hRes;
8018 IStream *pStream;
8019 LARGE_INTEGER iSeekPos;
8020 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8021 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8023 /* Open the CompObj Stream */
8024 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8025 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8026 if(hRes == S_OK)
8029 /*Get the OleType from the CompObj Stream */
8030 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8031 iSeekPos.u.HighPart = 0;
8033 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8034 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8035 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8036 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8037 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8038 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8039 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8041 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8042 if(*dwSize > 0)
8044 IStream_Read(pStream, strProgID, *dwSize, NULL);
8046 IStream_Release(pStream);
8048 else
8050 STATSTG stat;
8051 LPOLESTR wstrProgID;
8053 /* Get the OleType from the registry */
8054 REFCLSID clsid = &(stat.clsid);
8055 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8056 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8057 if(hRes == S_OK)
8059 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8063 return hRes;
8066 /*************************************************************************
8067 * OLECONVERT_GetOle10PresData [Internal]
8069 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8071 * PARAMS
8072 * pStorage [I] Src IStroage
8073 * pOleStream [I] Dest OleStream Mem Struct
8075 * RETURNS
8076 * Nothing
8078 * NOTES
8079 * This function is used by OleConvertIStorageToOLESTREAM only.
8081 * Memory allocated for pData must be freed by the caller
8085 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8088 HRESULT hRes;
8089 IStream *pStream;
8090 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8092 /* Initialize Default data for OLESTREAM */
8093 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8094 pOleStreamData[0].dwTypeID = 2;
8095 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8096 pOleStreamData[1].dwTypeID = 0;
8097 pOleStreamData[0].dwMetaFileWidth = 0;
8098 pOleStreamData[0].dwMetaFileHeight = 0;
8099 pOleStreamData[0].pData = NULL;
8100 pOleStreamData[1].pData = NULL;
8102 /* Open Ole10Native Stream */
8103 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8104 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8105 if(hRes == S_OK)
8108 /* Read Size and Data */
8109 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8110 if(pOleStreamData->dwDataLength > 0)
8112 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8113 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8115 IStream_Release(pStream);
8121 /*************************************************************************
8122 * OLECONVERT_GetOle20PresData[Internal]
8124 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8126 * PARAMS
8127 * pStorage [I] Src IStroage
8128 * pOleStreamData [I] Dest OleStream Mem Struct
8130 * RETURNS
8131 * Nothing
8133 * NOTES
8134 * This function is used by OleConvertIStorageToOLESTREAM only.
8136 * Memory allocated for pData must be freed by the caller
8138 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8140 HRESULT hRes;
8141 IStream *pStream;
8142 OLECONVERT_ISTORAGE_OLEPRES olePress;
8143 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8145 /* Initialize Default data for OLESTREAM */
8146 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8147 pOleStreamData[0].dwTypeID = 2;
8148 pOleStreamData[0].dwMetaFileWidth = 0;
8149 pOleStreamData[0].dwMetaFileHeight = 0;
8150 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8151 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8152 pOleStreamData[1].dwTypeID = 0;
8153 pOleStreamData[1].dwOleTypeNameLength = 0;
8154 pOleStreamData[1].strOleTypeName[0] = 0;
8155 pOleStreamData[1].dwMetaFileWidth = 0;
8156 pOleStreamData[1].dwMetaFileHeight = 0;
8157 pOleStreamData[1].pData = NULL;
8158 pOleStreamData[1].dwDataLength = 0;
8161 /* Open OlePress000 stream */
8162 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8163 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8164 if(hRes == S_OK)
8166 LARGE_INTEGER iSeekPos;
8167 METAFILEPICT16 MetaFilePict;
8168 static const char strMetafilePictName[] = "METAFILEPICT";
8170 /* Set the TypeID for a Metafile */
8171 pOleStreamData[1].dwTypeID = 5;
8173 /* Set the OleTypeName to Metafile */
8174 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8175 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8177 iSeekPos.u.HighPart = 0;
8178 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8180 /* Get Presentation Data */
8181 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8182 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8183 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8184 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8186 /*Set width and Height */
8187 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8188 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8189 if(olePress.dwSize > 0)
8191 /* Set Length */
8192 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8194 /* Set MetaFilePict struct */
8195 MetaFilePict.mm = 8;
8196 MetaFilePict.xExt = olePress.dwExtentX;
8197 MetaFilePict.yExt = olePress.dwExtentY;
8198 MetaFilePict.hMF = 0;
8200 /* Get Metafile Data */
8201 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8202 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8203 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8205 IStream_Release(pStream);
8209 /*************************************************************************
8210 * OleConvertOLESTREAMToIStorage [OLE32.@]
8212 * Read info on MSDN
8214 * TODO
8215 * DVTARGETDEVICE parameter is not handled
8216 * Still unsure of some mem fields for OLE 10 Stream
8217 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8218 * and "\001OLE" streams
8221 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8222 LPOLESTREAM pOleStream,
8223 LPSTORAGE pstg,
8224 const DVTARGETDEVICE* ptd)
8226 int i;
8227 HRESULT hRes=S_OK;
8228 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8230 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8232 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8234 if(ptd != NULL)
8236 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8239 if(pstg == NULL || pOleStream == NULL)
8241 hRes = E_INVALIDARG;
8244 if(hRes == S_OK)
8246 /* Load the OLESTREAM to Memory */
8247 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8250 if(hRes == S_OK)
8252 /* Load the OLESTREAM to Memory (part 2)*/
8253 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8256 if(hRes == S_OK)
8259 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8261 /* Do we have the IStorage Data in the OLESTREAM */
8262 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8264 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8265 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8267 else
8269 /* It must be an original OLE 1.0 source */
8270 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8273 else
8275 /* It must be an original OLE 1.0 source */
8276 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8279 /* Create CompObj Stream if necessary */
8280 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8281 if(hRes == S_OK)
8283 /*Create the Ole Stream if necessary */
8284 OLECONVERT_CreateOleStream(pstg);
8289 /* Free allocated memory */
8290 for(i=0; i < 2; i++)
8292 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8293 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8294 pOleStreamData[i].pstrOleObjFileName = NULL;
8296 return hRes;
8299 /*************************************************************************
8300 * OleConvertIStorageToOLESTREAM [OLE32.@]
8302 * Read info on MSDN
8304 * Read info on MSDN
8306 * TODO
8307 * Still unsure of some mem fields for OLE 10 Stream
8308 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8309 * and "\001OLE" streams.
8312 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8313 LPSTORAGE pstg,
8314 LPOLESTREAM pOleStream)
8316 int i;
8317 HRESULT hRes = S_OK;
8318 IStream *pStream;
8319 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8320 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8322 TRACE("%p %p\n", pstg, pOleStream);
8324 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8326 if(pstg == NULL || pOleStream == NULL)
8328 hRes = E_INVALIDARG;
8330 if(hRes == S_OK)
8332 /* Get the ProgID */
8333 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8334 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8336 if(hRes == S_OK)
8338 /* Was it originally Ole10 */
8339 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8340 if(hRes == S_OK)
8342 IStream_Release(pStream);
8343 /* Get Presentation Data for Ole10Native */
8344 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8346 else
8348 /* Get Presentation Data (OLE20) */
8349 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8352 /* Save OLESTREAM */
8353 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8354 if(hRes == S_OK)
8356 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8361 /* Free allocated memory */
8362 for(i=0; i < 2; i++)
8364 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8367 return hRes;
8370 /***********************************************************************
8371 * GetConvertStg (OLE32.@)
8373 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8374 FIXME("unimplemented stub!\n");
8375 return E_FAIL;
8378 /******************************************************************************
8379 * StgIsStorageFile [OLE32.@]
8380 * Verify if the file contains a storage object
8382 * PARAMS
8383 * fn [ I] Filename
8385 * RETURNS
8386 * S_OK if file has magic bytes as a storage object
8387 * S_FALSE if file is not storage
8389 HRESULT WINAPI
8390 StgIsStorageFile(LPCOLESTR fn)
8392 HANDLE hf;
8393 BYTE magic[8];
8394 DWORD bytes_read;
8396 TRACE("%s\n", debugstr_w(fn));
8397 hf = CreateFileW(fn, GENERIC_READ,
8398 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8399 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8401 if (hf == INVALID_HANDLE_VALUE)
8402 return STG_E_FILENOTFOUND;
8404 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8406 WARN(" unable to read file\n");
8407 CloseHandle(hf);
8408 return S_FALSE;
8411 CloseHandle(hf);
8413 if (bytes_read != 8) {
8414 TRACE(" too short\n");
8415 return S_FALSE;
8418 if (!memcmp(magic,STORAGE_magic,8)) {
8419 TRACE(" -> YES\n");
8420 return S_OK;
8423 TRACE(" -> Invalid header.\n");
8424 return S_FALSE;
8427 /***********************************************************************
8428 * WriteClassStm (OLE32.@)
8430 * Writes a CLSID to a stream.
8432 * PARAMS
8433 * pStm [I] Stream to write to.
8434 * rclsid [I] CLSID to write.
8436 * RETURNS
8437 * Success: S_OK.
8438 * Failure: HRESULT code.
8440 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8442 TRACE("(%p,%p)\n",pStm,rclsid);
8444 if (!pStm || !rclsid)
8445 return E_INVALIDARG;
8447 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8450 /***********************************************************************
8451 * ReadClassStm (OLE32.@)
8453 * Reads a CLSID from a stream.
8455 * PARAMS
8456 * pStm [I] Stream to read from.
8457 * rclsid [O] CLSID to read.
8459 * RETURNS
8460 * Success: S_OK.
8461 * Failure: HRESULT code.
8463 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8465 ULONG nbByte;
8466 HRESULT res;
8468 TRACE("(%p,%p)\n",pStm,pclsid);
8470 if (!pStm || !pclsid)
8471 return E_INVALIDARG;
8473 /* clear the output args */
8474 *pclsid = CLSID_NULL;
8476 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8478 if (FAILED(res))
8479 return res;
8481 if (nbByte != sizeof(CLSID))
8482 return STG_E_READFAULT;
8483 else
8484 return S_OK;