wined3d: Use the HDC from the acquired context to call SwapBuffer().
[wine.git] / dlls / ole32 / storage32.c
blob8fe05424e81dc1c6db71e0207e87a7153e0e8aad
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 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
801 &currentEntry);
803 /* Insert the element in a new position in the tree */
804 insertIntoTree(This, This->storageDirEntry,
805 currentEntryRef);
807 else
810 * There is no element with the old name
812 return STG_E_FILENOTFOUND;
815 return S_OK;
818 /************************************************************************
819 * Storage32BaseImpl_CreateStream (IStorage)
821 * This method will create a stream object within this storage
823 * See Windows documentation for more details on IStorage methods.
825 static HRESULT WINAPI StorageBaseImpl_CreateStream(
826 IStorage* iface,
827 const OLECHAR* pwcsName, /* [string][in] */
828 DWORD grfMode, /* [in] */
829 DWORD reserved1, /* [in] */
830 DWORD reserved2, /* [in] */
831 IStream** ppstm) /* [out] */
833 StorageBaseImpl *This = (StorageBaseImpl *)iface;
834 StgStreamImpl* newStream;
835 DirEntry currentEntry, newStreamEntry;
836 DirRef currentEntryRef, newStreamEntryRef;
838 TRACE("(%p, %s, %x, %d, %d, %p)\n",
839 iface, debugstr_w(pwcsName), grfMode,
840 reserved1, reserved2, ppstm);
842 if (ppstm == 0)
843 return STG_E_INVALIDPOINTER;
845 if (pwcsName == 0)
846 return STG_E_INVALIDNAME;
848 if (reserved1 || reserved2)
849 return STG_E_INVALIDPARAMETER;
851 if ( FAILED( validateSTGM(grfMode) ))
852 return STG_E_INVALIDFLAG;
854 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
855 return STG_E_INVALIDFLAG;
857 if (This->reverted)
858 return STG_E_REVERTED;
861 * As documented.
863 if ((grfMode & STGM_DELETEONRELEASE) ||
864 (grfMode & STGM_TRANSACTED))
865 return STG_E_INVALIDFUNCTION;
868 * Don't worry about permissions in transacted mode, as we can always write
869 * changes; we just can't always commit them.
871 if(!(This->openFlags & STGM_TRANSACTED)) {
872 /* Can't create a stream on read-only storage */
873 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
874 return STG_E_ACCESSDENIED;
876 /* Can't create a stream with greater access than the parent. */
877 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
878 return STG_E_ACCESSDENIED;
881 if(This->openFlags & STGM_SIMPLE)
882 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
884 *ppstm = 0;
886 currentEntryRef = findElement(This,
887 This->storageDirEntry,
888 pwcsName,
889 &currentEntry);
891 if (currentEntryRef != DIRENTRY_NULL)
894 * An element with this name already exists
896 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
898 IStorage_DestroyElement(iface, pwcsName);
900 else
901 return STG_E_FILEALREADYEXISTS;
905 * memset the empty entry
907 memset(&newStreamEntry, 0, sizeof(DirEntry));
909 newStreamEntry.sizeOfNameString =
910 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
912 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
913 return STG_E_INVALIDNAME;
915 strcpyW(newStreamEntry.name, pwcsName);
917 newStreamEntry.stgType = STGTY_STREAM;
918 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
919 newStreamEntry.size.u.LowPart = 0;
920 newStreamEntry.size.u.HighPart = 0;
922 newStreamEntry.leftChild = DIRENTRY_NULL;
923 newStreamEntry.rightChild = DIRENTRY_NULL;
924 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
926 /* call CoFileTime to get the current time
927 newStreamEntry.ctime
928 newStreamEntry.mtime
931 /* newStreamEntry.clsid */
934 * Create an entry with the new data
936 StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
938 * Insert the new entry in the parent storage's tree.
940 insertIntoTree(
941 This,
942 This->storageDirEntry,
943 newStreamEntryRef);
946 * Open the stream to return it.
948 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
950 if (newStream != 0)
952 *ppstm = (IStream*)newStream;
954 IStream_AddRef(*ppstm);
956 else
958 return STG_E_INSUFFICIENTMEMORY;
961 return S_OK;
964 /************************************************************************
965 * Storage32BaseImpl_SetClass (IStorage)
967 * This method will write the specified CLSID in the directory entry of this
968 * storage.
970 * See Windows documentation for more details on IStorage methods.
972 static HRESULT WINAPI StorageBaseImpl_SetClass(
973 IStorage* iface,
974 REFCLSID clsid) /* [in] */
976 StorageBaseImpl *This = (StorageBaseImpl *)iface;
977 HRESULT hRes;
978 DirEntry currentEntry;
980 TRACE("(%p, %p)\n", iface, clsid);
982 if (This->reverted)
983 return STG_E_REVERTED;
985 hRes = StorageBaseImpl_ReadDirEntry(This,
986 This->storageDirEntry,
987 &currentEntry);
988 if (SUCCEEDED(hRes))
990 currentEntry.clsid = *clsid;
992 hRes = StorageBaseImpl_WriteDirEntry(This,
993 This->storageDirEntry,
994 &currentEntry);
997 return hRes;
1000 /************************************************************************
1001 ** Storage32Impl implementation
1004 /************************************************************************
1005 * Storage32BaseImpl_CreateStorage (IStorage)
1007 * This method will create the storage object within the provided storage.
1009 * See Windows documentation for more details on IStorage methods.
1011 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1012 IStorage* iface,
1013 const OLECHAR *pwcsName, /* [string][in] */
1014 DWORD grfMode, /* [in] */
1015 DWORD reserved1, /* [in] */
1016 DWORD reserved2, /* [in] */
1017 IStorage **ppstg) /* [out] */
1019 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1021 DirEntry currentEntry;
1022 DirEntry newEntry;
1023 DirRef currentEntryRef;
1024 DirRef newEntryRef;
1025 HRESULT hr;
1027 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1028 iface, debugstr_w(pwcsName), grfMode,
1029 reserved1, reserved2, ppstg);
1031 if (ppstg == 0)
1032 return STG_E_INVALIDPOINTER;
1034 if (This->openFlags & STGM_SIMPLE)
1036 return STG_E_INVALIDFUNCTION;
1039 if (pwcsName == 0)
1040 return STG_E_INVALIDNAME;
1042 *ppstg = NULL;
1044 if ( FAILED( validateSTGM(grfMode) ) ||
1045 (grfMode & STGM_DELETEONRELEASE) )
1047 WARN("bad grfMode: 0x%x\n", grfMode);
1048 return STG_E_INVALIDFLAG;
1051 if (This->reverted)
1052 return STG_E_REVERTED;
1055 * Check that we're compatible with the parent's storage mode
1057 if ( !(This->openFlags & STGM_TRANSACTED) &&
1058 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1060 WARN("access denied\n");
1061 return STG_E_ACCESSDENIED;
1064 currentEntryRef = findElement(This,
1065 This->storageDirEntry,
1066 pwcsName,
1067 &currentEntry);
1069 if (currentEntryRef != DIRENTRY_NULL)
1072 * An element with this name already exists
1074 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1075 ((This->openFlags & STGM_TRANSACTED) ||
1076 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1078 hr = IStorage_DestroyElement(iface, pwcsName);
1079 if (FAILED(hr))
1080 return hr;
1082 else
1084 WARN("file already exists\n");
1085 return STG_E_FILEALREADYEXISTS;
1088 else if (!(This->openFlags & STGM_TRANSACTED) &&
1089 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1091 WARN("read-only storage\n");
1092 return STG_E_ACCESSDENIED;
1095 memset(&newEntry, 0, sizeof(DirEntry));
1097 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1099 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1101 FIXME("name too long\n");
1102 return STG_E_INVALIDNAME;
1105 strcpyW(newEntry.name, pwcsName);
1107 newEntry.stgType = STGTY_STORAGE;
1108 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1109 newEntry.size.u.LowPart = 0;
1110 newEntry.size.u.HighPart = 0;
1112 newEntry.leftChild = DIRENTRY_NULL;
1113 newEntry.rightChild = DIRENTRY_NULL;
1114 newEntry.dirRootEntry = DIRENTRY_NULL;
1116 /* call CoFileTime to get the current time
1117 newEntry.ctime
1118 newEntry.mtime
1121 /* newEntry.clsid */
1124 * Create a new directory entry for the storage
1126 StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1129 * Insert the new directory entry into the parent storage's tree
1131 insertIntoTree(
1132 This,
1133 This->storageDirEntry,
1134 newEntryRef);
1137 * Open it to get a pointer to return.
1139 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1141 if( (hr != S_OK) || (*ppstg == NULL))
1143 return hr;
1147 return S_OK;
1151 /***************************************************************************
1153 * Internal Method
1155 * Reserve a directory entry in the file and initialize it.
1157 static HRESULT StorageImpl_CreateDirEntry(
1158 StorageBaseImpl *base,
1159 const DirEntry *newData,
1160 DirRef *index)
1162 StorageImpl *storage = (StorageImpl*)base;
1163 ULONG currentEntryIndex = 0;
1164 ULONG newEntryIndex = DIRENTRY_NULL;
1165 HRESULT hr = S_OK;
1166 BYTE currentData[RAW_DIRENTRY_SIZE];
1167 WORD sizeOfNameString;
1171 hr = StorageImpl_ReadRawDirEntry(storage,
1172 currentEntryIndex,
1173 currentData);
1175 if (SUCCEEDED(hr))
1177 StorageUtl_ReadWord(
1178 currentData,
1179 OFFSET_PS_NAMELENGTH,
1180 &sizeOfNameString);
1182 if (sizeOfNameString == 0)
1185 * The entry exists and is available, we found it.
1187 newEntryIndex = currentEntryIndex;
1190 else
1193 * We exhausted the directory entries, we will create more space below
1195 newEntryIndex = currentEntryIndex;
1197 currentEntryIndex++;
1199 } while (newEntryIndex == DIRENTRY_NULL);
1202 * grow the directory stream
1204 if (FAILED(hr))
1206 BYTE emptyData[RAW_DIRENTRY_SIZE];
1207 ULARGE_INTEGER newSize;
1208 ULONG entryIndex;
1209 ULONG lastEntry = 0;
1210 ULONG blockCount = 0;
1213 * obtain the new count of blocks in the directory stream
1215 blockCount = BlockChainStream_GetCount(
1216 storage->rootBlockChain)+1;
1219 * initialize the size used by the directory stream
1221 newSize.u.HighPart = 0;
1222 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1225 * add a block to the directory stream
1227 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1230 * memset the empty entry in order to initialize the unused newly
1231 * created entries
1233 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1236 * initialize them
1238 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1240 for(
1241 entryIndex = newEntryIndex + 1;
1242 entryIndex < lastEntry;
1243 entryIndex++)
1245 StorageImpl_WriteRawDirEntry(
1246 storage,
1247 entryIndex,
1248 emptyData);
1252 UpdateRawDirEntry(currentData, newData);
1254 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1256 if (SUCCEEDED(hr))
1257 *index = newEntryIndex;
1259 return hr;
1262 /***************************************************************************
1264 * Internal Method
1266 * Mark a directory entry in the file as free.
1268 static HRESULT StorageImpl_DestroyDirEntry(
1269 StorageBaseImpl *base,
1270 DirRef index)
1272 HRESULT hr;
1273 BYTE emptyData[RAW_DIRENTRY_SIZE];
1274 StorageImpl *storage = (StorageImpl*)base;
1276 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1278 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1280 return hr;
1284 /***************************************************************************
1286 * Internal Method
1288 * Destroy an entry, its attached data, and all entries reachable from it.
1290 static HRESULT DestroyReachableEntries(
1291 StorageBaseImpl *base,
1292 DirRef index)
1294 HRESULT hr = S_OK;
1295 DirEntry data;
1296 ULARGE_INTEGER zero;
1298 zero.QuadPart = 0;
1300 if (index != DIRENTRY_NULL)
1302 hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1304 if (SUCCEEDED(hr))
1305 hr = DestroyReachableEntries(base, data.dirRootEntry);
1307 if (SUCCEEDED(hr))
1308 hr = DestroyReachableEntries(base, data.leftChild);
1310 if (SUCCEEDED(hr))
1311 hr = DestroyReachableEntries(base, data.rightChild);
1313 if (SUCCEEDED(hr))
1314 hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1316 if (SUCCEEDED(hr))
1317 hr = StorageBaseImpl_DestroyDirEntry(base, index);
1320 return hr;
1324 /****************************************************************************
1326 * Internal Method
1328 * Case insensitive comparison of DirEntry.name by first considering
1329 * their size.
1331 * Returns <0 when name1 < name2
1332 * >0 when name1 > name2
1333 * 0 when name1 == name2
1335 static LONG entryNameCmp(
1336 const OLECHAR *name1,
1337 const OLECHAR *name2)
1339 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1341 while (diff == 0 && *name1 != 0)
1344 * We compare the string themselves only when they are of the same length
1346 diff = toupperW(*name1++) - toupperW(*name2++);
1349 return diff;
1352 /****************************************************************************
1354 * Internal Method
1356 * Add a directory entry to a storage
1358 static HRESULT insertIntoTree(
1359 StorageBaseImpl *This,
1360 DirRef parentStorageIndex,
1361 DirRef newEntryIndex)
1363 DirEntry currentEntry;
1364 DirEntry newEntry;
1367 * Read the inserted entry
1369 StorageBaseImpl_ReadDirEntry(This,
1370 newEntryIndex,
1371 &newEntry);
1374 * Read the storage entry
1376 StorageBaseImpl_ReadDirEntry(This,
1377 parentStorageIndex,
1378 &currentEntry);
1380 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1383 * The root storage contains some element, therefore, start the research
1384 * for the appropriate location.
1386 BOOL found = 0;
1387 DirRef current, next, previous, currentEntryId;
1390 * Keep a reference to the root of the storage's element tree
1392 currentEntryId = currentEntry.dirRootEntry;
1395 * Read
1397 StorageBaseImpl_ReadDirEntry(This,
1398 currentEntry.dirRootEntry,
1399 &currentEntry);
1401 previous = currentEntry.leftChild;
1402 next = currentEntry.rightChild;
1403 current = currentEntryId;
1405 while (found == 0)
1407 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1409 if (diff < 0)
1411 if (previous != DIRENTRY_NULL)
1413 StorageBaseImpl_ReadDirEntry(This,
1414 previous,
1415 &currentEntry);
1416 current = previous;
1418 else
1420 currentEntry.leftChild = newEntryIndex;
1421 StorageBaseImpl_WriteDirEntry(This,
1422 current,
1423 &currentEntry);
1424 found = 1;
1427 else if (diff > 0)
1429 if (next != DIRENTRY_NULL)
1431 StorageBaseImpl_ReadDirEntry(This,
1432 next,
1433 &currentEntry);
1434 current = next;
1436 else
1438 currentEntry.rightChild = newEntryIndex;
1439 StorageBaseImpl_WriteDirEntry(This,
1440 current,
1441 &currentEntry);
1442 found = 1;
1445 else
1448 * Trying to insert an item with the same name in the
1449 * subtree structure.
1451 return STG_E_FILEALREADYEXISTS;
1454 previous = currentEntry.leftChild;
1455 next = currentEntry.rightChild;
1458 else
1461 * The storage is empty, make the new entry the root of its element tree
1463 currentEntry.dirRootEntry = newEntryIndex;
1464 StorageBaseImpl_WriteDirEntry(This,
1465 parentStorageIndex,
1466 &currentEntry);
1469 return S_OK;
1472 /****************************************************************************
1474 * Internal Method
1476 * Find and read the element of a storage with the given name.
1478 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1479 const OLECHAR *name, DirEntry *data)
1481 DirRef currentEntry;
1483 /* Read the storage entry to find the root of the tree. */
1484 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1486 currentEntry = data->dirRootEntry;
1488 while (currentEntry != DIRENTRY_NULL)
1490 LONG cmp;
1492 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1494 cmp = entryNameCmp(name, data->name);
1496 if (cmp == 0)
1497 /* found it */
1498 break;
1500 else if (cmp < 0)
1501 currentEntry = data->leftChild;
1503 else if (cmp > 0)
1504 currentEntry = data->rightChild;
1507 return currentEntry;
1510 /****************************************************************************
1512 * Internal Method
1514 * Find and read the binary tree parent of the element with the given name.
1516 * If there is no such element, find a place where it could be inserted and
1517 * return STG_E_FILENOTFOUND.
1519 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1520 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1521 ULONG *relation)
1523 DirRef childEntry;
1524 DirEntry childData;
1526 /* Read the storage entry to find the root of the tree. */
1527 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1529 *parentEntry = storageEntry;
1530 *relation = DIRENTRY_RELATION_DIR;
1532 childEntry = parentData->dirRootEntry;
1534 while (childEntry != DIRENTRY_NULL)
1536 LONG cmp;
1538 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1540 cmp = entryNameCmp(childName, childData.name);
1542 if (cmp == 0)
1543 /* found it */
1544 break;
1546 else if (cmp < 0)
1548 *parentData = childData;
1549 *parentEntry = childEntry;
1550 *relation = DIRENTRY_RELATION_PREVIOUS;
1552 childEntry = parentData->leftChild;
1555 else if (cmp > 0)
1557 *parentData = childData;
1558 *parentEntry = childEntry;
1559 *relation = DIRENTRY_RELATION_NEXT;
1561 childEntry = parentData->rightChild;
1565 if (childEntry == DIRENTRY_NULL)
1566 return STG_E_FILENOTFOUND;
1567 else
1568 return S_OK;
1572 /*************************************************************************
1573 * CopyTo (IStorage)
1575 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1576 IStorage* iface,
1577 DWORD ciidExclude, /* [in] */
1578 const IID* rgiidExclude, /* [size_is][unique][in] */
1579 SNB snbExclude, /* [unique][in] */
1580 IStorage* pstgDest) /* [unique][in] */
1582 IEnumSTATSTG *elements = 0;
1583 STATSTG curElement, strStat;
1584 HRESULT hr;
1585 IStorage *pstgTmp, *pstgChild;
1586 IStream *pstrTmp, *pstrChild;
1587 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1588 int i;
1590 TRACE("(%p, %d, %p, %p, %p)\n",
1591 iface, ciidExclude, rgiidExclude,
1592 snbExclude, pstgDest);
1594 if ( pstgDest == 0 )
1595 return STG_E_INVALIDPOINTER;
1598 * Enumerate the elements
1600 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1602 if ( hr != S_OK )
1603 return hr;
1606 * set the class ID
1608 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1609 IStorage_SetClass( pstgDest, &curElement.clsid );
1611 for(i = 0; i < ciidExclude; ++i)
1613 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1614 skip_storage = TRUE;
1615 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1616 skip_stream = TRUE;
1617 else
1618 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1624 * Obtain the next element
1626 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1628 if ( hr == S_FALSE )
1630 hr = S_OK; /* done, every element has been copied */
1631 break;
1634 if ( snbExclude )
1636 WCHAR **snb = snbExclude;
1637 skip = FALSE;
1638 while ( *snb != NULL && !skip )
1640 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1641 skip = TRUE;
1642 ++snb;
1646 if ( skip )
1647 goto cleanup;
1649 if (curElement.type == STGTY_STORAGE)
1651 if(skip_storage)
1652 goto cleanup;
1655 * open child source storage
1657 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1658 STGM_READ|STGM_SHARE_EXCLUSIVE,
1659 NULL, 0, &pstgChild );
1661 if (hr != S_OK)
1662 goto cleanup;
1665 * create a new storage in destination storage
1667 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1668 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1669 0, 0,
1670 &pstgTmp );
1672 * if it already exist, don't create a new one use this one
1674 if (hr == STG_E_FILEALREADYEXISTS)
1676 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1677 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1678 NULL, 0, &pstgTmp );
1681 if (hr == S_OK)
1684 * do the copy recursively
1686 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1687 NULL, pstgTmp );
1689 IStorage_Release( pstgTmp );
1692 IStorage_Release( pstgChild );
1694 else if (curElement.type == STGTY_STREAM)
1696 if(skip_stream)
1697 goto cleanup;
1700 * create a new stream in destination storage. If the stream already
1701 * exist, it will be deleted and a new one will be created.
1703 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1704 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1705 0, 0, &pstrTmp );
1707 if (hr != S_OK)
1708 goto cleanup;
1711 * open child stream storage
1713 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1714 STGM_READ|STGM_SHARE_EXCLUSIVE,
1715 0, &pstrChild );
1717 if (hr == S_OK)
1720 * Get the size of the source stream
1722 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1725 * Set the size of the destination stream.
1727 IStream_SetSize(pstrTmp, strStat.cbSize);
1730 * do the copy
1732 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1733 NULL, NULL );
1735 IStream_Release( pstrChild );
1738 IStream_Release( pstrTmp );
1740 else
1742 WARN("unknown element type: %d\n", curElement.type);
1745 cleanup:
1746 CoTaskMemFree(curElement.pwcsName);
1747 } while (hr == S_OK);
1750 * Clean-up
1752 IEnumSTATSTG_Release(elements);
1754 return hr;
1757 /*************************************************************************
1758 * MoveElementTo (IStorage)
1760 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1761 IStorage* iface,
1762 const OLECHAR *pwcsName, /* [string][in] */
1763 IStorage *pstgDest, /* [unique][in] */
1764 const OLECHAR *pwcsNewName,/* [string][in] */
1765 DWORD grfFlags) /* [in] */
1767 FIXME("(%p %s %p %s %u): stub\n", iface,
1768 debugstr_w(pwcsName), pstgDest,
1769 debugstr_w(pwcsNewName), grfFlags);
1770 return E_NOTIMPL;
1773 /*************************************************************************
1774 * Commit (IStorage)
1776 * Ensures that any changes made to a storage object open in transacted mode
1777 * are reflected in the parent storage
1779 * NOTES
1780 * Wine doesn't implement transacted mode, which seems to be a basic
1781 * optimization, so we can ignore this stub for now.
1783 static HRESULT WINAPI StorageImpl_Commit(
1784 IStorage* iface,
1785 DWORD grfCommitFlags)/* [in] */
1787 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1788 return S_OK;
1791 /*************************************************************************
1792 * Revert (IStorage)
1794 * Discard all changes that have been made since the last commit operation
1796 static HRESULT WINAPI StorageImpl_Revert(
1797 IStorage* iface)
1799 TRACE("(%p)\n", iface);
1800 return S_OK;
1803 /*************************************************************************
1804 * DestroyElement (IStorage)
1806 * Strategy: This implementation is built this way for simplicity not for speed.
1807 * I always delete the topmost element of the enumeration and adjust
1808 * the deleted element pointer all the time. This takes longer to
1809 * do but allow to reinvoke DestroyElement whenever we encounter a
1810 * storage object. The optimisation resides in the usage of another
1811 * enumeration strategy that would give all the leaves of a storage
1812 * first. (postfix order)
1814 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1815 IStorage* iface,
1816 const OLECHAR *pwcsName)/* [string][in] */
1818 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1820 HRESULT hr = S_OK;
1821 DirEntry entryToDelete;
1822 DirRef entryToDeleteRef;
1824 TRACE("(%p, %s)\n",
1825 iface, debugstr_w(pwcsName));
1827 if (pwcsName==NULL)
1828 return STG_E_INVALIDPOINTER;
1830 if (This->reverted)
1831 return STG_E_REVERTED;
1833 if ( !(This->openFlags & STGM_TRANSACTED) &&
1834 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1835 return STG_E_ACCESSDENIED;
1837 entryToDeleteRef = findElement(
1838 This,
1839 This->storageDirEntry,
1840 pwcsName,
1841 &entryToDelete);
1843 if ( entryToDeleteRef == DIRENTRY_NULL )
1845 return STG_E_FILENOTFOUND;
1848 if ( entryToDelete.stgType == STGTY_STORAGE )
1850 hr = deleteStorageContents(
1851 This,
1852 entryToDeleteRef,
1853 entryToDelete);
1855 else if ( entryToDelete.stgType == STGTY_STREAM )
1857 hr = deleteStreamContents(
1858 This,
1859 entryToDeleteRef,
1860 entryToDelete);
1863 if (hr!=S_OK)
1864 return hr;
1867 * Remove the entry from its parent storage
1869 hr = removeFromTree(
1870 This,
1871 This->storageDirEntry,
1872 entryToDeleteRef);
1875 * Invalidate the entry
1877 if (SUCCEEDED(hr))
1878 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1880 return hr;
1884 /******************************************************************************
1885 * Internal stream list handlers
1888 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1890 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1891 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1894 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1896 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1897 list_remove(&(strm->StrmListEntry));
1900 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1902 StgStreamImpl *strm;
1904 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1906 if (strm->dirEntry == streamEntry)
1908 return TRUE;
1912 return FALSE;
1915 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1917 StorageInternalImpl *childstg;
1919 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1921 if (childstg->base.storageDirEntry == storageEntry)
1923 return TRUE;
1927 return FALSE;
1930 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1932 struct list *cur, *cur2;
1933 StgStreamImpl *strm=NULL;
1934 StorageInternalImpl *childstg=NULL;
1936 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1937 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1938 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1939 strm->parentStorage = NULL;
1940 list_remove(cur);
1943 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1944 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1945 StorageBaseImpl_Invalidate( &childstg->base );
1948 if (stg->transactedChild)
1950 StorageBaseImpl_Invalidate(stg->transactedChild);
1952 stg->transactedChild = NULL;
1957 /*********************************************************************
1959 * Internal Method
1961 * Delete the contents of a storage entry.
1964 static HRESULT deleteStorageContents(
1965 StorageBaseImpl *parentStorage,
1966 DirRef indexToDelete,
1967 DirEntry entryDataToDelete)
1969 IEnumSTATSTG *elements = 0;
1970 IStorage *childStorage = 0;
1971 STATSTG currentElement;
1972 HRESULT hr;
1973 HRESULT destroyHr = S_OK;
1974 StorageInternalImpl *stg, *stg2;
1976 /* Invalidate any open storage objects. */
1977 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1979 if (stg->base.storageDirEntry == indexToDelete)
1981 StorageBaseImpl_Invalidate(&stg->base);
1986 * Open the storage and enumerate it
1988 hr = StorageBaseImpl_OpenStorage(
1989 (IStorage*)parentStorage,
1990 entryDataToDelete.name,
1992 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1995 &childStorage);
1997 if (hr != S_OK)
1999 return hr;
2003 * Enumerate the elements
2005 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2010 * Obtain the next element
2012 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2013 if (hr==S_OK)
2015 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2017 CoTaskMemFree(currentElement.pwcsName);
2021 * We need to Reset the enumeration every time because we delete elements
2022 * and the enumeration could be invalid
2024 IEnumSTATSTG_Reset(elements);
2026 } while ((hr == S_OK) && (destroyHr == S_OK));
2028 IStorage_Release(childStorage);
2029 IEnumSTATSTG_Release(elements);
2031 return destroyHr;
2034 /*********************************************************************
2036 * Internal Method
2038 * Perform the deletion of a stream's data
2041 static HRESULT deleteStreamContents(
2042 StorageBaseImpl *parentStorage,
2043 DirRef indexToDelete,
2044 DirEntry entryDataToDelete)
2046 IStream *pis;
2047 HRESULT hr;
2048 ULARGE_INTEGER size;
2049 StgStreamImpl *strm, *strm2;
2051 /* Invalidate any open stream objects. */
2052 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2054 if (strm->dirEntry == indexToDelete)
2056 TRACE("Stream deleted %p\n", strm);
2057 strm->parentStorage = NULL;
2058 list_remove(&strm->StrmListEntry);
2062 size.u.HighPart = 0;
2063 size.u.LowPart = 0;
2065 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2066 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2068 if (hr!=S_OK)
2070 return(hr);
2074 * Zap the stream
2076 hr = IStream_SetSize(pis, size);
2078 if(hr != S_OK)
2080 return hr;
2084 * Release the stream object.
2086 IStream_Release(pis);
2088 return S_OK;
2091 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2093 switch (relation)
2095 case DIRENTRY_RELATION_PREVIOUS:
2096 entry->leftChild = new_target;
2097 break;
2098 case DIRENTRY_RELATION_NEXT:
2099 entry->rightChild = new_target;
2100 break;
2101 case DIRENTRY_RELATION_DIR:
2102 entry->dirRootEntry = new_target;
2103 break;
2104 default:
2105 assert(0);
2109 /*************************************************************************
2111 * Internal Method
2113 * This method removes a directory entry from its parent storage tree without
2114 * freeing any resources attached to it.
2116 static HRESULT removeFromTree(
2117 StorageBaseImpl *This,
2118 DirRef parentStorageIndex,
2119 DirRef deletedIndex)
2121 HRESULT hr = S_OK;
2122 DirEntry entryToDelete;
2123 DirEntry parentEntry;
2124 DirRef parentEntryRef;
2125 ULONG typeOfRelation;
2127 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2129 if (hr != S_OK)
2130 return hr;
2133 * Find the element that links to the one we want to delete.
2135 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2136 &parentEntry, &parentEntryRef, &typeOfRelation);
2138 if (hr != S_OK)
2139 return hr;
2141 if (entryToDelete.leftChild != DIRENTRY_NULL)
2144 * Replace the deleted entry with its left child
2146 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2148 hr = StorageBaseImpl_WriteDirEntry(
2149 This,
2150 parentEntryRef,
2151 &parentEntry);
2152 if(FAILED(hr))
2154 return hr;
2157 if (entryToDelete.rightChild != DIRENTRY_NULL)
2160 * We need to reinsert the right child somewhere. We already know it and
2161 * its children are greater than everything in the left tree, so we
2162 * insert it at the rightmost point in the left tree.
2164 DirRef newRightChildParent = entryToDelete.leftChild;
2165 DirEntry newRightChildParentEntry;
2169 hr = StorageBaseImpl_ReadDirEntry(
2170 This,
2171 newRightChildParent,
2172 &newRightChildParentEntry);
2173 if (FAILED(hr))
2175 return hr;
2178 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2179 newRightChildParent = newRightChildParentEntry.rightChild;
2180 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2182 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2184 hr = StorageBaseImpl_WriteDirEntry(
2185 This,
2186 newRightChildParent,
2187 &newRightChildParentEntry);
2188 if (FAILED(hr))
2190 return hr;
2194 else
2197 * Replace the deleted entry with its right child
2199 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2201 hr = StorageBaseImpl_WriteDirEntry(
2202 This,
2203 parentEntryRef,
2204 &parentEntry);
2205 if(FAILED(hr))
2207 return hr;
2211 return hr;
2215 /******************************************************************************
2216 * SetElementTimes (IStorage)
2218 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2219 IStorage* iface,
2220 const OLECHAR *pwcsName,/* [string][in] */
2221 const FILETIME *pctime, /* [in] */
2222 const FILETIME *patime, /* [in] */
2223 const FILETIME *pmtime) /* [in] */
2225 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2226 return S_OK;
2229 /******************************************************************************
2230 * SetStateBits (IStorage)
2232 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2233 IStorage* iface,
2234 DWORD grfStateBits,/* [in] */
2235 DWORD grfMask) /* [in] */
2237 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2239 if (This->reverted)
2240 return STG_E_REVERTED;
2242 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2243 return S_OK;
2246 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2247 DirRef index, const DirEntry *data)
2249 StorageImpl *This = (StorageImpl*)base;
2250 return StorageImpl_WriteDirEntry(This, index, data);
2253 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2254 DirRef index, DirEntry *data)
2256 StorageImpl *This = (StorageImpl*)base;
2257 return StorageImpl_ReadDirEntry(This, index, data);
2260 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2262 int i;
2264 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2266 if (!This->blockChainCache[i])
2268 return &This->blockChainCache[i];
2272 i = This->blockChainToEvict;
2274 BlockChainStream_Destroy(This->blockChainCache[i]);
2275 This->blockChainCache[i] = NULL;
2277 This->blockChainToEvict++;
2278 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2279 This->blockChainToEvict = 0;
2281 return &This->blockChainCache[i];
2284 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2285 DirRef index)
2287 int i, free_index=-1;
2289 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2291 if (!This->blockChainCache[i])
2293 if (free_index == -1) free_index = i;
2295 else if (This->blockChainCache[i]->ownerDirEntry == index)
2297 return &This->blockChainCache[i];
2301 if (free_index == -1)
2303 free_index = This->blockChainToEvict;
2305 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2306 This->blockChainCache[free_index] = NULL;
2308 This->blockChainToEvict++;
2309 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2310 This->blockChainToEvict = 0;
2313 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2314 return &This->blockChainCache[free_index];
2317 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2318 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2320 StorageImpl *This = (StorageImpl*)base;
2321 DirEntry data;
2322 HRESULT hr;
2323 ULONG bytesToRead;
2325 hr = StorageImpl_ReadDirEntry(This, index, &data);
2326 if (FAILED(hr)) return hr;
2328 if (data.size.QuadPart == 0)
2330 *bytesRead = 0;
2331 return S_OK;
2334 if (offset.QuadPart + size > data.size.QuadPart)
2336 bytesToRead = data.size.QuadPart - offset.QuadPart;
2338 else
2340 bytesToRead = size;
2343 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2345 SmallBlockChainStream *stream;
2347 stream = SmallBlockChainStream_Construct(This, NULL, index);
2348 if (!stream) return E_OUTOFMEMORY;
2350 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2352 SmallBlockChainStream_Destroy(stream);
2354 return hr;
2356 else
2358 BlockChainStream *stream = NULL;
2360 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2361 if (!stream) return E_OUTOFMEMORY;
2363 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2365 return hr;
2369 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2370 ULARGE_INTEGER newsize)
2372 StorageImpl *This = (StorageImpl*)base;
2373 DirEntry data;
2374 HRESULT hr;
2375 SmallBlockChainStream *smallblock=NULL;
2376 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2378 hr = StorageImpl_ReadDirEntry(This, index, &data);
2379 if (FAILED(hr)) return hr;
2381 /* In simple mode keep the stream size above the small block limit */
2382 if (This->base.openFlags & STGM_SIMPLE)
2383 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2385 if (data.size.QuadPart == newsize.QuadPart)
2386 return S_OK;
2388 /* Create a block chain object of the appropriate type */
2389 if (data.size.QuadPart == 0)
2391 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2393 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2394 if (!smallblock) return E_OUTOFMEMORY;
2396 else
2398 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2399 bigblock = *pbigblock;
2400 if (!bigblock) return E_OUTOFMEMORY;
2403 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2405 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2406 if (!smallblock) return E_OUTOFMEMORY;
2408 else
2410 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2411 bigblock = *pbigblock;
2412 if (!bigblock) return E_OUTOFMEMORY;
2415 /* Change the block chain type if necessary. */
2416 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2418 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2419 if (!bigblock)
2421 SmallBlockChainStream_Destroy(smallblock);
2422 return E_FAIL;
2425 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2426 *pbigblock = bigblock;
2428 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2430 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2431 if (!smallblock)
2432 return E_FAIL;
2435 /* Set the size of the block chain. */
2436 if (smallblock)
2438 SmallBlockChainStream_SetSize(smallblock, newsize);
2439 SmallBlockChainStream_Destroy(smallblock);
2441 else
2443 BlockChainStream_SetSize(bigblock, newsize);
2446 /* Set the size in the directory entry. */
2447 hr = StorageImpl_ReadDirEntry(This, index, &data);
2448 if (SUCCEEDED(hr))
2450 data.size = newsize;
2452 hr = StorageImpl_WriteDirEntry(This, index, &data);
2454 return hr;
2457 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2458 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2460 StorageImpl *This = (StorageImpl*)base;
2461 DirEntry data;
2462 HRESULT hr;
2463 ULARGE_INTEGER newSize;
2465 hr = StorageImpl_ReadDirEntry(This, index, &data);
2466 if (FAILED(hr)) return hr;
2468 /* Grow the stream if necessary */
2469 newSize.QuadPart = 0;
2470 newSize.QuadPart = offset.QuadPart + size;
2472 if (newSize.QuadPart > data.size.QuadPart)
2474 hr = StorageImpl_StreamSetSize(base, index, newSize);
2475 if (FAILED(hr))
2476 return hr;
2478 hr = StorageImpl_ReadDirEntry(This, index, &data);
2479 if (FAILED(hr)) return hr;
2482 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2484 SmallBlockChainStream *stream;
2486 stream = SmallBlockChainStream_Construct(This, NULL, index);
2487 if (!stream) return E_OUTOFMEMORY;
2489 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2491 SmallBlockChainStream_Destroy(stream);
2493 return hr;
2495 else
2497 BlockChainStream *stream;
2499 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2500 if (!stream) return E_OUTOFMEMORY;
2502 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2504 return hr;
2509 * Virtual function table for the IStorage32Impl class.
2511 static const IStorageVtbl Storage32Impl_Vtbl =
2513 StorageBaseImpl_QueryInterface,
2514 StorageBaseImpl_AddRef,
2515 StorageBaseImpl_Release,
2516 StorageBaseImpl_CreateStream,
2517 StorageBaseImpl_OpenStream,
2518 StorageBaseImpl_CreateStorage,
2519 StorageBaseImpl_OpenStorage,
2520 StorageBaseImpl_CopyTo,
2521 StorageBaseImpl_MoveElementTo,
2522 StorageImpl_Commit,
2523 StorageImpl_Revert,
2524 StorageBaseImpl_EnumElements,
2525 StorageBaseImpl_DestroyElement,
2526 StorageBaseImpl_RenameElement,
2527 StorageBaseImpl_SetElementTimes,
2528 StorageBaseImpl_SetClass,
2529 StorageBaseImpl_SetStateBits,
2530 StorageBaseImpl_Stat
2533 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2535 StorageImpl_Destroy,
2536 StorageImpl_Invalidate,
2537 StorageImpl_CreateDirEntry,
2538 StorageImpl_BaseWriteDirEntry,
2539 StorageImpl_BaseReadDirEntry,
2540 StorageImpl_DestroyDirEntry,
2541 StorageImpl_StreamReadAt,
2542 StorageImpl_StreamWriteAt,
2543 StorageImpl_StreamSetSize
2546 static HRESULT StorageImpl_Construct(
2547 HANDLE hFile,
2548 LPCOLESTR pwcsName,
2549 ILockBytes* pLkbyt,
2550 DWORD openFlags,
2551 BOOL fileBased,
2552 BOOL create,
2553 StorageImpl** result)
2555 StorageImpl* This;
2556 HRESULT hr = S_OK;
2557 DirEntry currentEntry;
2558 DirRef currentEntryRef;
2559 WCHAR fullpath[MAX_PATH];
2561 if ( FAILED( validateSTGM(openFlags) ))
2562 return STG_E_INVALIDFLAG;
2564 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2565 if (!This)
2566 return E_OUTOFMEMORY;
2568 memset(This, 0, sizeof(StorageImpl));
2570 list_init(&This->base.strmHead);
2572 list_init(&This->base.storageHead);
2574 This->base.lpVtbl = &Storage32Impl_Vtbl;
2575 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2576 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2577 This->base.openFlags = (openFlags & ~STGM_CREATE);
2578 This->base.ref = 1;
2579 This->base.create = create;
2581 This->base.reverted = 0;
2583 This->hFile = hFile;
2585 if(pwcsName) {
2586 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2588 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2590 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2591 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2592 if (!This->pwcsName)
2594 hr = STG_E_INSUFFICIENTMEMORY;
2595 goto end;
2597 strcpyW(This->pwcsName, fullpath);
2598 This->base.filename = This->pwcsName;
2602 * Initialize the big block cache.
2604 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2605 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2606 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2607 pLkbyt,
2608 openFlags,
2609 This->bigBlockSize,
2610 fileBased);
2612 if (This->bigBlockFile == 0)
2614 hr = E_FAIL;
2615 goto end;
2618 if (create)
2620 ULARGE_INTEGER size;
2621 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2624 * Initialize all header variables:
2625 * - The big block depot consists of one block and it is at block 0
2626 * - The directory table starts at block 1
2627 * - There is no small block depot
2629 memset( This->bigBlockDepotStart,
2630 BLOCK_UNUSED,
2631 sizeof(This->bigBlockDepotStart));
2633 This->bigBlockDepotCount = 1;
2634 This->bigBlockDepotStart[0] = 0;
2635 This->rootStartBlock = 1;
2636 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2637 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2638 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2639 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2640 This->extBigBlockDepotCount = 0;
2642 StorageImpl_SaveFileHeader(This);
2645 * Add one block for the big block depot and one block for the directory table
2647 size.u.HighPart = 0;
2648 size.u.LowPart = This->bigBlockSize * 3;
2649 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2652 * Initialize the big block depot
2654 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2655 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2656 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2657 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2659 else
2662 * Load the header for the file.
2664 hr = StorageImpl_LoadFileHeader(This);
2666 if (FAILED(hr))
2668 goto end;
2673 * There is no block depot cached yet.
2675 This->indexBlockDepotCached = 0xFFFFFFFF;
2678 * Start searching for free blocks with block 0.
2680 This->prevFreeBlock = 0;
2683 * Create the block chain abstractions.
2685 if(!(This->rootBlockChain =
2686 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2688 hr = STG_E_READFAULT;
2689 goto end;
2692 if(!(This->smallBlockDepotChain =
2693 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2694 DIRENTRY_NULL)))
2696 hr = STG_E_READFAULT;
2697 goto end;
2701 * Write the root storage entry (memory only)
2703 if (create)
2705 DirEntry rootEntry;
2707 * Initialize the directory table
2709 memset(&rootEntry, 0, sizeof(rootEntry));
2710 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2711 sizeof(rootEntry.name)/sizeof(WCHAR) );
2712 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2713 rootEntry.stgType = STGTY_ROOT;
2714 rootEntry.leftChild = DIRENTRY_NULL;
2715 rootEntry.rightChild = DIRENTRY_NULL;
2716 rootEntry.dirRootEntry = DIRENTRY_NULL;
2717 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2718 rootEntry.size.u.HighPart = 0;
2719 rootEntry.size.u.LowPart = 0;
2721 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2725 * Find the ID of the root storage.
2727 currentEntryRef = 0;
2731 hr = StorageImpl_ReadDirEntry(
2732 This,
2733 currentEntryRef,
2734 &currentEntry);
2736 if (SUCCEEDED(hr))
2738 if ( (currentEntry.sizeOfNameString != 0 ) &&
2739 (currentEntry.stgType == STGTY_ROOT) )
2741 This->base.storageDirEntry = currentEntryRef;
2745 currentEntryRef++;
2747 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2749 if (FAILED(hr))
2751 hr = STG_E_READFAULT;
2752 goto end;
2756 * Create the block chain abstraction for the small block root chain.
2758 if(!(This->smallBlockRootChain =
2759 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2761 hr = STG_E_READFAULT;
2764 end:
2765 if (FAILED(hr))
2767 IStorage_Release((IStorage*)This);
2768 *result = NULL;
2770 else
2771 *result = This;
2773 return hr;
2776 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2778 StorageImpl *This = (StorageImpl*) iface;
2780 StorageBaseImpl_DeleteAll(&This->base);
2782 This->base.reverted = 1;
2785 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2787 StorageImpl *This = (StorageImpl*) iface;
2788 int i;
2789 TRACE("(%p)\n", This);
2791 StorageImpl_Invalidate(iface);
2793 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2795 BlockChainStream_Destroy(This->smallBlockRootChain);
2796 BlockChainStream_Destroy(This->rootBlockChain);
2797 BlockChainStream_Destroy(This->smallBlockDepotChain);
2799 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2800 BlockChainStream_Destroy(This->blockChainCache[i]);
2802 if (This->bigBlockFile)
2803 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2804 HeapFree(GetProcessHeap(), 0, This);
2807 /******************************************************************************
2808 * Storage32Impl_GetNextFreeBigBlock
2810 * Returns the index of the next free big block.
2811 * If the big block depot is filled, this method will enlarge it.
2814 static ULONG StorageImpl_GetNextFreeBigBlock(
2815 StorageImpl* This)
2817 ULONG depotBlockIndexPos;
2818 BYTE depotBuffer[BIG_BLOCK_SIZE];
2819 BOOL success;
2820 ULONG depotBlockOffset;
2821 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2822 ULONG nextBlockIndex = BLOCK_SPECIAL;
2823 int depotIndex = 0;
2824 ULONG freeBlock = BLOCK_UNUSED;
2826 depotIndex = This->prevFreeBlock / blocksPerDepot;
2827 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2830 * Scan the entire big block depot until we find a block marked free
2832 while (nextBlockIndex != BLOCK_UNUSED)
2834 if (depotIndex < COUNT_BBDEPOTINHEADER)
2836 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2839 * Grow the primary depot.
2841 if (depotBlockIndexPos == BLOCK_UNUSED)
2843 depotBlockIndexPos = depotIndex*blocksPerDepot;
2846 * Add a block depot.
2848 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2849 This->bigBlockDepotCount++;
2850 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2853 * Flag it as a block depot.
2855 StorageImpl_SetNextBlockInChain(This,
2856 depotBlockIndexPos,
2857 BLOCK_SPECIAL);
2859 /* Save new header information.
2861 StorageImpl_SaveFileHeader(This);
2864 else
2866 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2868 if (depotBlockIndexPos == BLOCK_UNUSED)
2871 * Grow the extended depot.
2873 ULONG extIndex = BLOCK_UNUSED;
2874 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2875 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2877 if (extBlockOffset == 0)
2879 /* We need an extended block.
2881 extIndex = Storage32Impl_AddExtBlockDepot(This);
2882 This->extBigBlockDepotCount++;
2883 depotBlockIndexPos = extIndex + 1;
2885 else
2886 depotBlockIndexPos = depotIndex * blocksPerDepot;
2889 * Add a block depot and mark it in the extended block.
2891 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2892 This->bigBlockDepotCount++;
2893 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2895 /* Flag the block depot.
2897 StorageImpl_SetNextBlockInChain(This,
2898 depotBlockIndexPos,
2899 BLOCK_SPECIAL);
2901 /* If necessary, flag the extended depot block.
2903 if (extIndex != BLOCK_UNUSED)
2904 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2906 /* Save header information.
2908 StorageImpl_SaveFileHeader(This);
2912 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2914 if (success)
2916 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2917 ( nextBlockIndex != BLOCK_UNUSED))
2919 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2921 if (nextBlockIndex == BLOCK_UNUSED)
2923 freeBlock = (depotIndex * blocksPerDepot) +
2924 (depotBlockOffset/sizeof(ULONG));
2927 depotBlockOffset += sizeof(ULONG);
2931 depotIndex++;
2932 depotBlockOffset = 0;
2936 * make sure that the block physically exists before using it
2938 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2940 This->prevFreeBlock = freeBlock;
2942 return freeBlock;
2945 /******************************************************************************
2946 * Storage32Impl_AddBlockDepot
2948 * This will create a depot block, essentially it is a block initialized
2949 * to BLOCK_UNUSEDs.
2951 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2953 BYTE blockBuffer[BIG_BLOCK_SIZE];
2956 * Initialize blocks as free
2958 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2959 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2962 /******************************************************************************
2963 * Storage32Impl_GetExtDepotBlock
2965 * Returns the index of the block that corresponds to the specified depot
2966 * index. This method is only for depot indexes equal or greater than
2967 * COUNT_BBDEPOTINHEADER.
2969 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2971 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2972 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2973 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2974 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2975 ULONG blockIndex = BLOCK_UNUSED;
2976 ULONG extBlockIndex = This->extBigBlockDepotStart;
2978 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2980 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2981 return BLOCK_UNUSED;
2983 while (extBlockCount > 0)
2985 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2986 extBlockCount--;
2989 if (extBlockIndex != BLOCK_UNUSED)
2990 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2991 extBlockOffset * sizeof(ULONG), &blockIndex);
2993 return blockIndex;
2996 /******************************************************************************
2997 * Storage32Impl_SetExtDepotBlock
2999 * Associates the specified block index to the specified depot index.
3000 * This method is only for depot indexes equal or greater than
3001 * COUNT_BBDEPOTINHEADER.
3003 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3005 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3006 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3007 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3008 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3009 ULONG extBlockIndex = This->extBigBlockDepotStart;
3011 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3013 while (extBlockCount > 0)
3015 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3016 extBlockCount--;
3019 if (extBlockIndex != BLOCK_UNUSED)
3021 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3022 extBlockOffset * sizeof(ULONG),
3023 blockIndex);
3027 /******************************************************************************
3028 * Storage32Impl_AddExtBlockDepot
3030 * Creates an extended depot block.
3032 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3034 ULONG numExtBlocks = This->extBigBlockDepotCount;
3035 ULONG nextExtBlock = This->extBigBlockDepotStart;
3036 BYTE depotBuffer[BIG_BLOCK_SIZE];
3037 ULONG index = BLOCK_UNUSED;
3038 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3039 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3040 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3042 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3043 blocksPerDepotBlock;
3045 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3048 * The first extended block.
3050 This->extBigBlockDepotStart = index;
3052 else
3054 unsigned int i;
3056 * Follow the chain to the last one.
3058 for (i = 0; i < (numExtBlocks - 1); i++)
3060 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3064 * Add the new extended block to the chain.
3066 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3067 index);
3071 * Initialize this block.
3073 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3074 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3076 return index;
3079 /******************************************************************************
3080 * Storage32Impl_FreeBigBlock
3082 * This method will flag the specified block as free in the big block depot.
3084 static void StorageImpl_FreeBigBlock(
3085 StorageImpl* This,
3086 ULONG blockIndex)
3088 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3090 if (blockIndex < This->prevFreeBlock)
3091 This->prevFreeBlock = blockIndex;
3094 /************************************************************************
3095 * Storage32Impl_GetNextBlockInChain
3097 * This method will retrieve the block index of the next big block in
3098 * in the chain.
3100 * Params: This - Pointer to the Storage object.
3101 * blockIndex - Index of the block to retrieve the chain
3102 * for.
3103 * nextBlockIndex - receives the return value.
3105 * Returns: This method returns the index of the next block in the chain.
3106 * It will return the constants:
3107 * BLOCK_SPECIAL - If the block given was not part of a
3108 * chain.
3109 * BLOCK_END_OF_CHAIN - If the block given was the last in
3110 * a chain.
3111 * BLOCK_UNUSED - If the block given was not past of a chain
3112 * and is available.
3113 * BLOCK_EXTBBDEPOT - This block is part of the extended
3114 * big block depot.
3116 * See Windows documentation for more details on IStorage methods.
3118 static HRESULT StorageImpl_GetNextBlockInChain(
3119 StorageImpl* This,
3120 ULONG blockIndex,
3121 ULONG* nextBlockIndex)
3123 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3124 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3125 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3126 BYTE depotBuffer[BIG_BLOCK_SIZE];
3127 BOOL success;
3128 ULONG depotBlockIndexPos;
3129 int index;
3131 *nextBlockIndex = BLOCK_SPECIAL;
3133 if(depotBlockCount >= This->bigBlockDepotCount)
3135 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3136 This->bigBlockDepotCount);
3137 return STG_E_READFAULT;
3141 * Cache the currently accessed depot block.
3143 if (depotBlockCount != This->indexBlockDepotCached)
3145 This->indexBlockDepotCached = depotBlockCount;
3147 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3149 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3151 else
3154 * We have to look in the extended depot.
3156 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3159 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3161 if (!success)
3162 return STG_E_READFAULT;
3164 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
3166 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3167 This->blockDepotCached[index] = *nextBlockIndex;
3171 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3173 return S_OK;
3176 /******************************************************************************
3177 * Storage32Impl_GetNextExtendedBlock
3179 * Given an extended block this method will return the next extended block.
3181 * NOTES:
3182 * The last ULONG of an extended block is the block index of the next
3183 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3184 * depot.
3186 * Return values:
3187 * - The index of the next extended block
3188 * - BLOCK_UNUSED: there is no next extended block.
3189 * - Any other return values denotes failure.
3191 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3193 ULONG nextBlockIndex = BLOCK_SPECIAL;
3194 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3196 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3197 &nextBlockIndex);
3199 return nextBlockIndex;
3202 /******************************************************************************
3203 * Storage32Impl_SetNextBlockInChain
3205 * This method will write the index of the specified block's next block
3206 * in the big block depot.
3208 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3209 * do the following
3211 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3212 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3213 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3216 static void StorageImpl_SetNextBlockInChain(
3217 StorageImpl* This,
3218 ULONG blockIndex,
3219 ULONG nextBlock)
3221 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3222 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3223 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3224 ULONG depotBlockIndexPos;
3226 assert(depotBlockCount < This->bigBlockDepotCount);
3227 assert(blockIndex != nextBlock);
3229 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3231 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3233 else
3236 * We have to look in the extended depot.
3238 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3241 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3242 nextBlock);
3244 * Update the cached block depot, if necessary.
3246 if (depotBlockCount == This->indexBlockDepotCached)
3248 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3252 /******************************************************************************
3253 * Storage32Impl_LoadFileHeader
3255 * This method will read in the file header, i.e. big block index -1.
3257 static HRESULT StorageImpl_LoadFileHeader(
3258 StorageImpl* This)
3260 HRESULT hr = STG_E_FILENOTFOUND;
3261 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3262 BOOL success;
3263 int index;
3265 TRACE("\n");
3267 * Get a pointer to the big block of data containing the header.
3269 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3272 * Extract the information from the header.
3274 if (success)
3277 * Check for the "magic number" signature and return an error if it is not
3278 * found.
3280 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3282 return STG_E_OLDFORMAT;
3285 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3287 return STG_E_INVALIDHEADER;
3290 StorageUtl_ReadWord(
3291 headerBigBlock,
3292 OFFSET_BIGBLOCKSIZEBITS,
3293 &This->bigBlockSizeBits);
3295 StorageUtl_ReadWord(
3296 headerBigBlock,
3297 OFFSET_SMALLBLOCKSIZEBITS,
3298 &This->smallBlockSizeBits);
3300 StorageUtl_ReadDWord(
3301 headerBigBlock,
3302 OFFSET_BBDEPOTCOUNT,
3303 &This->bigBlockDepotCount);
3305 StorageUtl_ReadDWord(
3306 headerBigBlock,
3307 OFFSET_ROOTSTARTBLOCK,
3308 &This->rootStartBlock);
3310 StorageUtl_ReadDWord(
3311 headerBigBlock,
3312 OFFSET_SBDEPOTSTART,
3313 &This->smallBlockDepotStart);
3315 StorageUtl_ReadDWord(
3316 headerBigBlock,
3317 OFFSET_EXTBBDEPOTSTART,
3318 &This->extBigBlockDepotStart);
3320 StorageUtl_ReadDWord(
3321 headerBigBlock,
3322 OFFSET_EXTBBDEPOTCOUNT,
3323 &This->extBigBlockDepotCount);
3325 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3327 StorageUtl_ReadDWord(
3328 headerBigBlock,
3329 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3330 &(This->bigBlockDepotStart[index]));
3334 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3336 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3337 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3340 * Right now, the code is making some assumptions about the size of the
3341 * blocks, just make sure they are what we're expecting.
3343 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3344 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3346 WARN("Broken OLE storage file\n");
3347 hr = STG_E_INVALIDHEADER;
3349 else
3350 hr = S_OK;
3353 return hr;
3356 /******************************************************************************
3357 * Storage32Impl_SaveFileHeader
3359 * This method will save to the file the header, i.e. big block -1.
3361 static void StorageImpl_SaveFileHeader(
3362 StorageImpl* This)
3364 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3365 int index;
3366 BOOL success;
3369 * Get a pointer to the big block of data containing the header.
3371 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3374 * If the block read failed, the file is probably new.
3376 if (!success)
3379 * Initialize for all unknown fields.
3381 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3384 * Initialize the magic number.
3386 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3389 * And a bunch of things we don't know what they mean
3391 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3392 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3393 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3394 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3398 * Write the information to the header.
3400 StorageUtl_WriteWord(
3401 headerBigBlock,
3402 OFFSET_BIGBLOCKSIZEBITS,
3403 This->bigBlockSizeBits);
3405 StorageUtl_WriteWord(
3406 headerBigBlock,
3407 OFFSET_SMALLBLOCKSIZEBITS,
3408 This->smallBlockSizeBits);
3410 StorageUtl_WriteDWord(
3411 headerBigBlock,
3412 OFFSET_BBDEPOTCOUNT,
3413 This->bigBlockDepotCount);
3415 StorageUtl_WriteDWord(
3416 headerBigBlock,
3417 OFFSET_ROOTSTARTBLOCK,
3418 This->rootStartBlock);
3420 StorageUtl_WriteDWord(
3421 headerBigBlock,
3422 OFFSET_SBDEPOTSTART,
3423 This->smallBlockDepotStart);
3425 StorageUtl_WriteDWord(
3426 headerBigBlock,
3427 OFFSET_SBDEPOTCOUNT,
3428 This->smallBlockDepotChain ?
3429 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3431 StorageUtl_WriteDWord(
3432 headerBigBlock,
3433 OFFSET_EXTBBDEPOTSTART,
3434 This->extBigBlockDepotStart);
3436 StorageUtl_WriteDWord(
3437 headerBigBlock,
3438 OFFSET_EXTBBDEPOTCOUNT,
3439 This->extBigBlockDepotCount);
3441 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3443 StorageUtl_WriteDWord(
3444 headerBigBlock,
3445 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3446 (This->bigBlockDepotStart[index]));
3450 * Write the big block back to the file.
3452 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3455 /******************************************************************************
3456 * StorageImpl_ReadRawDirEntry
3458 * This method will read the raw data from a directory entry in the file.
3460 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3462 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3464 ULARGE_INTEGER offset;
3465 HRESULT hr;
3466 ULONG bytesRead;
3468 offset.u.HighPart = 0;
3469 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3471 hr = BlockChainStream_ReadAt(
3472 This->rootBlockChain,
3473 offset,
3474 RAW_DIRENTRY_SIZE,
3475 buffer,
3476 &bytesRead);
3478 return hr;
3481 /******************************************************************************
3482 * StorageImpl_WriteRawDirEntry
3484 * This method will write the raw data from a directory entry in the file.
3486 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3488 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3490 ULARGE_INTEGER offset;
3491 HRESULT hr;
3492 ULONG bytesRead;
3494 offset.u.HighPart = 0;
3495 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3497 hr = BlockChainStream_WriteAt(
3498 This->rootBlockChain,
3499 offset,
3500 RAW_DIRENTRY_SIZE,
3501 buffer,
3502 &bytesRead);
3504 return hr;
3507 /******************************************************************************
3508 * UpdateRawDirEntry
3510 * Update raw directory entry data from the fields in newData.
3512 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3514 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3516 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3518 memcpy(
3519 buffer + OFFSET_PS_NAME,
3520 newData->name,
3521 DIRENTRY_NAME_BUFFER_LEN );
3523 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3525 StorageUtl_WriteWord(
3526 buffer,
3527 OFFSET_PS_NAMELENGTH,
3528 newData->sizeOfNameString);
3530 StorageUtl_WriteDWord(
3531 buffer,
3532 OFFSET_PS_LEFTCHILD,
3533 newData->leftChild);
3535 StorageUtl_WriteDWord(
3536 buffer,
3537 OFFSET_PS_RIGHTCHILD,
3538 newData->rightChild);
3540 StorageUtl_WriteDWord(
3541 buffer,
3542 OFFSET_PS_DIRROOT,
3543 newData->dirRootEntry);
3545 StorageUtl_WriteGUID(
3546 buffer,
3547 OFFSET_PS_GUID,
3548 &newData->clsid);
3550 StorageUtl_WriteDWord(
3551 buffer,
3552 OFFSET_PS_CTIMELOW,
3553 newData->ctime.dwLowDateTime);
3555 StorageUtl_WriteDWord(
3556 buffer,
3557 OFFSET_PS_CTIMEHIGH,
3558 newData->ctime.dwHighDateTime);
3560 StorageUtl_WriteDWord(
3561 buffer,
3562 OFFSET_PS_MTIMELOW,
3563 newData->mtime.dwLowDateTime);
3565 StorageUtl_WriteDWord(
3566 buffer,
3567 OFFSET_PS_MTIMEHIGH,
3568 newData->ctime.dwHighDateTime);
3570 StorageUtl_WriteDWord(
3571 buffer,
3572 OFFSET_PS_STARTBLOCK,
3573 newData->startingBlock);
3575 StorageUtl_WriteDWord(
3576 buffer,
3577 OFFSET_PS_SIZE,
3578 newData->size.u.LowPart);
3581 /******************************************************************************
3582 * Storage32Impl_ReadDirEntry
3584 * This method will read the specified directory entry.
3586 HRESULT StorageImpl_ReadDirEntry(
3587 StorageImpl* This,
3588 DirRef index,
3589 DirEntry* buffer)
3591 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3592 HRESULT readRes;
3594 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3596 if (SUCCEEDED(readRes))
3598 memset(buffer->name, 0, sizeof(buffer->name));
3599 memcpy(
3600 buffer->name,
3601 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3602 DIRENTRY_NAME_BUFFER_LEN );
3603 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3605 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3607 StorageUtl_ReadWord(
3608 currentEntry,
3609 OFFSET_PS_NAMELENGTH,
3610 &buffer->sizeOfNameString);
3612 StorageUtl_ReadDWord(
3613 currentEntry,
3614 OFFSET_PS_LEFTCHILD,
3615 &buffer->leftChild);
3617 StorageUtl_ReadDWord(
3618 currentEntry,
3619 OFFSET_PS_RIGHTCHILD,
3620 &buffer->rightChild);
3622 StorageUtl_ReadDWord(
3623 currentEntry,
3624 OFFSET_PS_DIRROOT,
3625 &buffer->dirRootEntry);
3627 StorageUtl_ReadGUID(
3628 currentEntry,
3629 OFFSET_PS_GUID,
3630 &buffer->clsid);
3632 StorageUtl_ReadDWord(
3633 currentEntry,
3634 OFFSET_PS_CTIMELOW,
3635 &buffer->ctime.dwLowDateTime);
3637 StorageUtl_ReadDWord(
3638 currentEntry,
3639 OFFSET_PS_CTIMEHIGH,
3640 &buffer->ctime.dwHighDateTime);
3642 StorageUtl_ReadDWord(
3643 currentEntry,
3644 OFFSET_PS_MTIMELOW,
3645 &buffer->mtime.dwLowDateTime);
3647 StorageUtl_ReadDWord(
3648 currentEntry,
3649 OFFSET_PS_MTIMEHIGH,
3650 &buffer->mtime.dwHighDateTime);
3652 StorageUtl_ReadDWord(
3653 currentEntry,
3654 OFFSET_PS_STARTBLOCK,
3655 &buffer->startingBlock);
3657 StorageUtl_ReadDWord(
3658 currentEntry,
3659 OFFSET_PS_SIZE,
3660 &buffer->size.u.LowPart);
3662 buffer->size.u.HighPart = 0;
3665 return readRes;
3668 /*********************************************************************
3669 * Write the specified directory entry to the file
3671 HRESULT StorageImpl_WriteDirEntry(
3672 StorageImpl* This,
3673 DirRef index,
3674 const DirEntry* buffer)
3676 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3677 HRESULT writeRes;
3679 UpdateRawDirEntry(currentEntry, buffer);
3681 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3682 return writeRes;
3685 static BOOL StorageImpl_ReadBigBlock(
3686 StorageImpl* This,
3687 ULONG blockIndex,
3688 void* buffer)
3690 ULARGE_INTEGER ulOffset;
3691 DWORD read;
3693 ulOffset.u.HighPart = 0;
3694 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3696 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3697 return (read == This->bigBlockSize);
3700 static BOOL StorageImpl_ReadDWordFromBigBlock(
3701 StorageImpl* This,
3702 ULONG blockIndex,
3703 ULONG offset,
3704 DWORD* value)
3706 ULARGE_INTEGER ulOffset;
3707 DWORD read;
3708 DWORD tmp;
3710 ulOffset.u.HighPart = 0;
3711 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3712 ulOffset.u.LowPart += offset;
3714 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3715 *value = lendian32toh(tmp);
3716 return (read == sizeof(DWORD));
3719 static BOOL StorageImpl_WriteBigBlock(
3720 StorageImpl* This,
3721 ULONG blockIndex,
3722 const void* buffer)
3724 ULARGE_INTEGER ulOffset;
3725 DWORD wrote;
3727 ulOffset.u.HighPart = 0;
3728 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3730 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3731 return (wrote == This->bigBlockSize);
3734 static BOOL StorageImpl_WriteDWordToBigBlock(
3735 StorageImpl* This,
3736 ULONG blockIndex,
3737 ULONG offset,
3738 DWORD value)
3740 ULARGE_INTEGER ulOffset;
3741 DWORD wrote;
3743 ulOffset.u.HighPart = 0;
3744 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3745 ulOffset.u.LowPart += offset;
3747 value = htole32(value);
3748 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3749 return (wrote == sizeof(DWORD));
3752 /******************************************************************************
3753 * Storage32Impl_SmallBlocksToBigBlocks
3755 * This method will convert a small block chain to a big block chain.
3756 * The small block chain will be destroyed.
3758 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3759 StorageImpl* This,
3760 SmallBlockChainStream** ppsbChain)
3762 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3763 ULARGE_INTEGER size, offset;
3764 ULONG cbRead, cbWritten;
3765 ULARGE_INTEGER cbTotalRead;
3766 DirRef streamEntryRef;
3767 HRESULT resWrite = S_OK;
3768 HRESULT resRead;
3769 DirEntry streamEntry;
3770 BYTE *buffer;
3771 BlockChainStream *bbTempChain = NULL;
3772 BlockChainStream *bigBlockChain = NULL;
3775 * Create a temporary big block chain that doesn't have
3776 * an associated directory entry. This temporary chain will be
3777 * used to copy data from small blocks to big blocks.
3779 bbTempChain = BlockChainStream_Construct(This,
3780 &bbHeadOfChain,
3781 DIRENTRY_NULL);
3782 if(!bbTempChain) return NULL;
3784 * Grow the big block chain.
3786 size = SmallBlockChainStream_GetSize(*ppsbChain);
3787 BlockChainStream_SetSize(bbTempChain, size);
3790 * Copy the contents of the small block chain to the big block chain
3791 * by small block size increments.
3793 offset.u.LowPart = 0;
3794 offset.u.HighPart = 0;
3795 cbTotalRead.QuadPart = 0;
3797 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3800 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3801 offset,
3802 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3803 buffer,
3804 &cbRead);
3805 if (FAILED(resRead))
3806 break;
3808 if (cbRead > 0)
3810 cbTotalRead.QuadPart += cbRead;
3812 resWrite = BlockChainStream_WriteAt(bbTempChain,
3813 offset,
3814 cbRead,
3815 buffer,
3816 &cbWritten);
3818 if (FAILED(resWrite))
3819 break;
3821 offset.u.LowPart += cbRead;
3823 } while (cbTotalRead.QuadPart < size.QuadPart);
3824 HeapFree(GetProcessHeap(),0,buffer);
3826 size.u.HighPart = 0;
3827 size.u.LowPart = 0;
3829 if (FAILED(resRead) || FAILED(resWrite))
3831 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3832 BlockChainStream_SetSize(bbTempChain, size);
3833 BlockChainStream_Destroy(bbTempChain);
3834 return NULL;
3838 * Destroy the small block chain.
3840 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3841 SmallBlockChainStream_SetSize(*ppsbChain, size);
3842 SmallBlockChainStream_Destroy(*ppsbChain);
3843 *ppsbChain = 0;
3846 * Change the directory entry. This chain is now a big block chain
3847 * and it doesn't reside in the small blocks chain anymore.
3849 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3851 streamEntry.startingBlock = bbHeadOfChain;
3853 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3856 * Destroy the temporary entryless big block chain.
3857 * Create a new big block chain associated with this entry.
3859 BlockChainStream_Destroy(bbTempChain);
3860 bigBlockChain = BlockChainStream_Construct(This,
3861 NULL,
3862 streamEntryRef);
3864 return bigBlockChain;
3867 /******************************************************************************
3868 * Storage32Impl_BigBlocksToSmallBlocks
3870 * This method will convert a big block chain to a small block chain.
3871 * The big block chain will be destroyed on success.
3873 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3874 StorageImpl* This,
3875 BlockChainStream** ppbbChain)
3877 ULARGE_INTEGER size, offset, cbTotalRead;
3878 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3879 DirRef streamEntryRef;
3880 HRESULT resWrite = S_OK, resRead;
3881 DirEntry streamEntry;
3882 BYTE* buffer;
3883 SmallBlockChainStream* sbTempChain;
3885 TRACE("%p %p\n", This, ppbbChain);
3887 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3888 DIRENTRY_NULL);
3890 if(!sbTempChain)
3891 return NULL;
3893 size = BlockChainStream_GetSize(*ppbbChain);
3894 SmallBlockChainStream_SetSize(sbTempChain, size);
3896 offset.u.HighPart = 0;
3897 offset.u.LowPart = 0;
3898 cbTotalRead.QuadPart = 0;
3899 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3902 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3903 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3904 buffer, &cbRead);
3906 if(FAILED(resRead))
3907 break;
3909 if(cbRead > 0)
3911 cbTotalRead.QuadPart += cbRead;
3913 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3914 cbRead, buffer, &cbWritten);
3916 if(FAILED(resWrite))
3917 break;
3919 offset.u.LowPart += cbRead;
3921 }while(cbTotalRead.QuadPart < size.QuadPart);
3922 HeapFree(GetProcessHeap(), 0, buffer);
3924 size.u.HighPart = 0;
3925 size.u.LowPart = 0;
3927 if(FAILED(resRead) || FAILED(resWrite))
3929 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3930 SmallBlockChainStream_SetSize(sbTempChain, size);
3931 SmallBlockChainStream_Destroy(sbTempChain);
3932 return NULL;
3935 /* destroy the original big block chain */
3936 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3937 BlockChainStream_SetSize(*ppbbChain, size);
3938 BlockChainStream_Destroy(*ppbbChain);
3939 *ppbbChain = NULL;
3941 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3942 streamEntry.startingBlock = sbHeadOfChain;
3943 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3945 SmallBlockChainStream_Destroy(sbTempChain);
3946 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3949 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
3951 HRESULT hr;
3952 DirEntry parentData, snapshotData;
3954 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
3955 0, (IStorage**)snapshot);
3957 if (SUCCEEDED(hr))
3959 hr = StorageBaseImpl_ReadDirEntry(original,
3960 original->storageDirEntry, &parentData);
3962 if (SUCCEEDED(hr))
3963 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
3964 (*snapshot)->storageDirEntry, &snapshotData);
3966 if (SUCCEEDED(hr))
3968 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
3969 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
3970 snapshotData.stgType = parentData.stgType;
3971 snapshotData.clsid = parentData.clsid;
3972 snapshotData.ctime = parentData.ctime;
3973 snapshotData.mtime = parentData.mtime;
3974 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
3975 (*snapshot)->storageDirEntry, &snapshotData);
3978 if (SUCCEEDED(hr))
3979 hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
3980 (IStorage*)(*snapshot));
3982 if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
3985 return hr;
3988 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
3989 IStorage* iface,
3990 DWORD grfCommitFlags) /* [in] */
3992 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
3993 HRESULT hr;
3994 DirEntry data, tempStorageData, snapshotRootData;
3995 DirRef tempStorageEntry, oldDirRoot;
3996 StorageInternalImpl *tempStorage;
3998 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4000 /* Cannot commit a read-only transacted storage */
4001 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4002 return STG_E_ACCESSDENIED;
4004 /* To prevent data loss, we create the new structure in the file before we
4005 * delete the old one, so that in case of errors the old data is intact. We
4006 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4007 * needed in the rare situation where we have just enough free disk space to
4008 * overwrite the existing data. */
4010 /* Create an orphaned storage in the parent for the new directory structure. */
4011 memset(&data, 0, sizeof(data));
4012 data.name[0] = 'D';
4013 data.sizeOfNameString = 1;
4014 data.stgType = STGTY_STORAGE;
4015 data.leftChild = DIRENTRY_NULL;
4016 data.rightChild = DIRENTRY_NULL;
4017 data.dirRootEntry = DIRENTRY_NULL;
4018 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
4020 if (FAILED(hr)) return hr;
4022 tempStorage = StorageInternalImpl_Construct(This->transactedParent,
4023 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
4024 if (tempStorage)
4026 hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
4027 (IStorage*)tempStorage);
4029 list_init(&tempStorage->ParentListEntry);
4031 IStorage_Release((IStorage*) tempStorage);
4033 else
4034 hr = E_OUTOFMEMORY;
4036 if (FAILED(hr))
4038 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4039 return hr;
4042 /* Update the storage to use the new data in one step. */
4043 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4044 This->transactedParent->storageDirEntry, &data);
4046 if (SUCCEEDED(hr))
4048 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4049 tempStorageEntry, &tempStorageData);
4052 if (SUCCEEDED(hr))
4054 hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4055 This->snapshot->storageDirEntry, &snapshotRootData);
4058 if (SUCCEEDED(hr))
4060 oldDirRoot = data.dirRootEntry;
4061 data.dirRootEntry = tempStorageData.dirRootEntry;
4062 data.clsid = snapshotRootData.clsid;
4063 data.ctime = snapshotRootData.ctime;
4064 data.mtime = snapshotRootData.mtime;
4066 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4067 This->transactedParent->storageDirEntry, &data);
4070 if (SUCCEEDED(hr))
4072 /* Destroy the old now-orphaned data. */
4073 DestroyReachableEntries(This->transactedParent, oldDirRoot);
4074 StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4076 else
4078 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4081 return hr;
4084 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4085 IStorage* iface)
4087 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4088 StorageBaseImpl *newSnapshot;
4089 HRESULT hr;
4091 TRACE("(%p)\n", iface);
4093 /* Create a new copy of the parent data. */
4094 hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4095 if (FAILED(hr)) return hr;
4097 /* Destroy the open objects. */
4098 StorageBaseImpl_DeleteAll(&This->base);
4100 /* Replace our current snapshot. */
4101 IStorage_Release((IStorage*)This->snapshot);
4102 This->snapshot = newSnapshot;
4104 return S_OK;
4107 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4109 if (!This->reverted)
4111 TRACE("Storage invalidated (stg=%p)\n", This);
4113 This->reverted = 1;
4115 StorageBaseImpl_DeleteAll(This);
4119 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4121 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4123 TransactedSnapshotImpl_Invalidate(iface);
4125 IStorage_Release((IStorage*)This->transactedParent);
4127 IStorage_Release((IStorage*)This->snapshot);
4129 HeapFree(GetProcessHeap(), 0, This);
4132 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4133 const DirEntry *newData, DirRef *index)
4135 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4137 return StorageBaseImpl_CreateDirEntry(This->snapshot,
4138 newData, index);
4141 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4142 DirRef index, const DirEntry *data)
4144 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4146 return StorageBaseImpl_WriteDirEntry(This->snapshot,
4147 index, data);
4150 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4151 DirRef index, DirEntry *data)
4153 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4155 return StorageBaseImpl_ReadDirEntry(This->snapshot,
4156 index, data);
4159 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4160 DirRef index)
4162 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4164 return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4165 index);
4168 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4169 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4171 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4173 return StorageBaseImpl_StreamReadAt(This->snapshot,
4174 index, offset, size, buffer, bytesRead);
4177 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4178 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4180 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4182 return StorageBaseImpl_StreamWriteAt(This->snapshot,
4183 index, offset, size, buffer, bytesWritten);
4186 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4187 DirRef index, ULARGE_INTEGER newsize)
4189 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4191 return StorageBaseImpl_StreamSetSize(This->snapshot,
4192 index, newsize);
4195 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4197 StorageBaseImpl_QueryInterface,
4198 StorageBaseImpl_AddRef,
4199 StorageBaseImpl_Release,
4200 StorageBaseImpl_CreateStream,
4201 StorageBaseImpl_OpenStream,
4202 StorageBaseImpl_CreateStorage,
4203 StorageBaseImpl_OpenStorage,
4204 StorageBaseImpl_CopyTo,
4205 StorageBaseImpl_MoveElementTo,
4206 TransactedSnapshotImpl_Commit,
4207 TransactedSnapshotImpl_Revert,
4208 StorageBaseImpl_EnumElements,
4209 StorageBaseImpl_DestroyElement,
4210 StorageBaseImpl_RenameElement,
4211 StorageBaseImpl_SetElementTimes,
4212 StorageBaseImpl_SetClass,
4213 StorageBaseImpl_SetStateBits,
4214 StorageBaseImpl_Stat
4217 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4219 TransactedSnapshotImpl_Destroy,
4220 TransactedSnapshotImpl_Invalidate,
4221 TransactedSnapshotImpl_CreateDirEntry,
4222 TransactedSnapshotImpl_WriteDirEntry,
4223 TransactedSnapshotImpl_ReadDirEntry,
4224 TransactedSnapshotImpl_DestroyDirEntry,
4225 TransactedSnapshotImpl_StreamReadAt,
4226 TransactedSnapshotImpl_StreamWriteAt,
4227 TransactedSnapshotImpl_StreamSetSize
4230 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4231 TransactedSnapshotImpl** result)
4233 HRESULT hr;
4235 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4236 if (*result)
4238 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4240 /* This is OK because the property set storage functions use the IStorage functions. */
4241 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4243 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4245 list_init(&(*result)->base.strmHead);
4247 list_init(&(*result)->base.storageHead);
4249 (*result)->base.ref = 1;
4251 (*result)->base.openFlags = parentStorage->openFlags;
4253 (*result)->base.filename = parentStorage->filename;
4255 /* Create a new temporary storage to act as the snapshot */
4256 hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4258 if (SUCCEEDED(hr))
4260 (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4262 /* parentStorage already has 1 reference, which we take over here. */
4263 (*result)->transactedParent = parentStorage;
4265 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4268 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4270 return hr;
4272 else
4273 return E_OUTOFMEMORY;
4276 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4277 StorageBaseImpl** result)
4279 static int fixme=0;
4281 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4283 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4286 return TransactedSnapshotImpl_Construct(parentStorage,
4287 (TransactedSnapshotImpl**)result);
4290 static HRESULT Storage_Construct(
4291 HANDLE hFile,
4292 LPCOLESTR pwcsName,
4293 ILockBytes* pLkbyt,
4294 DWORD openFlags,
4295 BOOL fileBased,
4296 BOOL create,
4297 StorageBaseImpl** result)
4299 StorageImpl *newStorage;
4300 StorageBaseImpl *newTransactedStorage;
4301 HRESULT hr;
4303 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, &newStorage);
4304 if (FAILED(hr)) goto end;
4306 if (openFlags & STGM_TRANSACTED)
4308 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4309 if (FAILED(hr))
4310 IStorage_Release((IStorage*)newStorage);
4311 else
4312 *result = newTransactedStorage;
4314 else
4315 *result = &newStorage->base;
4317 end:
4318 return hr;
4321 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4323 StorageInternalImpl* This = (StorageInternalImpl*) base;
4325 if (!This->base.reverted)
4327 TRACE("Storage invalidated (stg=%p)\n", This);
4329 This->base.reverted = 1;
4331 This->parentStorage = NULL;
4333 StorageBaseImpl_DeleteAll(&This->base);
4335 list_remove(&This->ParentListEntry);
4339 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4341 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4343 StorageInternalImpl_Invalidate(&This->base);
4345 HeapFree(GetProcessHeap(), 0, This);
4348 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4349 const DirEntry *newData, DirRef *index)
4351 StorageInternalImpl* This = (StorageInternalImpl*) base;
4353 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4354 newData, index);
4357 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4358 DirRef index, const DirEntry *data)
4360 StorageInternalImpl* This = (StorageInternalImpl*) base;
4362 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4363 index, data);
4366 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4367 DirRef index, DirEntry *data)
4369 StorageInternalImpl* This = (StorageInternalImpl*) base;
4371 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4372 index, data);
4375 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4376 DirRef index)
4378 StorageInternalImpl* This = (StorageInternalImpl*) base;
4380 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4381 index);
4384 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4385 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4387 StorageInternalImpl* This = (StorageInternalImpl*) base;
4389 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4390 index, offset, size, buffer, bytesRead);
4393 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4394 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4396 StorageInternalImpl* This = (StorageInternalImpl*) base;
4398 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4399 index, offset, size, buffer, bytesWritten);
4402 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4403 DirRef index, ULARGE_INTEGER newsize)
4405 StorageInternalImpl* This = (StorageInternalImpl*) base;
4407 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4408 index, newsize);
4411 /******************************************************************************
4413 ** Storage32InternalImpl_Commit
4416 static HRESULT WINAPI StorageInternalImpl_Commit(
4417 IStorage* iface,
4418 DWORD grfCommitFlags) /* [in] */
4420 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4421 return S_OK;
4424 /******************************************************************************
4426 ** Storage32InternalImpl_Revert
4429 static HRESULT WINAPI StorageInternalImpl_Revert(
4430 IStorage* iface)
4432 FIXME("(%p): stub\n", iface);
4433 return S_OK;
4436 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4438 IStorage_Release((IStorage*)This->parentStorage);
4439 HeapFree(GetProcessHeap(), 0, This);
4442 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4443 IEnumSTATSTG* iface,
4444 REFIID riid,
4445 void** ppvObject)
4447 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4449 if (ppvObject==0)
4450 return E_INVALIDARG;
4452 *ppvObject = 0;
4454 if (IsEqualGUID(&IID_IUnknown, riid) ||
4455 IsEqualGUID(&IID_IEnumSTATSTG, riid))
4457 *ppvObject = This;
4458 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4459 return S_OK;
4462 return E_NOINTERFACE;
4465 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
4466 IEnumSTATSTG* iface)
4468 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4469 return InterlockedIncrement(&This->ref);
4472 static ULONG WINAPI IEnumSTATSTGImpl_Release(
4473 IEnumSTATSTG* iface)
4475 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4477 ULONG newRef;
4479 newRef = InterlockedDecrement(&This->ref);
4481 if (newRef==0)
4483 IEnumSTATSTGImpl_Destroy(This);
4486 return newRef;
4489 static HRESULT IEnumSTATSTGImpl_GetNextRef(
4490 IEnumSTATSTGImpl* This,
4491 DirRef *ref)
4493 DirRef result = DIRENTRY_NULL;
4494 DirRef searchNode;
4495 DirEntry entry;
4496 HRESULT hr;
4497 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
4499 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
4500 This->parentStorage->storageDirEntry, &entry);
4501 searchNode = entry.dirRootEntry;
4503 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
4505 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
4507 if (SUCCEEDED(hr))
4509 LONG diff = entryNameCmp( entry.name, This->name);
4511 if (diff <= 0)
4513 searchNode = entry.rightChild;
4515 else
4517 result = searchNode;
4518 memcpy(result_name, entry.name, sizeof(result_name));
4519 searchNode = entry.leftChild;
4524 if (SUCCEEDED(hr))
4526 *ref = result;
4527 if (result != DIRENTRY_NULL)
4528 memcpy(This->name, result_name, sizeof(result_name));
4531 return hr;
4534 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4535 IEnumSTATSTG* iface,
4536 ULONG celt,
4537 STATSTG* rgelt,
4538 ULONG* pceltFetched)
4540 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4542 DirEntry currentEntry;
4543 STATSTG* currentReturnStruct = rgelt;
4544 ULONG objectFetched = 0;
4545 DirRef currentSearchNode;
4546 HRESULT hr=S_OK;
4548 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4549 return E_INVALIDARG;
4551 if (This->parentStorage->reverted)
4552 return STG_E_REVERTED;
4555 * To avoid the special case, get another pointer to a ULONG value if
4556 * the caller didn't supply one.
4558 if (pceltFetched==0)
4559 pceltFetched = &objectFetched;
4562 * Start the iteration, we will iterate until we hit the end of the
4563 * linked list or until we hit the number of items to iterate through
4565 *pceltFetched = 0;
4567 while ( *pceltFetched < celt )
4569 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
4571 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4572 break;
4575 * Read the entry from the storage.
4577 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4578 currentSearchNode,
4579 &currentEntry);
4582 * Copy the information to the return buffer.
4584 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4585 currentReturnStruct,
4586 &currentEntry,
4587 STATFLAG_DEFAULT);
4590 * Step to the next item in the iteration
4592 (*pceltFetched)++;
4593 currentReturnStruct++;
4596 if (SUCCEEDED(hr) && *pceltFetched != celt)
4597 hr = S_FALSE;
4599 return hr;
4603 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4604 IEnumSTATSTG* iface,
4605 ULONG celt)
4607 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4609 ULONG objectFetched = 0;
4610 DirRef currentSearchNode;
4611 HRESULT hr=S_OK;
4613 if (This->parentStorage->reverted)
4614 return STG_E_REVERTED;
4616 while ( (objectFetched < celt) )
4618 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
4620 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4621 break;
4623 objectFetched++;
4626 if (SUCCEEDED(hr) && objectFetched != celt)
4627 return S_FALSE;
4629 return hr;
4632 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4633 IEnumSTATSTG* iface)
4635 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4637 if (This->parentStorage->reverted)
4638 return STG_E_REVERTED;
4640 This->name[0] = 0;
4642 return S_OK;
4645 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4646 IEnumSTATSTG* iface,
4647 IEnumSTATSTG** ppenum)
4649 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4651 IEnumSTATSTGImpl* newClone;
4653 if (This->parentStorage->reverted)
4654 return STG_E_REVERTED;
4657 * Perform a sanity check on the parameters.
4659 if (ppenum==0)
4660 return E_INVALIDARG;
4662 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4663 This->storageDirEntry);
4667 * The new clone enumeration must point to the same current node as
4668 * the ole one.
4670 memcpy(newClone->name, This->name, sizeof(newClone->name));
4672 *ppenum = (IEnumSTATSTG*)newClone;
4675 * Don't forget to nail down a reference to the clone before
4676 * returning it.
4678 IEnumSTATSTGImpl_AddRef(*ppenum);
4680 return S_OK;
4684 * Virtual function table for the IEnumSTATSTGImpl class.
4686 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4688 IEnumSTATSTGImpl_QueryInterface,
4689 IEnumSTATSTGImpl_AddRef,
4690 IEnumSTATSTGImpl_Release,
4691 IEnumSTATSTGImpl_Next,
4692 IEnumSTATSTGImpl_Skip,
4693 IEnumSTATSTGImpl_Reset,
4694 IEnumSTATSTGImpl_Clone
4697 /******************************************************************************
4698 ** IEnumSTATSTGImpl implementation
4701 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4702 StorageBaseImpl* parentStorage,
4703 DirRef storageDirEntry)
4705 IEnumSTATSTGImpl* newEnumeration;
4707 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4709 if (newEnumeration!=0)
4712 * Set-up the virtual function table and reference count.
4714 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4715 newEnumeration->ref = 0;
4718 * We want to nail-down the reference to the storage in case the
4719 * enumeration out-lives the storage in the client application.
4721 newEnumeration->parentStorage = parentStorage;
4722 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4724 newEnumeration->storageDirEntry = storageDirEntry;
4727 * Make sure the current node of the iterator is the first one.
4729 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4732 return newEnumeration;
4736 * Virtual function table for the Storage32InternalImpl class.
4738 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4740 StorageBaseImpl_QueryInterface,
4741 StorageBaseImpl_AddRef,
4742 StorageBaseImpl_Release,
4743 StorageBaseImpl_CreateStream,
4744 StorageBaseImpl_OpenStream,
4745 StorageBaseImpl_CreateStorage,
4746 StorageBaseImpl_OpenStorage,
4747 StorageBaseImpl_CopyTo,
4748 StorageBaseImpl_MoveElementTo,
4749 StorageInternalImpl_Commit,
4750 StorageInternalImpl_Revert,
4751 StorageBaseImpl_EnumElements,
4752 StorageBaseImpl_DestroyElement,
4753 StorageBaseImpl_RenameElement,
4754 StorageBaseImpl_SetElementTimes,
4755 StorageBaseImpl_SetClass,
4756 StorageBaseImpl_SetStateBits,
4757 StorageBaseImpl_Stat
4760 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4762 StorageInternalImpl_Destroy,
4763 StorageInternalImpl_Invalidate,
4764 StorageInternalImpl_CreateDirEntry,
4765 StorageInternalImpl_WriteDirEntry,
4766 StorageInternalImpl_ReadDirEntry,
4767 StorageInternalImpl_DestroyDirEntry,
4768 StorageInternalImpl_StreamReadAt,
4769 StorageInternalImpl_StreamWriteAt,
4770 StorageInternalImpl_StreamSetSize
4773 /******************************************************************************
4774 ** Storage32InternalImpl implementation
4777 static StorageInternalImpl* StorageInternalImpl_Construct(
4778 StorageBaseImpl* parentStorage,
4779 DWORD openFlags,
4780 DirRef storageDirEntry)
4782 StorageInternalImpl* newStorage;
4784 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4786 if (newStorage!=0)
4788 list_init(&newStorage->base.strmHead);
4790 list_init(&newStorage->base.storageHead);
4793 * Initialize the virtual function table.
4795 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4796 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4797 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4799 newStorage->base.reverted = 0;
4801 newStorage->base.ref = 1;
4803 newStorage->parentStorage = parentStorage;
4806 * Keep a reference to the directory entry of this storage
4808 newStorage->base.storageDirEntry = storageDirEntry;
4810 newStorage->base.create = 0;
4812 return newStorage;
4815 return 0;
4818 /******************************************************************************
4819 ** StorageUtl implementation
4822 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4824 WORD tmp;
4826 memcpy(&tmp, buffer+offset, sizeof(WORD));
4827 *value = lendian16toh(tmp);
4830 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4832 value = htole16(value);
4833 memcpy(buffer+offset, &value, sizeof(WORD));
4836 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4838 DWORD tmp;
4840 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4841 *value = lendian32toh(tmp);
4844 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4846 value = htole32(value);
4847 memcpy(buffer+offset, &value, sizeof(DWORD));
4850 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4851 ULARGE_INTEGER* value)
4853 #ifdef WORDS_BIGENDIAN
4854 ULARGE_INTEGER tmp;
4856 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4857 value->u.LowPart = htole32(tmp.u.HighPart);
4858 value->u.HighPart = htole32(tmp.u.LowPart);
4859 #else
4860 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4861 #endif
4864 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4865 const ULARGE_INTEGER *value)
4867 #ifdef WORDS_BIGENDIAN
4868 ULARGE_INTEGER tmp;
4870 tmp.u.LowPart = htole32(value->u.HighPart);
4871 tmp.u.HighPart = htole32(value->u.LowPart);
4872 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4873 #else
4874 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4875 #endif
4878 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4880 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4881 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4882 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4884 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4887 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4889 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4890 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4891 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4893 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4896 void StorageUtl_CopyDirEntryToSTATSTG(
4897 StorageBaseImpl* storage,
4898 STATSTG* destination,
4899 const DirEntry* source,
4900 int statFlags)
4902 LPCWSTR entryName;
4904 if (source->stgType == STGTY_ROOT)
4906 /* replace the name of root entry (often "Root Entry") by the file name */
4907 entryName = storage->filename;
4909 else
4911 entryName = source->name;
4915 * The copy of the string occurs only when the flag is not set
4917 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4918 (entryName == NULL) ||
4919 (entryName[0] == 0) )
4921 destination->pwcsName = 0;
4923 else
4925 destination->pwcsName =
4926 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4928 strcpyW(destination->pwcsName, entryName);
4931 switch (source->stgType)
4933 case STGTY_STORAGE:
4934 case STGTY_ROOT:
4935 destination->type = STGTY_STORAGE;
4936 break;
4937 case STGTY_STREAM:
4938 destination->type = STGTY_STREAM;
4939 break;
4940 default:
4941 destination->type = STGTY_STREAM;
4942 break;
4945 destination->cbSize = source->size;
4947 currentReturnStruct->mtime = {0}; TODO
4948 currentReturnStruct->ctime = {0};
4949 currentReturnStruct->atime = {0};
4951 destination->grfMode = 0;
4952 destination->grfLocksSupported = 0;
4953 destination->clsid = source->clsid;
4954 destination->grfStateBits = 0;
4955 destination->reserved = 0;
4958 /******************************************************************************
4959 ** BlockChainStream implementation
4962 BlockChainStream* BlockChainStream_Construct(
4963 StorageImpl* parentStorage,
4964 ULONG* headOfStreamPlaceHolder,
4965 DirRef dirEntry)
4967 BlockChainStream* newStream;
4968 ULONG blockIndex;
4970 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4972 newStream->parentStorage = parentStorage;
4973 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4974 newStream->ownerDirEntry = dirEntry;
4975 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4976 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4977 newStream->numBlocks = 0;
4979 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4981 while (blockIndex != BLOCK_END_OF_CHAIN)
4983 newStream->numBlocks++;
4984 newStream->tailIndex = blockIndex;
4986 if(FAILED(StorageImpl_GetNextBlockInChain(
4987 parentStorage,
4988 blockIndex,
4989 &blockIndex)))
4991 HeapFree(GetProcessHeap(), 0, newStream);
4992 return NULL;
4996 return newStream;
4999 void BlockChainStream_Destroy(BlockChainStream* This)
5001 HeapFree(GetProcessHeap(), 0, This);
5004 /******************************************************************************
5005 * BlockChainStream_GetHeadOfChain
5007 * Returns the head of this stream chain.
5008 * Some special chains don't have directory entries, their heads are kept in
5009 * This->headOfStreamPlaceHolder.
5012 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5014 DirEntry chainEntry;
5015 HRESULT hr;
5017 if (This->headOfStreamPlaceHolder != 0)
5018 return *(This->headOfStreamPlaceHolder);
5020 if (This->ownerDirEntry != DIRENTRY_NULL)
5022 hr = StorageImpl_ReadDirEntry(
5023 This->parentStorage,
5024 This->ownerDirEntry,
5025 &chainEntry);
5027 if (SUCCEEDED(hr))
5029 return chainEntry.startingBlock;
5033 return BLOCK_END_OF_CHAIN;
5036 /******************************************************************************
5037 * BlockChainStream_GetCount
5039 * Returns the number of blocks that comprises this chain.
5040 * This is not the size of the stream as the last block may not be full!
5043 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5045 ULONG blockIndex;
5046 ULONG count = 0;
5048 blockIndex = BlockChainStream_GetHeadOfChain(This);
5050 while (blockIndex != BLOCK_END_OF_CHAIN)
5052 count++;
5054 if(FAILED(StorageImpl_GetNextBlockInChain(
5055 This->parentStorage,
5056 blockIndex,
5057 &blockIndex)))
5058 return 0;
5061 return count;
5064 /******************************************************************************
5065 * BlockChainStream_ReadAt
5067 * Reads a specified number of bytes from this chain at the specified offset.
5068 * bytesRead may be NULL.
5069 * Failure will be returned if the specified number of bytes has not been read.
5071 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5072 ULARGE_INTEGER offset,
5073 ULONG size,
5074 void* buffer,
5075 ULONG* bytesRead)
5077 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5078 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5079 ULONG bytesToReadInBuffer;
5080 ULONG blockIndex;
5081 BYTE* bufferWalker;
5083 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5086 * Find the first block in the stream that contains part of the buffer.
5088 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5089 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5090 (blockNoInSequence < This->lastBlockNoInSequence) )
5092 blockIndex = BlockChainStream_GetHeadOfChain(This);
5093 This->lastBlockNoInSequence = blockNoInSequence;
5095 else
5097 ULONG temp = blockNoInSequence;
5099 blockIndex = This->lastBlockNoInSequenceIndex;
5100 blockNoInSequence -= This->lastBlockNoInSequence;
5101 This->lastBlockNoInSequence = temp;
5104 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5106 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5107 return STG_E_DOCFILECORRUPT;
5108 blockNoInSequence--;
5111 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5112 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5114 This->lastBlockNoInSequenceIndex = blockIndex;
5117 * Start reading the buffer.
5119 *bytesRead = 0;
5120 bufferWalker = buffer;
5122 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5124 ULARGE_INTEGER ulOffset;
5125 DWORD bytesReadAt;
5127 * Calculate how many bytes we can copy from this big block.
5129 bytesToReadInBuffer =
5130 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5132 TRACE("block %i\n",blockIndex);
5133 ulOffset.u.HighPart = 0;
5134 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5135 offsetInBlock;
5137 StorageImpl_ReadAt(This->parentStorage,
5138 ulOffset,
5139 bufferWalker,
5140 bytesToReadInBuffer,
5141 &bytesReadAt);
5143 * Step to the next big block.
5145 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5146 return STG_E_DOCFILECORRUPT;
5148 bufferWalker += bytesReadAt;
5149 size -= bytesReadAt;
5150 *bytesRead += bytesReadAt;
5151 offsetInBlock = 0; /* There is no offset on the next block */
5153 if (bytesToReadInBuffer != bytesReadAt)
5154 break;
5157 return (size == 0) ? S_OK : STG_E_READFAULT;
5160 /******************************************************************************
5161 * BlockChainStream_WriteAt
5163 * Writes the specified number of bytes to this chain at the specified offset.
5164 * Will fail if not all specified number of bytes have been written.
5166 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5167 ULARGE_INTEGER offset,
5168 ULONG size,
5169 const void* buffer,
5170 ULONG* bytesWritten)
5172 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5173 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5174 ULONG bytesToWrite;
5175 ULONG blockIndex;
5176 const BYTE* bufferWalker;
5179 * Find the first block in the stream that contains part of the buffer.
5181 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5182 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5183 (blockNoInSequence < This->lastBlockNoInSequence) )
5185 blockIndex = BlockChainStream_GetHeadOfChain(This);
5186 This->lastBlockNoInSequence = blockNoInSequence;
5188 else
5190 ULONG temp = blockNoInSequence;
5192 blockIndex = This->lastBlockNoInSequenceIndex;
5193 blockNoInSequence -= This->lastBlockNoInSequence;
5194 This->lastBlockNoInSequence = temp;
5197 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5199 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5200 &blockIndex)))
5201 return STG_E_DOCFILECORRUPT;
5202 blockNoInSequence--;
5205 This->lastBlockNoInSequenceIndex = blockIndex;
5207 /* BlockChainStream_SetSize should have already been called to ensure we have
5208 * enough blocks in the chain to write into */
5209 if (blockIndex == BLOCK_END_OF_CHAIN)
5211 ERR("not enough blocks in chain to write data\n");
5212 return STG_E_DOCFILECORRUPT;
5215 *bytesWritten = 0;
5216 bufferWalker = buffer;
5218 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5220 ULARGE_INTEGER ulOffset;
5221 DWORD bytesWrittenAt;
5223 * Calculate how many bytes we can copy from this big block.
5225 bytesToWrite =
5226 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5228 TRACE("block %i\n",blockIndex);
5229 ulOffset.u.HighPart = 0;
5230 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5231 offsetInBlock;
5233 StorageImpl_WriteAt(This->parentStorage,
5234 ulOffset,
5235 bufferWalker,
5236 bytesToWrite,
5237 &bytesWrittenAt);
5240 * Step to the next big block.
5242 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5243 &blockIndex)))
5244 return STG_E_DOCFILECORRUPT;
5246 bufferWalker += bytesWrittenAt;
5247 size -= bytesWrittenAt;
5248 *bytesWritten += bytesWrittenAt;
5249 offsetInBlock = 0; /* There is no offset on the next block */
5251 if (bytesWrittenAt != bytesToWrite)
5252 break;
5255 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5258 /******************************************************************************
5259 * BlockChainStream_Shrink
5261 * Shrinks this chain in the big block depot.
5263 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5264 ULARGE_INTEGER newSize)
5266 ULONG blockIndex, extraBlock;
5267 ULONG numBlocks;
5268 ULONG count = 1;
5271 * Reset the last accessed block cache.
5273 This->lastBlockNoInSequence = 0xFFFFFFFF;
5274 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5277 * Figure out how many blocks are needed to contain the new size
5279 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5281 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5282 numBlocks++;
5284 blockIndex = BlockChainStream_GetHeadOfChain(This);
5287 * Go to the new end of chain
5289 while (count < numBlocks)
5291 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5292 &blockIndex)))
5293 return FALSE;
5294 count++;
5297 /* Get the next block before marking the new end */
5298 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5299 &extraBlock)))
5300 return FALSE;
5302 /* Mark the new end of chain */
5303 StorageImpl_SetNextBlockInChain(
5304 This->parentStorage,
5305 blockIndex,
5306 BLOCK_END_OF_CHAIN);
5308 This->tailIndex = blockIndex;
5309 This->numBlocks = numBlocks;
5312 * Mark the extra blocks as free
5314 while (extraBlock != BLOCK_END_OF_CHAIN)
5316 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5317 &blockIndex)))
5318 return FALSE;
5319 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5320 extraBlock = blockIndex;
5323 return TRUE;
5326 /******************************************************************************
5327 * BlockChainStream_Enlarge
5329 * Grows this chain in the big block depot.
5331 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5332 ULARGE_INTEGER newSize)
5334 ULONG blockIndex, currentBlock;
5335 ULONG newNumBlocks;
5336 ULONG oldNumBlocks = 0;
5338 blockIndex = BlockChainStream_GetHeadOfChain(This);
5341 * Empty chain. Create the head.
5343 if (blockIndex == BLOCK_END_OF_CHAIN)
5345 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5346 StorageImpl_SetNextBlockInChain(This->parentStorage,
5347 blockIndex,
5348 BLOCK_END_OF_CHAIN);
5350 if (This->headOfStreamPlaceHolder != 0)
5352 *(This->headOfStreamPlaceHolder) = blockIndex;
5354 else
5356 DirEntry chainEntry;
5357 assert(This->ownerDirEntry != DIRENTRY_NULL);
5359 StorageImpl_ReadDirEntry(
5360 This->parentStorage,
5361 This->ownerDirEntry,
5362 &chainEntry);
5364 chainEntry.startingBlock = blockIndex;
5366 StorageImpl_WriteDirEntry(
5367 This->parentStorage,
5368 This->ownerDirEntry,
5369 &chainEntry);
5372 This->tailIndex = blockIndex;
5373 This->numBlocks = 1;
5377 * Figure out how many blocks are needed to contain this stream
5379 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5381 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5382 newNumBlocks++;
5385 * Go to the current end of chain
5387 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5389 currentBlock = blockIndex;
5391 while (blockIndex != BLOCK_END_OF_CHAIN)
5393 This->numBlocks++;
5394 currentBlock = blockIndex;
5396 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5397 &blockIndex)))
5398 return FALSE;
5401 This->tailIndex = currentBlock;
5404 currentBlock = This->tailIndex;
5405 oldNumBlocks = This->numBlocks;
5408 * Add new blocks to the chain
5410 if (oldNumBlocks < newNumBlocks)
5412 while (oldNumBlocks < newNumBlocks)
5414 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5416 StorageImpl_SetNextBlockInChain(
5417 This->parentStorage,
5418 currentBlock,
5419 blockIndex);
5421 StorageImpl_SetNextBlockInChain(
5422 This->parentStorage,
5423 blockIndex,
5424 BLOCK_END_OF_CHAIN);
5426 currentBlock = blockIndex;
5427 oldNumBlocks++;
5430 This->tailIndex = blockIndex;
5431 This->numBlocks = newNumBlocks;
5434 return TRUE;
5437 /******************************************************************************
5438 * BlockChainStream_SetSize
5440 * Sets the size of this stream. The big block depot will be updated.
5441 * The file will grow if we grow the chain.
5443 * TODO: Free the actual blocks in the file when we shrink the chain.
5444 * Currently, the blocks are still in the file. So the file size
5445 * doesn't shrink even if we shrink streams.
5447 BOOL BlockChainStream_SetSize(
5448 BlockChainStream* This,
5449 ULARGE_INTEGER newSize)
5451 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5453 if (newSize.u.LowPart == size.u.LowPart)
5454 return TRUE;
5456 if (newSize.u.LowPart < size.u.LowPart)
5458 BlockChainStream_Shrink(This, newSize);
5460 else
5462 BlockChainStream_Enlarge(This, newSize);
5465 return TRUE;
5468 /******************************************************************************
5469 * BlockChainStream_GetSize
5471 * Returns the size of this chain.
5472 * Will return the block count if this chain doesn't have a directory entry.
5474 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5476 DirEntry chainEntry;
5478 if(This->headOfStreamPlaceHolder == NULL)
5481 * This chain has a directory entry so use the size value from there.
5483 StorageImpl_ReadDirEntry(
5484 This->parentStorage,
5485 This->ownerDirEntry,
5486 &chainEntry);
5488 return chainEntry.size;
5490 else
5493 * this chain is a chain that does not have a directory entry, figure out the
5494 * size by making the product number of used blocks times the
5495 * size of them
5497 ULARGE_INTEGER result;
5498 result.u.HighPart = 0;
5500 result.u.LowPart =
5501 BlockChainStream_GetCount(This) *
5502 This->parentStorage->bigBlockSize;
5504 return result;
5508 /******************************************************************************
5509 ** SmallBlockChainStream implementation
5512 SmallBlockChainStream* SmallBlockChainStream_Construct(
5513 StorageImpl* parentStorage,
5514 ULONG* headOfStreamPlaceHolder,
5515 DirRef dirEntry)
5517 SmallBlockChainStream* newStream;
5519 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5521 newStream->parentStorage = parentStorage;
5522 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5523 newStream->ownerDirEntry = dirEntry;
5525 return newStream;
5528 void SmallBlockChainStream_Destroy(
5529 SmallBlockChainStream* This)
5531 HeapFree(GetProcessHeap(), 0, This);
5534 /******************************************************************************
5535 * SmallBlockChainStream_GetHeadOfChain
5537 * Returns the head of this chain of small blocks.
5539 static ULONG SmallBlockChainStream_GetHeadOfChain(
5540 SmallBlockChainStream* This)
5542 DirEntry chainEntry;
5543 HRESULT hr;
5545 if (This->headOfStreamPlaceHolder != NULL)
5546 return *(This->headOfStreamPlaceHolder);
5548 if (This->ownerDirEntry)
5550 hr = StorageImpl_ReadDirEntry(
5551 This->parentStorage,
5552 This->ownerDirEntry,
5553 &chainEntry);
5555 if (SUCCEEDED(hr))
5557 return chainEntry.startingBlock;
5562 return BLOCK_END_OF_CHAIN;
5565 /******************************************************************************
5566 * SmallBlockChainStream_GetNextBlockInChain
5568 * Returns the index of the next small block in this chain.
5570 * Return Values:
5571 * - BLOCK_END_OF_CHAIN: end of this chain
5572 * - BLOCK_UNUSED: small block 'blockIndex' is free
5574 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5575 SmallBlockChainStream* This,
5576 ULONG blockIndex,
5577 ULONG* nextBlockInChain)
5579 ULARGE_INTEGER offsetOfBlockInDepot;
5580 DWORD buffer;
5581 ULONG bytesRead;
5582 HRESULT res;
5584 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5586 offsetOfBlockInDepot.u.HighPart = 0;
5587 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5590 * Read those bytes in the buffer from the small block file.
5592 res = BlockChainStream_ReadAt(
5593 This->parentStorage->smallBlockDepotChain,
5594 offsetOfBlockInDepot,
5595 sizeof(DWORD),
5596 &buffer,
5597 &bytesRead);
5599 if (SUCCEEDED(res))
5601 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5602 return S_OK;
5605 return res;
5608 /******************************************************************************
5609 * SmallBlockChainStream_SetNextBlockInChain
5611 * Writes the index of the next block of the specified block in the small
5612 * block depot.
5613 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5614 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5616 static void SmallBlockChainStream_SetNextBlockInChain(
5617 SmallBlockChainStream* This,
5618 ULONG blockIndex,
5619 ULONG nextBlock)
5621 ULARGE_INTEGER offsetOfBlockInDepot;
5622 DWORD buffer;
5623 ULONG bytesWritten;
5625 offsetOfBlockInDepot.u.HighPart = 0;
5626 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5628 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5631 * Read those bytes in the buffer from the small block file.
5633 BlockChainStream_WriteAt(
5634 This->parentStorage->smallBlockDepotChain,
5635 offsetOfBlockInDepot,
5636 sizeof(DWORD),
5637 &buffer,
5638 &bytesWritten);
5641 /******************************************************************************
5642 * SmallBlockChainStream_FreeBlock
5644 * Flag small block 'blockIndex' as free in the small block depot.
5646 static void SmallBlockChainStream_FreeBlock(
5647 SmallBlockChainStream* This,
5648 ULONG blockIndex)
5650 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5653 /******************************************************************************
5654 * SmallBlockChainStream_GetNextFreeBlock
5656 * Returns the index of a free small block. The small block depot will be
5657 * enlarged if necessary. The small block chain will also be enlarged if
5658 * necessary.
5660 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5661 SmallBlockChainStream* This)
5663 ULARGE_INTEGER offsetOfBlockInDepot;
5664 DWORD buffer;
5665 ULONG bytesRead;
5666 ULONG blockIndex = 0;
5667 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5668 HRESULT res = S_OK;
5669 ULONG smallBlocksPerBigBlock;
5671 offsetOfBlockInDepot.u.HighPart = 0;
5674 * Scan the small block depot for a free block
5676 while (nextBlockIndex != BLOCK_UNUSED)
5678 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5680 res = BlockChainStream_ReadAt(
5681 This->parentStorage->smallBlockDepotChain,
5682 offsetOfBlockInDepot,
5683 sizeof(DWORD),
5684 &buffer,
5685 &bytesRead);
5688 * If we run out of space for the small block depot, enlarge it
5690 if (SUCCEEDED(res))
5692 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5694 if (nextBlockIndex != BLOCK_UNUSED)
5695 blockIndex++;
5697 else
5699 ULONG count =
5700 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5702 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5703 ULONG nextBlock, newsbdIndex;
5704 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5706 nextBlock = sbdIndex;
5707 while (nextBlock != BLOCK_END_OF_CHAIN)
5709 sbdIndex = nextBlock;
5710 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5713 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5714 if (sbdIndex != BLOCK_END_OF_CHAIN)
5715 StorageImpl_SetNextBlockInChain(
5716 This->parentStorage,
5717 sbdIndex,
5718 newsbdIndex);
5720 StorageImpl_SetNextBlockInChain(
5721 This->parentStorage,
5722 newsbdIndex,
5723 BLOCK_END_OF_CHAIN);
5726 * Initialize all the small blocks to free
5728 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5729 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5731 if (count == 0)
5734 * We have just created the small block depot.
5736 DirEntry rootEntry;
5737 ULONG sbStartIndex;
5740 * Save it in the header
5742 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5743 StorageImpl_SaveFileHeader(This->parentStorage);
5746 * And allocate the first big block that will contain small blocks
5748 sbStartIndex =
5749 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5751 StorageImpl_SetNextBlockInChain(
5752 This->parentStorage,
5753 sbStartIndex,
5754 BLOCK_END_OF_CHAIN);
5756 StorageImpl_ReadDirEntry(
5757 This->parentStorage,
5758 This->parentStorage->base.storageDirEntry,
5759 &rootEntry);
5761 rootEntry.startingBlock = sbStartIndex;
5762 rootEntry.size.u.HighPart = 0;
5763 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5765 StorageImpl_WriteDirEntry(
5766 This->parentStorage,
5767 This->parentStorage->base.storageDirEntry,
5768 &rootEntry);
5770 else
5771 StorageImpl_SaveFileHeader(This->parentStorage);
5775 smallBlocksPerBigBlock =
5776 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5779 * Verify if we have to allocate big blocks to contain small blocks
5781 if (blockIndex % smallBlocksPerBigBlock == 0)
5783 DirEntry rootEntry;
5784 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5786 StorageImpl_ReadDirEntry(
5787 This->parentStorage,
5788 This->parentStorage->base.storageDirEntry,
5789 &rootEntry);
5791 if (rootEntry.size.u.LowPart <
5792 (blocksRequired * This->parentStorage->bigBlockSize))
5794 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5796 BlockChainStream_SetSize(
5797 This->parentStorage->smallBlockRootChain,
5798 rootEntry.size);
5800 StorageImpl_WriteDirEntry(
5801 This->parentStorage,
5802 This->parentStorage->base.storageDirEntry,
5803 &rootEntry);
5807 return blockIndex;
5810 /******************************************************************************
5811 * SmallBlockChainStream_ReadAt
5813 * Reads a specified number of bytes from this chain at the specified offset.
5814 * bytesRead may be NULL.
5815 * Failure will be returned if the specified number of bytes has not been read.
5817 HRESULT SmallBlockChainStream_ReadAt(
5818 SmallBlockChainStream* This,
5819 ULARGE_INTEGER offset,
5820 ULONG size,
5821 void* buffer,
5822 ULONG* bytesRead)
5824 HRESULT rc = S_OK;
5825 ULARGE_INTEGER offsetInBigBlockFile;
5826 ULONG blockNoInSequence =
5827 offset.u.LowPart / This->parentStorage->smallBlockSize;
5829 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5830 ULONG bytesToReadInBuffer;
5831 ULONG blockIndex;
5832 ULONG bytesReadFromBigBlockFile;
5833 BYTE* bufferWalker;
5836 * This should never happen on a small block file.
5838 assert(offset.u.HighPart==0);
5841 * Find the first block in the stream that contains part of the buffer.
5843 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5845 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5847 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5848 if(FAILED(rc))
5849 return rc;
5850 blockNoInSequence--;
5854 * Start reading the buffer.
5856 *bytesRead = 0;
5857 bufferWalker = buffer;
5859 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5862 * Calculate how many bytes we can copy from this small block.
5864 bytesToReadInBuffer =
5865 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5868 * Calculate the offset of the small block in the small block file.
5870 offsetInBigBlockFile.u.HighPart = 0;
5871 offsetInBigBlockFile.u.LowPart =
5872 blockIndex * This->parentStorage->smallBlockSize;
5874 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5877 * Read those bytes in the buffer from the small block file.
5878 * The small block has already been identified so it shouldn't fail
5879 * unless the file is corrupt.
5881 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5882 offsetInBigBlockFile,
5883 bytesToReadInBuffer,
5884 bufferWalker,
5885 &bytesReadFromBigBlockFile);
5887 if (FAILED(rc))
5888 return rc;
5891 * Step to the next big block.
5893 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5894 if(FAILED(rc))
5895 return STG_E_DOCFILECORRUPT;
5897 bufferWalker += bytesReadFromBigBlockFile;
5898 size -= bytesReadFromBigBlockFile;
5899 *bytesRead += bytesReadFromBigBlockFile;
5900 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5903 return (size == 0) ? S_OK : STG_E_READFAULT;
5906 /******************************************************************************
5907 * SmallBlockChainStream_WriteAt
5909 * Writes the specified number of bytes to this chain at the specified offset.
5910 * Will fail if not all specified number of bytes have been written.
5912 HRESULT SmallBlockChainStream_WriteAt(
5913 SmallBlockChainStream* This,
5914 ULARGE_INTEGER offset,
5915 ULONG size,
5916 const void* buffer,
5917 ULONG* bytesWritten)
5919 ULARGE_INTEGER offsetInBigBlockFile;
5920 ULONG blockNoInSequence =
5921 offset.u.LowPart / This->parentStorage->smallBlockSize;
5923 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5924 ULONG bytesToWriteInBuffer;
5925 ULONG blockIndex;
5926 ULONG bytesWrittenToBigBlockFile;
5927 const BYTE* bufferWalker;
5928 HRESULT res;
5931 * This should never happen on a small block file.
5933 assert(offset.u.HighPart==0);
5936 * Find the first block in the stream that contains part of the buffer.
5938 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5940 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5942 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5943 return STG_E_DOCFILECORRUPT;
5944 blockNoInSequence--;
5948 * Start writing the buffer.
5950 *bytesWritten = 0;
5951 bufferWalker = buffer;
5952 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5955 * Calculate how many bytes we can copy to this small block.
5957 bytesToWriteInBuffer =
5958 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5961 * Calculate the offset of the small block in the small block file.
5963 offsetInBigBlockFile.u.HighPart = 0;
5964 offsetInBigBlockFile.u.LowPart =
5965 blockIndex * This->parentStorage->smallBlockSize;
5967 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5970 * Write those bytes in the buffer to the small block file.
5972 res = BlockChainStream_WriteAt(
5973 This->parentStorage->smallBlockRootChain,
5974 offsetInBigBlockFile,
5975 bytesToWriteInBuffer,
5976 bufferWalker,
5977 &bytesWrittenToBigBlockFile);
5978 if (FAILED(res))
5979 return res;
5982 * Step to the next big block.
5984 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5985 &blockIndex)))
5986 return FALSE;
5987 bufferWalker += bytesWrittenToBigBlockFile;
5988 size -= bytesWrittenToBigBlockFile;
5989 *bytesWritten += bytesWrittenToBigBlockFile;
5990 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5993 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5996 /******************************************************************************
5997 * SmallBlockChainStream_Shrink
5999 * Shrinks this chain in the small block depot.
6001 static BOOL SmallBlockChainStream_Shrink(
6002 SmallBlockChainStream* This,
6003 ULARGE_INTEGER newSize)
6005 ULONG blockIndex, extraBlock;
6006 ULONG numBlocks;
6007 ULONG count = 0;
6009 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6011 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6012 numBlocks++;
6014 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6017 * Go to the new end of chain
6019 while (count < numBlocks)
6021 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6022 &blockIndex)))
6023 return FALSE;
6024 count++;
6028 * If the count is 0, we have a special case, the head of the chain was
6029 * just freed.
6031 if (count == 0)
6033 DirEntry chainEntry;
6035 StorageImpl_ReadDirEntry(This->parentStorage,
6036 This->ownerDirEntry,
6037 &chainEntry);
6039 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6041 StorageImpl_WriteDirEntry(This->parentStorage,
6042 This->ownerDirEntry,
6043 &chainEntry);
6046 * We start freeing the chain at the head block.
6048 extraBlock = blockIndex;
6050 else
6052 /* Get the next block before marking the new end */
6053 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6054 &extraBlock)))
6055 return FALSE;
6057 /* Mark the new end of chain */
6058 SmallBlockChainStream_SetNextBlockInChain(
6059 This,
6060 blockIndex,
6061 BLOCK_END_OF_CHAIN);
6065 * Mark the extra blocks as free
6067 while (extraBlock != BLOCK_END_OF_CHAIN)
6069 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6070 &blockIndex)))
6071 return FALSE;
6072 SmallBlockChainStream_FreeBlock(This, extraBlock);
6073 extraBlock = blockIndex;
6076 return TRUE;
6079 /******************************************************************************
6080 * SmallBlockChainStream_Enlarge
6082 * Grows this chain in the small block depot.
6084 static BOOL SmallBlockChainStream_Enlarge(
6085 SmallBlockChainStream* This,
6086 ULARGE_INTEGER newSize)
6088 ULONG blockIndex, currentBlock;
6089 ULONG newNumBlocks;
6090 ULONG oldNumBlocks = 0;
6092 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6095 * Empty chain. Create the head.
6097 if (blockIndex == BLOCK_END_OF_CHAIN)
6099 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6100 SmallBlockChainStream_SetNextBlockInChain(
6101 This,
6102 blockIndex,
6103 BLOCK_END_OF_CHAIN);
6105 if (This->headOfStreamPlaceHolder != NULL)
6107 *(This->headOfStreamPlaceHolder) = blockIndex;
6109 else
6111 DirEntry chainEntry;
6113 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6114 &chainEntry);
6116 chainEntry.startingBlock = blockIndex;
6118 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6119 &chainEntry);
6123 currentBlock = blockIndex;
6126 * Figure out how many blocks are needed to contain this stream
6128 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6130 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6131 newNumBlocks++;
6134 * Go to the current end of chain
6136 while (blockIndex != BLOCK_END_OF_CHAIN)
6138 oldNumBlocks++;
6139 currentBlock = blockIndex;
6140 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6141 return FALSE;
6145 * Add new blocks to the chain
6147 while (oldNumBlocks < newNumBlocks)
6149 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6150 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6152 SmallBlockChainStream_SetNextBlockInChain(
6153 This,
6154 blockIndex,
6155 BLOCK_END_OF_CHAIN);
6157 currentBlock = blockIndex;
6158 oldNumBlocks++;
6161 return TRUE;
6164 /******************************************************************************
6165 * SmallBlockChainStream_SetSize
6167 * Sets the size of this stream.
6168 * The file will grow if we grow the chain.
6170 * TODO: Free the actual blocks in the file when we shrink the chain.
6171 * Currently, the blocks are still in the file. So the file size
6172 * doesn't shrink even if we shrink streams.
6174 BOOL SmallBlockChainStream_SetSize(
6175 SmallBlockChainStream* This,
6176 ULARGE_INTEGER newSize)
6178 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6180 if (newSize.u.LowPart == size.u.LowPart)
6181 return TRUE;
6183 if (newSize.u.LowPart < size.u.LowPart)
6185 SmallBlockChainStream_Shrink(This, newSize);
6187 else
6189 SmallBlockChainStream_Enlarge(This, newSize);
6192 return TRUE;
6195 /******************************************************************************
6196 * SmallBlockChainStream_GetCount
6198 * Returns the number of small blocks that comprises this chain.
6199 * This is not the size of the stream as the last block may not be full!
6202 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6204 ULONG blockIndex;
6205 ULONG count = 0;
6207 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6209 while(blockIndex != BLOCK_END_OF_CHAIN)
6211 count++;
6213 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6214 blockIndex, &blockIndex)))
6215 return 0;
6218 return count;
6221 /******************************************************************************
6222 * SmallBlockChainStream_GetSize
6224 * Returns the size of this chain.
6226 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6228 DirEntry chainEntry;
6230 if(This->headOfStreamPlaceHolder != NULL)
6232 ULARGE_INTEGER result;
6233 result.u.HighPart = 0;
6235 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6236 This->parentStorage->smallBlockSize;
6238 return result;
6241 StorageImpl_ReadDirEntry(
6242 This->parentStorage,
6243 This->ownerDirEntry,
6244 &chainEntry);
6246 return chainEntry.size;
6249 /******************************************************************************
6250 * StgCreateDocfile [OLE32.@]
6251 * Creates a new compound file storage object
6253 * PARAMS
6254 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6255 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6256 * reserved [ ?] unused?, usually 0
6257 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6259 * RETURNS
6260 * S_OK if the file was successfully created
6261 * some STG_E_ value if error
6262 * NOTES
6263 * if pwcsName is NULL, create file with new unique name
6264 * the function can returns
6265 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6266 * (unrealized now)
6268 HRESULT WINAPI StgCreateDocfile(
6269 LPCOLESTR pwcsName,
6270 DWORD grfMode,
6271 DWORD reserved,
6272 IStorage **ppstgOpen)
6274 StorageBaseImpl* newStorage = 0;
6275 HANDLE hFile = INVALID_HANDLE_VALUE;
6276 HRESULT hr = STG_E_INVALIDFLAG;
6277 DWORD shareMode;
6278 DWORD accessMode;
6279 DWORD creationMode;
6280 DWORD fileAttributes;
6281 WCHAR tempFileName[MAX_PATH];
6283 TRACE("(%s, %x, %d, %p)\n",
6284 debugstr_w(pwcsName), grfMode,
6285 reserved, ppstgOpen);
6287 if (ppstgOpen == 0)
6288 return STG_E_INVALIDPOINTER;
6289 if (reserved != 0)
6290 return STG_E_INVALIDPARAMETER;
6292 /* if no share mode given then DENY_NONE is the default */
6293 if (STGM_SHARE_MODE(grfMode) == 0)
6294 grfMode |= STGM_SHARE_DENY_NONE;
6296 if ( FAILED( validateSTGM(grfMode) ))
6297 goto end;
6299 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6300 switch(STGM_ACCESS_MODE(grfMode))
6302 case STGM_WRITE:
6303 case STGM_READWRITE:
6304 break;
6305 default:
6306 goto end;
6309 /* in direct mode, can only use SHARE_EXCLUSIVE */
6310 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6311 goto end;
6313 /* but in transacted mode, any share mode is valid */
6316 * Generate a unique name.
6318 if (pwcsName == 0)
6320 WCHAR tempPath[MAX_PATH];
6321 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6323 memset(tempPath, 0, sizeof(tempPath));
6324 memset(tempFileName, 0, sizeof(tempFileName));
6326 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6327 tempPath[0] = '.';
6329 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6330 pwcsName = tempFileName;
6331 else
6333 hr = STG_E_INSUFFICIENTMEMORY;
6334 goto end;
6337 creationMode = TRUNCATE_EXISTING;
6339 else
6341 creationMode = GetCreationModeFromSTGM(grfMode);
6345 * Interpret the STGM value grfMode
6347 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6348 accessMode = GetAccessModeFromSTGM(grfMode);
6350 if (grfMode & STGM_DELETEONRELEASE)
6351 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6352 else
6353 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6355 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6356 FIXME("Storage share mode not implemented.\n");
6358 *ppstgOpen = 0;
6360 hFile = CreateFileW(pwcsName,
6361 accessMode,
6362 shareMode,
6363 NULL,
6364 creationMode,
6365 fileAttributes,
6368 if (hFile == INVALID_HANDLE_VALUE)
6370 if(GetLastError() == ERROR_FILE_EXISTS)
6371 hr = STG_E_FILEALREADYEXISTS;
6372 else
6373 hr = E_FAIL;
6374 goto end;
6378 * Allocate and initialize the new IStorage32object.
6380 hr = Storage_Construct(
6381 hFile,
6382 pwcsName,
6383 NULL,
6384 grfMode,
6385 TRUE,
6386 TRUE,
6387 &newStorage);
6389 if (FAILED(hr))
6391 goto end;
6395 * Get an "out" pointer for the caller.
6397 *ppstgOpen = (IStorage*)newStorage;
6399 end:
6400 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6402 return hr;
6405 /******************************************************************************
6406 * StgCreateStorageEx [OLE32.@]
6408 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6410 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6411 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6413 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6415 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6416 return STG_E_INVALIDPARAMETER;
6419 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6421 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6422 return STG_E_INVALIDPARAMETER;
6425 if (stgfmt == STGFMT_FILE)
6427 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6428 return STG_E_INVALIDPARAMETER;
6431 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6433 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6434 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
6437 ERR("Invalid stgfmt argument\n");
6438 return STG_E_INVALIDPARAMETER;
6441 /******************************************************************************
6442 * StgCreatePropSetStg [OLE32.@]
6444 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6445 IPropertySetStorage **ppPropSetStg)
6447 HRESULT hr;
6449 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6450 if (reserved)
6451 hr = STG_E_INVALIDPARAMETER;
6452 else
6453 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6454 (void**)ppPropSetStg);
6455 return hr;
6458 /******************************************************************************
6459 * StgOpenStorageEx [OLE32.@]
6461 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6463 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6464 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6466 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6468 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6469 return STG_E_INVALIDPARAMETER;
6472 switch (stgfmt)
6474 case STGFMT_FILE:
6475 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6476 return STG_E_INVALIDPARAMETER;
6478 case STGFMT_STORAGE:
6479 break;
6481 case STGFMT_DOCFILE:
6482 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6484 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6485 return STG_E_INVALIDPARAMETER;
6487 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6488 break;
6490 case STGFMT_ANY:
6491 WARN("STGFMT_ANY assuming storage\n");
6492 break;
6494 default:
6495 return STG_E_INVALIDPARAMETER;
6498 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6502 /******************************************************************************
6503 * StgOpenStorage [OLE32.@]
6505 HRESULT WINAPI StgOpenStorage(
6506 const OLECHAR *pwcsName,
6507 IStorage *pstgPriority,
6508 DWORD grfMode,
6509 SNB snbExclude,
6510 DWORD reserved,
6511 IStorage **ppstgOpen)
6513 StorageBaseImpl* newStorage = 0;
6514 HRESULT hr = S_OK;
6515 HANDLE hFile = 0;
6516 DWORD shareMode;
6517 DWORD accessMode;
6519 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6520 debugstr_w(pwcsName), pstgPriority, grfMode,
6521 snbExclude, reserved, ppstgOpen);
6523 if (pwcsName == 0)
6525 hr = STG_E_INVALIDNAME;
6526 goto end;
6529 if (ppstgOpen == 0)
6531 hr = STG_E_INVALIDPOINTER;
6532 goto end;
6535 if (reserved)
6537 hr = STG_E_INVALIDPARAMETER;
6538 goto end;
6541 if (grfMode & STGM_PRIORITY)
6543 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6544 return STG_E_INVALIDFLAG;
6545 if (grfMode & STGM_DELETEONRELEASE)
6546 return STG_E_INVALIDFUNCTION;
6547 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6548 return STG_E_INVALIDFLAG;
6549 grfMode &= ~0xf0; /* remove the existing sharing mode */
6550 grfMode |= STGM_SHARE_DENY_NONE;
6552 /* STGM_PRIORITY stops other IStorage objects on the same file from
6553 * committing until the STGM_PRIORITY IStorage is closed. it also
6554 * stops non-transacted mode StgOpenStorage calls with write access from
6555 * succeeding. obviously, both of these cannot be achieved through just
6556 * file share flags */
6557 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6561 * Validate the sharing mode
6563 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6564 switch(STGM_SHARE_MODE(grfMode))
6566 case STGM_SHARE_EXCLUSIVE:
6567 case STGM_SHARE_DENY_WRITE:
6568 break;
6569 default:
6570 hr = STG_E_INVALIDFLAG;
6571 goto end;
6574 if ( FAILED( validateSTGM(grfMode) ) ||
6575 (grfMode&STGM_CREATE))
6577 hr = STG_E_INVALIDFLAG;
6578 goto end;
6581 /* shared reading requires transacted mode */
6582 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6583 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6584 !(grfMode&STGM_TRANSACTED) )
6586 hr = STG_E_INVALIDFLAG;
6587 goto end;
6591 * Interpret the STGM value grfMode
6593 shareMode = GetShareModeFromSTGM(grfMode);
6594 accessMode = GetAccessModeFromSTGM(grfMode);
6596 *ppstgOpen = 0;
6598 hFile = CreateFileW( pwcsName,
6599 accessMode,
6600 shareMode,
6601 NULL,
6602 OPEN_EXISTING,
6603 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6606 if (hFile==INVALID_HANDLE_VALUE)
6608 DWORD last_error = GetLastError();
6610 hr = E_FAIL;
6612 switch (last_error)
6614 case ERROR_FILE_NOT_FOUND:
6615 hr = STG_E_FILENOTFOUND;
6616 break;
6618 case ERROR_PATH_NOT_FOUND:
6619 hr = STG_E_PATHNOTFOUND;
6620 break;
6622 case ERROR_ACCESS_DENIED:
6623 case ERROR_WRITE_PROTECT:
6624 hr = STG_E_ACCESSDENIED;
6625 break;
6627 case ERROR_SHARING_VIOLATION:
6628 hr = STG_E_SHAREVIOLATION;
6629 break;
6631 default:
6632 hr = E_FAIL;
6635 goto end;
6639 * Refuse to open the file if it's too small to be a structured storage file
6640 * FIXME: verify the file when reading instead of here
6642 if (GetFileSize(hFile, NULL) < 0x100)
6644 CloseHandle(hFile);
6645 hr = STG_E_FILEALREADYEXISTS;
6646 goto end;
6650 * Allocate and initialize the new IStorage32object.
6652 hr = Storage_Construct(
6653 hFile,
6654 pwcsName,
6655 NULL,
6656 grfMode,
6657 TRUE,
6658 FALSE,
6659 &newStorage);
6661 if (FAILED(hr))
6664 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6666 if(hr == STG_E_INVALIDHEADER)
6667 hr = STG_E_FILEALREADYEXISTS;
6668 goto end;
6672 * Get an "out" pointer for the caller.
6674 *ppstgOpen = (IStorage*)newStorage;
6676 end:
6677 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6678 return hr;
6681 /******************************************************************************
6682 * StgCreateDocfileOnILockBytes [OLE32.@]
6684 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6685 ILockBytes *plkbyt,
6686 DWORD grfMode,
6687 DWORD reserved,
6688 IStorage** ppstgOpen)
6690 StorageBaseImpl* newStorage = 0;
6691 HRESULT hr = S_OK;
6693 if ((ppstgOpen == 0) || (plkbyt == 0))
6694 return STG_E_INVALIDPOINTER;
6697 * Allocate and initialize the new IStorage object.
6699 hr = Storage_Construct(
6702 plkbyt,
6703 grfMode,
6704 FALSE,
6705 TRUE,
6706 &newStorage);
6708 if (FAILED(hr))
6710 return hr;
6714 * Get an "out" pointer for the caller.
6716 *ppstgOpen = (IStorage*)newStorage;
6718 return hr;
6721 /******************************************************************************
6722 * StgOpenStorageOnILockBytes [OLE32.@]
6724 HRESULT WINAPI StgOpenStorageOnILockBytes(
6725 ILockBytes *plkbyt,
6726 IStorage *pstgPriority,
6727 DWORD grfMode,
6728 SNB snbExclude,
6729 DWORD reserved,
6730 IStorage **ppstgOpen)
6732 StorageBaseImpl* newStorage = 0;
6733 HRESULT hr = S_OK;
6735 if ((plkbyt == 0) || (ppstgOpen == 0))
6736 return STG_E_INVALIDPOINTER;
6738 if ( FAILED( validateSTGM(grfMode) ))
6739 return STG_E_INVALIDFLAG;
6741 *ppstgOpen = 0;
6744 * Allocate and initialize the new IStorage object.
6746 hr = Storage_Construct(
6749 plkbyt,
6750 grfMode,
6751 FALSE,
6752 FALSE,
6753 &newStorage);
6755 if (FAILED(hr))
6757 return hr;
6761 * Get an "out" pointer for the caller.
6763 *ppstgOpen = (IStorage*)newStorage;
6765 return hr;
6768 /******************************************************************************
6769 * StgSetTimes [ole32.@]
6770 * StgSetTimes [OLE32.@]
6774 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6775 FILETIME const *patime, FILETIME const *pmtime)
6777 IStorage *stg = NULL;
6778 HRESULT r;
6780 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6782 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6783 0, 0, &stg);
6784 if( SUCCEEDED(r) )
6786 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6787 IStorage_Release(stg);
6790 return r;
6793 /******************************************************************************
6794 * StgIsStorageILockBytes [OLE32.@]
6796 * Determines if the ILockBytes contains a storage object.
6798 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6800 BYTE sig[8];
6801 ULARGE_INTEGER offset;
6803 offset.u.HighPart = 0;
6804 offset.u.LowPart = 0;
6806 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6808 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6809 return S_OK;
6811 return S_FALSE;
6814 /******************************************************************************
6815 * WriteClassStg [OLE32.@]
6817 * This method will store the specified CLSID in the specified storage object
6819 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6821 HRESULT hRes;
6823 if(!pStg)
6824 return E_INVALIDARG;
6826 if(!rclsid)
6827 return STG_E_INVALIDPOINTER;
6829 hRes = IStorage_SetClass(pStg, rclsid);
6831 return hRes;
6834 /***********************************************************************
6835 * ReadClassStg (OLE32.@)
6837 * This method reads the CLSID previously written to a storage object with
6838 * the WriteClassStg.
6840 * PARAMS
6841 * pstg [I] IStorage pointer
6842 * pclsid [O] Pointer to where the CLSID is written
6844 * RETURNS
6845 * Success: S_OK.
6846 * Failure: HRESULT code.
6848 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6850 STATSTG pstatstg;
6851 HRESULT hRes;
6853 TRACE("(%p, %p)\n", pstg, pclsid);
6855 if(!pstg || !pclsid)
6856 return E_INVALIDARG;
6859 * read a STATSTG structure (contains the clsid) from the storage
6861 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6863 if(SUCCEEDED(hRes))
6864 *pclsid=pstatstg.clsid;
6866 return hRes;
6869 /***********************************************************************
6870 * OleLoadFromStream (OLE32.@)
6872 * This function loads an object from stream
6874 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6876 CLSID clsid;
6877 HRESULT res;
6878 LPPERSISTSTREAM xstm;
6880 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6882 res=ReadClassStm(pStm,&clsid);
6883 if (FAILED(res))
6884 return res;
6885 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6886 if (FAILED(res))
6887 return res;
6888 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6889 if (FAILED(res)) {
6890 IUnknown_Release((IUnknown*)*ppvObj);
6891 return res;
6893 res=IPersistStream_Load(xstm,pStm);
6894 IPersistStream_Release(xstm);
6895 /* FIXME: all refcounts ok at this point? I think they should be:
6896 * pStm : unchanged
6897 * ppvObj : 1
6898 * xstm : 0 (released)
6900 return res;
6903 /***********************************************************************
6904 * OleSaveToStream (OLE32.@)
6906 * This function saves an object with the IPersistStream interface on it
6907 * to the specified stream.
6909 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6912 CLSID clsid;
6913 HRESULT res;
6915 TRACE("(%p,%p)\n",pPStm,pStm);
6917 res=IPersistStream_GetClassID(pPStm,&clsid);
6919 if (SUCCEEDED(res)){
6921 res=WriteClassStm(pStm,&clsid);
6923 if (SUCCEEDED(res))
6925 res=IPersistStream_Save(pPStm,pStm,TRUE);
6928 TRACE("Finished Save\n");
6929 return res;
6932 /****************************************************************************
6933 * This method validate a STGM parameter that can contain the values below
6935 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6936 * The stgm values contained in 0xffff0000 are bitmasks.
6938 * STGM_DIRECT 0x00000000
6939 * STGM_TRANSACTED 0x00010000
6940 * STGM_SIMPLE 0x08000000
6942 * STGM_READ 0x00000000
6943 * STGM_WRITE 0x00000001
6944 * STGM_READWRITE 0x00000002
6946 * STGM_SHARE_DENY_NONE 0x00000040
6947 * STGM_SHARE_DENY_READ 0x00000030
6948 * STGM_SHARE_DENY_WRITE 0x00000020
6949 * STGM_SHARE_EXCLUSIVE 0x00000010
6951 * STGM_PRIORITY 0x00040000
6952 * STGM_DELETEONRELEASE 0x04000000
6954 * STGM_CREATE 0x00001000
6955 * STGM_CONVERT 0x00020000
6956 * STGM_FAILIFTHERE 0x00000000
6958 * STGM_NOSCRATCH 0x00100000
6959 * STGM_NOSNAPSHOT 0x00200000
6961 static HRESULT validateSTGM(DWORD stgm)
6963 DWORD access = STGM_ACCESS_MODE(stgm);
6964 DWORD share = STGM_SHARE_MODE(stgm);
6965 DWORD create = STGM_CREATE_MODE(stgm);
6967 if (stgm&~STGM_KNOWN_FLAGS)
6969 ERR("unknown flags %08x\n", stgm);
6970 return E_FAIL;
6973 switch (access)
6975 case STGM_READ:
6976 case STGM_WRITE:
6977 case STGM_READWRITE:
6978 break;
6979 default:
6980 return E_FAIL;
6983 switch (share)
6985 case STGM_SHARE_DENY_NONE:
6986 case STGM_SHARE_DENY_READ:
6987 case STGM_SHARE_DENY_WRITE:
6988 case STGM_SHARE_EXCLUSIVE:
6989 break;
6990 default:
6991 return E_FAIL;
6994 switch (create)
6996 case STGM_CREATE:
6997 case STGM_FAILIFTHERE:
6998 break;
6999 default:
7000 return E_FAIL;
7004 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7006 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7007 return E_FAIL;
7010 * STGM_CREATE | STGM_CONVERT
7011 * if both are false, STGM_FAILIFTHERE is set to TRUE
7013 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7014 return E_FAIL;
7017 * STGM_NOSCRATCH requires STGM_TRANSACTED
7019 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7020 return E_FAIL;
7023 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7024 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7026 if ( (stgm & STGM_NOSNAPSHOT) &&
7027 (!(stgm & STGM_TRANSACTED) ||
7028 share == STGM_SHARE_EXCLUSIVE ||
7029 share == STGM_SHARE_DENY_WRITE) )
7030 return E_FAIL;
7032 return S_OK;
7035 /****************************************************************************
7036 * GetShareModeFromSTGM
7038 * This method will return a share mode flag from a STGM value.
7039 * The STGM value is assumed valid.
7041 static DWORD GetShareModeFromSTGM(DWORD stgm)
7043 switch (STGM_SHARE_MODE(stgm))
7045 case STGM_SHARE_DENY_NONE:
7046 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7047 case STGM_SHARE_DENY_READ:
7048 return FILE_SHARE_WRITE;
7049 case STGM_SHARE_DENY_WRITE:
7050 return FILE_SHARE_READ;
7051 case STGM_SHARE_EXCLUSIVE:
7052 return 0;
7054 ERR("Invalid share mode!\n");
7055 assert(0);
7056 return 0;
7059 /****************************************************************************
7060 * GetAccessModeFromSTGM
7062 * This method will return an access mode flag from a STGM value.
7063 * The STGM value is assumed valid.
7065 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7067 switch (STGM_ACCESS_MODE(stgm))
7069 case STGM_READ:
7070 return GENERIC_READ;
7071 case STGM_WRITE:
7072 case STGM_READWRITE:
7073 return GENERIC_READ | GENERIC_WRITE;
7075 ERR("Invalid access mode!\n");
7076 assert(0);
7077 return 0;
7080 /****************************************************************************
7081 * GetCreationModeFromSTGM
7083 * This method will return a creation mode flag from a STGM value.
7084 * The STGM value is assumed valid.
7086 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7088 switch(STGM_CREATE_MODE(stgm))
7090 case STGM_CREATE:
7091 return CREATE_ALWAYS;
7092 case STGM_CONVERT:
7093 FIXME("STGM_CONVERT not implemented!\n");
7094 return CREATE_NEW;
7095 case STGM_FAILIFTHERE:
7096 return CREATE_NEW;
7098 ERR("Invalid create mode!\n");
7099 assert(0);
7100 return 0;
7104 /*************************************************************************
7105 * OLECONVERT_LoadOLE10 [Internal]
7107 * Loads the OLE10 STREAM to memory
7109 * PARAMS
7110 * pOleStream [I] The OLESTREAM
7111 * pData [I] Data Structure for the OLESTREAM Data
7113 * RETURNS
7114 * Success: S_OK
7115 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7116 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7118 * NOTES
7119 * This function is used by OleConvertOLESTREAMToIStorage only.
7121 * Memory allocated for pData must be freed by the caller
7123 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7125 DWORD dwSize;
7126 HRESULT hRes = S_OK;
7127 int nTryCnt=0;
7128 int max_try = 6;
7130 pData->pData = NULL;
7131 pData->pstrOleObjFileName = NULL;
7133 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7135 /* Get the OleID */
7136 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7137 if(dwSize != sizeof(pData->dwOleID))
7139 hRes = CONVERT10_E_OLESTREAM_GET;
7141 else if(pData->dwOleID != OLESTREAM_ID)
7143 hRes = CONVERT10_E_OLESTREAM_FMT;
7145 else
7147 hRes = S_OK;
7148 break;
7152 if(hRes == S_OK)
7154 /* Get the TypeID... more info needed for this field */
7155 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7156 if(dwSize != sizeof(pData->dwTypeID))
7158 hRes = CONVERT10_E_OLESTREAM_GET;
7161 if(hRes == S_OK)
7163 if(pData->dwTypeID != 0)
7165 /* Get the length of the OleTypeName */
7166 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7167 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7169 hRes = CONVERT10_E_OLESTREAM_GET;
7172 if(hRes == S_OK)
7174 if(pData->dwOleTypeNameLength > 0)
7176 /* Get the OleTypeName */
7177 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7178 if(dwSize != pData->dwOleTypeNameLength)
7180 hRes = CONVERT10_E_OLESTREAM_GET;
7184 if(bStrem1)
7186 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7187 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7189 hRes = CONVERT10_E_OLESTREAM_GET;
7191 if(hRes == S_OK)
7193 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7194 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7195 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7196 if(pData->pstrOleObjFileName)
7198 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7199 if(dwSize != pData->dwOleObjFileNameLength)
7201 hRes = CONVERT10_E_OLESTREAM_GET;
7204 else
7205 hRes = CONVERT10_E_OLESTREAM_GET;
7208 else
7210 /* Get the Width of the Metafile */
7211 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7212 if(dwSize != sizeof(pData->dwMetaFileWidth))
7214 hRes = CONVERT10_E_OLESTREAM_GET;
7216 if(hRes == S_OK)
7218 /* Get the Height of the Metafile */
7219 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7220 if(dwSize != sizeof(pData->dwMetaFileHeight))
7222 hRes = CONVERT10_E_OLESTREAM_GET;
7226 if(hRes == S_OK)
7228 /* Get the Length of the Data */
7229 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7230 if(dwSize != sizeof(pData->dwDataLength))
7232 hRes = CONVERT10_E_OLESTREAM_GET;
7236 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7238 if(!bStrem1) /* if it is a second OLE stream data */
7240 pData->dwDataLength -= 8;
7241 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7242 if(dwSize != sizeof(pData->strUnknown))
7244 hRes = CONVERT10_E_OLESTREAM_GET;
7248 if(hRes == S_OK)
7250 if(pData->dwDataLength > 0)
7252 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7254 /* Get Data (ex. IStorage, Metafile, or BMP) */
7255 if(pData->pData)
7257 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7258 if(dwSize != pData->dwDataLength)
7260 hRes = CONVERT10_E_OLESTREAM_GET;
7263 else
7265 hRes = CONVERT10_E_OLESTREAM_GET;
7271 return hRes;
7274 /*************************************************************************
7275 * OLECONVERT_SaveOLE10 [Internal]
7277 * Saves the OLE10 STREAM From memory
7279 * PARAMS
7280 * pData [I] Data Structure for the OLESTREAM Data
7281 * pOleStream [I] The OLESTREAM to save
7283 * RETURNS
7284 * Success: S_OK
7285 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7287 * NOTES
7288 * This function is used by OleConvertIStorageToOLESTREAM only.
7291 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7293 DWORD dwSize;
7294 HRESULT hRes = S_OK;
7297 /* Set the OleID */
7298 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7299 if(dwSize != sizeof(pData->dwOleID))
7301 hRes = CONVERT10_E_OLESTREAM_PUT;
7304 if(hRes == S_OK)
7306 /* Set the TypeID */
7307 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7308 if(dwSize != sizeof(pData->dwTypeID))
7310 hRes = CONVERT10_E_OLESTREAM_PUT;
7314 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7316 /* Set the Length of the OleTypeName */
7317 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7318 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7320 hRes = CONVERT10_E_OLESTREAM_PUT;
7323 if(hRes == S_OK)
7325 if(pData->dwOleTypeNameLength > 0)
7327 /* Set the OleTypeName */
7328 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7329 if(dwSize != pData->dwOleTypeNameLength)
7331 hRes = CONVERT10_E_OLESTREAM_PUT;
7336 if(hRes == S_OK)
7338 /* Set the width of the Metafile */
7339 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7340 if(dwSize != sizeof(pData->dwMetaFileWidth))
7342 hRes = CONVERT10_E_OLESTREAM_PUT;
7346 if(hRes == S_OK)
7348 /* Set the height of the Metafile */
7349 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7350 if(dwSize != sizeof(pData->dwMetaFileHeight))
7352 hRes = CONVERT10_E_OLESTREAM_PUT;
7356 if(hRes == S_OK)
7358 /* Set the length of the Data */
7359 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7360 if(dwSize != sizeof(pData->dwDataLength))
7362 hRes = CONVERT10_E_OLESTREAM_PUT;
7366 if(hRes == S_OK)
7368 if(pData->dwDataLength > 0)
7370 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7371 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7372 if(dwSize != pData->dwDataLength)
7374 hRes = CONVERT10_E_OLESTREAM_PUT;
7379 return hRes;
7382 /*************************************************************************
7383 * OLECONVERT_GetOLE20FromOLE10[Internal]
7385 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7386 * opens it, and copies the content to the dest IStorage for
7387 * OleConvertOLESTREAMToIStorage
7390 * PARAMS
7391 * pDestStorage [I] The IStorage to copy the data to
7392 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7393 * nBufferLength [I] The size of the buffer
7395 * RETURNS
7396 * Nothing
7398 * NOTES
7402 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7404 HRESULT hRes;
7405 HANDLE hFile;
7406 IStorage *pTempStorage;
7407 DWORD dwNumOfBytesWritten;
7408 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7409 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7411 /* Create a temp File */
7412 GetTempPathW(MAX_PATH, wstrTempDir);
7413 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7414 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7416 if(hFile != INVALID_HANDLE_VALUE)
7418 /* Write IStorage Data to File */
7419 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7420 CloseHandle(hFile);
7422 /* Open and copy temp storage to the Dest Storage */
7423 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7424 if(hRes == S_OK)
7426 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7427 IStorage_Release(pTempStorage);
7429 DeleteFileW(wstrTempFile);
7434 /*************************************************************************
7435 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7437 * Saves the OLE10 STREAM From memory
7439 * PARAMS
7440 * pStorage [I] The Src IStorage to copy
7441 * pData [I] The Dest Memory to write to.
7443 * RETURNS
7444 * The size in bytes allocated for pData
7446 * NOTES
7447 * Memory allocated for pData must be freed by the caller
7449 * Used by OleConvertIStorageToOLESTREAM only.
7452 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7454 HANDLE hFile;
7455 HRESULT hRes;
7456 DWORD nDataLength = 0;
7457 IStorage *pTempStorage;
7458 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7459 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7461 *pData = NULL;
7463 /* Create temp Storage */
7464 GetTempPathW(MAX_PATH, wstrTempDir);
7465 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7466 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7468 if(hRes == S_OK)
7470 /* Copy Src Storage to the Temp Storage */
7471 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7472 IStorage_Release(pTempStorage);
7474 /* Open Temp Storage as a file and copy to memory */
7475 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7476 if(hFile != INVALID_HANDLE_VALUE)
7478 nDataLength = GetFileSize(hFile, NULL);
7479 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7480 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7481 CloseHandle(hFile);
7483 DeleteFileW(wstrTempFile);
7485 return nDataLength;
7488 /*************************************************************************
7489 * OLECONVERT_CreateOleStream [Internal]
7491 * Creates the "\001OLE" stream in the IStorage if necessary.
7493 * PARAMS
7494 * pStorage [I] Dest storage to create the stream in
7496 * RETURNS
7497 * Nothing
7499 * NOTES
7500 * This function is used by OleConvertOLESTREAMToIStorage only.
7502 * This stream is still unknown, MS Word seems to have extra data
7503 * but since the data is stored in the OLESTREAM there should be
7504 * no need to recreate the stream. If the stream is manually
7505 * deleted it will create it with this default data.
7508 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7510 HRESULT hRes;
7511 IStream *pStream;
7512 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7513 BYTE pOleStreamHeader [] =
7515 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7516 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7517 0x00, 0x00, 0x00, 0x00
7520 /* Create stream if not present */
7521 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7522 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7524 if(hRes == S_OK)
7526 /* Write default Data */
7527 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7528 IStream_Release(pStream);
7532 /* write a string to a stream, preceded by its length */
7533 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7535 HRESULT r;
7536 LPSTR str;
7537 DWORD len = 0;
7539 if( string )
7540 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7541 r = IStream_Write( stm, &len, sizeof(len), NULL);
7542 if( FAILED( r ) )
7543 return r;
7544 if(len == 0)
7545 return r;
7546 str = CoTaskMemAlloc( len );
7547 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7548 r = IStream_Write( stm, str, len, NULL);
7549 CoTaskMemFree( str );
7550 return r;
7553 /* read a string preceded by its length from a stream */
7554 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7556 HRESULT r;
7557 DWORD len, count = 0;
7558 LPSTR str;
7559 LPWSTR wstr;
7561 r = IStream_Read( stm, &len, sizeof(len), &count );
7562 if( FAILED( r ) )
7563 return r;
7564 if( count != sizeof(len) )
7565 return E_OUTOFMEMORY;
7567 TRACE("%d bytes\n",len);
7569 str = CoTaskMemAlloc( len );
7570 if( !str )
7571 return E_OUTOFMEMORY;
7572 count = 0;
7573 r = IStream_Read( stm, str, len, &count );
7574 if( FAILED( r ) )
7575 return r;
7576 if( count != len )
7578 CoTaskMemFree( str );
7579 return E_OUTOFMEMORY;
7582 TRACE("Read string %s\n",debugstr_an(str,len));
7584 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7585 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7586 if( wstr )
7587 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7588 CoTaskMemFree( str );
7590 *string = wstr;
7592 return r;
7596 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7597 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7599 IStream *pstm;
7600 HRESULT r = S_OK;
7601 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7603 static const BYTE unknown1[12] =
7604 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7605 0xFF, 0xFF, 0xFF, 0xFF};
7606 static const BYTE unknown2[16] =
7607 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7608 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7610 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7611 debugstr_w(lpszUserType), debugstr_w(szClipName),
7612 debugstr_w(szProgIDName));
7614 /* Create a CompObj stream */
7615 r = IStorage_CreateStream(pstg, szwStreamName,
7616 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7617 if( FAILED (r) )
7618 return r;
7620 /* Write CompObj Structure to stream */
7621 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7623 if( SUCCEEDED( r ) )
7624 r = WriteClassStm( pstm, clsid );
7626 if( SUCCEEDED( r ) )
7627 r = STREAM_WriteString( pstm, lpszUserType );
7628 if( SUCCEEDED( r ) )
7629 r = STREAM_WriteString( pstm, szClipName );
7630 if( SUCCEEDED( r ) )
7631 r = STREAM_WriteString( pstm, szProgIDName );
7632 if( SUCCEEDED( r ) )
7633 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7635 IStream_Release( pstm );
7637 return r;
7640 /***********************************************************************
7641 * WriteFmtUserTypeStg (OLE32.@)
7643 HRESULT WINAPI WriteFmtUserTypeStg(
7644 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7646 HRESULT r;
7647 WCHAR szwClipName[0x40];
7648 CLSID clsid = CLSID_NULL;
7649 LPWSTR wstrProgID = NULL;
7650 DWORD n;
7652 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7654 /* get the clipboard format name */
7655 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7656 szwClipName[n]=0;
7658 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7660 /* FIXME: There's room to save a CLSID and its ProgID, but
7661 the CLSID is not looked up in the registry and in all the
7662 tests I wrote it was CLSID_NULL. Where does it come from?
7665 /* get the real program ID. This may fail, but that's fine */
7666 ProgIDFromCLSID(&clsid, &wstrProgID);
7668 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7670 r = STORAGE_WriteCompObj( pstg, &clsid,
7671 lpszUserType, szwClipName, wstrProgID );
7673 CoTaskMemFree(wstrProgID);
7675 return r;
7679 /******************************************************************************
7680 * ReadFmtUserTypeStg [OLE32.@]
7682 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7684 HRESULT r;
7685 IStream *stm = 0;
7686 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7687 unsigned char unknown1[12];
7688 unsigned char unknown2[16];
7689 DWORD count;
7690 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7691 CLSID clsid;
7693 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7695 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7696 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7697 if( FAILED ( r ) )
7699 WARN("Failed to open stream r = %08x\n", r);
7700 return r;
7703 /* read the various parts of the structure */
7704 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7705 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7706 goto end;
7707 r = ReadClassStm( stm, &clsid );
7708 if( FAILED( r ) )
7709 goto end;
7711 r = STREAM_ReadString( stm, &szCLSIDName );
7712 if( FAILED( r ) )
7713 goto end;
7715 r = STREAM_ReadString( stm, &szOleTypeName );
7716 if( FAILED( r ) )
7717 goto end;
7719 r = STREAM_ReadString( stm, &szProgIDName );
7720 if( FAILED( r ) )
7721 goto end;
7723 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7724 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7725 goto end;
7727 /* ok, success... now we just need to store what we found */
7728 if( pcf )
7729 *pcf = RegisterClipboardFormatW( szOleTypeName );
7730 CoTaskMemFree( szOleTypeName );
7732 if( lplpszUserType )
7733 *lplpszUserType = szCLSIDName;
7734 CoTaskMemFree( szProgIDName );
7736 end:
7737 IStream_Release( stm );
7739 return r;
7743 /*************************************************************************
7744 * OLECONVERT_CreateCompObjStream [Internal]
7746 * Creates a "\001CompObj" is the destination IStorage if necessary.
7748 * PARAMS
7749 * pStorage [I] The dest IStorage to create the CompObj Stream
7750 * if necessary.
7751 * strOleTypeName [I] The ProgID
7753 * RETURNS
7754 * Success: S_OK
7755 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7757 * NOTES
7758 * This function is used by OleConvertOLESTREAMToIStorage only.
7760 * The stream data is stored in the OLESTREAM and there should be
7761 * no need to recreate the stream. If the stream is manually
7762 * deleted it will attempt to create it by querying the registry.
7766 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7768 IStream *pStream;
7769 HRESULT hStorageRes, hRes = S_OK;
7770 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7771 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7772 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7774 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7775 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7777 /* Initialize the CompObj structure */
7778 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7779 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7780 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7783 /* Create a CompObj stream if it doesn't exist */
7784 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7785 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7786 if(hStorageRes == S_OK)
7788 /* copy the OleTypeName to the compobj struct */
7789 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7790 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7792 /* copy the OleTypeName to the compobj struct */
7793 /* Note: in the test made, these were Identical */
7794 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7795 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7797 /* Get the CLSID */
7798 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7799 bufferW, OLESTREAM_MAX_STR_LEN );
7800 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7802 if(hRes == S_OK)
7804 HKEY hKey;
7805 LONG hErr;
7806 /* Get the CLSID Default Name from the Registry */
7807 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7808 if(hErr == ERROR_SUCCESS)
7810 char strTemp[OLESTREAM_MAX_STR_LEN];
7811 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7812 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7813 if(hErr == ERROR_SUCCESS)
7815 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7817 RegCloseKey(hKey);
7821 /* Write CompObj Structure to stream */
7822 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7824 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7826 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7827 if(IStorageCompObj.dwCLSIDNameLength > 0)
7829 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7831 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7832 if(IStorageCompObj.dwOleTypeNameLength > 0)
7834 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7836 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7837 if(IStorageCompObj.dwProgIDNameLength > 0)
7839 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7841 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7842 IStream_Release(pStream);
7844 return hRes;
7848 /*************************************************************************
7849 * OLECONVERT_CreateOlePresStream[Internal]
7851 * Creates the "\002OlePres000" Stream with the Metafile data
7853 * PARAMS
7854 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7855 * dwExtentX [I] Width of the Metafile
7856 * dwExtentY [I] Height of the Metafile
7857 * pData [I] Metafile data
7858 * dwDataLength [I] Size of the Metafile data
7860 * RETURNS
7861 * Success: S_OK
7862 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7864 * NOTES
7865 * This function is used by OleConvertOLESTREAMToIStorage only.
7868 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7870 HRESULT hRes;
7871 IStream *pStream;
7872 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7873 BYTE pOlePresStreamHeader [] =
7875 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7876 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7877 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7878 0x00, 0x00, 0x00, 0x00
7881 BYTE pOlePresStreamHeaderEmpty [] =
7883 0x00, 0x00, 0x00, 0x00,
7884 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7885 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7886 0x00, 0x00, 0x00, 0x00
7889 /* Create the OlePres000 Stream */
7890 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7891 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7893 if(hRes == S_OK)
7895 DWORD nHeaderSize;
7896 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7898 memset(&OlePres, 0, sizeof(OlePres));
7899 /* Do we have any metafile data to save */
7900 if(dwDataLength > 0)
7902 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7903 nHeaderSize = sizeof(pOlePresStreamHeader);
7905 else
7907 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7908 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7910 /* Set width and height of the metafile */
7911 OlePres.dwExtentX = dwExtentX;
7912 OlePres.dwExtentY = -dwExtentY;
7914 /* Set Data and Length */
7915 if(dwDataLength > sizeof(METAFILEPICT16))
7917 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7918 OlePres.pData = &(pData[8]);
7920 /* Save OlePres000 Data to Stream */
7921 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7922 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7923 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7924 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7925 if(OlePres.dwSize > 0)
7927 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7929 IStream_Release(pStream);
7933 /*************************************************************************
7934 * OLECONVERT_CreateOle10NativeStream [Internal]
7936 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7938 * PARAMS
7939 * pStorage [I] Dest storage to create the stream in
7940 * pData [I] Ole10 Native Data (ex. bmp)
7941 * dwDataLength [I] Size of the Ole10 Native Data
7943 * RETURNS
7944 * Nothing
7946 * NOTES
7947 * This function is used by OleConvertOLESTREAMToIStorage only.
7949 * Might need to verify the data and return appropriate error message
7952 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7954 HRESULT hRes;
7955 IStream *pStream;
7956 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7958 /* Create the Ole10Native Stream */
7959 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7960 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7962 if(hRes == S_OK)
7964 /* Write info to stream */
7965 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7966 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7967 IStream_Release(pStream);
7972 /*************************************************************************
7973 * OLECONVERT_GetOLE10ProgID [Internal]
7975 * Finds the ProgID (or OleTypeID) from the IStorage
7977 * PARAMS
7978 * pStorage [I] The Src IStorage to get the ProgID
7979 * strProgID [I] the ProgID string to get
7980 * dwSize [I] the size of the string
7982 * RETURNS
7983 * Success: S_OK
7984 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7986 * NOTES
7987 * This function is used by OleConvertIStorageToOLESTREAM only.
7991 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7993 HRESULT hRes;
7994 IStream *pStream;
7995 LARGE_INTEGER iSeekPos;
7996 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7997 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7999 /* Open the CompObj Stream */
8000 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8001 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8002 if(hRes == S_OK)
8005 /*Get the OleType from the CompObj Stream */
8006 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8007 iSeekPos.u.HighPart = 0;
8009 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8010 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8011 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8012 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8013 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8014 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8015 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8017 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8018 if(*dwSize > 0)
8020 IStream_Read(pStream, strProgID, *dwSize, NULL);
8022 IStream_Release(pStream);
8024 else
8026 STATSTG stat;
8027 LPOLESTR wstrProgID;
8029 /* Get the OleType from the registry */
8030 REFCLSID clsid = &(stat.clsid);
8031 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8032 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8033 if(hRes == S_OK)
8035 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8039 return hRes;
8042 /*************************************************************************
8043 * OLECONVERT_GetOle10PresData [Internal]
8045 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8047 * PARAMS
8048 * pStorage [I] Src IStroage
8049 * pOleStream [I] Dest OleStream Mem Struct
8051 * RETURNS
8052 * Nothing
8054 * NOTES
8055 * This function is used by OleConvertIStorageToOLESTREAM only.
8057 * Memory allocated for pData must be freed by the caller
8061 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8064 HRESULT hRes;
8065 IStream *pStream;
8066 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8068 /* Initialize Default data for OLESTREAM */
8069 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8070 pOleStreamData[0].dwTypeID = 2;
8071 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8072 pOleStreamData[1].dwTypeID = 0;
8073 pOleStreamData[0].dwMetaFileWidth = 0;
8074 pOleStreamData[0].dwMetaFileHeight = 0;
8075 pOleStreamData[0].pData = NULL;
8076 pOleStreamData[1].pData = NULL;
8078 /* Open Ole10Native Stream */
8079 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8080 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8081 if(hRes == S_OK)
8084 /* Read Size and Data */
8085 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8086 if(pOleStreamData->dwDataLength > 0)
8088 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8089 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8091 IStream_Release(pStream);
8097 /*************************************************************************
8098 * OLECONVERT_GetOle20PresData[Internal]
8100 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8102 * PARAMS
8103 * pStorage [I] Src IStroage
8104 * pOleStreamData [I] Dest OleStream Mem Struct
8106 * RETURNS
8107 * Nothing
8109 * NOTES
8110 * This function is used by OleConvertIStorageToOLESTREAM only.
8112 * Memory allocated for pData must be freed by the caller
8114 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8116 HRESULT hRes;
8117 IStream *pStream;
8118 OLECONVERT_ISTORAGE_OLEPRES olePress;
8119 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8121 /* Initialize Default data for OLESTREAM */
8122 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8123 pOleStreamData[0].dwTypeID = 2;
8124 pOleStreamData[0].dwMetaFileWidth = 0;
8125 pOleStreamData[0].dwMetaFileHeight = 0;
8126 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8127 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8128 pOleStreamData[1].dwTypeID = 0;
8129 pOleStreamData[1].dwOleTypeNameLength = 0;
8130 pOleStreamData[1].strOleTypeName[0] = 0;
8131 pOleStreamData[1].dwMetaFileWidth = 0;
8132 pOleStreamData[1].dwMetaFileHeight = 0;
8133 pOleStreamData[1].pData = NULL;
8134 pOleStreamData[1].dwDataLength = 0;
8137 /* Open OlePress000 stream */
8138 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8139 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8140 if(hRes == S_OK)
8142 LARGE_INTEGER iSeekPos;
8143 METAFILEPICT16 MetaFilePict;
8144 static const char strMetafilePictName[] = "METAFILEPICT";
8146 /* Set the TypeID for a Metafile */
8147 pOleStreamData[1].dwTypeID = 5;
8149 /* Set the OleTypeName to Metafile */
8150 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8151 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8153 iSeekPos.u.HighPart = 0;
8154 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8156 /* Get Presentation Data */
8157 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8158 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8159 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8160 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8162 /*Set width and Height */
8163 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8164 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8165 if(olePress.dwSize > 0)
8167 /* Set Length */
8168 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8170 /* Set MetaFilePict struct */
8171 MetaFilePict.mm = 8;
8172 MetaFilePict.xExt = olePress.dwExtentX;
8173 MetaFilePict.yExt = olePress.dwExtentY;
8174 MetaFilePict.hMF = 0;
8176 /* Get Metafile Data */
8177 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8178 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8179 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8181 IStream_Release(pStream);
8185 /*************************************************************************
8186 * OleConvertOLESTREAMToIStorage [OLE32.@]
8188 * Read info on MSDN
8190 * TODO
8191 * DVTARGETDEVICE parameter is not handled
8192 * Still unsure of some mem fields for OLE 10 Stream
8193 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8194 * and "\001OLE" streams
8197 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8198 LPOLESTREAM pOleStream,
8199 LPSTORAGE pstg,
8200 const DVTARGETDEVICE* ptd)
8202 int i;
8203 HRESULT hRes=S_OK;
8204 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8206 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8208 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8210 if(ptd != NULL)
8212 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8215 if(pstg == NULL || pOleStream == NULL)
8217 hRes = E_INVALIDARG;
8220 if(hRes == S_OK)
8222 /* Load the OLESTREAM to Memory */
8223 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8226 if(hRes == S_OK)
8228 /* Load the OLESTREAM to Memory (part 2)*/
8229 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8232 if(hRes == S_OK)
8235 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8237 /* Do we have the IStorage Data in the OLESTREAM */
8238 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8240 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8241 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8243 else
8245 /* It must be an original OLE 1.0 source */
8246 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8249 else
8251 /* It must be an original OLE 1.0 source */
8252 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8255 /* Create CompObj Stream if necessary */
8256 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8257 if(hRes == S_OK)
8259 /*Create the Ole Stream if necessary */
8260 OLECONVERT_CreateOleStream(pstg);
8265 /* Free allocated memory */
8266 for(i=0; i < 2; i++)
8268 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8269 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8270 pOleStreamData[i].pstrOleObjFileName = NULL;
8272 return hRes;
8275 /*************************************************************************
8276 * OleConvertIStorageToOLESTREAM [OLE32.@]
8278 * Read info on MSDN
8280 * Read info on MSDN
8282 * TODO
8283 * Still unsure of some mem fields for OLE 10 Stream
8284 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8285 * and "\001OLE" streams.
8288 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8289 LPSTORAGE pstg,
8290 LPOLESTREAM pOleStream)
8292 int i;
8293 HRESULT hRes = S_OK;
8294 IStream *pStream;
8295 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8296 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8298 TRACE("%p %p\n", pstg, pOleStream);
8300 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8302 if(pstg == NULL || pOleStream == NULL)
8304 hRes = E_INVALIDARG;
8306 if(hRes == S_OK)
8308 /* Get the ProgID */
8309 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8310 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8312 if(hRes == S_OK)
8314 /* Was it originally Ole10 */
8315 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8316 if(hRes == S_OK)
8318 IStream_Release(pStream);
8319 /* Get Presentation Data for Ole10Native */
8320 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8322 else
8324 /* Get Presentation Data (OLE20) */
8325 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8328 /* Save OLESTREAM */
8329 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8330 if(hRes == S_OK)
8332 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8337 /* Free allocated memory */
8338 for(i=0; i < 2; i++)
8340 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8343 return hRes;
8346 /***********************************************************************
8347 * GetConvertStg (OLE32.@)
8349 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8350 FIXME("unimplemented stub!\n");
8351 return E_FAIL;
8354 /******************************************************************************
8355 * StgIsStorageFile [OLE32.@]
8356 * Verify if the file contains a storage object
8358 * PARAMS
8359 * fn [ I] Filename
8361 * RETURNS
8362 * S_OK if file has magic bytes as a storage object
8363 * S_FALSE if file is not storage
8365 HRESULT WINAPI
8366 StgIsStorageFile(LPCOLESTR fn)
8368 HANDLE hf;
8369 BYTE magic[8];
8370 DWORD bytes_read;
8372 TRACE("%s\n", debugstr_w(fn));
8373 hf = CreateFileW(fn, GENERIC_READ,
8374 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8375 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8377 if (hf == INVALID_HANDLE_VALUE)
8378 return STG_E_FILENOTFOUND;
8380 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8382 WARN(" unable to read file\n");
8383 CloseHandle(hf);
8384 return S_FALSE;
8387 CloseHandle(hf);
8389 if (bytes_read != 8) {
8390 TRACE(" too short\n");
8391 return S_FALSE;
8394 if (!memcmp(magic,STORAGE_magic,8)) {
8395 TRACE(" -> YES\n");
8396 return S_OK;
8399 TRACE(" -> Invalid header.\n");
8400 return S_FALSE;
8403 /***********************************************************************
8404 * WriteClassStm (OLE32.@)
8406 * Writes a CLSID to a stream.
8408 * PARAMS
8409 * pStm [I] Stream to write to.
8410 * rclsid [I] CLSID to write.
8412 * RETURNS
8413 * Success: S_OK.
8414 * Failure: HRESULT code.
8416 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8418 TRACE("(%p,%p)\n",pStm,rclsid);
8420 if (!pStm || !rclsid)
8421 return E_INVALIDARG;
8423 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8426 /***********************************************************************
8427 * ReadClassStm (OLE32.@)
8429 * Reads a CLSID from a stream.
8431 * PARAMS
8432 * pStm [I] Stream to read from.
8433 * rclsid [O] CLSID to read.
8435 * RETURNS
8436 * Success: S_OK.
8437 * Failure: HRESULT code.
8439 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8441 ULONG nbByte;
8442 HRESULT res;
8444 TRACE("(%p,%p)\n",pStm,pclsid);
8446 if (!pStm || !pclsid)
8447 return E_INVALIDARG;
8449 /* clear the output args */
8450 *pclsid = CLSID_NULL;
8452 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8454 if (FAILED(res))
8455 return res;
8457 if (nbByte != sizeof(CLSID))
8458 return STG_E_READFAULT;
8459 else
8460 return S_OK;