ole32: Only warn about storage share mode once.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob3beff78b18137617aa7278674f0d5dea330c0636
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))
6357 static int fixme;
6358 if (!fixme++)
6359 FIXME("Storage share mode not implemented.\n");
6362 *ppstgOpen = 0;
6364 hFile = CreateFileW(pwcsName,
6365 accessMode,
6366 shareMode,
6367 NULL,
6368 creationMode,
6369 fileAttributes,
6372 if (hFile == INVALID_HANDLE_VALUE)
6374 if(GetLastError() == ERROR_FILE_EXISTS)
6375 hr = STG_E_FILEALREADYEXISTS;
6376 else
6377 hr = E_FAIL;
6378 goto end;
6382 * Allocate and initialize the new IStorage32object.
6384 hr = Storage_Construct(
6385 hFile,
6386 pwcsName,
6387 NULL,
6388 grfMode,
6389 TRUE,
6390 TRUE,
6391 &newStorage);
6393 if (FAILED(hr))
6395 goto end;
6399 * Get an "out" pointer for the caller.
6401 *ppstgOpen = (IStorage*)newStorage;
6403 end:
6404 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6406 return hr;
6409 /******************************************************************************
6410 * StgCreateStorageEx [OLE32.@]
6412 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6414 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6415 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6417 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6419 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6420 return STG_E_INVALIDPARAMETER;
6423 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6425 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6426 return STG_E_INVALIDPARAMETER;
6429 if (stgfmt == STGFMT_FILE)
6431 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6432 return STG_E_INVALIDPARAMETER;
6435 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6437 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6438 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
6441 ERR("Invalid stgfmt argument\n");
6442 return STG_E_INVALIDPARAMETER;
6445 /******************************************************************************
6446 * StgCreatePropSetStg [OLE32.@]
6448 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6449 IPropertySetStorage **ppPropSetStg)
6451 HRESULT hr;
6453 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6454 if (reserved)
6455 hr = STG_E_INVALIDPARAMETER;
6456 else
6457 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6458 (void**)ppPropSetStg);
6459 return hr;
6462 /******************************************************************************
6463 * StgOpenStorageEx [OLE32.@]
6465 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6467 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6468 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6470 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6472 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6473 return STG_E_INVALIDPARAMETER;
6476 switch (stgfmt)
6478 case STGFMT_FILE:
6479 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6480 return STG_E_INVALIDPARAMETER;
6482 case STGFMT_STORAGE:
6483 break;
6485 case STGFMT_DOCFILE:
6486 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6488 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6489 return STG_E_INVALIDPARAMETER;
6491 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6492 break;
6494 case STGFMT_ANY:
6495 WARN("STGFMT_ANY assuming storage\n");
6496 break;
6498 default:
6499 return STG_E_INVALIDPARAMETER;
6502 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6506 /******************************************************************************
6507 * StgOpenStorage [OLE32.@]
6509 HRESULT WINAPI StgOpenStorage(
6510 const OLECHAR *pwcsName,
6511 IStorage *pstgPriority,
6512 DWORD grfMode,
6513 SNB snbExclude,
6514 DWORD reserved,
6515 IStorage **ppstgOpen)
6517 StorageBaseImpl* newStorage = 0;
6518 HRESULT hr = S_OK;
6519 HANDLE hFile = 0;
6520 DWORD shareMode;
6521 DWORD accessMode;
6523 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6524 debugstr_w(pwcsName), pstgPriority, grfMode,
6525 snbExclude, reserved, ppstgOpen);
6527 if (pwcsName == 0)
6529 hr = STG_E_INVALIDNAME;
6530 goto end;
6533 if (ppstgOpen == 0)
6535 hr = STG_E_INVALIDPOINTER;
6536 goto end;
6539 if (reserved)
6541 hr = STG_E_INVALIDPARAMETER;
6542 goto end;
6545 if (grfMode & STGM_PRIORITY)
6547 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6548 return STG_E_INVALIDFLAG;
6549 if (grfMode & STGM_DELETEONRELEASE)
6550 return STG_E_INVALIDFUNCTION;
6551 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6552 return STG_E_INVALIDFLAG;
6553 grfMode &= ~0xf0; /* remove the existing sharing mode */
6554 grfMode |= STGM_SHARE_DENY_NONE;
6556 /* STGM_PRIORITY stops other IStorage objects on the same file from
6557 * committing until the STGM_PRIORITY IStorage is closed. it also
6558 * stops non-transacted mode StgOpenStorage calls with write access from
6559 * succeeding. obviously, both of these cannot be achieved through just
6560 * file share flags */
6561 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6565 * Validate the sharing mode
6567 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6568 switch(STGM_SHARE_MODE(grfMode))
6570 case STGM_SHARE_EXCLUSIVE:
6571 case STGM_SHARE_DENY_WRITE:
6572 break;
6573 default:
6574 hr = STG_E_INVALIDFLAG;
6575 goto end;
6578 if ( FAILED( validateSTGM(grfMode) ) ||
6579 (grfMode&STGM_CREATE))
6581 hr = STG_E_INVALIDFLAG;
6582 goto end;
6585 /* shared reading requires transacted mode */
6586 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6587 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6588 !(grfMode&STGM_TRANSACTED) )
6590 hr = STG_E_INVALIDFLAG;
6591 goto end;
6595 * Interpret the STGM value grfMode
6597 shareMode = GetShareModeFromSTGM(grfMode);
6598 accessMode = GetAccessModeFromSTGM(grfMode);
6600 *ppstgOpen = 0;
6602 hFile = CreateFileW( pwcsName,
6603 accessMode,
6604 shareMode,
6605 NULL,
6606 OPEN_EXISTING,
6607 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6610 if (hFile==INVALID_HANDLE_VALUE)
6612 DWORD last_error = GetLastError();
6614 hr = E_FAIL;
6616 switch (last_error)
6618 case ERROR_FILE_NOT_FOUND:
6619 hr = STG_E_FILENOTFOUND;
6620 break;
6622 case ERROR_PATH_NOT_FOUND:
6623 hr = STG_E_PATHNOTFOUND;
6624 break;
6626 case ERROR_ACCESS_DENIED:
6627 case ERROR_WRITE_PROTECT:
6628 hr = STG_E_ACCESSDENIED;
6629 break;
6631 case ERROR_SHARING_VIOLATION:
6632 hr = STG_E_SHAREVIOLATION;
6633 break;
6635 default:
6636 hr = E_FAIL;
6639 goto end;
6643 * Refuse to open the file if it's too small to be a structured storage file
6644 * FIXME: verify the file when reading instead of here
6646 if (GetFileSize(hFile, NULL) < 0x100)
6648 CloseHandle(hFile);
6649 hr = STG_E_FILEALREADYEXISTS;
6650 goto end;
6654 * Allocate and initialize the new IStorage32object.
6656 hr = Storage_Construct(
6657 hFile,
6658 pwcsName,
6659 NULL,
6660 grfMode,
6661 TRUE,
6662 FALSE,
6663 &newStorage);
6665 if (FAILED(hr))
6668 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6670 if(hr == STG_E_INVALIDHEADER)
6671 hr = STG_E_FILEALREADYEXISTS;
6672 goto end;
6676 * Get an "out" pointer for the caller.
6678 *ppstgOpen = (IStorage*)newStorage;
6680 end:
6681 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6682 return hr;
6685 /******************************************************************************
6686 * StgCreateDocfileOnILockBytes [OLE32.@]
6688 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6689 ILockBytes *plkbyt,
6690 DWORD grfMode,
6691 DWORD reserved,
6692 IStorage** ppstgOpen)
6694 StorageBaseImpl* newStorage = 0;
6695 HRESULT hr = S_OK;
6697 if ((ppstgOpen == 0) || (plkbyt == 0))
6698 return STG_E_INVALIDPOINTER;
6701 * Allocate and initialize the new IStorage object.
6703 hr = Storage_Construct(
6706 plkbyt,
6707 grfMode,
6708 FALSE,
6709 TRUE,
6710 &newStorage);
6712 if (FAILED(hr))
6714 return hr;
6718 * Get an "out" pointer for the caller.
6720 *ppstgOpen = (IStorage*)newStorage;
6722 return hr;
6725 /******************************************************************************
6726 * StgOpenStorageOnILockBytes [OLE32.@]
6728 HRESULT WINAPI StgOpenStorageOnILockBytes(
6729 ILockBytes *plkbyt,
6730 IStorage *pstgPriority,
6731 DWORD grfMode,
6732 SNB snbExclude,
6733 DWORD reserved,
6734 IStorage **ppstgOpen)
6736 StorageBaseImpl* newStorage = 0;
6737 HRESULT hr = S_OK;
6739 if ((plkbyt == 0) || (ppstgOpen == 0))
6740 return STG_E_INVALIDPOINTER;
6742 if ( FAILED( validateSTGM(grfMode) ))
6743 return STG_E_INVALIDFLAG;
6745 *ppstgOpen = 0;
6748 * Allocate and initialize the new IStorage object.
6750 hr = Storage_Construct(
6753 plkbyt,
6754 grfMode,
6755 FALSE,
6756 FALSE,
6757 &newStorage);
6759 if (FAILED(hr))
6761 return hr;
6765 * Get an "out" pointer for the caller.
6767 *ppstgOpen = (IStorage*)newStorage;
6769 return hr;
6772 /******************************************************************************
6773 * StgSetTimes [ole32.@]
6774 * StgSetTimes [OLE32.@]
6778 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6779 FILETIME const *patime, FILETIME const *pmtime)
6781 IStorage *stg = NULL;
6782 HRESULT r;
6784 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6786 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6787 0, 0, &stg);
6788 if( SUCCEEDED(r) )
6790 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6791 IStorage_Release(stg);
6794 return r;
6797 /******************************************************************************
6798 * StgIsStorageILockBytes [OLE32.@]
6800 * Determines if the ILockBytes contains a storage object.
6802 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6804 BYTE sig[8];
6805 ULARGE_INTEGER offset;
6807 offset.u.HighPart = 0;
6808 offset.u.LowPart = 0;
6810 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6812 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6813 return S_OK;
6815 return S_FALSE;
6818 /******************************************************************************
6819 * WriteClassStg [OLE32.@]
6821 * This method will store the specified CLSID in the specified storage object
6823 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6825 HRESULT hRes;
6827 if(!pStg)
6828 return E_INVALIDARG;
6830 if(!rclsid)
6831 return STG_E_INVALIDPOINTER;
6833 hRes = IStorage_SetClass(pStg, rclsid);
6835 return hRes;
6838 /***********************************************************************
6839 * ReadClassStg (OLE32.@)
6841 * This method reads the CLSID previously written to a storage object with
6842 * the WriteClassStg.
6844 * PARAMS
6845 * pstg [I] IStorage pointer
6846 * pclsid [O] Pointer to where the CLSID is written
6848 * RETURNS
6849 * Success: S_OK.
6850 * Failure: HRESULT code.
6852 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6854 STATSTG pstatstg;
6855 HRESULT hRes;
6857 TRACE("(%p, %p)\n", pstg, pclsid);
6859 if(!pstg || !pclsid)
6860 return E_INVALIDARG;
6863 * read a STATSTG structure (contains the clsid) from the storage
6865 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6867 if(SUCCEEDED(hRes))
6868 *pclsid=pstatstg.clsid;
6870 return hRes;
6873 /***********************************************************************
6874 * OleLoadFromStream (OLE32.@)
6876 * This function loads an object from stream
6878 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6880 CLSID clsid;
6881 HRESULT res;
6882 LPPERSISTSTREAM xstm;
6884 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6886 res=ReadClassStm(pStm,&clsid);
6887 if (FAILED(res))
6888 return res;
6889 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6890 if (FAILED(res))
6891 return res;
6892 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6893 if (FAILED(res)) {
6894 IUnknown_Release((IUnknown*)*ppvObj);
6895 return res;
6897 res=IPersistStream_Load(xstm,pStm);
6898 IPersistStream_Release(xstm);
6899 /* FIXME: all refcounts ok at this point? I think they should be:
6900 * pStm : unchanged
6901 * ppvObj : 1
6902 * xstm : 0 (released)
6904 return res;
6907 /***********************************************************************
6908 * OleSaveToStream (OLE32.@)
6910 * This function saves an object with the IPersistStream interface on it
6911 * to the specified stream.
6913 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6916 CLSID clsid;
6917 HRESULT res;
6919 TRACE("(%p,%p)\n",pPStm,pStm);
6921 res=IPersistStream_GetClassID(pPStm,&clsid);
6923 if (SUCCEEDED(res)){
6925 res=WriteClassStm(pStm,&clsid);
6927 if (SUCCEEDED(res))
6929 res=IPersistStream_Save(pPStm,pStm,TRUE);
6932 TRACE("Finished Save\n");
6933 return res;
6936 /****************************************************************************
6937 * This method validate a STGM parameter that can contain the values below
6939 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6940 * The stgm values contained in 0xffff0000 are bitmasks.
6942 * STGM_DIRECT 0x00000000
6943 * STGM_TRANSACTED 0x00010000
6944 * STGM_SIMPLE 0x08000000
6946 * STGM_READ 0x00000000
6947 * STGM_WRITE 0x00000001
6948 * STGM_READWRITE 0x00000002
6950 * STGM_SHARE_DENY_NONE 0x00000040
6951 * STGM_SHARE_DENY_READ 0x00000030
6952 * STGM_SHARE_DENY_WRITE 0x00000020
6953 * STGM_SHARE_EXCLUSIVE 0x00000010
6955 * STGM_PRIORITY 0x00040000
6956 * STGM_DELETEONRELEASE 0x04000000
6958 * STGM_CREATE 0x00001000
6959 * STGM_CONVERT 0x00020000
6960 * STGM_FAILIFTHERE 0x00000000
6962 * STGM_NOSCRATCH 0x00100000
6963 * STGM_NOSNAPSHOT 0x00200000
6965 static HRESULT validateSTGM(DWORD stgm)
6967 DWORD access = STGM_ACCESS_MODE(stgm);
6968 DWORD share = STGM_SHARE_MODE(stgm);
6969 DWORD create = STGM_CREATE_MODE(stgm);
6971 if (stgm&~STGM_KNOWN_FLAGS)
6973 ERR("unknown flags %08x\n", stgm);
6974 return E_FAIL;
6977 switch (access)
6979 case STGM_READ:
6980 case STGM_WRITE:
6981 case STGM_READWRITE:
6982 break;
6983 default:
6984 return E_FAIL;
6987 switch (share)
6989 case STGM_SHARE_DENY_NONE:
6990 case STGM_SHARE_DENY_READ:
6991 case STGM_SHARE_DENY_WRITE:
6992 case STGM_SHARE_EXCLUSIVE:
6993 break;
6994 default:
6995 return E_FAIL;
6998 switch (create)
7000 case STGM_CREATE:
7001 case STGM_FAILIFTHERE:
7002 break;
7003 default:
7004 return E_FAIL;
7008 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7010 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7011 return E_FAIL;
7014 * STGM_CREATE | STGM_CONVERT
7015 * if both are false, STGM_FAILIFTHERE is set to TRUE
7017 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7018 return E_FAIL;
7021 * STGM_NOSCRATCH requires STGM_TRANSACTED
7023 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7024 return E_FAIL;
7027 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7028 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7030 if ( (stgm & STGM_NOSNAPSHOT) &&
7031 (!(stgm & STGM_TRANSACTED) ||
7032 share == STGM_SHARE_EXCLUSIVE ||
7033 share == STGM_SHARE_DENY_WRITE) )
7034 return E_FAIL;
7036 return S_OK;
7039 /****************************************************************************
7040 * GetShareModeFromSTGM
7042 * This method will return a share mode flag from a STGM value.
7043 * The STGM value is assumed valid.
7045 static DWORD GetShareModeFromSTGM(DWORD stgm)
7047 switch (STGM_SHARE_MODE(stgm))
7049 case STGM_SHARE_DENY_NONE:
7050 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7051 case STGM_SHARE_DENY_READ:
7052 return FILE_SHARE_WRITE;
7053 case STGM_SHARE_DENY_WRITE:
7054 return FILE_SHARE_READ;
7055 case STGM_SHARE_EXCLUSIVE:
7056 return 0;
7058 ERR("Invalid share mode!\n");
7059 assert(0);
7060 return 0;
7063 /****************************************************************************
7064 * GetAccessModeFromSTGM
7066 * This method will return an access mode flag from a STGM value.
7067 * The STGM value is assumed valid.
7069 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7071 switch (STGM_ACCESS_MODE(stgm))
7073 case STGM_READ:
7074 return GENERIC_READ;
7075 case STGM_WRITE:
7076 case STGM_READWRITE:
7077 return GENERIC_READ | GENERIC_WRITE;
7079 ERR("Invalid access mode!\n");
7080 assert(0);
7081 return 0;
7084 /****************************************************************************
7085 * GetCreationModeFromSTGM
7087 * This method will return a creation mode flag from a STGM value.
7088 * The STGM value is assumed valid.
7090 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7092 switch(STGM_CREATE_MODE(stgm))
7094 case STGM_CREATE:
7095 return CREATE_ALWAYS;
7096 case STGM_CONVERT:
7097 FIXME("STGM_CONVERT not implemented!\n");
7098 return CREATE_NEW;
7099 case STGM_FAILIFTHERE:
7100 return CREATE_NEW;
7102 ERR("Invalid create mode!\n");
7103 assert(0);
7104 return 0;
7108 /*************************************************************************
7109 * OLECONVERT_LoadOLE10 [Internal]
7111 * Loads the OLE10 STREAM to memory
7113 * PARAMS
7114 * pOleStream [I] The OLESTREAM
7115 * pData [I] Data Structure for the OLESTREAM Data
7117 * RETURNS
7118 * Success: S_OK
7119 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7120 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7122 * NOTES
7123 * This function is used by OleConvertOLESTREAMToIStorage only.
7125 * Memory allocated for pData must be freed by the caller
7127 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7129 DWORD dwSize;
7130 HRESULT hRes = S_OK;
7131 int nTryCnt=0;
7132 int max_try = 6;
7134 pData->pData = NULL;
7135 pData->pstrOleObjFileName = NULL;
7137 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7139 /* Get the OleID */
7140 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7141 if(dwSize != sizeof(pData->dwOleID))
7143 hRes = CONVERT10_E_OLESTREAM_GET;
7145 else if(pData->dwOleID != OLESTREAM_ID)
7147 hRes = CONVERT10_E_OLESTREAM_FMT;
7149 else
7151 hRes = S_OK;
7152 break;
7156 if(hRes == S_OK)
7158 /* Get the TypeID... more info needed for this field */
7159 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7160 if(dwSize != sizeof(pData->dwTypeID))
7162 hRes = CONVERT10_E_OLESTREAM_GET;
7165 if(hRes == S_OK)
7167 if(pData->dwTypeID != 0)
7169 /* Get the length of the OleTypeName */
7170 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7171 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7173 hRes = CONVERT10_E_OLESTREAM_GET;
7176 if(hRes == S_OK)
7178 if(pData->dwOleTypeNameLength > 0)
7180 /* Get the OleTypeName */
7181 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7182 if(dwSize != pData->dwOleTypeNameLength)
7184 hRes = CONVERT10_E_OLESTREAM_GET;
7188 if(bStrem1)
7190 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7191 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7193 hRes = CONVERT10_E_OLESTREAM_GET;
7195 if(hRes == S_OK)
7197 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7198 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7199 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7200 if(pData->pstrOleObjFileName)
7202 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7203 if(dwSize != pData->dwOleObjFileNameLength)
7205 hRes = CONVERT10_E_OLESTREAM_GET;
7208 else
7209 hRes = CONVERT10_E_OLESTREAM_GET;
7212 else
7214 /* Get the Width of the Metafile */
7215 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7216 if(dwSize != sizeof(pData->dwMetaFileWidth))
7218 hRes = CONVERT10_E_OLESTREAM_GET;
7220 if(hRes == S_OK)
7222 /* Get the Height of the Metafile */
7223 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7224 if(dwSize != sizeof(pData->dwMetaFileHeight))
7226 hRes = CONVERT10_E_OLESTREAM_GET;
7230 if(hRes == S_OK)
7232 /* Get the Length of the Data */
7233 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7234 if(dwSize != sizeof(pData->dwDataLength))
7236 hRes = CONVERT10_E_OLESTREAM_GET;
7240 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7242 if(!bStrem1) /* if it is a second OLE stream data */
7244 pData->dwDataLength -= 8;
7245 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7246 if(dwSize != sizeof(pData->strUnknown))
7248 hRes = CONVERT10_E_OLESTREAM_GET;
7252 if(hRes == S_OK)
7254 if(pData->dwDataLength > 0)
7256 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7258 /* Get Data (ex. IStorage, Metafile, or BMP) */
7259 if(pData->pData)
7261 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7262 if(dwSize != pData->dwDataLength)
7264 hRes = CONVERT10_E_OLESTREAM_GET;
7267 else
7269 hRes = CONVERT10_E_OLESTREAM_GET;
7275 return hRes;
7278 /*************************************************************************
7279 * OLECONVERT_SaveOLE10 [Internal]
7281 * Saves the OLE10 STREAM From memory
7283 * PARAMS
7284 * pData [I] Data Structure for the OLESTREAM Data
7285 * pOleStream [I] The OLESTREAM to save
7287 * RETURNS
7288 * Success: S_OK
7289 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7291 * NOTES
7292 * This function is used by OleConvertIStorageToOLESTREAM only.
7295 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7297 DWORD dwSize;
7298 HRESULT hRes = S_OK;
7301 /* Set the OleID */
7302 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7303 if(dwSize != sizeof(pData->dwOleID))
7305 hRes = CONVERT10_E_OLESTREAM_PUT;
7308 if(hRes == S_OK)
7310 /* Set the TypeID */
7311 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7312 if(dwSize != sizeof(pData->dwTypeID))
7314 hRes = CONVERT10_E_OLESTREAM_PUT;
7318 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7320 /* Set the Length of the OleTypeName */
7321 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7322 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7324 hRes = CONVERT10_E_OLESTREAM_PUT;
7327 if(hRes == S_OK)
7329 if(pData->dwOleTypeNameLength > 0)
7331 /* Set the OleTypeName */
7332 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7333 if(dwSize != pData->dwOleTypeNameLength)
7335 hRes = CONVERT10_E_OLESTREAM_PUT;
7340 if(hRes == S_OK)
7342 /* Set the width of the Metafile */
7343 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7344 if(dwSize != sizeof(pData->dwMetaFileWidth))
7346 hRes = CONVERT10_E_OLESTREAM_PUT;
7350 if(hRes == S_OK)
7352 /* Set the height of the Metafile */
7353 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7354 if(dwSize != sizeof(pData->dwMetaFileHeight))
7356 hRes = CONVERT10_E_OLESTREAM_PUT;
7360 if(hRes == S_OK)
7362 /* Set the length of the Data */
7363 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7364 if(dwSize != sizeof(pData->dwDataLength))
7366 hRes = CONVERT10_E_OLESTREAM_PUT;
7370 if(hRes == S_OK)
7372 if(pData->dwDataLength > 0)
7374 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7375 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7376 if(dwSize != pData->dwDataLength)
7378 hRes = CONVERT10_E_OLESTREAM_PUT;
7383 return hRes;
7386 /*************************************************************************
7387 * OLECONVERT_GetOLE20FromOLE10[Internal]
7389 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7390 * opens it, and copies the content to the dest IStorage for
7391 * OleConvertOLESTREAMToIStorage
7394 * PARAMS
7395 * pDestStorage [I] The IStorage to copy the data to
7396 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7397 * nBufferLength [I] The size of the buffer
7399 * RETURNS
7400 * Nothing
7402 * NOTES
7406 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7408 HRESULT hRes;
7409 HANDLE hFile;
7410 IStorage *pTempStorage;
7411 DWORD dwNumOfBytesWritten;
7412 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7413 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7415 /* Create a temp File */
7416 GetTempPathW(MAX_PATH, wstrTempDir);
7417 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7418 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7420 if(hFile != INVALID_HANDLE_VALUE)
7422 /* Write IStorage Data to File */
7423 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7424 CloseHandle(hFile);
7426 /* Open and copy temp storage to the Dest Storage */
7427 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7428 if(hRes == S_OK)
7430 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7431 IStorage_Release(pTempStorage);
7433 DeleteFileW(wstrTempFile);
7438 /*************************************************************************
7439 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7441 * Saves the OLE10 STREAM From memory
7443 * PARAMS
7444 * pStorage [I] The Src IStorage to copy
7445 * pData [I] The Dest Memory to write to.
7447 * RETURNS
7448 * The size in bytes allocated for pData
7450 * NOTES
7451 * Memory allocated for pData must be freed by the caller
7453 * Used by OleConvertIStorageToOLESTREAM only.
7456 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7458 HANDLE hFile;
7459 HRESULT hRes;
7460 DWORD nDataLength = 0;
7461 IStorage *pTempStorage;
7462 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7463 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7465 *pData = NULL;
7467 /* Create temp Storage */
7468 GetTempPathW(MAX_PATH, wstrTempDir);
7469 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7470 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7472 if(hRes == S_OK)
7474 /* Copy Src Storage to the Temp Storage */
7475 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7476 IStorage_Release(pTempStorage);
7478 /* Open Temp Storage as a file and copy to memory */
7479 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7480 if(hFile != INVALID_HANDLE_VALUE)
7482 nDataLength = GetFileSize(hFile, NULL);
7483 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7484 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7485 CloseHandle(hFile);
7487 DeleteFileW(wstrTempFile);
7489 return nDataLength;
7492 /*************************************************************************
7493 * OLECONVERT_CreateOleStream [Internal]
7495 * Creates the "\001OLE" stream in the IStorage if necessary.
7497 * PARAMS
7498 * pStorage [I] Dest storage to create the stream in
7500 * RETURNS
7501 * Nothing
7503 * NOTES
7504 * This function is used by OleConvertOLESTREAMToIStorage only.
7506 * This stream is still unknown, MS Word seems to have extra data
7507 * but since the data is stored in the OLESTREAM there should be
7508 * no need to recreate the stream. If the stream is manually
7509 * deleted it will create it with this default data.
7512 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7514 HRESULT hRes;
7515 IStream *pStream;
7516 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7517 BYTE pOleStreamHeader [] =
7519 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7520 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7521 0x00, 0x00, 0x00, 0x00
7524 /* Create stream if not present */
7525 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7526 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7528 if(hRes == S_OK)
7530 /* Write default Data */
7531 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7532 IStream_Release(pStream);
7536 /* write a string to a stream, preceded by its length */
7537 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7539 HRESULT r;
7540 LPSTR str;
7541 DWORD len = 0;
7543 if( string )
7544 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7545 r = IStream_Write( stm, &len, sizeof(len), NULL);
7546 if( FAILED( r ) )
7547 return r;
7548 if(len == 0)
7549 return r;
7550 str = CoTaskMemAlloc( len );
7551 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7552 r = IStream_Write( stm, str, len, NULL);
7553 CoTaskMemFree( str );
7554 return r;
7557 /* read a string preceded by its length from a stream */
7558 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7560 HRESULT r;
7561 DWORD len, count = 0;
7562 LPSTR str;
7563 LPWSTR wstr;
7565 r = IStream_Read( stm, &len, sizeof(len), &count );
7566 if( FAILED( r ) )
7567 return r;
7568 if( count != sizeof(len) )
7569 return E_OUTOFMEMORY;
7571 TRACE("%d bytes\n",len);
7573 str = CoTaskMemAlloc( len );
7574 if( !str )
7575 return E_OUTOFMEMORY;
7576 count = 0;
7577 r = IStream_Read( stm, str, len, &count );
7578 if( FAILED( r ) )
7579 return r;
7580 if( count != len )
7582 CoTaskMemFree( str );
7583 return E_OUTOFMEMORY;
7586 TRACE("Read string %s\n",debugstr_an(str,len));
7588 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7589 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7590 if( wstr )
7591 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7592 CoTaskMemFree( str );
7594 *string = wstr;
7596 return r;
7600 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7601 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7603 IStream *pstm;
7604 HRESULT r = S_OK;
7605 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7607 static const BYTE unknown1[12] =
7608 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7609 0xFF, 0xFF, 0xFF, 0xFF};
7610 static const BYTE unknown2[16] =
7611 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7612 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7614 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7615 debugstr_w(lpszUserType), debugstr_w(szClipName),
7616 debugstr_w(szProgIDName));
7618 /* Create a CompObj stream */
7619 r = IStorage_CreateStream(pstg, szwStreamName,
7620 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7621 if( FAILED (r) )
7622 return r;
7624 /* Write CompObj Structure to stream */
7625 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7627 if( SUCCEEDED( r ) )
7628 r = WriteClassStm( pstm, clsid );
7630 if( SUCCEEDED( r ) )
7631 r = STREAM_WriteString( pstm, lpszUserType );
7632 if( SUCCEEDED( r ) )
7633 r = STREAM_WriteString( pstm, szClipName );
7634 if( SUCCEEDED( r ) )
7635 r = STREAM_WriteString( pstm, szProgIDName );
7636 if( SUCCEEDED( r ) )
7637 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7639 IStream_Release( pstm );
7641 return r;
7644 /***********************************************************************
7645 * WriteFmtUserTypeStg (OLE32.@)
7647 HRESULT WINAPI WriteFmtUserTypeStg(
7648 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7650 HRESULT r;
7651 WCHAR szwClipName[0x40];
7652 CLSID clsid = CLSID_NULL;
7653 LPWSTR wstrProgID = NULL;
7654 DWORD n;
7656 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7658 /* get the clipboard format name */
7659 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7660 szwClipName[n]=0;
7662 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7664 /* FIXME: There's room to save a CLSID and its ProgID, but
7665 the CLSID is not looked up in the registry and in all the
7666 tests I wrote it was CLSID_NULL. Where does it come from?
7669 /* get the real program ID. This may fail, but that's fine */
7670 ProgIDFromCLSID(&clsid, &wstrProgID);
7672 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7674 r = STORAGE_WriteCompObj( pstg, &clsid,
7675 lpszUserType, szwClipName, wstrProgID );
7677 CoTaskMemFree(wstrProgID);
7679 return r;
7683 /******************************************************************************
7684 * ReadFmtUserTypeStg [OLE32.@]
7686 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7688 HRESULT r;
7689 IStream *stm = 0;
7690 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7691 unsigned char unknown1[12];
7692 unsigned char unknown2[16];
7693 DWORD count;
7694 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7695 CLSID clsid;
7697 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7699 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7700 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7701 if( FAILED ( r ) )
7703 WARN("Failed to open stream r = %08x\n", r);
7704 return r;
7707 /* read the various parts of the structure */
7708 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7709 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7710 goto end;
7711 r = ReadClassStm( stm, &clsid );
7712 if( FAILED( r ) )
7713 goto end;
7715 r = STREAM_ReadString( stm, &szCLSIDName );
7716 if( FAILED( r ) )
7717 goto end;
7719 r = STREAM_ReadString( stm, &szOleTypeName );
7720 if( FAILED( r ) )
7721 goto end;
7723 r = STREAM_ReadString( stm, &szProgIDName );
7724 if( FAILED( r ) )
7725 goto end;
7727 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7728 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7729 goto end;
7731 /* ok, success... now we just need to store what we found */
7732 if( pcf )
7733 *pcf = RegisterClipboardFormatW( szOleTypeName );
7734 CoTaskMemFree( szOleTypeName );
7736 if( lplpszUserType )
7737 *lplpszUserType = szCLSIDName;
7738 CoTaskMemFree( szProgIDName );
7740 end:
7741 IStream_Release( stm );
7743 return r;
7747 /*************************************************************************
7748 * OLECONVERT_CreateCompObjStream [Internal]
7750 * Creates a "\001CompObj" is the destination IStorage if necessary.
7752 * PARAMS
7753 * pStorage [I] The dest IStorage to create the CompObj Stream
7754 * if necessary.
7755 * strOleTypeName [I] The ProgID
7757 * RETURNS
7758 * Success: S_OK
7759 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7761 * NOTES
7762 * This function is used by OleConvertOLESTREAMToIStorage only.
7764 * The stream data is stored in the OLESTREAM and there should be
7765 * no need to recreate the stream. If the stream is manually
7766 * deleted it will attempt to create it by querying the registry.
7770 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7772 IStream *pStream;
7773 HRESULT hStorageRes, hRes = S_OK;
7774 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7775 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7776 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7778 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7779 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7781 /* Initialize the CompObj structure */
7782 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7783 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7784 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7787 /* Create a CompObj stream if it doesn't exist */
7788 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7789 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7790 if(hStorageRes == S_OK)
7792 /* copy the OleTypeName to the compobj struct */
7793 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7794 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7796 /* copy the OleTypeName to the compobj struct */
7797 /* Note: in the test made, these were Identical */
7798 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7799 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7801 /* Get the CLSID */
7802 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7803 bufferW, OLESTREAM_MAX_STR_LEN );
7804 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7806 if(hRes == S_OK)
7808 HKEY hKey;
7809 LONG hErr;
7810 /* Get the CLSID Default Name from the Registry */
7811 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7812 if(hErr == ERROR_SUCCESS)
7814 char strTemp[OLESTREAM_MAX_STR_LEN];
7815 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7816 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7817 if(hErr == ERROR_SUCCESS)
7819 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7821 RegCloseKey(hKey);
7825 /* Write CompObj Structure to stream */
7826 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7828 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7830 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7831 if(IStorageCompObj.dwCLSIDNameLength > 0)
7833 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7835 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7836 if(IStorageCompObj.dwOleTypeNameLength > 0)
7838 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7840 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7841 if(IStorageCompObj.dwProgIDNameLength > 0)
7843 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7845 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7846 IStream_Release(pStream);
7848 return hRes;
7852 /*************************************************************************
7853 * OLECONVERT_CreateOlePresStream[Internal]
7855 * Creates the "\002OlePres000" Stream with the Metafile data
7857 * PARAMS
7858 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7859 * dwExtentX [I] Width of the Metafile
7860 * dwExtentY [I] Height of the Metafile
7861 * pData [I] Metafile data
7862 * dwDataLength [I] Size of the Metafile data
7864 * RETURNS
7865 * Success: S_OK
7866 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7868 * NOTES
7869 * This function is used by OleConvertOLESTREAMToIStorage only.
7872 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7874 HRESULT hRes;
7875 IStream *pStream;
7876 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7877 BYTE pOlePresStreamHeader [] =
7879 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7880 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7881 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7882 0x00, 0x00, 0x00, 0x00
7885 BYTE pOlePresStreamHeaderEmpty [] =
7887 0x00, 0x00, 0x00, 0x00,
7888 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7889 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7890 0x00, 0x00, 0x00, 0x00
7893 /* Create the OlePres000 Stream */
7894 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7895 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7897 if(hRes == S_OK)
7899 DWORD nHeaderSize;
7900 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7902 memset(&OlePres, 0, sizeof(OlePres));
7903 /* Do we have any metafile data to save */
7904 if(dwDataLength > 0)
7906 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7907 nHeaderSize = sizeof(pOlePresStreamHeader);
7909 else
7911 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7912 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7914 /* Set width and height of the metafile */
7915 OlePres.dwExtentX = dwExtentX;
7916 OlePres.dwExtentY = -dwExtentY;
7918 /* Set Data and Length */
7919 if(dwDataLength > sizeof(METAFILEPICT16))
7921 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7922 OlePres.pData = &(pData[8]);
7924 /* Save OlePres000 Data to Stream */
7925 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7926 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7927 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7928 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7929 if(OlePres.dwSize > 0)
7931 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7933 IStream_Release(pStream);
7937 /*************************************************************************
7938 * OLECONVERT_CreateOle10NativeStream [Internal]
7940 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7942 * PARAMS
7943 * pStorage [I] Dest storage to create the stream in
7944 * pData [I] Ole10 Native Data (ex. bmp)
7945 * dwDataLength [I] Size of the Ole10 Native Data
7947 * RETURNS
7948 * Nothing
7950 * NOTES
7951 * This function is used by OleConvertOLESTREAMToIStorage only.
7953 * Might need to verify the data and return appropriate error message
7956 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7958 HRESULT hRes;
7959 IStream *pStream;
7960 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7962 /* Create the Ole10Native Stream */
7963 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7964 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7966 if(hRes == S_OK)
7968 /* Write info to stream */
7969 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7970 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7971 IStream_Release(pStream);
7976 /*************************************************************************
7977 * OLECONVERT_GetOLE10ProgID [Internal]
7979 * Finds the ProgID (or OleTypeID) from the IStorage
7981 * PARAMS
7982 * pStorage [I] The Src IStorage to get the ProgID
7983 * strProgID [I] the ProgID string to get
7984 * dwSize [I] the size of the string
7986 * RETURNS
7987 * Success: S_OK
7988 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7990 * NOTES
7991 * This function is used by OleConvertIStorageToOLESTREAM only.
7995 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7997 HRESULT hRes;
7998 IStream *pStream;
7999 LARGE_INTEGER iSeekPos;
8000 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8001 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8003 /* Open the CompObj Stream */
8004 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8005 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8006 if(hRes == S_OK)
8009 /*Get the OleType from the CompObj Stream */
8010 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8011 iSeekPos.u.HighPart = 0;
8013 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8014 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8015 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8016 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8017 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8018 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8019 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8021 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8022 if(*dwSize > 0)
8024 IStream_Read(pStream, strProgID, *dwSize, NULL);
8026 IStream_Release(pStream);
8028 else
8030 STATSTG stat;
8031 LPOLESTR wstrProgID;
8033 /* Get the OleType from the registry */
8034 REFCLSID clsid = &(stat.clsid);
8035 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8036 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8037 if(hRes == S_OK)
8039 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8043 return hRes;
8046 /*************************************************************************
8047 * OLECONVERT_GetOle10PresData [Internal]
8049 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8051 * PARAMS
8052 * pStorage [I] Src IStroage
8053 * pOleStream [I] Dest OleStream Mem Struct
8055 * RETURNS
8056 * Nothing
8058 * NOTES
8059 * This function is used by OleConvertIStorageToOLESTREAM only.
8061 * Memory allocated for pData must be freed by the caller
8065 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8068 HRESULT hRes;
8069 IStream *pStream;
8070 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8072 /* Initialize Default data for OLESTREAM */
8073 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8074 pOleStreamData[0].dwTypeID = 2;
8075 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8076 pOleStreamData[1].dwTypeID = 0;
8077 pOleStreamData[0].dwMetaFileWidth = 0;
8078 pOleStreamData[0].dwMetaFileHeight = 0;
8079 pOleStreamData[0].pData = NULL;
8080 pOleStreamData[1].pData = NULL;
8082 /* Open Ole10Native Stream */
8083 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8084 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8085 if(hRes == S_OK)
8088 /* Read Size and Data */
8089 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8090 if(pOleStreamData->dwDataLength > 0)
8092 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8093 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8095 IStream_Release(pStream);
8101 /*************************************************************************
8102 * OLECONVERT_GetOle20PresData[Internal]
8104 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8106 * PARAMS
8107 * pStorage [I] Src IStroage
8108 * pOleStreamData [I] Dest OleStream Mem Struct
8110 * RETURNS
8111 * Nothing
8113 * NOTES
8114 * This function is used by OleConvertIStorageToOLESTREAM only.
8116 * Memory allocated for pData must be freed by the caller
8118 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8120 HRESULT hRes;
8121 IStream *pStream;
8122 OLECONVERT_ISTORAGE_OLEPRES olePress;
8123 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8125 /* Initialize Default data for OLESTREAM */
8126 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8127 pOleStreamData[0].dwTypeID = 2;
8128 pOleStreamData[0].dwMetaFileWidth = 0;
8129 pOleStreamData[0].dwMetaFileHeight = 0;
8130 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8131 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8132 pOleStreamData[1].dwTypeID = 0;
8133 pOleStreamData[1].dwOleTypeNameLength = 0;
8134 pOleStreamData[1].strOleTypeName[0] = 0;
8135 pOleStreamData[1].dwMetaFileWidth = 0;
8136 pOleStreamData[1].dwMetaFileHeight = 0;
8137 pOleStreamData[1].pData = NULL;
8138 pOleStreamData[1].dwDataLength = 0;
8141 /* Open OlePress000 stream */
8142 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8143 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8144 if(hRes == S_OK)
8146 LARGE_INTEGER iSeekPos;
8147 METAFILEPICT16 MetaFilePict;
8148 static const char strMetafilePictName[] = "METAFILEPICT";
8150 /* Set the TypeID for a Metafile */
8151 pOleStreamData[1].dwTypeID = 5;
8153 /* Set the OleTypeName to Metafile */
8154 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8155 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8157 iSeekPos.u.HighPart = 0;
8158 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8160 /* Get Presentation Data */
8161 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8162 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8163 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8164 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8166 /*Set width and Height */
8167 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8168 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8169 if(olePress.dwSize > 0)
8171 /* Set Length */
8172 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8174 /* Set MetaFilePict struct */
8175 MetaFilePict.mm = 8;
8176 MetaFilePict.xExt = olePress.dwExtentX;
8177 MetaFilePict.yExt = olePress.dwExtentY;
8178 MetaFilePict.hMF = 0;
8180 /* Get Metafile Data */
8181 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8182 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8183 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8185 IStream_Release(pStream);
8189 /*************************************************************************
8190 * OleConvertOLESTREAMToIStorage [OLE32.@]
8192 * Read info on MSDN
8194 * TODO
8195 * DVTARGETDEVICE parameter is not handled
8196 * Still unsure of some mem fields for OLE 10 Stream
8197 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8198 * and "\001OLE" streams
8201 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8202 LPOLESTREAM pOleStream,
8203 LPSTORAGE pstg,
8204 const DVTARGETDEVICE* ptd)
8206 int i;
8207 HRESULT hRes=S_OK;
8208 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8210 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8212 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8214 if(ptd != NULL)
8216 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8219 if(pstg == NULL || pOleStream == NULL)
8221 hRes = E_INVALIDARG;
8224 if(hRes == S_OK)
8226 /* Load the OLESTREAM to Memory */
8227 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8230 if(hRes == S_OK)
8232 /* Load the OLESTREAM to Memory (part 2)*/
8233 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8236 if(hRes == S_OK)
8239 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8241 /* Do we have the IStorage Data in the OLESTREAM */
8242 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8244 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8245 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8247 else
8249 /* It must be an original OLE 1.0 source */
8250 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8253 else
8255 /* It must be an original OLE 1.0 source */
8256 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8259 /* Create CompObj Stream if necessary */
8260 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8261 if(hRes == S_OK)
8263 /*Create the Ole Stream if necessary */
8264 OLECONVERT_CreateOleStream(pstg);
8269 /* Free allocated memory */
8270 for(i=0; i < 2; i++)
8272 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8273 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8274 pOleStreamData[i].pstrOleObjFileName = NULL;
8276 return hRes;
8279 /*************************************************************************
8280 * OleConvertIStorageToOLESTREAM [OLE32.@]
8282 * Read info on MSDN
8284 * Read info on MSDN
8286 * TODO
8287 * Still unsure of some mem fields for OLE 10 Stream
8288 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8289 * and "\001OLE" streams.
8292 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8293 LPSTORAGE pstg,
8294 LPOLESTREAM pOleStream)
8296 int i;
8297 HRESULT hRes = S_OK;
8298 IStream *pStream;
8299 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8300 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8302 TRACE("%p %p\n", pstg, pOleStream);
8304 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8306 if(pstg == NULL || pOleStream == NULL)
8308 hRes = E_INVALIDARG;
8310 if(hRes == S_OK)
8312 /* Get the ProgID */
8313 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8314 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8316 if(hRes == S_OK)
8318 /* Was it originally Ole10 */
8319 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8320 if(hRes == S_OK)
8322 IStream_Release(pStream);
8323 /* Get Presentation Data for Ole10Native */
8324 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8326 else
8328 /* Get Presentation Data (OLE20) */
8329 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8332 /* Save OLESTREAM */
8333 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8334 if(hRes == S_OK)
8336 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8341 /* Free allocated memory */
8342 for(i=0; i < 2; i++)
8344 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8347 return hRes;
8350 /***********************************************************************
8351 * GetConvertStg (OLE32.@)
8353 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8354 FIXME("unimplemented stub!\n");
8355 return E_FAIL;
8358 /******************************************************************************
8359 * StgIsStorageFile [OLE32.@]
8360 * Verify if the file contains a storage object
8362 * PARAMS
8363 * fn [ I] Filename
8365 * RETURNS
8366 * S_OK if file has magic bytes as a storage object
8367 * S_FALSE if file is not storage
8369 HRESULT WINAPI
8370 StgIsStorageFile(LPCOLESTR fn)
8372 HANDLE hf;
8373 BYTE magic[8];
8374 DWORD bytes_read;
8376 TRACE("%s\n", debugstr_w(fn));
8377 hf = CreateFileW(fn, GENERIC_READ,
8378 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8379 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8381 if (hf == INVALID_HANDLE_VALUE)
8382 return STG_E_FILENOTFOUND;
8384 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8386 WARN(" unable to read file\n");
8387 CloseHandle(hf);
8388 return S_FALSE;
8391 CloseHandle(hf);
8393 if (bytes_read != 8) {
8394 TRACE(" too short\n");
8395 return S_FALSE;
8398 if (!memcmp(magic,STORAGE_magic,8)) {
8399 TRACE(" -> YES\n");
8400 return S_OK;
8403 TRACE(" -> Invalid header.\n");
8404 return S_FALSE;
8407 /***********************************************************************
8408 * WriteClassStm (OLE32.@)
8410 * Writes a CLSID to a stream.
8412 * PARAMS
8413 * pStm [I] Stream to write to.
8414 * rclsid [I] CLSID to write.
8416 * RETURNS
8417 * Success: S_OK.
8418 * Failure: HRESULT code.
8420 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8422 TRACE("(%p,%p)\n",pStm,rclsid);
8424 if (!pStm || !rclsid)
8425 return E_INVALIDARG;
8427 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8430 /***********************************************************************
8431 * ReadClassStm (OLE32.@)
8433 * Reads a CLSID from a stream.
8435 * PARAMS
8436 * pStm [I] Stream to read from.
8437 * rclsid [O] CLSID to read.
8439 * RETURNS
8440 * Success: S_OK.
8441 * Failure: HRESULT code.
8443 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8445 ULONG nbByte;
8446 HRESULT res;
8448 TRACE("(%p,%p)\n",pStm,pclsid);
8450 if (!pStm || !pclsid)
8451 return E_INVALIDARG;
8453 /* clear the output args */
8454 *pclsid = CLSID_NULL;
8456 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8458 if (FAILED(res))
8459 return res;
8461 if (nbByte != sizeof(CLSID))
8462 return STG_E_READFAULT;
8463 else
8464 return S_OK;