ole32: Add error checking to StorageBaseImpl_CreateStream.
[wine/multimedia.git] / dlls / ole32 / storage32.c
bloba7c12b535fae6e6efbef56089468d9a655806c0a
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;
837 HRESULT hr;
839 TRACE("(%p, %s, %x, %d, %d, %p)\n",
840 iface, debugstr_w(pwcsName), grfMode,
841 reserved1, reserved2, ppstm);
843 if (ppstm == 0)
844 return STG_E_INVALIDPOINTER;
846 if (pwcsName == 0)
847 return STG_E_INVALIDNAME;
849 if (reserved1 || reserved2)
850 return STG_E_INVALIDPARAMETER;
852 if ( FAILED( validateSTGM(grfMode) ))
853 return STG_E_INVALIDFLAG;
855 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
856 return STG_E_INVALIDFLAG;
858 if (This->reverted)
859 return STG_E_REVERTED;
862 * As documented.
864 if ((grfMode & STGM_DELETEONRELEASE) ||
865 (grfMode & STGM_TRANSACTED))
866 return STG_E_INVALIDFUNCTION;
869 * Don't worry about permissions in transacted mode, as we can always write
870 * changes; we just can't always commit them.
872 if(!(This->openFlags & STGM_TRANSACTED)) {
873 /* Can't create a stream on read-only storage */
874 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
875 return STG_E_ACCESSDENIED;
877 /* Can't create a stream with greater access than the parent. */
878 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
879 return STG_E_ACCESSDENIED;
882 if(This->openFlags & STGM_SIMPLE)
883 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
885 *ppstm = 0;
887 currentEntryRef = findElement(This,
888 This->storageDirEntry,
889 pwcsName,
890 &currentEntry);
892 if (currentEntryRef != DIRENTRY_NULL)
895 * An element with this name already exists
897 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
899 IStorage_DestroyElement(iface, pwcsName);
901 else
902 return STG_E_FILEALREADYEXISTS;
906 * memset the empty entry
908 memset(&newStreamEntry, 0, sizeof(DirEntry));
910 newStreamEntry.sizeOfNameString =
911 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
913 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
914 return STG_E_INVALIDNAME;
916 strcpyW(newStreamEntry.name, pwcsName);
918 newStreamEntry.stgType = STGTY_STREAM;
919 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
920 newStreamEntry.size.u.LowPart = 0;
921 newStreamEntry.size.u.HighPart = 0;
923 newStreamEntry.leftChild = DIRENTRY_NULL;
924 newStreamEntry.rightChild = DIRENTRY_NULL;
925 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
927 /* call CoFileTime to get the current time
928 newStreamEntry.ctime
929 newStreamEntry.mtime
932 /* newStreamEntry.clsid */
935 * Create an entry with the new data
937 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
938 if (FAILED(hr))
939 return hr;
942 * Insert the new entry in the parent storage's tree.
944 hr = insertIntoTree(
945 This,
946 This->storageDirEntry,
947 newStreamEntryRef);
948 if (FAILED(hr))
950 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
951 return hr;
955 * Open the stream to return it.
957 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
959 if (newStream != 0)
961 *ppstm = (IStream*)newStream;
963 IStream_AddRef(*ppstm);
965 else
967 return STG_E_INSUFFICIENTMEMORY;
970 return S_OK;
973 /************************************************************************
974 * Storage32BaseImpl_SetClass (IStorage)
976 * This method will write the specified CLSID in the directory entry of this
977 * storage.
979 * See Windows documentation for more details on IStorage methods.
981 static HRESULT WINAPI StorageBaseImpl_SetClass(
982 IStorage* iface,
983 REFCLSID clsid) /* [in] */
985 StorageBaseImpl *This = (StorageBaseImpl *)iface;
986 HRESULT hRes;
987 DirEntry currentEntry;
989 TRACE("(%p, %p)\n", iface, clsid);
991 if (This->reverted)
992 return STG_E_REVERTED;
994 hRes = StorageBaseImpl_ReadDirEntry(This,
995 This->storageDirEntry,
996 &currentEntry);
997 if (SUCCEEDED(hRes))
999 currentEntry.clsid = *clsid;
1001 hRes = StorageBaseImpl_WriteDirEntry(This,
1002 This->storageDirEntry,
1003 &currentEntry);
1006 return hRes;
1009 /************************************************************************
1010 ** Storage32Impl implementation
1013 /************************************************************************
1014 * Storage32BaseImpl_CreateStorage (IStorage)
1016 * This method will create the storage object within the provided storage.
1018 * See Windows documentation for more details on IStorage methods.
1020 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1021 IStorage* iface,
1022 const OLECHAR *pwcsName, /* [string][in] */
1023 DWORD grfMode, /* [in] */
1024 DWORD reserved1, /* [in] */
1025 DWORD reserved2, /* [in] */
1026 IStorage **ppstg) /* [out] */
1028 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1030 DirEntry currentEntry;
1031 DirEntry newEntry;
1032 DirRef currentEntryRef;
1033 DirRef newEntryRef;
1034 HRESULT hr;
1036 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1037 iface, debugstr_w(pwcsName), grfMode,
1038 reserved1, reserved2, ppstg);
1040 if (ppstg == 0)
1041 return STG_E_INVALIDPOINTER;
1043 if (This->openFlags & STGM_SIMPLE)
1045 return STG_E_INVALIDFUNCTION;
1048 if (pwcsName == 0)
1049 return STG_E_INVALIDNAME;
1051 *ppstg = NULL;
1053 if ( FAILED( validateSTGM(grfMode) ) ||
1054 (grfMode & STGM_DELETEONRELEASE) )
1056 WARN("bad grfMode: 0x%x\n", grfMode);
1057 return STG_E_INVALIDFLAG;
1060 if (This->reverted)
1061 return STG_E_REVERTED;
1064 * Check that we're compatible with the parent's storage mode
1066 if ( !(This->openFlags & STGM_TRANSACTED) &&
1067 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1069 WARN("access denied\n");
1070 return STG_E_ACCESSDENIED;
1073 currentEntryRef = findElement(This,
1074 This->storageDirEntry,
1075 pwcsName,
1076 &currentEntry);
1078 if (currentEntryRef != DIRENTRY_NULL)
1081 * An element with this name already exists
1083 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1084 ((This->openFlags & STGM_TRANSACTED) ||
1085 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1087 hr = IStorage_DestroyElement(iface, pwcsName);
1088 if (FAILED(hr))
1089 return hr;
1091 else
1093 WARN("file already exists\n");
1094 return STG_E_FILEALREADYEXISTS;
1097 else if (!(This->openFlags & STGM_TRANSACTED) &&
1098 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1100 WARN("read-only storage\n");
1101 return STG_E_ACCESSDENIED;
1104 memset(&newEntry, 0, sizeof(DirEntry));
1106 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1108 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1110 FIXME("name too long\n");
1111 return STG_E_INVALIDNAME;
1114 strcpyW(newEntry.name, pwcsName);
1116 newEntry.stgType = STGTY_STORAGE;
1117 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1118 newEntry.size.u.LowPart = 0;
1119 newEntry.size.u.HighPart = 0;
1121 newEntry.leftChild = DIRENTRY_NULL;
1122 newEntry.rightChild = DIRENTRY_NULL;
1123 newEntry.dirRootEntry = DIRENTRY_NULL;
1125 /* call CoFileTime to get the current time
1126 newEntry.ctime
1127 newEntry.mtime
1130 /* newEntry.clsid */
1133 * Create a new directory entry for the storage
1135 StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1138 * Insert the new directory entry into the parent storage's tree
1140 insertIntoTree(
1141 This,
1142 This->storageDirEntry,
1143 newEntryRef);
1146 * Open it to get a pointer to return.
1148 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1150 if( (hr != S_OK) || (*ppstg == NULL))
1152 return hr;
1156 return S_OK;
1160 /***************************************************************************
1162 * Internal Method
1164 * Reserve a directory entry in the file and initialize it.
1166 static HRESULT StorageImpl_CreateDirEntry(
1167 StorageBaseImpl *base,
1168 const DirEntry *newData,
1169 DirRef *index)
1171 StorageImpl *storage = (StorageImpl*)base;
1172 ULONG currentEntryIndex = 0;
1173 ULONG newEntryIndex = DIRENTRY_NULL;
1174 HRESULT hr = S_OK;
1175 BYTE currentData[RAW_DIRENTRY_SIZE];
1176 WORD sizeOfNameString;
1180 hr = StorageImpl_ReadRawDirEntry(storage,
1181 currentEntryIndex,
1182 currentData);
1184 if (SUCCEEDED(hr))
1186 StorageUtl_ReadWord(
1187 currentData,
1188 OFFSET_PS_NAMELENGTH,
1189 &sizeOfNameString);
1191 if (sizeOfNameString == 0)
1194 * The entry exists and is available, we found it.
1196 newEntryIndex = currentEntryIndex;
1199 else
1202 * We exhausted the directory entries, we will create more space below
1204 newEntryIndex = currentEntryIndex;
1206 currentEntryIndex++;
1208 } while (newEntryIndex == DIRENTRY_NULL);
1211 * grow the directory stream
1213 if (FAILED(hr))
1215 BYTE emptyData[RAW_DIRENTRY_SIZE];
1216 ULARGE_INTEGER newSize;
1217 ULONG entryIndex;
1218 ULONG lastEntry = 0;
1219 ULONG blockCount = 0;
1222 * obtain the new count of blocks in the directory stream
1224 blockCount = BlockChainStream_GetCount(
1225 storage->rootBlockChain)+1;
1228 * initialize the size used by the directory stream
1230 newSize.u.HighPart = 0;
1231 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1234 * add a block to the directory stream
1236 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1239 * memset the empty entry in order to initialize the unused newly
1240 * created entries
1242 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1245 * initialize them
1247 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1249 for(
1250 entryIndex = newEntryIndex + 1;
1251 entryIndex < lastEntry;
1252 entryIndex++)
1254 StorageImpl_WriteRawDirEntry(
1255 storage,
1256 entryIndex,
1257 emptyData);
1261 UpdateRawDirEntry(currentData, newData);
1263 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1265 if (SUCCEEDED(hr))
1266 *index = newEntryIndex;
1268 return hr;
1271 /***************************************************************************
1273 * Internal Method
1275 * Mark a directory entry in the file as free.
1277 static HRESULT StorageImpl_DestroyDirEntry(
1278 StorageBaseImpl *base,
1279 DirRef index)
1281 HRESULT hr;
1282 BYTE emptyData[RAW_DIRENTRY_SIZE];
1283 StorageImpl *storage = (StorageImpl*)base;
1285 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1287 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1289 return hr;
1293 /***************************************************************************
1295 * Internal Method
1297 * Destroy an entry, its attached data, and all entries reachable from it.
1299 static HRESULT DestroyReachableEntries(
1300 StorageBaseImpl *base,
1301 DirRef index)
1303 HRESULT hr = S_OK;
1304 DirEntry data;
1305 ULARGE_INTEGER zero;
1307 zero.QuadPart = 0;
1309 if (index != DIRENTRY_NULL)
1311 hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1313 if (SUCCEEDED(hr))
1314 hr = DestroyReachableEntries(base, data.dirRootEntry);
1316 if (SUCCEEDED(hr))
1317 hr = DestroyReachableEntries(base, data.leftChild);
1319 if (SUCCEEDED(hr))
1320 hr = DestroyReachableEntries(base, data.rightChild);
1322 if (SUCCEEDED(hr))
1323 hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1325 if (SUCCEEDED(hr))
1326 hr = StorageBaseImpl_DestroyDirEntry(base, index);
1329 return hr;
1333 /****************************************************************************
1335 * Internal Method
1337 * Case insensitive comparison of DirEntry.name by first considering
1338 * their size.
1340 * Returns <0 when name1 < name2
1341 * >0 when name1 > name2
1342 * 0 when name1 == name2
1344 static LONG entryNameCmp(
1345 const OLECHAR *name1,
1346 const OLECHAR *name2)
1348 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1350 while (diff == 0 && *name1 != 0)
1353 * We compare the string themselves only when they are of the same length
1355 diff = toupperW(*name1++) - toupperW(*name2++);
1358 return diff;
1361 /****************************************************************************
1363 * Internal Method
1365 * Add a directory entry to a storage
1367 static HRESULT insertIntoTree(
1368 StorageBaseImpl *This,
1369 DirRef parentStorageIndex,
1370 DirRef newEntryIndex)
1372 DirEntry currentEntry;
1373 DirEntry newEntry;
1376 * Read the inserted entry
1378 StorageBaseImpl_ReadDirEntry(This,
1379 newEntryIndex,
1380 &newEntry);
1383 * Read the storage entry
1385 StorageBaseImpl_ReadDirEntry(This,
1386 parentStorageIndex,
1387 &currentEntry);
1389 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1392 * The root storage contains some element, therefore, start the research
1393 * for the appropriate location.
1395 BOOL found = 0;
1396 DirRef current, next, previous, currentEntryId;
1399 * Keep a reference to the root of the storage's element tree
1401 currentEntryId = currentEntry.dirRootEntry;
1404 * Read
1406 StorageBaseImpl_ReadDirEntry(This,
1407 currentEntry.dirRootEntry,
1408 &currentEntry);
1410 previous = currentEntry.leftChild;
1411 next = currentEntry.rightChild;
1412 current = currentEntryId;
1414 while (found == 0)
1416 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1418 if (diff < 0)
1420 if (previous != DIRENTRY_NULL)
1422 StorageBaseImpl_ReadDirEntry(This,
1423 previous,
1424 &currentEntry);
1425 current = previous;
1427 else
1429 currentEntry.leftChild = newEntryIndex;
1430 StorageBaseImpl_WriteDirEntry(This,
1431 current,
1432 &currentEntry);
1433 found = 1;
1436 else if (diff > 0)
1438 if (next != DIRENTRY_NULL)
1440 StorageBaseImpl_ReadDirEntry(This,
1441 next,
1442 &currentEntry);
1443 current = next;
1445 else
1447 currentEntry.rightChild = newEntryIndex;
1448 StorageBaseImpl_WriteDirEntry(This,
1449 current,
1450 &currentEntry);
1451 found = 1;
1454 else
1457 * Trying to insert an item with the same name in the
1458 * subtree structure.
1460 return STG_E_FILEALREADYEXISTS;
1463 previous = currentEntry.leftChild;
1464 next = currentEntry.rightChild;
1467 else
1470 * The storage is empty, make the new entry the root of its element tree
1472 currentEntry.dirRootEntry = newEntryIndex;
1473 StorageBaseImpl_WriteDirEntry(This,
1474 parentStorageIndex,
1475 &currentEntry);
1478 return S_OK;
1481 /****************************************************************************
1483 * Internal Method
1485 * Find and read the element of a storage with the given name.
1487 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1488 const OLECHAR *name, DirEntry *data)
1490 DirRef currentEntry;
1492 /* Read the storage entry to find the root of the tree. */
1493 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1495 currentEntry = data->dirRootEntry;
1497 while (currentEntry != DIRENTRY_NULL)
1499 LONG cmp;
1501 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1503 cmp = entryNameCmp(name, data->name);
1505 if (cmp == 0)
1506 /* found it */
1507 break;
1509 else if (cmp < 0)
1510 currentEntry = data->leftChild;
1512 else if (cmp > 0)
1513 currentEntry = data->rightChild;
1516 return currentEntry;
1519 /****************************************************************************
1521 * Internal Method
1523 * Find and read the binary tree parent of the element with the given name.
1525 * If there is no such element, find a place where it could be inserted and
1526 * return STG_E_FILENOTFOUND.
1528 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1529 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1530 ULONG *relation)
1532 DirRef childEntry;
1533 DirEntry childData;
1535 /* Read the storage entry to find the root of the tree. */
1536 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1538 *parentEntry = storageEntry;
1539 *relation = DIRENTRY_RELATION_DIR;
1541 childEntry = parentData->dirRootEntry;
1543 while (childEntry != DIRENTRY_NULL)
1545 LONG cmp;
1547 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1549 cmp = entryNameCmp(childName, childData.name);
1551 if (cmp == 0)
1552 /* found it */
1553 break;
1555 else if (cmp < 0)
1557 *parentData = childData;
1558 *parentEntry = childEntry;
1559 *relation = DIRENTRY_RELATION_PREVIOUS;
1561 childEntry = parentData->leftChild;
1564 else if (cmp > 0)
1566 *parentData = childData;
1567 *parentEntry = childEntry;
1568 *relation = DIRENTRY_RELATION_NEXT;
1570 childEntry = parentData->rightChild;
1574 if (childEntry == DIRENTRY_NULL)
1575 return STG_E_FILENOTFOUND;
1576 else
1577 return S_OK;
1581 /*************************************************************************
1582 * CopyTo (IStorage)
1584 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1585 IStorage* iface,
1586 DWORD ciidExclude, /* [in] */
1587 const IID* rgiidExclude, /* [size_is][unique][in] */
1588 SNB snbExclude, /* [unique][in] */
1589 IStorage* pstgDest) /* [unique][in] */
1591 IEnumSTATSTG *elements = 0;
1592 STATSTG curElement, strStat;
1593 HRESULT hr;
1594 IStorage *pstgTmp, *pstgChild;
1595 IStream *pstrTmp, *pstrChild;
1596 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1597 int i;
1599 TRACE("(%p, %d, %p, %p, %p)\n",
1600 iface, ciidExclude, rgiidExclude,
1601 snbExclude, pstgDest);
1603 if ( pstgDest == 0 )
1604 return STG_E_INVALIDPOINTER;
1607 * Enumerate the elements
1609 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1611 if ( hr != S_OK )
1612 return hr;
1615 * set the class ID
1617 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1618 IStorage_SetClass( pstgDest, &curElement.clsid );
1620 for(i = 0; i < ciidExclude; ++i)
1622 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1623 skip_storage = TRUE;
1624 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1625 skip_stream = TRUE;
1626 else
1627 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1633 * Obtain the next element
1635 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1637 if ( hr == S_FALSE )
1639 hr = S_OK; /* done, every element has been copied */
1640 break;
1643 if ( snbExclude )
1645 WCHAR **snb = snbExclude;
1646 skip = FALSE;
1647 while ( *snb != NULL && !skip )
1649 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1650 skip = TRUE;
1651 ++snb;
1655 if ( skip )
1656 goto cleanup;
1658 if (curElement.type == STGTY_STORAGE)
1660 if(skip_storage)
1661 goto cleanup;
1664 * open child source storage
1666 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1667 STGM_READ|STGM_SHARE_EXCLUSIVE,
1668 NULL, 0, &pstgChild );
1670 if (hr != S_OK)
1671 goto cleanup;
1674 * create a new storage in destination storage
1676 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1677 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1678 0, 0,
1679 &pstgTmp );
1681 * if it already exist, don't create a new one use this one
1683 if (hr == STG_E_FILEALREADYEXISTS)
1685 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1686 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1687 NULL, 0, &pstgTmp );
1690 if (hr == S_OK)
1693 * do the copy recursively
1695 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1696 NULL, pstgTmp );
1698 IStorage_Release( pstgTmp );
1701 IStorage_Release( pstgChild );
1703 else if (curElement.type == STGTY_STREAM)
1705 if(skip_stream)
1706 goto cleanup;
1709 * create a new stream in destination storage. If the stream already
1710 * exist, it will be deleted and a new one will be created.
1712 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1713 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1714 0, 0, &pstrTmp );
1716 if (hr != S_OK)
1717 goto cleanup;
1720 * open child stream storage
1722 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1723 STGM_READ|STGM_SHARE_EXCLUSIVE,
1724 0, &pstrChild );
1726 if (hr == S_OK)
1729 * Get the size of the source stream
1731 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1734 * Set the size of the destination stream.
1736 IStream_SetSize(pstrTmp, strStat.cbSize);
1739 * do the copy
1741 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1742 NULL, NULL );
1744 IStream_Release( pstrChild );
1747 IStream_Release( pstrTmp );
1749 else
1751 WARN("unknown element type: %d\n", curElement.type);
1754 cleanup:
1755 CoTaskMemFree(curElement.pwcsName);
1756 } while (hr == S_OK);
1759 * Clean-up
1761 IEnumSTATSTG_Release(elements);
1763 return hr;
1766 /*************************************************************************
1767 * MoveElementTo (IStorage)
1769 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1770 IStorage* iface,
1771 const OLECHAR *pwcsName, /* [string][in] */
1772 IStorage *pstgDest, /* [unique][in] */
1773 const OLECHAR *pwcsNewName,/* [string][in] */
1774 DWORD grfFlags) /* [in] */
1776 FIXME("(%p %s %p %s %u): stub\n", iface,
1777 debugstr_w(pwcsName), pstgDest,
1778 debugstr_w(pwcsNewName), grfFlags);
1779 return E_NOTIMPL;
1782 /*************************************************************************
1783 * Commit (IStorage)
1785 * Ensures that any changes made to a storage object open in transacted mode
1786 * are reflected in the parent storage
1788 * NOTES
1789 * Wine doesn't implement transacted mode, which seems to be a basic
1790 * optimization, so we can ignore this stub for now.
1792 static HRESULT WINAPI StorageImpl_Commit(
1793 IStorage* iface,
1794 DWORD grfCommitFlags)/* [in] */
1796 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1797 return S_OK;
1800 /*************************************************************************
1801 * Revert (IStorage)
1803 * Discard all changes that have been made since the last commit operation
1805 static HRESULT WINAPI StorageImpl_Revert(
1806 IStorage* iface)
1808 TRACE("(%p)\n", iface);
1809 return S_OK;
1812 /*************************************************************************
1813 * DestroyElement (IStorage)
1815 * Strategy: This implementation is built this way for simplicity not for speed.
1816 * I always delete the topmost element of the enumeration and adjust
1817 * the deleted element pointer all the time. This takes longer to
1818 * do but allow to reinvoke DestroyElement whenever we encounter a
1819 * storage object. The optimisation resides in the usage of another
1820 * enumeration strategy that would give all the leaves of a storage
1821 * first. (postfix order)
1823 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1824 IStorage* iface,
1825 const OLECHAR *pwcsName)/* [string][in] */
1827 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1829 HRESULT hr = S_OK;
1830 DirEntry entryToDelete;
1831 DirRef entryToDeleteRef;
1833 TRACE("(%p, %s)\n",
1834 iface, debugstr_w(pwcsName));
1836 if (pwcsName==NULL)
1837 return STG_E_INVALIDPOINTER;
1839 if (This->reverted)
1840 return STG_E_REVERTED;
1842 if ( !(This->openFlags & STGM_TRANSACTED) &&
1843 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1844 return STG_E_ACCESSDENIED;
1846 entryToDeleteRef = findElement(
1847 This,
1848 This->storageDirEntry,
1849 pwcsName,
1850 &entryToDelete);
1852 if ( entryToDeleteRef == DIRENTRY_NULL )
1854 return STG_E_FILENOTFOUND;
1857 if ( entryToDelete.stgType == STGTY_STORAGE )
1859 hr = deleteStorageContents(
1860 This,
1861 entryToDeleteRef,
1862 entryToDelete);
1864 else if ( entryToDelete.stgType == STGTY_STREAM )
1866 hr = deleteStreamContents(
1867 This,
1868 entryToDeleteRef,
1869 entryToDelete);
1872 if (hr!=S_OK)
1873 return hr;
1876 * Remove the entry from its parent storage
1878 hr = removeFromTree(
1879 This,
1880 This->storageDirEntry,
1881 entryToDeleteRef);
1884 * Invalidate the entry
1886 if (SUCCEEDED(hr))
1887 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1889 return hr;
1893 /******************************************************************************
1894 * Internal stream list handlers
1897 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1899 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1900 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1903 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1905 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1906 list_remove(&(strm->StrmListEntry));
1909 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1911 StgStreamImpl *strm;
1913 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1915 if (strm->dirEntry == streamEntry)
1917 return TRUE;
1921 return FALSE;
1924 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1926 StorageInternalImpl *childstg;
1928 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1930 if (childstg->base.storageDirEntry == storageEntry)
1932 return TRUE;
1936 return FALSE;
1939 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1941 struct list *cur, *cur2;
1942 StgStreamImpl *strm=NULL;
1943 StorageInternalImpl *childstg=NULL;
1945 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1946 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1947 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1948 strm->parentStorage = NULL;
1949 list_remove(cur);
1952 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1953 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1954 StorageBaseImpl_Invalidate( &childstg->base );
1957 if (stg->transactedChild)
1959 StorageBaseImpl_Invalidate(stg->transactedChild);
1961 stg->transactedChild = NULL;
1966 /*********************************************************************
1968 * Internal Method
1970 * Delete the contents of a storage entry.
1973 static HRESULT deleteStorageContents(
1974 StorageBaseImpl *parentStorage,
1975 DirRef indexToDelete,
1976 DirEntry entryDataToDelete)
1978 IEnumSTATSTG *elements = 0;
1979 IStorage *childStorage = 0;
1980 STATSTG currentElement;
1981 HRESULT hr;
1982 HRESULT destroyHr = S_OK;
1983 StorageInternalImpl *stg, *stg2;
1985 /* Invalidate any open storage objects. */
1986 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1988 if (stg->base.storageDirEntry == indexToDelete)
1990 StorageBaseImpl_Invalidate(&stg->base);
1995 * Open the storage and enumerate it
1997 hr = StorageBaseImpl_OpenStorage(
1998 (IStorage*)parentStorage,
1999 entryDataToDelete.name,
2001 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2004 &childStorage);
2006 if (hr != S_OK)
2008 return hr;
2012 * Enumerate the elements
2014 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2019 * Obtain the next element
2021 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2022 if (hr==S_OK)
2024 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2026 CoTaskMemFree(currentElement.pwcsName);
2030 * We need to Reset the enumeration every time because we delete elements
2031 * and the enumeration could be invalid
2033 IEnumSTATSTG_Reset(elements);
2035 } while ((hr == S_OK) && (destroyHr == S_OK));
2037 IStorage_Release(childStorage);
2038 IEnumSTATSTG_Release(elements);
2040 return destroyHr;
2043 /*********************************************************************
2045 * Internal Method
2047 * Perform the deletion of a stream's data
2050 static HRESULT deleteStreamContents(
2051 StorageBaseImpl *parentStorage,
2052 DirRef indexToDelete,
2053 DirEntry entryDataToDelete)
2055 IStream *pis;
2056 HRESULT hr;
2057 ULARGE_INTEGER size;
2058 StgStreamImpl *strm, *strm2;
2060 /* Invalidate any open stream objects. */
2061 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2063 if (strm->dirEntry == indexToDelete)
2065 TRACE("Stream deleted %p\n", strm);
2066 strm->parentStorage = NULL;
2067 list_remove(&strm->StrmListEntry);
2071 size.u.HighPart = 0;
2072 size.u.LowPart = 0;
2074 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2075 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2077 if (hr!=S_OK)
2079 return(hr);
2083 * Zap the stream
2085 hr = IStream_SetSize(pis, size);
2087 if(hr != S_OK)
2089 return hr;
2093 * Release the stream object.
2095 IStream_Release(pis);
2097 return S_OK;
2100 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2102 switch (relation)
2104 case DIRENTRY_RELATION_PREVIOUS:
2105 entry->leftChild = new_target;
2106 break;
2107 case DIRENTRY_RELATION_NEXT:
2108 entry->rightChild = new_target;
2109 break;
2110 case DIRENTRY_RELATION_DIR:
2111 entry->dirRootEntry = new_target;
2112 break;
2113 default:
2114 assert(0);
2118 /*************************************************************************
2120 * Internal Method
2122 * This method removes a directory entry from its parent storage tree without
2123 * freeing any resources attached to it.
2125 static HRESULT removeFromTree(
2126 StorageBaseImpl *This,
2127 DirRef parentStorageIndex,
2128 DirRef deletedIndex)
2130 HRESULT hr = S_OK;
2131 DirEntry entryToDelete;
2132 DirEntry parentEntry;
2133 DirRef parentEntryRef;
2134 ULONG typeOfRelation;
2136 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2138 if (hr != S_OK)
2139 return hr;
2142 * Find the element that links to the one we want to delete.
2144 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2145 &parentEntry, &parentEntryRef, &typeOfRelation);
2147 if (hr != S_OK)
2148 return hr;
2150 if (entryToDelete.leftChild != DIRENTRY_NULL)
2153 * Replace the deleted entry with its left child
2155 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2157 hr = StorageBaseImpl_WriteDirEntry(
2158 This,
2159 parentEntryRef,
2160 &parentEntry);
2161 if(FAILED(hr))
2163 return hr;
2166 if (entryToDelete.rightChild != DIRENTRY_NULL)
2169 * We need to reinsert the right child somewhere. We already know it and
2170 * its children are greater than everything in the left tree, so we
2171 * insert it at the rightmost point in the left tree.
2173 DirRef newRightChildParent = entryToDelete.leftChild;
2174 DirEntry newRightChildParentEntry;
2178 hr = StorageBaseImpl_ReadDirEntry(
2179 This,
2180 newRightChildParent,
2181 &newRightChildParentEntry);
2182 if (FAILED(hr))
2184 return hr;
2187 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2188 newRightChildParent = newRightChildParentEntry.rightChild;
2189 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2191 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2193 hr = StorageBaseImpl_WriteDirEntry(
2194 This,
2195 newRightChildParent,
2196 &newRightChildParentEntry);
2197 if (FAILED(hr))
2199 return hr;
2203 else
2206 * Replace the deleted entry with its right child
2208 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2210 hr = StorageBaseImpl_WriteDirEntry(
2211 This,
2212 parentEntryRef,
2213 &parentEntry);
2214 if(FAILED(hr))
2216 return hr;
2220 return hr;
2224 /******************************************************************************
2225 * SetElementTimes (IStorage)
2227 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2228 IStorage* iface,
2229 const OLECHAR *pwcsName,/* [string][in] */
2230 const FILETIME *pctime, /* [in] */
2231 const FILETIME *patime, /* [in] */
2232 const FILETIME *pmtime) /* [in] */
2234 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2235 return S_OK;
2238 /******************************************************************************
2239 * SetStateBits (IStorage)
2241 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2242 IStorage* iface,
2243 DWORD grfStateBits,/* [in] */
2244 DWORD grfMask) /* [in] */
2246 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2248 if (This->reverted)
2249 return STG_E_REVERTED;
2251 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2252 return S_OK;
2255 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2256 DirRef index, const DirEntry *data)
2258 StorageImpl *This = (StorageImpl*)base;
2259 return StorageImpl_WriteDirEntry(This, index, data);
2262 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2263 DirRef index, DirEntry *data)
2265 StorageImpl *This = (StorageImpl*)base;
2266 return StorageImpl_ReadDirEntry(This, index, data);
2269 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2271 int i;
2273 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2275 if (!This->blockChainCache[i])
2277 return &This->blockChainCache[i];
2281 i = This->blockChainToEvict;
2283 BlockChainStream_Destroy(This->blockChainCache[i]);
2284 This->blockChainCache[i] = NULL;
2286 This->blockChainToEvict++;
2287 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2288 This->blockChainToEvict = 0;
2290 return &This->blockChainCache[i];
2293 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2294 DirRef index)
2296 int i, free_index=-1;
2298 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2300 if (!This->blockChainCache[i])
2302 if (free_index == -1) free_index = i;
2304 else if (This->blockChainCache[i]->ownerDirEntry == index)
2306 return &This->blockChainCache[i];
2310 if (free_index == -1)
2312 free_index = This->blockChainToEvict;
2314 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2315 This->blockChainCache[free_index] = NULL;
2317 This->blockChainToEvict++;
2318 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2319 This->blockChainToEvict = 0;
2322 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2323 return &This->blockChainCache[free_index];
2326 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2327 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2329 StorageImpl *This = (StorageImpl*)base;
2330 DirEntry data;
2331 HRESULT hr;
2332 ULONG bytesToRead;
2334 hr = StorageImpl_ReadDirEntry(This, index, &data);
2335 if (FAILED(hr)) return hr;
2337 if (data.size.QuadPart == 0)
2339 *bytesRead = 0;
2340 return S_OK;
2343 if (offset.QuadPart + size > data.size.QuadPart)
2345 bytesToRead = data.size.QuadPart - offset.QuadPart;
2347 else
2349 bytesToRead = size;
2352 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2354 SmallBlockChainStream *stream;
2356 stream = SmallBlockChainStream_Construct(This, NULL, index);
2357 if (!stream) return E_OUTOFMEMORY;
2359 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2361 SmallBlockChainStream_Destroy(stream);
2363 return hr;
2365 else
2367 BlockChainStream *stream = NULL;
2369 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2370 if (!stream) return E_OUTOFMEMORY;
2372 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2374 return hr;
2378 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2379 ULARGE_INTEGER newsize)
2381 StorageImpl *This = (StorageImpl*)base;
2382 DirEntry data;
2383 HRESULT hr;
2384 SmallBlockChainStream *smallblock=NULL;
2385 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2387 hr = StorageImpl_ReadDirEntry(This, index, &data);
2388 if (FAILED(hr)) return hr;
2390 /* In simple mode keep the stream size above the small block limit */
2391 if (This->base.openFlags & STGM_SIMPLE)
2392 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2394 if (data.size.QuadPart == newsize.QuadPart)
2395 return S_OK;
2397 /* Create a block chain object of the appropriate type */
2398 if (data.size.QuadPart == 0)
2400 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2402 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2403 if (!smallblock) return E_OUTOFMEMORY;
2405 else
2407 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2408 bigblock = *pbigblock;
2409 if (!bigblock) return E_OUTOFMEMORY;
2412 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2414 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2415 if (!smallblock) return E_OUTOFMEMORY;
2417 else
2419 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2420 bigblock = *pbigblock;
2421 if (!bigblock) return E_OUTOFMEMORY;
2424 /* Change the block chain type if necessary. */
2425 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2427 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2428 if (!bigblock)
2430 SmallBlockChainStream_Destroy(smallblock);
2431 return E_FAIL;
2434 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2435 *pbigblock = bigblock;
2437 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2439 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2440 if (!smallblock)
2441 return E_FAIL;
2444 /* Set the size of the block chain. */
2445 if (smallblock)
2447 SmallBlockChainStream_SetSize(smallblock, newsize);
2448 SmallBlockChainStream_Destroy(smallblock);
2450 else
2452 BlockChainStream_SetSize(bigblock, newsize);
2455 /* Set the size in the directory entry. */
2456 hr = StorageImpl_ReadDirEntry(This, index, &data);
2457 if (SUCCEEDED(hr))
2459 data.size = newsize;
2461 hr = StorageImpl_WriteDirEntry(This, index, &data);
2463 return hr;
2466 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2467 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2469 StorageImpl *This = (StorageImpl*)base;
2470 DirEntry data;
2471 HRESULT hr;
2472 ULARGE_INTEGER newSize;
2474 hr = StorageImpl_ReadDirEntry(This, index, &data);
2475 if (FAILED(hr)) return hr;
2477 /* Grow the stream if necessary */
2478 newSize.QuadPart = 0;
2479 newSize.QuadPart = offset.QuadPart + size;
2481 if (newSize.QuadPart > data.size.QuadPart)
2483 hr = StorageImpl_StreamSetSize(base, index, newSize);
2484 if (FAILED(hr))
2485 return hr;
2487 hr = StorageImpl_ReadDirEntry(This, index, &data);
2488 if (FAILED(hr)) return hr;
2491 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2493 SmallBlockChainStream *stream;
2495 stream = SmallBlockChainStream_Construct(This, NULL, index);
2496 if (!stream) return E_OUTOFMEMORY;
2498 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2500 SmallBlockChainStream_Destroy(stream);
2502 return hr;
2504 else
2506 BlockChainStream *stream;
2508 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2509 if (!stream) return E_OUTOFMEMORY;
2511 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2513 return hr;
2518 * Virtual function table for the IStorage32Impl class.
2520 static const IStorageVtbl Storage32Impl_Vtbl =
2522 StorageBaseImpl_QueryInterface,
2523 StorageBaseImpl_AddRef,
2524 StorageBaseImpl_Release,
2525 StorageBaseImpl_CreateStream,
2526 StorageBaseImpl_OpenStream,
2527 StorageBaseImpl_CreateStorage,
2528 StorageBaseImpl_OpenStorage,
2529 StorageBaseImpl_CopyTo,
2530 StorageBaseImpl_MoveElementTo,
2531 StorageImpl_Commit,
2532 StorageImpl_Revert,
2533 StorageBaseImpl_EnumElements,
2534 StorageBaseImpl_DestroyElement,
2535 StorageBaseImpl_RenameElement,
2536 StorageBaseImpl_SetElementTimes,
2537 StorageBaseImpl_SetClass,
2538 StorageBaseImpl_SetStateBits,
2539 StorageBaseImpl_Stat
2542 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2544 StorageImpl_Destroy,
2545 StorageImpl_Invalidate,
2546 StorageImpl_CreateDirEntry,
2547 StorageImpl_BaseWriteDirEntry,
2548 StorageImpl_BaseReadDirEntry,
2549 StorageImpl_DestroyDirEntry,
2550 StorageImpl_StreamReadAt,
2551 StorageImpl_StreamWriteAt,
2552 StorageImpl_StreamSetSize
2555 static HRESULT StorageImpl_Construct(
2556 HANDLE hFile,
2557 LPCOLESTR pwcsName,
2558 ILockBytes* pLkbyt,
2559 DWORD openFlags,
2560 BOOL fileBased,
2561 BOOL create,
2562 StorageImpl** result)
2564 StorageImpl* This;
2565 HRESULT hr = S_OK;
2566 DirEntry currentEntry;
2567 DirRef currentEntryRef;
2568 WCHAR fullpath[MAX_PATH];
2570 if ( FAILED( validateSTGM(openFlags) ))
2571 return STG_E_INVALIDFLAG;
2573 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2574 if (!This)
2575 return E_OUTOFMEMORY;
2577 memset(This, 0, sizeof(StorageImpl));
2579 list_init(&This->base.strmHead);
2581 list_init(&This->base.storageHead);
2583 This->base.lpVtbl = &Storage32Impl_Vtbl;
2584 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2585 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2586 This->base.openFlags = (openFlags & ~STGM_CREATE);
2587 This->base.ref = 1;
2588 This->base.create = create;
2590 This->base.reverted = 0;
2592 This->hFile = hFile;
2594 if(pwcsName) {
2595 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2597 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2599 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2600 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2601 if (!This->pwcsName)
2603 hr = STG_E_INSUFFICIENTMEMORY;
2604 goto end;
2606 strcpyW(This->pwcsName, fullpath);
2607 This->base.filename = This->pwcsName;
2611 * Initialize the big block cache.
2613 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2614 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2615 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2616 pLkbyt,
2617 openFlags,
2618 This->bigBlockSize,
2619 fileBased);
2621 if (This->bigBlockFile == 0)
2623 hr = E_FAIL;
2624 goto end;
2627 if (create)
2629 ULARGE_INTEGER size;
2630 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2633 * Initialize all header variables:
2634 * - The big block depot consists of one block and it is at block 0
2635 * - The directory table starts at block 1
2636 * - There is no small block depot
2638 memset( This->bigBlockDepotStart,
2639 BLOCK_UNUSED,
2640 sizeof(This->bigBlockDepotStart));
2642 This->bigBlockDepotCount = 1;
2643 This->bigBlockDepotStart[0] = 0;
2644 This->rootStartBlock = 1;
2645 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2646 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2647 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2648 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2649 This->extBigBlockDepotCount = 0;
2651 StorageImpl_SaveFileHeader(This);
2654 * Add one block for the big block depot and one block for the directory table
2656 size.u.HighPart = 0;
2657 size.u.LowPart = This->bigBlockSize * 3;
2658 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2661 * Initialize the big block depot
2663 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2664 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2665 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2666 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2668 else
2671 * Load the header for the file.
2673 hr = StorageImpl_LoadFileHeader(This);
2675 if (FAILED(hr))
2677 goto end;
2682 * There is no block depot cached yet.
2684 This->indexBlockDepotCached = 0xFFFFFFFF;
2687 * Start searching for free blocks with block 0.
2689 This->prevFreeBlock = 0;
2692 * Create the block chain abstractions.
2694 if(!(This->rootBlockChain =
2695 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2697 hr = STG_E_READFAULT;
2698 goto end;
2701 if(!(This->smallBlockDepotChain =
2702 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2703 DIRENTRY_NULL)))
2705 hr = STG_E_READFAULT;
2706 goto end;
2710 * Write the root storage entry (memory only)
2712 if (create)
2714 DirEntry rootEntry;
2716 * Initialize the directory table
2718 memset(&rootEntry, 0, sizeof(rootEntry));
2719 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2720 sizeof(rootEntry.name)/sizeof(WCHAR) );
2721 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2722 rootEntry.stgType = STGTY_ROOT;
2723 rootEntry.leftChild = DIRENTRY_NULL;
2724 rootEntry.rightChild = DIRENTRY_NULL;
2725 rootEntry.dirRootEntry = DIRENTRY_NULL;
2726 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2727 rootEntry.size.u.HighPart = 0;
2728 rootEntry.size.u.LowPart = 0;
2730 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2734 * Find the ID of the root storage.
2736 currentEntryRef = 0;
2740 hr = StorageImpl_ReadDirEntry(
2741 This,
2742 currentEntryRef,
2743 &currentEntry);
2745 if (SUCCEEDED(hr))
2747 if ( (currentEntry.sizeOfNameString != 0 ) &&
2748 (currentEntry.stgType == STGTY_ROOT) )
2750 This->base.storageDirEntry = currentEntryRef;
2754 currentEntryRef++;
2756 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2758 if (FAILED(hr))
2760 hr = STG_E_READFAULT;
2761 goto end;
2765 * Create the block chain abstraction for the small block root chain.
2767 if(!(This->smallBlockRootChain =
2768 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2770 hr = STG_E_READFAULT;
2773 end:
2774 if (FAILED(hr))
2776 IStorage_Release((IStorage*)This);
2777 *result = NULL;
2779 else
2780 *result = This;
2782 return hr;
2785 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2787 StorageImpl *This = (StorageImpl*) iface;
2789 StorageBaseImpl_DeleteAll(&This->base);
2791 This->base.reverted = 1;
2794 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2796 StorageImpl *This = (StorageImpl*) iface;
2797 int i;
2798 TRACE("(%p)\n", This);
2800 StorageImpl_Invalidate(iface);
2802 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2804 BlockChainStream_Destroy(This->smallBlockRootChain);
2805 BlockChainStream_Destroy(This->rootBlockChain);
2806 BlockChainStream_Destroy(This->smallBlockDepotChain);
2808 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2809 BlockChainStream_Destroy(This->blockChainCache[i]);
2811 if (This->bigBlockFile)
2812 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2813 HeapFree(GetProcessHeap(), 0, This);
2816 /******************************************************************************
2817 * Storage32Impl_GetNextFreeBigBlock
2819 * Returns the index of the next free big block.
2820 * If the big block depot is filled, this method will enlarge it.
2823 static ULONG StorageImpl_GetNextFreeBigBlock(
2824 StorageImpl* This)
2826 ULONG depotBlockIndexPos;
2827 BYTE depotBuffer[BIG_BLOCK_SIZE];
2828 BOOL success;
2829 ULONG depotBlockOffset;
2830 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2831 ULONG nextBlockIndex = BLOCK_SPECIAL;
2832 int depotIndex = 0;
2833 ULONG freeBlock = BLOCK_UNUSED;
2835 depotIndex = This->prevFreeBlock / blocksPerDepot;
2836 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2839 * Scan the entire big block depot until we find a block marked free
2841 while (nextBlockIndex != BLOCK_UNUSED)
2843 if (depotIndex < COUNT_BBDEPOTINHEADER)
2845 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2848 * Grow the primary depot.
2850 if (depotBlockIndexPos == BLOCK_UNUSED)
2852 depotBlockIndexPos = depotIndex*blocksPerDepot;
2855 * Add a block depot.
2857 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2858 This->bigBlockDepotCount++;
2859 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2862 * Flag it as a block depot.
2864 StorageImpl_SetNextBlockInChain(This,
2865 depotBlockIndexPos,
2866 BLOCK_SPECIAL);
2868 /* Save new header information.
2870 StorageImpl_SaveFileHeader(This);
2873 else
2875 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2877 if (depotBlockIndexPos == BLOCK_UNUSED)
2880 * Grow the extended depot.
2882 ULONG extIndex = BLOCK_UNUSED;
2883 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2884 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2886 if (extBlockOffset == 0)
2888 /* We need an extended block.
2890 extIndex = Storage32Impl_AddExtBlockDepot(This);
2891 This->extBigBlockDepotCount++;
2892 depotBlockIndexPos = extIndex + 1;
2894 else
2895 depotBlockIndexPos = depotIndex * blocksPerDepot;
2898 * Add a block depot and mark it in the extended block.
2900 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2901 This->bigBlockDepotCount++;
2902 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2904 /* Flag the block depot.
2906 StorageImpl_SetNextBlockInChain(This,
2907 depotBlockIndexPos,
2908 BLOCK_SPECIAL);
2910 /* If necessary, flag the extended depot block.
2912 if (extIndex != BLOCK_UNUSED)
2913 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2915 /* Save header information.
2917 StorageImpl_SaveFileHeader(This);
2921 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2923 if (success)
2925 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2926 ( nextBlockIndex != BLOCK_UNUSED))
2928 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2930 if (nextBlockIndex == BLOCK_UNUSED)
2932 freeBlock = (depotIndex * blocksPerDepot) +
2933 (depotBlockOffset/sizeof(ULONG));
2936 depotBlockOffset += sizeof(ULONG);
2940 depotIndex++;
2941 depotBlockOffset = 0;
2945 * make sure that the block physically exists before using it
2947 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2949 This->prevFreeBlock = freeBlock;
2951 return freeBlock;
2954 /******************************************************************************
2955 * Storage32Impl_AddBlockDepot
2957 * This will create a depot block, essentially it is a block initialized
2958 * to BLOCK_UNUSEDs.
2960 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2962 BYTE blockBuffer[BIG_BLOCK_SIZE];
2965 * Initialize blocks as free
2967 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2968 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2971 /******************************************************************************
2972 * Storage32Impl_GetExtDepotBlock
2974 * Returns the index of the block that corresponds to the specified depot
2975 * index. This method is only for depot indexes equal or greater than
2976 * COUNT_BBDEPOTINHEADER.
2978 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2980 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2981 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2982 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2983 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2984 ULONG blockIndex = BLOCK_UNUSED;
2985 ULONG extBlockIndex = This->extBigBlockDepotStart;
2987 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2989 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2990 return BLOCK_UNUSED;
2992 while (extBlockCount > 0)
2994 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2995 extBlockCount--;
2998 if (extBlockIndex != BLOCK_UNUSED)
2999 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3000 extBlockOffset * sizeof(ULONG), &blockIndex);
3002 return blockIndex;
3005 /******************************************************************************
3006 * Storage32Impl_SetExtDepotBlock
3008 * Associates the specified block index to the specified depot index.
3009 * This method is only for depot indexes equal or greater than
3010 * COUNT_BBDEPOTINHEADER.
3012 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3014 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3015 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3016 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3017 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3018 ULONG extBlockIndex = This->extBigBlockDepotStart;
3020 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3022 while (extBlockCount > 0)
3024 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3025 extBlockCount--;
3028 if (extBlockIndex != BLOCK_UNUSED)
3030 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3031 extBlockOffset * sizeof(ULONG),
3032 blockIndex);
3036 /******************************************************************************
3037 * Storage32Impl_AddExtBlockDepot
3039 * Creates an extended depot block.
3041 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3043 ULONG numExtBlocks = This->extBigBlockDepotCount;
3044 ULONG nextExtBlock = This->extBigBlockDepotStart;
3045 BYTE depotBuffer[BIG_BLOCK_SIZE];
3046 ULONG index = BLOCK_UNUSED;
3047 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3048 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3049 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3051 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3052 blocksPerDepotBlock;
3054 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3057 * The first extended block.
3059 This->extBigBlockDepotStart = index;
3061 else
3063 unsigned int i;
3065 * Follow the chain to the last one.
3067 for (i = 0; i < (numExtBlocks - 1); i++)
3069 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3073 * Add the new extended block to the chain.
3075 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3076 index);
3080 * Initialize this block.
3082 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3083 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3085 return index;
3088 /******************************************************************************
3089 * Storage32Impl_FreeBigBlock
3091 * This method will flag the specified block as free in the big block depot.
3093 static void StorageImpl_FreeBigBlock(
3094 StorageImpl* This,
3095 ULONG blockIndex)
3097 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3099 if (blockIndex < This->prevFreeBlock)
3100 This->prevFreeBlock = blockIndex;
3103 /************************************************************************
3104 * Storage32Impl_GetNextBlockInChain
3106 * This method will retrieve the block index of the next big block in
3107 * in the chain.
3109 * Params: This - Pointer to the Storage object.
3110 * blockIndex - Index of the block to retrieve the chain
3111 * for.
3112 * nextBlockIndex - receives the return value.
3114 * Returns: This method returns the index of the next block in the chain.
3115 * It will return the constants:
3116 * BLOCK_SPECIAL - If the block given was not part of a
3117 * chain.
3118 * BLOCK_END_OF_CHAIN - If the block given was the last in
3119 * a chain.
3120 * BLOCK_UNUSED - If the block given was not past of a chain
3121 * and is available.
3122 * BLOCK_EXTBBDEPOT - This block is part of the extended
3123 * big block depot.
3125 * See Windows documentation for more details on IStorage methods.
3127 static HRESULT StorageImpl_GetNextBlockInChain(
3128 StorageImpl* This,
3129 ULONG blockIndex,
3130 ULONG* nextBlockIndex)
3132 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3133 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3134 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3135 BYTE depotBuffer[BIG_BLOCK_SIZE];
3136 BOOL success;
3137 ULONG depotBlockIndexPos;
3138 int index;
3140 *nextBlockIndex = BLOCK_SPECIAL;
3142 if(depotBlockCount >= This->bigBlockDepotCount)
3144 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3145 This->bigBlockDepotCount);
3146 return STG_E_READFAULT;
3150 * Cache the currently accessed depot block.
3152 if (depotBlockCount != This->indexBlockDepotCached)
3154 This->indexBlockDepotCached = depotBlockCount;
3156 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3158 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3160 else
3163 * We have to look in the extended depot.
3165 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3168 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3170 if (!success)
3171 return STG_E_READFAULT;
3173 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
3175 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3176 This->blockDepotCached[index] = *nextBlockIndex;
3180 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3182 return S_OK;
3185 /******************************************************************************
3186 * Storage32Impl_GetNextExtendedBlock
3188 * Given an extended block this method will return the next extended block.
3190 * NOTES:
3191 * The last ULONG of an extended block is the block index of the next
3192 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3193 * depot.
3195 * Return values:
3196 * - The index of the next extended block
3197 * - BLOCK_UNUSED: there is no next extended block.
3198 * - Any other return values denotes failure.
3200 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3202 ULONG nextBlockIndex = BLOCK_SPECIAL;
3203 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3205 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3206 &nextBlockIndex);
3208 return nextBlockIndex;
3211 /******************************************************************************
3212 * Storage32Impl_SetNextBlockInChain
3214 * This method will write the index of the specified block's next block
3215 * in the big block depot.
3217 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3218 * do the following
3220 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3221 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3222 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3225 static void StorageImpl_SetNextBlockInChain(
3226 StorageImpl* This,
3227 ULONG blockIndex,
3228 ULONG nextBlock)
3230 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3231 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3232 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3233 ULONG depotBlockIndexPos;
3235 assert(depotBlockCount < This->bigBlockDepotCount);
3236 assert(blockIndex != nextBlock);
3238 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3240 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3242 else
3245 * We have to look in the extended depot.
3247 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3250 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3251 nextBlock);
3253 * Update the cached block depot, if necessary.
3255 if (depotBlockCount == This->indexBlockDepotCached)
3257 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3261 /******************************************************************************
3262 * Storage32Impl_LoadFileHeader
3264 * This method will read in the file header, i.e. big block index -1.
3266 static HRESULT StorageImpl_LoadFileHeader(
3267 StorageImpl* This)
3269 HRESULT hr = STG_E_FILENOTFOUND;
3270 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3271 BOOL success;
3272 int index;
3274 TRACE("\n");
3276 * Get a pointer to the big block of data containing the header.
3278 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3281 * Extract the information from the header.
3283 if (success)
3286 * Check for the "magic number" signature and return an error if it is not
3287 * found.
3289 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3291 return STG_E_OLDFORMAT;
3294 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3296 return STG_E_INVALIDHEADER;
3299 StorageUtl_ReadWord(
3300 headerBigBlock,
3301 OFFSET_BIGBLOCKSIZEBITS,
3302 &This->bigBlockSizeBits);
3304 StorageUtl_ReadWord(
3305 headerBigBlock,
3306 OFFSET_SMALLBLOCKSIZEBITS,
3307 &This->smallBlockSizeBits);
3309 StorageUtl_ReadDWord(
3310 headerBigBlock,
3311 OFFSET_BBDEPOTCOUNT,
3312 &This->bigBlockDepotCount);
3314 StorageUtl_ReadDWord(
3315 headerBigBlock,
3316 OFFSET_ROOTSTARTBLOCK,
3317 &This->rootStartBlock);
3319 StorageUtl_ReadDWord(
3320 headerBigBlock,
3321 OFFSET_SBDEPOTSTART,
3322 &This->smallBlockDepotStart);
3324 StorageUtl_ReadDWord(
3325 headerBigBlock,
3326 OFFSET_EXTBBDEPOTSTART,
3327 &This->extBigBlockDepotStart);
3329 StorageUtl_ReadDWord(
3330 headerBigBlock,
3331 OFFSET_EXTBBDEPOTCOUNT,
3332 &This->extBigBlockDepotCount);
3334 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3336 StorageUtl_ReadDWord(
3337 headerBigBlock,
3338 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3339 &(This->bigBlockDepotStart[index]));
3343 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3345 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3346 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3349 * Right now, the code is making some assumptions about the size of the
3350 * blocks, just make sure they are what we're expecting.
3352 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3353 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3355 WARN("Broken OLE storage file\n");
3356 hr = STG_E_INVALIDHEADER;
3358 else
3359 hr = S_OK;
3362 return hr;
3365 /******************************************************************************
3366 * Storage32Impl_SaveFileHeader
3368 * This method will save to the file the header, i.e. big block -1.
3370 static void StorageImpl_SaveFileHeader(
3371 StorageImpl* This)
3373 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3374 int index;
3375 BOOL success;
3378 * Get a pointer to the big block of data containing the header.
3380 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3383 * If the block read failed, the file is probably new.
3385 if (!success)
3388 * Initialize for all unknown fields.
3390 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3393 * Initialize the magic number.
3395 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3398 * And a bunch of things we don't know what they mean
3400 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3401 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3402 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3403 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3407 * Write the information to the header.
3409 StorageUtl_WriteWord(
3410 headerBigBlock,
3411 OFFSET_BIGBLOCKSIZEBITS,
3412 This->bigBlockSizeBits);
3414 StorageUtl_WriteWord(
3415 headerBigBlock,
3416 OFFSET_SMALLBLOCKSIZEBITS,
3417 This->smallBlockSizeBits);
3419 StorageUtl_WriteDWord(
3420 headerBigBlock,
3421 OFFSET_BBDEPOTCOUNT,
3422 This->bigBlockDepotCount);
3424 StorageUtl_WriteDWord(
3425 headerBigBlock,
3426 OFFSET_ROOTSTARTBLOCK,
3427 This->rootStartBlock);
3429 StorageUtl_WriteDWord(
3430 headerBigBlock,
3431 OFFSET_SBDEPOTSTART,
3432 This->smallBlockDepotStart);
3434 StorageUtl_WriteDWord(
3435 headerBigBlock,
3436 OFFSET_SBDEPOTCOUNT,
3437 This->smallBlockDepotChain ?
3438 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3440 StorageUtl_WriteDWord(
3441 headerBigBlock,
3442 OFFSET_EXTBBDEPOTSTART,
3443 This->extBigBlockDepotStart);
3445 StorageUtl_WriteDWord(
3446 headerBigBlock,
3447 OFFSET_EXTBBDEPOTCOUNT,
3448 This->extBigBlockDepotCount);
3450 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3452 StorageUtl_WriteDWord(
3453 headerBigBlock,
3454 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3455 (This->bigBlockDepotStart[index]));
3459 * Write the big block back to the file.
3461 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3464 /******************************************************************************
3465 * StorageImpl_ReadRawDirEntry
3467 * This method will read the raw data from a directory entry in the file.
3469 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3471 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3473 ULARGE_INTEGER offset;
3474 HRESULT hr;
3475 ULONG bytesRead;
3477 offset.u.HighPart = 0;
3478 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3480 hr = BlockChainStream_ReadAt(
3481 This->rootBlockChain,
3482 offset,
3483 RAW_DIRENTRY_SIZE,
3484 buffer,
3485 &bytesRead);
3487 return hr;
3490 /******************************************************************************
3491 * StorageImpl_WriteRawDirEntry
3493 * This method will write the raw data from a directory entry in the file.
3495 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3497 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3499 ULARGE_INTEGER offset;
3500 HRESULT hr;
3501 ULONG bytesRead;
3503 offset.u.HighPart = 0;
3504 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3506 hr = BlockChainStream_WriteAt(
3507 This->rootBlockChain,
3508 offset,
3509 RAW_DIRENTRY_SIZE,
3510 buffer,
3511 &bytesRead);
3513 return hr;
3516 /******************************************************************************
3517 * UpdateRawDirEntry
3519 * Update raw directory entry data from the fields in newData.
3521 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3523 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3525 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3527 memcpy(
3528 buffer + OFFSET_PS_NAME,
3529 newData->name,
3530 DIRENTRY_NAME_BUFFER_LEN );
3532 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3534 StorageUtl_WriteWord(
3535 buffer,
3536 OFFSET_PS_NAMELENGTH,
3537 newData->sizeOfNameString);
3539 StorageUtl_WriteDWord(
3540 buffer,
3541 OFFSET_PS_LEFTCHILD,
3542 newData->leftChild);
3544 StorageUtl_WriteDWord(
3545 buffer,
3546 OFFSET_PS_RIGHTCHILD,
3547 newData->rightChild);
3549 StorageUtl_WriteDWord(
3550 buffer,
3551 OFFSET_PS_DIRROOT,
3552 newData->dirRootEntry);
3554 StorageUtl_WriteGUID(
3555 buffer,
3556 OFFSET_PS_GUID,
3557 &newData->clsid);
3559 StorageUtl_WriteDWord(
3560 buffer,
3561 OFFSET_PS_CTIMELOW,
3562 newData->ctime.dwLowDateTime);
3564 StorageUtl_WriteDWord(
3565 buffer,
3566 OFFSET_PS_CTIMEHIGH,
3567 newData->ctime.dwHighDateTime);
3569 StorageUtl_WriteDWord(
3570 buffer,
3571 OFFSET_PS_MTIMELOW,
3572 newData->mtime.dwLowDateTime);
3574 StorageUtl_WriteDWord(
3575 buffer,
3576 OFFSET_PS_MTIMEHIGH,
3577 newData->ctime.dwHighDateTime);
3579 StorageUtl_WriteDWord(
3580 buffer,
3581 OFFSET_PS_STARTBLOCK,
3582 newData->startingBlock);
3584 StorageUtl_WriteDWord(
3585 buffer,
3586 OFFSET_PS_SIZE,
3587 newData->size.u.LowPart);
3590 /******************************************************************************
3591 * Storage32Impl_ReadDirEntry
3593 * This method will read the specified directory entry.
3595 HRESULT StorageImpl_ReadDirEntry(
3596 StorageImpl* This,
3597 DirRef index,
3598 DirEntry* buffer)
3600 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3601 HRESULT readRes;
3603 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3605 if (SUCCEEDED(readRes))
3607 memset(buffer->name, 0, sizeof(buffer->name));
3608 memcpy(
3609 buffer->name,
3610 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3611 DIRENTRY_NAME_BUFFER_LEN );
3612 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3614 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3616 StorageUtl_ReadWord(
3617 currentEntry,
3618 OFFSET_PS_NAMELENGTH,
3619 &buffer->sizeOfNameString);
3621 StorageUtl_ReadDWord(
3622 currentEntry,
3623 OFFSET_PS_LEFTCHILD,
3624 &buffer->leftChild);
3626 StorageUtl_ReadDWord(
3627 currentEntry,
3628 OFFSET_PS_RIGHTCHILD,
3629 &buffer->rightChild);
3631 StorageUtl_ReadDWord(
3632 currentEntry,
3633 OFFSET_PS_DIRROOT,
3634 &buffer->dirRootEntry);
3636 StorageUtl_ReadGUID(
3637 currentEntry,
3638 OFFSET_PS_GUID,
3639 &buffer->clsid);
3641 StorageUtl_ReadDWord(
3642 currentEntry,
3643 OFFSET_PS_CTIMELOW,
3644 &buffer->ctime.dwLowDateTime);
3646 StorageUtl_ReadDWord(
3647 currentEntry,
3648 OFFSET_PS_CTIMEHIGH,
3649 &buffer->ctime.dwHighDateTime);
3651 StorageUtl_ReadDWord(
3652 currentEntry,
3653 OFFSET_PS_MTIMELOW,
3654 &buffer->mtime.dwLowDateTime);
3656 StorageUtl_ReadDWord(
3657 currentEntry,
3658 OFFSET_PS_MTIMEHIGH,
3659 &buffer->mtime.dwHighDateTime);
3661 StorageUtl_ReadDWord(
3662 currentEntry,
3663 OFFSET_PS_STARTBLOCK,
3664 &buffer->startingBlock);
3666 StorageUtl_ReadDWord(
3667 currentEntry,
3668 OFFSET_PS_SIZE,
3669 &buffer->size.u.LowPart);
3671 buffer->size.u.HighPart = 0;
3674 return readRes;
3677 /*********************************************************************
3678 * Write the specified directory entry to the file
3680 HRESULT StorageImpl_WriteDirEntry(
3681 StorageImpl* This,
3682 DirRef index,
3683 const DirEntry* buffer)
3685 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3686 HRESULT writeRes;
3688 UpdateRawDirEntry(currentEntry, buffer);
3690 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3691 return writeRes;
3694 static BOOL StorageImpl_ReadBigBlock(
3695 StorageImpl* This,
3696 ULONG blockIndex,
3697 void* buffer)
3699 ULARGE_INTEGER ulOffset;
3700 DWORD read;
3702 ulOffset.u.HighPart = 0;
3703 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3705 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3706 return (read == This->bigBlockSize);
3709 static BOOL StorageImpl_ReadDWordFromBigBlock(
3710 StorageImpl* This,
3711 ULONG blockIndex,
3712 ULONG offset,
3713 DWORD* value)
3715 ULARGE_INTEGER ulOffset;
3716 DWORD read;
3717 DWORD tmp;
3719 ulOffset.u.HighPart = 0;
3720 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3721 ulOffset.u.LowPart += offset;
3723 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3724 *value = lendian32toh(tmp);
3725 return (read == sizeof(DWORD));
3728 static BOOL StorageImpl_WriteBigBlock(
3729 StorageImpl* This,
3730 ULONG blockIndex,
3731 const void* buffer)
3733 ULARGE_INTEGER ulOffset;
3734 DWORD wrote;
3736 ulOffset.u.HighPart = 0;
3737 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3739 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3740 return (wrote == This->bigBlockSize);
3743 static BOOL StorageImpl_WriteDWordToBigBlock(
3744 StorageImpl* This,
3745 ULONG blockIndex,
3746 ULONG offset,
3747 DWORD value)
3749 ULARGE_INTEGER ulOffset;
3750 DWORD wrote;
3752 ulOffset.u.HighPart = 0;
3753 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3754 ulOffset.u.LowPart += offset;
3756 value = htole32(value);
3757 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3758 return (wrote == sizeof(DWORD));
3761 /******************************************************************************
3762 * Storage32Impl_SmallBlocksToBigBlocks
3764 * This method will convert a small block chain to a big block chain.
3765 * The small block chain will be destroyed.
3767 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3768 StorageImpl* This,
3769 SmallBlockChainStream** ppsbChain)
3771 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3772 ULARGE_INTEGER size, offset;
3773 ULONG cbRead, cbWritten;
3774 ULARGE_INTEGER cbTotalRead;
3775 DirRef streamEntryRef;
3776 HRESULT resWrite = S_OK;
3777 HRESULT resRead;
3778 DirEntry streamEntry;
3779 BYTE *buffer;
3780 BlockChainStream *bbTempChain = NULL;
3781 BlockChainStream *bigBlockChain = NULL;
3784 * Create a temporary big block chain that doesn't have
3785 * an associated directory entry. This temporary chain will be
3786 * used to copy data from small blocks to big blocks.
3788 bbTempChain = BlockChainStream_Construct(This,
3789 &bbHeadOfChain,
3790 DIRENTRY_NULL);
3791 if(!bbTempChain) return NULL;
3793 * Grow the big block chain.
3795 size = SmallBlockChainStream_GetSize(*ppsbChain);
3796 BlockChainStream_SetSize(bbTempChain, size);
3799 * Copy the contents of the small block chain to the big block chain
3800 * by small block size increments.
3802 offset.u.LowPart = 0;
3803 offset.u.HighPart = 0;
3804 cbTotalRead.QuadPart = 0;
3806 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3809 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3810 offset,
3811 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3812 buffer,
3813 &cbRead);
3814 if (FAILED(resRead))
3815 break;
3817 if (cbRead > 0)
3819 cbTotalRead.QuadPart += cbRead;
3821 resWrite = BlockChainStream_WriteAt(bbTempChain,
3822 offset,
3823 cbRead,
3824 buffer,
3825 &cbWritten);
3827 if (FAILED(resWrite))
3828 break;
3830 offset.u.LowPart += cbRead;
3832 } while (cbTotalRead.QuadPart < size.QuadPart);
3833 HeapFree(GetProcessHeap(),0,buffer);
3835 size.u.HighPart = 0;
3836 size.u.LowPart = 0;
3838 if (FAILED(resRead) || FAILED(resWrite))
3840 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3841 BlockChainStream_SetSize(bbTempChain, size);
3842 BlockChainStream_Destroy(bbTempChain);
3843 return NULL;
3847 * Destroy the small block chain.
3849 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3850 SmallBlockChainStream_SetSize(*ppsbChain, size);
3851 SmallBlockChainStream_Destroy(*ppsbChain);
3852 *ppsbChain = 0;
3855 * Change the directory entry. This chain is now a big block chain
3856 * and it doesn't reside in the small blocks chain anymore.
3858 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3860 streamEntry.startingBlock = bbHeadOfChain;
3862 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3865 * Destroy the temporary entryless big block chain.
3866 * Create a new big block chain associated with this entry.
3868 BlockChainStream_Destroy(bbTempChain);
3869 bigBlockChain = BlockChainStream_Construct(This,
3870 NULL,
3871 streamEntryRef);
3873 return bigBlockChain;
3876 /******************************************************************************
3877 * Storage32Impl_BigBlocksToSmallBlocks
3879 * This method will convert a big block chain to a small block chain.
3880 * The big block chain will be destroyed on success.
3882 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3883 StorageImpl* This,
3884 BlockChainStream** ppbbChain)
3886 ULARGE_INTEGER size, offset, cbTotalRead;
3887 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3888 DirRef streamEntryRef;
3889 HRESULT resWrite = S_OK, resRead;
3890 DirEntry streamEntry;
3891 BYTE* buffer;
3892 SmallBlockChainStream* sbTempChain;
3894 TRACE("%p %p\n", This, ppbbChain);
3896 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3897 DIRENTRY_NULL);
3899 if(!sbTempChain)
3900 return NULL;
3902 size = BlockChainStream_GetSize(*ppbbChain);
3903 SmallBlockChainStream_SetSize(sbTempChain, size);
3905 offset.u.HighPart = 0;
3906 offset.u.LowPart = 0;
3907 cbTotalRead.QuadPart = 0;
3908 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3911 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3912 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3913 buffer, &cbRead);
3915 if(FAILED(resRead))
3916 break;
3918 if(cbRead > 0)
3920 cbTotalRead.QuadPart += cbRead;
3922 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3923 cbRead, buffer, &cbWritten);
3925 if(FAILED(resWrite))
3926 break;
3928 offset.u.LowPart += cbRead;
3930 }while(cbTotalRead.QuadPart < size.QuadPart);
3931 HeapFree(GetProcessHeap(), 0, buffer);
3933 size.u.HighPart = 0;
3934 size.u.LowPart = 0;
3936 if(FAILED(resRead) || FAILED(resWrite))
3938 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3939 SmallBlockChainStream_SetSize(sbTempChain, size);
3940 SmallBlockChainStream_Destroy(sbTempChain);
3941 return NULL;
3944 /* destroy the original big block chain */
3945 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3946 BlockChainStream_SetSize(*ppbbChain, size);
3947 BlockChainStream_Destroy(*ppbbChain);
3948 *ppbbChain = NULL;
3950 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3951 streamEntry.startingBlock = sbHeadOfChain;
3952 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3954 SmallBlockChainStream_Destroy(sbTempChain);
3955 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3958 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
3960 HRESULT hr;
3961 DirEntry parentData, snapshotData;
3963 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
3964 0, (IStorage**)snapshot);
3966 if (SUCCEEDED(hr))
3968 hr = StorageBaseImpl_ReadDirEntry(original,
3969 original->storageDirEntry, &parentData);
3971 if (SUCCEEDED(hr))
3972 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
3973 (*snapshot)->storageDirEntry, &snapshotData);
3975 if (SUCCEEDED(hr))
3977 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
3978 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
3979 snapshotData.stgType = parentData.stgType;
3980 snapshotData.clsid = parentData.clsid;
3981 snapshotData.ctime = parentData.ctime;
3982 snapshotData.mtime = parentData.mtime;
3983 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
3984 (*snapshot)->storageDirEntry, &snapshotData);
3987 if (SUCCEEDED(hr))
3988 hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
3989 (IStorage*)(*snapshot));
3991 if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
3994 return hr;
3997 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
3998 IStorage* iface,
3999 DWORD grfCommitFlags) /* [in] */
4001 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4002 HRESULT hr;
4003 DirEntry data, tempStorageData, snapshotRootData;
4004 DirRef tempStorageEntry, oldDirRoot;
4005 StorageInternalImpl *tempStorage;
4007 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4009 /* Cannot commit a read-only transacted storage */
4010 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4011 return STG_E_ACCESSDENIED;
4013 /* To prevent data loss, we create the new structure in the file before we
4014 * delete the old one, so that in case of errors the old data is intact. We
4015 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4016 * needed in the rare situation where we have just enough free disk space to
4017 * overwrite the existing data. */
4019 /* Create an orphaned storage in the parent for the new directory structure. */
4020 memset(&data, 0, sizeof(data));
4021 data.name[0] = 'D';
4022 data.sizeOfNameString = 1;
4023 data.stgType = STGTY_STORAGE;
4024 data.leftChild = DIRENTRY_NULL;
4025 data.rightChild = DIRENTRY_NULL;
4026 data.dirRootEntry = DIRENTRY_NULL;
4027 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
4029 if (FAILED(hr)) return hr;
4031 tempStorage = StorageInternalImpl_Construct(This->transactedParent,
4032 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
4033 if (tempStorage)
4035 hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
4036 (IStorage*)tempStorage);
4038 list_init(&tempStorage->ParentListEntry);
4040 IStorage_Release((IStorage*) tempStorage);
4042 else
4043 hr = E_OUTOFMEMORY;
4045 if (FAILED(hr))
4047 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4048 return hr;
4051 /* Update the storage to use the new data in one step. */
4052 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4053 This->transactedParent->storageDirEntry, &data);
4055 if (SUCCEEDED(hr))
4057 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4058 tempStorageEntry, &tempStorageData);
4061 if (SUCCEEDED(hr))
4063 hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4064 This->snapshot->storageDirEntry, &snapshotRootData);
4067 if (SUCCEEDED(hr))
4069 oldDirRoot = data.dirRootEntry;
4070 data.dirRootEntry = tempStorageData.dirRootEntry;
4071 data.clsid = snapshotRootData.clsid;
4072 data.ctime = snapshotRootData.ctime;
4073 data.mtime = snapshotRootData.mtime;
4075 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4076 This->transactedParent->storageDirEntry, &data);
4079 if (SUCCEEDED(hr))
4081 /* Destroy the old now-orphaned data. */
4082 DestroyReachableEntries(This->transactedParent, oldDirRoot);
4083 StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4085 else
4087 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4090 return hr;
4093 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4094 IStorage* iface)
4096 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4097 StorageBaseImpl *newSnapshot;
4098 HRESULT hr;
4100 TRACE("(%p)\n", iface);
4102 /* Create a new copy of the parent data. */
4103 hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4104 if (FAILED(hr)) return hr;
4106 /* Destroy the open objects. */
4107 StorageBaseImpl_DeleteAll(&This->base);
4109 /* Replace our current snapshot. */
4110 IStorage_Release((IStorage*)This->snapshot);
4111 This->snapshot = newSnapshot;
4113 return S_OK;
4116 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4118 if (!This->reverted)
4120 TRACE("Storage invalidated (stg=%p)\n", This);
4122 This->reverted = 1;
4124 StorageBaseImpl_DeleteAll(This);
4128 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4130 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4132 TransactedSnapshotImpl_Invalidate(iface);
4134 IStorage_Release((IStorage*)This->transactedParent);
4136 IStorage_Release((IStorage*)This->snapshot);
4138 HeapFree(GetProcessHeap(), 0, This);
4141 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4142 const DirEntry *newData, DirRef *index)
4144 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4146 return StorageBaseImpl_CreateDirEntry(This->snapshot,
4147 newData, index);
4150 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4151 DirRef index, const DirEntry *data)
4153 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4155 return StorageBaseImpl_WriteDirEntry(This->snapshot,
4156 index, data);
4159 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4160 DirRef index, DirEntry *data)
4162 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4164 return StorageBaseImpl_ReadDirEntry(This->snapshot,
4165 index, data);
4168 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4169 DirRef index)
4171 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4173 return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4174 index);
4177 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4178 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4180 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4182 return StorageBaseImpl_StreamReadAt(This->snapshot,
4183 index, offset, size, buffer, bytesRead);
4186 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4187 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4189 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4191 return StorageBaseImpl_StreamWriteAt(This->snapshot,
4192 index, offset, size, buffer, bytesWritten);
4195 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4196 DirRef index, ULARGE_INTEGER newsize)
4198 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4200 return StorageBaseImpl_StreamSetSize(This->snapshot,
4201 index, newsize);
4204 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4206 StorageBaseImpl_QueryInterface,
4207 StorageBaseImpl_AddRef,
4208 StorageBaseImpl_Release,
4209 StorageBaseImpl_CreateStream,
4210 StorageBaseImpl_OpenStream,
4211 StorageBaseImpl_CreateStorage,
4212 StorageBaseImpl_OpenStorage,
4213 StorageBaseImpl_CopyTo,
4214 StorageBaseImpl_MoveElementTo,
4215 TransactedSnapshotImpl_Commit,
4216 TransactedSnapshotImpl_Revert,
4217 StorageBaseImpl_EnumElements,
4218 StorageBaseImpl_DestroyElement,
4219 StorageBaseImpl_RenameElement,
4220 StorageBaseImpl_SetElementTimes,
4221 StorageBaseImpl_SetClass,
4222 StorageBaseImpl_SetStateBits,
4223 StorageBaseImpl_Stat
4226 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4228 TransactedSnapshotImpl_Destroy,
4229 TransactedSnapshotImpl_Invalidate,
4230 TransactedSnapshotImpl_CreateDirEntry,
4231 TransactedSnapshotImpl_WriteDirEntry,
4232 TransactedSnapshotImpl_ReadDirEntry,
4233 TransactedSnapshotImpl_DestroyDirEntry,
4234 TransactedSnapshotImpl_StreamReadAt,
4235 TransactedSnapshotImpl_StreamWriteAt,
4236 TransactedSnapshotImpl_StreamSetSize
4239 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4240 TransactedSnapshotImpl** result)
4242 HRESULT hr;
4244 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4245 if (*result)
4247 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4249 /* This is OK because the property set storage functions use the IStorage functions. */
4250 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4252 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4254 list_init(&(*result)->base.strmHead);
4256 list_init(&(*result)->base.storageHead);
4258 (*result)->base.ref = 1;
4260 (*result)->base.openFlags = parentStorage->openFlags;
4262 (*result)->base.filename = parentStorage->filename;
4264 /* Create a new temporary storage to act as the snapshot */
4265 hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4267 if (SUCCEEDED(hr))
4269 (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4271 /* parentStorage already has 1 reference, which we take over here. */
4272 (*result)->transactedParent = parentStorage;
4274 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4277 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4279 return hr;
4281 else
4282 return E_OUTOFMEMORY;
4285 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4286 StorageBaseImpl** result)
4288 static int fixme=0;
4290 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4292 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4295 return TransactedSnapshotImpl_Construct(parentStorage,
4296 (TransactedSnapshotImpl**)result);
4299 static HRESULT Storage_Construct(
4300 HANDLE hFile,
4301 LPCOLESTR pwcsName,
4302 ILockBytes* pLkbyt,
4303 DWORD openFlags,
4304 BOOL fileBased,
4305 BOOL create,
4306 StorageBaseImpl** result)
4308 StorageImpl *newStorage;
4309 StorageBaseImpl *newTransactedStorage;
4310 HRESULT hr;
4312 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, &newStorage);
4313 if (FAILED(hr)) goto end;
4315 if (openFlags & STGM_TRANSACTED)
4317 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4318 if (FAILED(hr))
4319 IStorage_Release((IStorage*)newStorage);
4320 else
4321 *result = newTransactedStorage;
4323 else
4324 *result = &newStorage->base;
4326 end:
4327 return hr;
4330 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4332 StorageInternalImpl* This = (StorageInternalImpl*) base;
4334 if (!This->base.reverted)
4336 TRACE("Storage invalidated (stg=%p)\n", This);
4338 This->base.reverted = 1;
4340 This->parentStorage = NULL;
4342 StorageBaseImpl_DeleteAll(&This->base);
4344 list_remove(&This->ParentListEntry);
4348 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4350 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4352 StorageInternalImpl_Invalidate(&This->base);
4354 HeapFree(GetProcessHeap(), 0, This);
4357 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4358 const DirEntry *newData, DirRef *index)
4360 StorageInternalImpl* This = (StorageInternalImpl*) base;
4362 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4363 newData, index);
4366 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4367 DirRef index, const DirEntry *data)
4369 StorageInternalImpl* This = (StorageInternalImpl*) base;
4371 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4372 index, data);
4375 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4376 DirRef index, DirEntry *data)
4378 StorageInternalImpl* This = (StorageInternalImpl*) base;
4380 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4381 index, data);
4384 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4385 DirRef index)
4387 StorageInternalImpl* This = (StorageInternalImpl*) base;
4389 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4390 index);
4393 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4394 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4396 StorageInternalImpl* This = (StorageInternalImpl*) base;
4398 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4399 index, offset, size, buffer, bytesRead);
4402 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4403 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4405 StorageInternalImpl* This = (StorageInternalImpl*) base;
4407 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4408 index, offset, size, buffer, bytesWritten);
4411 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4412 DirRef index, ULARGE_INTEGER newsize)
4414 StorageInternalImpl* This = (StorageInternalImpl*) base;
4416 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4417 index, newsize);
4420 /******************************************************************************
4422 ** Storage32InternalImpl_Commit
4425 static HRESULT WINAPI StorageInternalImpl_Commit(
4426 IStorage* iface,
4427 DWORD grfCommitFlags) /* [in] */
4429 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4430 return S_OK;
4433 /******************************************************************************
4435 ** Storage32InternalImpl_Revert
4438 static HRESULT WINAPI StorageInternalImpl_Revert(
4439 IStorage* iface)
4441 FIXME("(%p): stub\n", iface);
4442 return S_OK;
4445 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4447 IStorage_Release((IStorage*)This->parentStorage);
4448 HeapFree(GetProcessHeap(), 0, This);
4451 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4452 IEnumSTATSTG* iface,
4453 REFIID riid,
4454 void** ppvObject)
4456 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4458 if (ppvObject==0)
4459 return E_INVALIDARG;
4461 *ppvObject = 0;
4463 if (IsEqualGUID(&IID_IUnknown, riid) ||
4464 IsEqualGUID(&IID_IEnumSTATSTG, riid))
4466 *ppvObject = This;
4467 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4468 return S_OK;
4471 return E_NOINTERFACE;
4474 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
4475 IEnumSTATSTG* iface)
4477 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4478 return InterlockedIncrement(&This->ref);
4481 static ULONG WINAPI IEnumSTATSTGImpl_Release(
4482 IEnumSTATSTG* iface)
4484 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4486 ULONG newRef;
4488 newRef = InterlockedDecrement(&This->ref);
4490 if (newRef==0)
4492 IEnumSTATSTGImpl_Destroy(This);
4495 return newRef;
4498 static HRESULT IEnumSTATSTGImpl_GetNextRef(
4499 IEnumSTATSTGImpl* This,
4500 DirRef *ref)
4502 DirRef result = DIRENTRY_NULL;
4503 DirRef searchNode;
4504 DirEntry entry;
4505 HRESULT hr;
4506 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
4508 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
4509 This->parentStorage->storageDirEntry, &entry);
4510 searchNode = entry.dirRootEntry;
4512 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
4514 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
4516 if (SUCCEEDED(hr))
4518 LONG diff = entryNameCmp( entry.name, This->name);
4520 if (diff <= 0)
4522 searchNode = entry.rightChild;
4524 else
4526 result = searchNode;
4527 memcpy(result_name, entry.name, sizeof(result_name));
4528 searchNode = entry.leftChild;
4533 if (SUCCEEDED(hr))
4535 *ref = result;
4536 if (result != DIRENTRY_NULL)
4537 memcpy(This->name, result_name, sizeof(result_name));
4540 return hr;
4543 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4544 IEnumSTATSTG* iface,
4545 ULONG celt,
4546 STATSTG* rgelt,
4547 ULONG* pceltFetched)
4549 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4551 DirEntry currentEntry;
4552 STATSTG* currentReturnStruct = rgelt;
4553 ULONG objectFetched = 0;
4554 DirRef currentSearchNode;
4555 HRESULT hr=S_OK;
4557 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4558 return E_INVALIDARG;
4560 if (This->parentStorage->reverted)
4561 return STG_E_REVERTED;
4564 * To avoid the special case, get another pointer to a ULONG value if
4565 * the caller didn't supply one.
4567 if (pceltFetched==0)
4568 pceltFetched = &objectFetched;
4571 * Start the iteration, we will iterate until we hit the end of the
4572 * linked list or until we hit the number of items to iterate through
4574 *pceltFetched = 0;
4576 while ( *pceltFetched < celt )
4578 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
4580 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4581 break;
4584 * Read the entry from the storage.
4586 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4587 currentSearchNode,
4588 &currentEntry);
4591 * Copy the information to the return buffer.
4593 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4594 currentReturnStruct,
4595 &currentEntry,
4596 STATFLAG_DEFAULT);
4599 * Step to the next item in the iteration
4601 (*pceltFetched)++;
4602 currentReturnStruct++;
4605 if (SUCCEEDED(hr) && *pceltFetched != celt)
4606 hr = S_FALSE;
4608 return hr;
4612 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4613 IEnumSTATSTG* iface,
4614 ULONG celt)
4616 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4618 ULONG objectFetched = 0;
4619 DirRef currentSearchNode;
4620 HRESULT hr=S_OK;
4622 if (This->parentStorage->reverted)
4623 return STG_E_REVERTED;
4625 while ( (objectFetched < celt) )
4627 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
4629 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4630 break;
4632 objectFetched++;
4635 if (SUCCEEDED(hr) && objectFetched != celt)
4636 return S_FALSE;
4638 return hr;
4641 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4642 IEnumSTATSTG* iface)
4644 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4646 if (This->parentStorage->reverted)
4647 return STG_E_REVERTED;
4649 This->name[0] = 0;
4651 return S_OK;
4654 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4655 IEnumSTATSTG* iface,
4656 IEnumSTATSTG** ppenum)
4658 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4660 IEnumSTATSTGImpl* newClone;
4662 if (This->parentStorage->reverted)
4663 return STG_E_REVERTED;
4666 * Perform a sanity check on the parameters.
4668 if (ppenum==0)
4669 return E_INVALIDARG;
4671 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4672 This->storageDirEntry);
4676 * The new clone enumeration must point to the same current node as
4677 * the ole one.
4679 memcpy(newClone->name, This->name, sizeof(newClone->name));
4681 *ppenum = (IEnumSTATSTG*)newClone;
4684 * Don't forget to nail down a reference to the clone before
4685 * returning it.
4687 IEnumSTATSTGImpl_AddRef(*ppenum);
4689 return S_OK;
4693 * Virtual function table for the IEnumSTATSTGImpl class.
4695 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4697 IEnumSTATSTGImpl_QueryInterface,
4698 IEnumSTATSTGImpl_AddRef,
4699 IEnumSTATSTGImpl_Release,
4700 IEnumSTATSTGImpl_Next,
4701 IEnumSTATSTGImpl_Skip,
4702 IEnumSTATSTGImpl_Reset,
4703 IEnumSTATSTGImpl_Clone
4706 /******************************************************************************
4707 ** IEnumSTATSTGImpl implementation
4710 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4711 StorageBaseImpl* parentStorage,
4712 DirRef storageDirEntry)
4714 IEnumSTATSTGImpl* newEnumeration;
4716 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4718 if (newEnumeration!=0)
4721 * Set-up the virtual function table and reference count.
4723 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4724 newEnumeration->ref = 0;
4727 * We want to nail-down the reference to the storage in case the
4728 * enumeration out-lives the storage in the client application.
4730 newEnumeration->parentStorage = parentStorage;
4731 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4733 newEnumeration->storageDirEntry = storageDirEntry;
4736 * Make sure the current node of the iterator is the first one.
4738 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4741 return newEnumeration;
4745 * Virtual function table for the Storage32InternalImpl class.
4747 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4749 StorageBaseImpl_QueryInterface,
4750 StorageBaseImpl_AddRef,
4751 StorageBaseImpl_Release,
4752 StorageBaseImpl_CreateStream,
4753 StorageBaseImpl_OpenStream,
4754 StorageBaseImpl_CreateStorage,
4755 StorageBaseImpl_OpenStorage,
4756 StorageBaseImpl_CopyTo,
4757 StorageBaseImpl_MoveElementTo,
4758 StorageInternalImpl_Commit,
4759 StorageInternalImpl_Revert,
4760 StorageBaseImpl_EnumElements,
4761 StorageBaseImpl_DestroyElement,
4762 StorageBaseImpl_RenameElement,
4763 StorageBaseImpl_SetElementTimes,
4764 StorageBaseImpl_SetClass,
4765 StorageBaseImpl_SetStateBits,
4766 StorageBaseImpl_Stat
4769 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4771 StorageInternalImpl_Destroy,
4772 StorageInternalImpl_Invalidate,
4773 StorageInternalImpl_CreateDirEntry,
4774 StorageInternalImpl_WriteDirEntry,
4775 StorageInternalImpl_ReadDirEntry,
4776 StorageInternalImpl_DestroyDirEntry,
4777 StorageInternalImpl_StreamReadAt,
4778 StorageInternalImpl_StreamWriteAt,
4779 StorageInternalImpl_StreamSetSize
4782 /******************************************************************************
4783 ** Storage32InternalImpl implementation
4786 static StorageInternalImpl* StorageInternalImpl_Construct(
4787 StorageBaseImpl* parentStorage,
4788 DWORD openFlags,
4789 DirRef storageDirEntry)
4791 StorageInternalImpl* newStorage;
4793 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4795 if (newStorage!=0)
4797 list_init(&newStorage->base.strmHead);
4799 list_init(&newStorage->base.storageHead);
4802 * Initialize the virtual function table.
4804 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4805 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4806 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4808 newStorage->base.reverted = 0;
4810 newStorage->base.ref = 1;
4812 newStorage->parentStorage = parentStorage;
4815 * Keep a reference to the directory entry of this storage
4817 newStorage->base.storageDirEntry = storageDirEntry;
4819 newStorage->base.create = 0;
4821 return newStorage;
4824 return 0;
4827 /******************************************************************************
4828 ** StorageUtl implementation
4831 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4833 WORD tmp;
4835 memcpy(&tmp, buffer+offset, sizeof(WORD));
4836 *value = lendian16toh(tmp);
4839 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4841 value = htole16(value);
4842 memcpy(buffer+offset, &value, sizeof(WORD));
4845 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4847 DWORD tmp;
4849 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4850 *value = lendian32toh(tmp);
4853 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4855 value = htole32(value);
4856 memcpy(buffer+offset, &value, sizeof(DWORD));
4859 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4860 ULARGE_INTEGER* value)
4862 #ifdef WORDS_BIGENDIAN
4863 ULARGE_INTEGER tmp;
4865 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4866 value->u.LowPart = htole32(tmp.u.HighPart);
4867 value->u.HighPart = htole32(tmp.u.LowPart);
4868 #else
4869 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4870 #endif
4873 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4874 const ULARGE_INTEGER *value)
4876 #ifdef WORDS_BIGENDIAN
4877 ULARGE_INTEGER tmp;
4879 tmp.u.LowPart = htole32(value->u.HighPart);
4880 tmp.u.HighPart = htole32(value->u.LowPart);
4881 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4882 #else
4883 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4884 #endif
4887 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4889 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4890 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4891 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4893 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4896 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4898 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4899 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4900 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4902 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4905 void StorageUtl_CopyDirEntryToSTATSTG(
4906 StorageBaseImpl* storage,
4907 STATSTG* destination,
4908 const DirEntry* source,
4909 int statFlags)
4911 LPCWSTR entryName;
4913 if (source->stgType == STGTY_ROOT)
4915 /* replace the name of root entry (often "Root Entry") by the file name */
4916 entryName = storage->filename;
4918 else
4920 entryName = source->name;
4924 * The copy of the string occurs only when the flag is not set
4926 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4927 (entryName == NULL) ||
4928 (entryName[0] == 0) )
4930 destination->pwcsName = 0;
4932 else
4934 destination->pwcsName =
4935 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4937 strcpyW(destination->pwcsName, entryName);
4940 switch (source->stgType)
4942 case STGTY_STORAGE:
4943 case STGTY_ROOT:
4944 destination->type = STGTY_STORAGE;
4945 break;
4946 case STGTY_STREAM:
4947 destination->type = STGTY_STREAM;
4948 break;
4949 default:
4950 destination->type = STGTY_STREAM;
4951 break;
4954 destination->cbSize = source->size;
4956 currentReturnStruct->mtime = {0}; TODO
4957 currentReturnStruct->ctime = {0};
4958 currentReturnStruct->atime = {0};
4960 destination->grfMode = 0;
4961 destination->grfLocksSupported = 0;
4962 destination->clsid = source->clsid;
4963 destination->grfStateBits = 0;
4964 destination->reserved = 0;
4967 /******************************************************************************
4968 ** BlockChainStream implementation
4971 BlockChainStream* BlockChainStream_Construct(
4972 StorageImpl* parentStorage,
4973 ULONG* headOfStreamPlaceHolder,
4974 DirRef dirEntry)
4976 BlockChainStream* newStream;
4977 ULONG blockIndex;
4979 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4981 newStream->parentStorage = parentStorage;
4982 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4983 newStream->ownerDirEntry = dirEntry;
4984 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4985 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4986 newStream->numBlocks = 0;
4988 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4990 while (blockIndex != BLOCK_END_OF_CHAIN)
4992 newStream->numBlocks++;
4993 newStream->tailIndex = blockIndex;
4995 if(FAILED(StorageImpl_GetNextBlockInChain(
4996 parentStorage,
4997 blockIndex,
4998 &blockIndex)))
5000 HeapFree(GetProcessHeap(), 0, newStream);
5001 return NULL;
5005 return newStream;
5008 void BlockChainStream_Destroy(BlockChainStream* This)
5010 HeapFree(GetProcessHeap(), 0, This);
5013 /******************************************************************************
5014 * BlockChainStream_GetHeadOfChain
5016 * Returns the head of this stream chain.
5017 * Some special chains don't have directory entries, their heads are kept in
5018 * This->headOfStreamPlaceHolder.
5021 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5023 DirEntry chainEntry;
5024 HRESULT hr;
5026 if (This->headOfStreamPlaceHolder != 0)
5027 return *(This->headOfStreamPlaceHolder);
5029 if (This->ownerDirEntry != DIRENTRY_NULL)
5031 hr = StorageImpl_ReadDirEntry(
5032 This->parentStorage,
5033 This->ownerDirEntry,
5034 &chainEntry);
5036 if (SUCCEEDED(hr))
5038 return chainEntry.startingBlock;
5042 return BLOCK_END_OF_CHAIN;
5045 /******************************************************************************
5046 * BlockChainStream_GetCount
5048 * Returns the number of blocks that comprises this chain.
5049 * This is not the size of the stream as the last block may not be full!
5052 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5054 ULONG blockIndex;
5055 ULONG count = 0;
5057 blockIndex = BlockChainStream_GetHeadOfChain(This);
5059 while (blockIndex != BLOCK_END_OF_CHAIN)
5061 count++;
5063 if(FAILED(StorageImpl_GetNextBlockInChain(
5064 This->parentStorage,
5065 blockIndex,
5066 &blockIndex)))
5067 return 0;
5070 return count;
5073 /******************************************************************************
5074 * BlockChainStream_ReadAt
5076 * Reads a specified number of bytes from this chain at the specified offset.
5077 * bytesRead may be NULL.
5078 * Failure will be returned if the specified number of bytes has not been read.
5080 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5081 ULARGE_INTEGER offset,
5082 ULONG size,
5083 void* buffer,
5084 ULONG* bytesRead)
5086 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5087 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5088 ULONG bytesToReadInBuffer;
5089 ULONG blockIndex;
5090 BYTE* bufferWalker;
5092 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5095 * Find the first block in the stream that contains part of the buffer.
5097 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5098 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5099 (blockNoInSequence < This->lastBlockNoInSequence) )
5101 blockIndex = BlockChainStream_GetHeadOfChain(This);
5102 This->lastBlockNoInSequence = blockNoInSequence;
5104 else
5106 ULONG temp = blockNoInSequence;
5108 blockIndex = This->lastBlockNoInSequenceIndex;
5109 blockNoInSequence -= This->lastBlockNoInSequence;
5110 This->lastBlockNoInSequence = temp;
5113 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5115 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5116 return STG_E_DOCFILECORRUPT;
5117 blockNoInSequence--;
5120 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5121 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5123 This->lastBlockNoInSequenceIndex = blockIndex;
5126 * Start reading the buffer.
5128 *bytesRead = 0;
5129 bufferWalker = buffer;
5131 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5133 ULARGE_INTEGER ulOffset;
5134 DWORD bytesReadAt;
5136 * Calculate how many bytes we can copy from this big block.
5138 bytesToReadInBuffer =
5139 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5141 TRACE("block %i\n",blockIndex);
5142 ulOffset.u.HighPart = 0;
5143 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5144 offsetInBlock;
5146 StorageImpl_ReadAt(This->parentStorage,
5147 ulOffset,
5148 bufferWalker,
5149 bytesToReadInBuffer,
5150 &bytesReadAt);
5152 * Step to the next big block.
5154 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5155 return STG_E_DOCFILECORRUPT;
5157 bufferWalker += bytesReadAt;
5158 size -= bytesReadAt;
5159 *bytesRead += bytesReadAt;
5160 offsetInBlock = 0; /* There is no offset on the next block */
5162 if (bytesToReadInBuffer != bytesReadAt)
5163 break;
5166 return (size == 0) ? S_OK : STG_E_READFAULT;
5169 /******************************************************************************
5170 * BlockChainStream_WriteAt
5172 * Writes the specified number of bytes to this chain at the specified offset.
5173 * Will fail if not all specified number of bytes have been written.
5175 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5176 ULARGE_INTEGER offset,
5177 ULONG size,
5178 const void* buffer,
5179 ULONG* bytesWritten)
5181 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5182 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5183 ULONG bytesToWrite;
5184 ULONG blockIndex;
5185 const BYTE* bufferWalker;
5188 * Find the first block in the stream that contains part of the buffer.
5190 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5191 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5192 (blockNoInSequence < This->lastBlockNoInSequence) )
5194 blockIndex = BlockChainStream_GetHeadOfChain(This);
5195 This->lastBlockNoInSequence = blockNoInSequence;
5197 else
5199 ULONG temp = blockNoInSequence;
5201 blockIndex = This->lastBlockNoInSequenceIndex;
5202 blockNoInSequence -= This->lastBlockNoInSequence;
5203 This->lastBlockNoInSequence = temp;
5206 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5208 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5209 &blockIndex)))
5210 return STG_E_DOCFILECORRUPT;
5211 blockNoInSequence--;
5214 This->lastBlockNoInSequenceIndex = blockIndex;
5216 /* BlockChainStream_SetSize should have already been called to ensure we have
5217 * enough blocks in the chain to write into */
5218 if (blockIndex == BLOCK_END_OF_CHAIN)
5220 ERR("not enough blocks in chain to write data\n");
5221 return STG_E_DOCFILECORRUPT;
5224 *bytesWritten = 0;
5225 bufferWalker = buffer;
5227 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5229 ULARGE_INTEGER ulOffset;
5230 DWORD bytesWrittenAt;
5232 * Calculate how many bytes we can copy from this big block.
5234 bytesToWrite =
5235 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5237 TRACE("block %i\n",blockIndex);
5238 ulOffset.u.HighPart = 0;
5239 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5240 offsetInBlock;
5242 StorageImpl_WriteAt(This->parentStorage,
5243 ulOffset,
5244 bufferWalker,
5245 bytesToWrite,
5246 &bytesWrittenAt);
5249 * Step to the next big block.
5251 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5252 &blockIndex)))
5253 return STG_E_DOCFILECORRUPT;
5255 bufferWalker += bytesWrittenAt;
5256 size -= bytesWrittenAt;
5257 *bytesWritten += bytesWrittenAt;
5258 offsetInBlock = 0; /* There is no offset on the next block */
5260 if (bytesWrittenAt != bytesToWrite)
5261 break;
5264 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5267 /******************************************************************************
5268 * BlockChainStream_Shrink
5270 * Shrinks this chain in the big block depot.
5272 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5273 ULARGE_INTEGER newSize)
5275 ULONG blockIndex, extraBlock;
5276 ULONG numBlocks;
5277 ULONG count = 1;
5280 * Reset the last accessed block cache.
5282 This->lastBlockNoInSequence = 0xFFFFFFFF;
5283 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5286 * Figure out how many blocks are needed to contain the new size
5288 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5290 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5291 numBlocks++;
5293 blockIndex = BlockChainStream_GetHeadOfChain(This);
5296 * Go to the new end of chain
5298 while (count < numBlocks)
5300 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5301 &blockIndex)))
5302 return FALSE;
5303 count++;
5306 /* Get the next block before marking the new end */
5307 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5308 &extraBlock)))
5309 return FALSE;
5311 /* Mark the new end of chain */
5312 StorageImpl_SetNextBlockInChain(
5313 This->parentStorage,
5314 blockIndex,
5315 BLOCK_END_OF_CHAIN);
5317 This->tailIndex = blockIndex;
5318 This->numBlocks = numBlocks;
5321 * Mark the extra blocks as free
5323 while (extraBlock != BLOCK_END_OF_CHAIN)
5325 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5326 &blockIndex)))
5327 return FALSE;
5328 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5329 extraBlock = blockIndex;
5332 return TRUE;
5335 /******************************************************************************
5336 * BlockChainStream_Enlarge
5338 * Grows this chain in the big block depot.
5340 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5341 ULARGE_INTEGER newSize)
5343 ULONG blockIndex, currentBlock;
5344 ULONG newNumBlocks;
5345 ULONG oldNumBlocks = 0;
5347 blockIndex = BlockChainStream_GetHeadOfChain(This);
5350 * Empty chain. Create the head.
5352 if (blockIndex == BLOCK_END_OF_CHAIN)
5354 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5355 StorageImpl_SetNextBlockInChain(This->parentStorage,
5356 blockIndex,
5357 BLOCK_END_OF_CHAIN);
5359 if (This->headOfStreamPlaceHolder != 0)
5361 *(This->headOfStreamPlaceHolder) = blockIndex;
5363 else
5365 DirEntry chainEntry;
5366 assert(This->ownerDirEntry != DIRENTRY_NULL);
5368 StorageImpl_ReadDirEntry(
5369 This->parentStorage,
5370 This->ownerDirEntry,
5371 &chainEntry);
5373 chainEntry.startingBlock = blockIndex;
5375 StorageImpl_WriteDirEntry(
5376 This->parentStorage,
5377 This->ownerDirEntry,
5378 &chainEntry);
5381 This->tailIndex = blockIndex;
5382 This->numBlocks = 1;
5386 * Figure out how many blocks are needed to contain this stream
5388 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5390 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5391 newNumBlocks++;
5394 * Go to the current end of chain
5396 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5398 currentBlock = blockIndex;
5400 while (blockIndex != BLOCK_END_OF_CHAIN)
5402 This->numBlocks++;
5403 currentBlock = blockIndex;
5405 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5406 &blockIndex)))
5407 return FALSE;
5410 This->tailIndex = currentBlock;
5413 currentBlock = This->tailIndex;
5414 oldNumBlocks = This->numBlocks;
5417 * Add new blocks to the chain
5419 if (oldNumBlocks < newNumBlocks)
5421 while (oldNumBlocks < newNumBlocks)
5423 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5425 StorageImpl_SetNextBlockInChain(
5426 This->parentStorage,
5427 currentBlock,
5428 blockIndex);
5430 StorageImpl_SetNextBlockInChain(
5431 This->parentStorage,
5432 blockIndex,
5433 BLOCK_END_OF_CHAIN);
5435 currentBlock = blockIndex;
5436 oldNumBlocks++;
5439 This->tailIndex = blockIndex;
5440 This->numBlocks = newNumBlocks;
5443 return TRUE;
5446 /******************************************************************************
5447 * BlockChainStream_SetSize
5449 * Sets the size of this stream. The big block depot will be updated.
5450 * The file will grow if we grow the chain.
5452 * TODO: Free the actual blocks in the file when we shrink the chain.
5453 * Currently, the blocks are still in the file. So the file size
5454 * doesn't shrink even if we shrink streams.
5456 BOOL BlockChainStream_SetSize(
5457 BlockChainStream* This,
5458 ULARGE_INTEGER newSize)
5460 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5462 if (newSize.u.LowPart == size.u.LowPart)
5463 return TRUE;
5465 if (newSize.u.LowPart < size.u.LowPart)
5467 BlockChainStream_Shrink(This, newSize);
5469 else
5471 BlockChainStream_Enlarge(This, newSize);
5474 return TRUE;
5477 /******************************************************************************
5478 * BlockChainStream_GetSize
5480 * Returns the size of this chain.
5481 * Will return the block count if this chain doesn't have a directory entry.
5483 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5485 DirEntry chainEntry;
5487 if(This->headOfStreamPlaceHolder == NULL)
5490 * This chain has a directory entry so use the size value from there.
5492 StorageImpl_ReadDirEntry(
5493 This->parentStorage,
5494 This->ownerDirEntry,
5495 &chainEntry);
5497 return chainEntry.size;
5499 else
5502 * this chain is a chain that does not have a directory entry, figure out the
5503 * size by making the product number of used blocks times the
5504 * size of them
5506 ULARGE_INTEGER result;
5507 result.u.HighPart = 0;
5509 result.u.LowPart =
5510 BlockChainStream_GetCount(This) *
5511 This->parentStorage->bigBlockSize;
5513 return result;
5517 /******************************************************************************
5518 ** SmallBlockChainStream implementation
5521 SmallBlockChainStream* SmallBlockChainStream_Construct(
5522 StorageImpl* parentStorage,
5523 ULONG* headOfStreamPlaceHolder,
5524 DirRef dirEntry)
5526 SmallBlockChainStream* newStream;
5528 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5530 newStream->parentStorage = parentStorage;
5531 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5532 newStream->ownerDirEntry = dirEntry;
5534 return newStream;
5537 void SmallBlockChainStream_Destroy(
5538 SmallBlockChainStream* This)
5540 HeapFree(GetProcessHeap(), 0, This);
5543 /******************************************************************************
5544 * SmallBlockChainStream_GetHeadOfChain
5546 * Returns the head of this chain of small blocks.
5548 static ULONG SmallBlockChainStream_GetHeadOfChain(
5549 SmallBlockChainStream* This)
5551 DirEntry chainEntry;
5552 HRESULT hr;
5554 if (This->headOfStreamPlaceHolder != NULL)
5555 return *(This->headOfStreamPlaceHolder);
5557 if (This->ownerDirEntry)
5559 hr = StorageImpl_ReadDirEntry(
5560 This->parentStorage,
5561 This->ownerDirEntry,
5562 &chainEntry);
5564 if (SUCCEEDED(hr))
5566 return chainEntry.startingBlock;
5571 return BLOCK_END_OF_CHAIN;
5574 /******************************************************************************
5575 * SmallBlockChainStream_GetNextBlockInChain
5577 * Returns the index of the next small block in this chain.
5579 * Return Values:
5580 * - BLOCK_END_OF_CHAIN: end of this chain
5581 * - BLOCK_UNUSED: small block 'blockIndex' is free
5583 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5584 SmallBlockChainStream* This,
5585 ULONG blockIndex,
5586 ULONG* nextBlockInChain)
5588 ULARGE_INTEGER offsetOfBlockInDepot;
5589 DWORD buffer;
5590 ULONG bytesRead;
5591 HRESULT res;
5593 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5595 offsetOfBlockInDepot.u.HighPart = 0;
5596 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5599 * Read those bytes in the buffer from the small block file.
5601 res = BlockChainStream_ReadAt(
5602 This->parentStorage->smallBlockDepotChain,
5603 offsetOfBlockInDepot,
5604 sizeof(DWORD),
5605 &buffer,
5606 &bytesRead);
5608 if (SUCCEEDED(res))
5610 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5611 return S_OK;
5614 return res;
5617 /******************************************************************************
5618 * SmallBlockChainStream_SetNextBlockInChain
5620 * Writes the index of the next block of the specified block in the small
5621 * block depot.
5622 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5623 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5625 static void SmallBlockChainStream_SetNextBlockInChain(
5626 SmallBlockChainStream* This,
5627 ULONG blockIndex,
5628 ULONG nextBlock)
5630 ULARGE_INTEGER offsetOfBlockInDepot;
5631 DWORD buffer;
5632 ULONG bytesWritten;
5634 offsetOfBlockInDepot.u.HighPart = 0;
5635 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5637 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5640 * Read those bytes in the buffer from the small block file.
5642 BlockChainStream_WriteAt(
5643 This->parentStorage->smallBlockDepotChain,
5644 offsetOfBlockInDepot,
5645 sizeof(DWORD),
5646 &buffer,
5647 &bytesWritten);
5650 /******************************************************************************
5651 * SmallBlockChainStream_FreeBlock
5653 * Flag small block 'blockIndex' as free in the small block depot.
5655 static void SmallBlockChainStream_FreeBlock(
5656 SmallBlockChainStream* This,
5657 ULONG blockIndex)
5659 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5662 /******************************************************************************
5663 * SmallBlockChainStream_GetNextFreeBlock
5665 * Returns the index of a free small block. The small block depot will be
5666 * enlarged if necessary. The small block chain will also be enlarged if
5667 * necessary.
5669 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5670 SmallBlockChainStream* This)
5672 ULARGE_INTEGER offsetOfBlockInDepot;
5673 DWORD buffer;
5674 ULONG bytesRead;
5675 ULONG blockIndex = 0;
5676 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5677 HRESULT res = S_OK;
5678 ULONG smallBlocksPerBigBlock;
5680 offsetOfBlockInDepot.u.HighPart = 0;
5683 * Scan the small block depot for a free block
5685 while (nextBlockIndex != BLOCK_UNUSED)
5687 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5689 res = BlockChainStream_ReadAt(
5690 This->parentStorage->smallBlockDepotChain,
5691 offsetOfBlockInDepot,
5692 sizeof(DWORD),
5693 &buffer,
5694 &bytesRead);
5697 * If we run out of space for the small block depot, enlarge it
5699 if (SUCCEEDED(res))
5701 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5703 if (nextBlockIndex != BLOCK_UNUSED)
5704 blockIndex++;
5706 else
5708 ULONG count =
5709 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5711 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5712 ULONG nextBlock, newsbdIndex;
5713 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5715 nextBlock = sbdIndex;
5716 while (nextBlock != BLOCK_END_OF_CHAIN)
5718 sbdIndex = nextBlock;
5719 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5722 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5723 if (sbdIndex != BLOCK_END_OF_CHAIN)
5724 StorageImpl_SetNextBlockInChain(
5725 This->parentStorage,
5726 sbdIndex,
5727 newsbdIndex);
5729 StorageImpl_SetNextBlockInChain(
5730 This->parentStorage,
5731 newsbdIndex,
5732 BLOCK_END_OF_CHAIN);
5735 * Initialize all the small blocks to free
5737 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5738 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5740 if (count == 0)
5743 * We have just created the small block depot.
5745 DirEntry rootEntry;
5746 ULONG sbStartIndex;
5749 * Save it in the header
5751 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5752 StorageImpl_SaveFileHeader(This->parentStorage);
5755 * And allocate the first big block that will contain small blocks
5757 sbStartIndex =
5758 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5760 StorageImpl_SetNextBlockInChain(
5761 This->parentStorage,
5762 sbStartIndex,
5763 BLOCK_END_OF_CHAIN);
5765 StorageImpl_ReadDirEntry(
5766 This->parentStorage,
5767 This->parentStorage->base.storageDirEntry,
5768 &rootEntry);
5770 rootEntry.startingBlock = sbStartIndex;
5771 rootEntry.size.u.HighPart = 0;
5772 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5774 StorageImpl_WriteDirEntry(
5775 This->parentStorage,
5776 This->parentStorage->base.storageDirEntry,
5777 &rootEntry);
5779 else
5780 StorageImpl_SaveFileHeader(This->parentStorage);
5784 smallBlocksPerBigBlock =
5785 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5788 * Verify if we have to allocate big blocks to contain small blocks
5790 if (blockIndex % smallBlocksPerBigBlock == 0)
5792 DirEntry rootEntry;
5793 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5795 StorageImpl_ReadDirEntry(
5796 This->parentStorage,
5797 This->parentStorage->base.storageDirEntry,
5798 &rootEntry);
5800 if (rootEntry.size.u.LowPart <
5801 (blocksRequired * This->parentStorage->bigBlockSize))
5803 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5805 BlockChainStream_SetSize(
5806 This->parentStorage->smallBlockRootChain,
5807 rootEntry.size);
5809 StorageImpl_WriteDirEntry(
5810 This->parentStorage,
5811 This->parentStorage->base.storageDirEntry,
5812 &rootEntry);
5816 return blockIndex;
5819 /******************************************************************************
5820 * SmallBlockChainStream_ReadAt
5822 * Reads a specified number of bytes from this chain at the specified offset.
5823 * bytesRead may be NULL.
5824 * Failure will be returned if the specified number of bytes has not been read.
5826 HRESULT SmallBlockChainStream_ReadAt(
5827 SmallBlockChainStream* This,
5828 ULARGE_INTEGER offset,
5829 ULONG size,
5830 void* buffer,
5831 ULONG* bytesRead)
5833 HRESULT rc = S_OK;
5834 ULARGE_INTEGER offsetInBigBlockFile;
5835 ULONG blockNoInSequence =
5836 offset.u.LowPart / This->parentStorage->smallBlockSize;
5838 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5839 ULONG bytesToReadInBuffer;
5840 ULONG blockIndex;
5841 ULONG bytesReadFromBigBlockFile;
5842 BYTE* bufferWalker;
5845 * This should never happen on a small block file.
5847 assert(offset.u.HighPart==0);
5850 * Find the first block in the stream that contains part of the buffer.
5852 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5854 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5856 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5857 if(FAILED(rc))
5858 return rc;
5859 blockNoInSequence--;
5863 * Start reading the buffer.
5865 *bytesRead = 0;
5866 bufferWalker = buffer;
5868 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5871 * Calculate how many bytes we can copy from this small block.
5873 bytesToReadInBuffer =
5874 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5877 * Calculate the offset of the small block in the small block file.
5879 offsetInBigBlockFile.u.HighPart = 0;
5880 offsetInBigBlockFile.u.LowPart =
5881 blockIndex * This->parentStorage->smallBlockSize;
5883 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5886 * Read those bytes in the buffer from the small block file.
5887 * The small block has already been identified so it shouldn't fail
5888 * unless the file is corrupt.
5890 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5891 offsetInBigBlockFile,
5892 bytesToReadInBuffer,
5893 bufferWalker,
5894 &bytesReadFromBigBlockFile);
5896 if (FAILED(rc))
5897 return rc;
5900 * Step to the next big block.
5902 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5903 if(FAILED(rc))
5904 return STG_E_DOCFILECORRUPT;
5906 bufferWalker += bytesReadFromBigBlockFile;
5907 size -= bytesReadFromBigBlockFile;
5908 *bytesRead += bytesReadFromBigBlockFile;
5909 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5912 return (size == 0) ? S_OK : STG_E_READFAULT;
5915 /******************************************************************************
5916 * SmallBlockChainStream_WriteAt
5918 * Writes the specified number of bytes to this chain at the specified offset.
5919 * Will fail if not all specified number of bytes have been written.
5921 HRESULT SmallBlockChainStream_WriteAt(
5922 SmallBlockChainStream* This,
5923 ULARGE_INTEGER offset,
5924 ULONG size,
5925 const void* buffer,
5926 ULONG* bytesWritten)
5928 ULARGE_INTEGER offsetInBigBlockFile;
5929 ULONG blockNoInSequence =
5930 offset.u.LowPart / This->parentStorage->smallBlockSize;
5932 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5933 ULONG bytesToWriteInBuffer;
5934 ULONG blockIndex;
5935 ULONG bytesWrittenToBigBlockFile;
5936 const BYTE* bufferWalker;
5937 HRESULT res;
5940 * This should never happen on a small block file.
5942 assert(offset.u.HighPart==0);
5945 * Find the first block in the stream that contains part of the buffer.
5947 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5949 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5951 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5952 return STG_E_DOCFILECORRUPT;
5953 blockNoInSequence--;
5957 * Start writing the buffer.
5959 *bytesWritten = 0;
5960 bufferWalker = buffer;
5961 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5964 * Calculate how many bytes we can copy to this small block.
5966 bytesToWriteInBuffer =
5967 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5970 * Calculate the offset of the small block in the small block file.
5972 offsetInBigBlockFile.u.HighPart = 0;
5973 offsetInBigBlockFile.u.LowPart =
5974 blockIndex * This->parentStorage->smallBlockSize;
5976 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5979 * Write those bytes in the buffer to the small block file.
5981 res = BlockChainStream_WriteAt(
5982 This->parentStorage->smallBlockRootChain,
5983 offsetInBigBlockFile,
5984 bytesToWriteInBuffer,
5985 bufferWalker,
5986 &bytesWrittenToBigBlockFile);
5987 if (FAILED(res))
5988 return res;
5991 * Step to the next big block.
5993 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5994 &blockIndex)))
5995 return FALSE;
5996 bufferWalker += bytesWrittenToBigBlockFile;
5997 size -= bytesWrittenToBigBlockFile;
5998 *bytesWritten += bytesWrittenToBigBlockFile;
5999 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6002 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6005 /******************************************************************************
6006 * SmallBlockChainStream_Shrink
6008 * Shrinks this chain in the small block depot.
6010 static BOOL SmallBlockChainStream_Shrink(
6011 SmallBlockChainStream* This,
6012 ULARGE_INTEGER newSize)
6014 ULONG blockIndex, extraBlock;
6015 ULONG numBlocks;
6016 ULONG count = 0;
6018 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6020 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6021 numBlocks++;
6023 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6026 * Go to the new end of chain
6028 while (count < numBlocks)
6030 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6031 &blockIndex)))
6032 return FALSE;
6033 count++;
6037 * If the count is 0, we have a special case, the head of the chain was
6038 * just freed.
6040 if (count == 0)
6042 DirEntry chainEntry;
6044 StorageImpl_ReadDirEntry(This->parentStorage,
6045 This->ownerDirEntry,
6046 &chainEntry);
6048 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6050 StorageImpl_WriteDirEntry(This->parentStorage,
6051 This->ownerDirEntry,
6052 &chainEntry);
6055 * We start freeing the chain at the head block.
6057 extraBlock = blockIndex;
6059 else
6061 /* Get the next block before marking the new end */
6062 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6063 &extraBlock)))
6064 return FALSE;
6066 /* Mark the new end of chain */
6067 SmallBlockChainStream_SetNextBlockInChain(
6068 This,
6069 blockIndex,
6070 BLOCK_END_OF_CHAIN);
6074 * Mark the extra blocks as free
6076 while (extraBlock != BLOCK_END_OF_CHAIN)
6078 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6079 &blockIndex)))
6080 return FALSE;
6081 SmallBlockChainStream_FreeBlock(This, extraBlock);
6082 extraBlock = blockIndex;
6085 return TRUE;
6088 /******************************************************************************
6089 * SmallBlockChainStream_Enlarge
6091 * Grows this chain in the small block depot.
6093 static BOOL SmallBlockChainStream_Enlarge(
6094 SmallBlockChainStream* This,
6095 ULARGE_INTEGER newSize)
6097 ULONG blockIndex, currentBlock;
6098 ULONG newNumBlocks;
6099 ULONG oldNumBlocks = 0;
6101 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6104 * Empty chain. Create the head.
6106 if (blockIndex == BLOCK_END_OF_CHAIN)
6108 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6109 SmallBlockChainStream_SetNextBlockInChain(
6110 This,
6111 blockIndex,
6112 BLOCK_END_OF_CHAIN);
6114 if (This->headOfStreamPlaceHolder != NULL)
6116 *(This->headOfStreamPlaceHolder) = blockIndex;
6118 else
6120 DirEntry chainEntry;
6122 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6123 &chainEntry);
6125 chainEntry.startingBlock = blockIndex;
6127 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6128 &chainEntry);
6132 currentBlock = blockIndex;
6135 * Figure out how many blocks are needed to contain this stream
6137 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6139 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6140 newNumBlocks++;
6143 * Go to the current end of chain
6145 while (blockIndex != BLOCK_END_OF_CHAIN)
6147 oldNumBlocks++;
6148 currentBlock = blockIndex;
6149 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6150 return FALSE;
6154 * Add new blocks to the chain
6156 while (oldNumBlocks < newNumBlocks)
6158 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6159 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6161 SmallBlockChainStream_SetNextBlockInChain(
6162 This,
6163 blockIndex,
6164 BLOCK_END_OF_CHAIN);
6166 currentBlock = blockIndex;
6167 oldNumBlocks++;
6170 return TRUE;
6173 /******************************************************************************
6174 * SmallBlockChainStream_SetSize
6176 * Sets the size of this stream.
6177 * The file will grow if we grow the chain.
6179 * TODO: Free the actual blocks in the file when we shrink the chain.
6180 * Currently, the blocks are still in the file. So the file size
6181 * doesn't shrink even if we shrink streams.
6183 BOOL SmallBlockChainStream_SetSize(
6184 SmallBlockChainStream* This,
6185 ULARGE_INTEGER newSize)
6187 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6189 if (newSize.u.LowPart == size.u.LowPart)
6190 return TRUE;
6192 if (newSize.u.LowPart < size.u.LowPart)
6194 SmallBlockChainStream_Shrink(This, newSize);
6196 else
6198 SmallBlockChainStream_Enlarge(This, newSize);
6201 return TRUE;
6204 /******************************************************************************
6205 * SmallBlockChainStream_GetCount
6207 * Returns the number of small blocks that comprises this chain.
6208 * This is not the size of the stream as the last block may not be full!
6211 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6213 ULONG blockIndex;
6214 ULONG count = 0;
6216 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6218 while(blockIndex != BLOCK_END_OF_CHAIN)
6220 count++;
6222 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6223 blockIndex, &blockIndex)))
6224 return 0;
6227 return count;
6230 /******************************************************************************
6231 * SmallBlockChainStream_GetSize
6233 * Returns the size of this chain.
6235 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6237 DirEntry chainEntry;
6239 if(This->headOfStreamPlaceHolder != NULL)
6241 ULARGE_INTEGER result;
6242 result.u.HighPart = 0;
6244 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6245 This->parentStorage->smallBlockSize;
6247 return result;
6250 StorageImpl_ReadDirEntry(
6251 This->parentStorage,
6252 This->ownerDirEntry,
6253 &chainEntry);
6255 return chainEntry.size;
6258 /******************************************************************************
6259 * StgCreateDocfile [OLE32.@]
6260 * Creates a new compound file storage object
6262 * PARAMS
6263 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6264 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6265 * reserved [ ?] unused?, usually 0
6266 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6268 * RETURNS
6269 * S_OK if the file was successfully created
6270 * some STG_E_ value if error
6271 * NOTES
6272 * if pwcsName is NULL, create file with new unique name
6273 * the function can returns
6274 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6275 * (unrealized now)
6277 HRESULT WINAPI StgCreateDocfile(
6278 LPCOLESTR pwcsName,
6279 DWORD grfMode,
6280 DWORD reserved,
6281 IStorage **ppstgOpen)
6283 StorageBaseImpl* newStorage = 0;
6284 HANDLE hFile = INVALID_HANDLE_VALUE;
6285 HRESULT hr = STG_E_INVALIDFLAG;
6286 DWORD shareMode;
6287 DWORD accessMode;
6288 DWORD creationMode;
6289 DWORD fileAttributes;
6290 WCHAR tempFileName[MAX_PATH];
6292 TRACE("(%s, %x, %d, %p)\n",
6293 debugstr_w(pwcsName), grfMode,
6294 reserved, ppstgOpen);
6296 if (ppstgOpen == 0)
6297 return STG_E_INVALIDPOINTER;
6298 if (reserved != 0)
6299 return STG_E_INVALIDPARAMETER;
6301 /* if no share mode given then DENY_NONE is the default */
6302 if (STGM_SHARE_MODE(grfMode) == 0)
6303 grfMode |= STGM_SHARE_DENY_NONE;
6305 if ( FAILED( validateSTGM(grfMode) ))
6306 goto end;
6308 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6309 switch(STGM_ACCESS_MODE(grfMode))
6311 case STGM_WRITE:
6312 case STGM_READWRITE:
6313 break;
6314 default:
6315 goto end;
6318 /* in direct mode, can only use SHARE_EXCLUSIVE */
6319 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6320 goto end;
6322 /* but in transacted mode, any share mode is valid */
6325 * Generate a unique name.
6327 if (pwcsName == 0)
6329 WCHAR tempPath[MAX_PATH];
6330 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6332 memset(tempPath, 0, sizeof(tempPath));
6333 memset(tempFileName, 0, sizeof(tempFileName));
6335 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6336 tempPath[0] = '.';
6338 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6339 pwcsName = tempFileName;
6340 else
6342 hr = STG_E_INSUFFICIENTMEMORY;
6343 goto end;
6346 creationMode = TRUNCATE_EXISTING;
6348 else
6350 creationMode = GetCreationModeFromSTGM(grfMode);
6354 * Interpret the STGM value grfMode
6356 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6357 accessMode = GetAccessModeFromSTGM(grfMode);
6359 if (grfMode & STGM_DELETEONRELEASE)
6360 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6361 else
6362 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6364 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6366 static int fixme;
6367 if (!fixme++)
6368 FIXME("Storage share mode not implemented.\n");
6371 *ppstgOpen = 0;
6373 hFile = CreateFileW(pwcsName,
6374 accessMode,
6375 shareMode,
6376 NULL,
6377 creationMode,
6378 fileAttributes,
6381 if (hFile == INVALID_HANDLE_VALUE)
6383 if(GetLastError() == ERROR_FILE_EXISTS)
6384 hr = STG_E_FILEALREADYEXISTS;
6385 else
6386 hr = E_FAIL;
6387 goto end;
6391 * Allocate and initialize the new IStorage32object.
6393 hr = Storage_Construct(
6394 hFile,
6395 pwcsName,
6396 NULL,
6397 grfMode,
6398 TRUE,
6399 TRUE,
6400 &newStorage);
6402 if (FAILED(hr))
6404 goto end;
6408 * Get an "out" pointer for the caller.
6410 *ppstgOpen = (IStorage*)newStorage;
6412 end:
6413 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6415 return hr;
6418 /******************************************************************************
6419 * StgCreateStorageEx [OLE32.@]
6421 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6423 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6424 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6426 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6428 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6429 return STG_E_INVALIDPARAMETER;
6432 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6434 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6435 return STG_E_INVALIDPARAMETER;
6438 if (stgfmt == STGFMT_FILE)
6440 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6441 return STG_E_INVALIDPARAMETER;
6444 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6446 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6447 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
6450 ERR("Invalid stgfmt argument\n");
6451 return STG_E_INVALIDPARAMETER;
6454 /******************************************************************************
6455 * StgCreatePropSetStg [OLE32.@]
6457 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6458 IPropertySetStorage **ppPropSetStg)
6460 HRESULT hr;
6462 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6463 if (reserved)
6464 hr = STG_E_INVALIDPARAMETER;
6465 else
6466 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6467 (void**)ppPropSetStg);
6468 return hr;
6471 /******************************************************************************
6472 * StgOpenStorageEx [OLE32.@]
6474 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6476 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6477 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6479 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6481 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6482 return STG_E_INVALIDPARAMETER;
6485 switch (stgfmt)
6487 case STGFMT_FILE:
6488 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6489 return STG_E_INVALIDPARAMETER;
6491 case STGFMT_STORAGE:
6492 break;
6494 case STGFMT_DOCFILE:
6495 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6497 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6498 return STG_E_INVALIDPARAMETER;
6500 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6501 break;
6503 case STGFMT_ANY:
6504 WARN("STGFMT_ANY assuming storage\n");
6505 break;
6507 default:
6508 return STG_E_INVALIDPARAMETER;
6511 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6515 /******************************************************************************
6516 * StgOpenStorage [OLE32.@]
6518 HRESULT WINAPI StgOpenStorage(
6519 const OLECHAR *pwcsName,
6520 IStorage *pstgPriority,
6521 DWORD grfMode,
6522 SNB snbExclude,
6523 DWORD reserved,
6524 IStorage **ppstgOpen)
6526 StorageBaseImpl* newStorage = 0;
6527 HRESULT hr = S_OK;
6528 HANDLE hFile = 0;
6529 DWORD shareMode;
6530 DWORD accessMode;
6532 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6533 debugstr_w(pwcsName), pstgPriority, grfMode,
6534 snbExclude, reserved, ppstgOpen);
6536 if (pwcsName == 0)
6538 hr = STG_E_INVALIDNAME;
6539 goto end;
6542 if (ppstgOpen == 0)
6544 hr = STG_E_INVALIDPOINTER;
6545 goto end;
6548 if (reserved)
6550 hr = STG_E_INVALIDPARAMETER;
6551 goto end;
6554 if (grfMode & STGM_PRIORITY)
6556 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6557 return STG_E_INVALIDFLAG;
6558 if (grfMode & STGM_DELETEONRELEASE)
6559 return STG_E_INVALIDFUNCTION;
6560 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6561 return STG_E_INVALIDFLAG;
6562 grfMode &= ~0xf0; /* remove the existing sharing mode */
6563 grfMode |= STGM_SHARE_DENY_NONE;
6565 /* STGM_PRIORITY stops other IStorage objects on the same file from
6566 * committing until the STGM_PRIORITY IStorage is closed. it also
6567 * stops non-transacted mode StgOpenStorage calls with write access from
6568 * succeeding. obviously, both of these cannot be achieved through just
6569 * file share flags */
6570 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6574 * Validate the sharing mode
6576 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6577 switch(STGM_SHARE_MODE(grfMode))
6579 case STGM_SHARE_EXCLUSIVE:
6580 case STGM_SHARE_DENY_WRITE:
6581 break;
6582 default:
6583 hr = STG_E_INVALIDFLAG;
6584 goto end;
6587 if ( FAILED( validateSTGM(grfMode) ) ||
6588 (grfMode&STGM_CREATE))
6590 hr = STG_E_INVALIDFLAG;
6591 goto end;
6594 /* shared reading requires transacted mode */
6595 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6596 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6597 !(grfMode&STGM_TRANSACTED) )
6599 hr = STG_E_INVALIDFLAG;
6600 goto end;
6604 * Interpret the STGM value grfMode
6606 shareMode = GetShareModeFromSTGM(grfMode);
6607 accessMode = GetAccessModeFromSTGM(grfMode);
6609 *ppstgOpen = 0;
6611 hFile = CreateFileW( pwcsName,
6612 accessMode,
6613 shareMode,
6614 NULL,
6615 OPEN_EXISTING,
6616 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6619 if (hFile==INVALID_HANDLE_VALUE)
6621 DWORD last_error = GetLastError();
6623 hr = E_FAIL;
6625 switch (last_error)
6627 case ERROR_FILE_NOT_FOUND:
6628 hr = STG_E_FILENOTFOUND;
6629 break;
6631 case ERROR_PATH_NOT_FOUND:
6632 hr = STG_E_PATHNOTFOUND;
6633 break;
6635 case ERROR_ACCESS_DENIED:
6636 case ERROR_WRITE_PROTECT:
6637 hr = STG_E_ACCESSDENIED;
6638 break;
6640 case ERROR_SHARING_VIOLATION:
6641 hr = STG_E_SHAREVIOLATION;
6642 break;
6644 default:
6645 hr = E_FAIL;
6648 goto end;
6652 * Refuse to open the file if it's too small to be a structured storage file
6653 * FIXME: verify the file when reading instead of here
6655 if (GetFileSize(hFile, NULL) < 0x100)
6657 CloseHandle(hFile);
6658 hr = STG_E_FILEALREADYEXISTS;
6659 goto end;
6663 * Allocate and initialize the new IStorage32object.
6665 hr = Storage_Construct(
6666 hFile,
6667 pwcsName,
6668 NULL,
6669 grfMode,
6670 TRUE,
6671 FALSE,
6672 &newStorage);
6674 if (FAILED(hr))
6677 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6679 if(hr == STG_E_INVALIDHEADER)
6680 hr = STG_E_FILEALREADYEXISTS;
6681 goto end;
6685 * Get an "out" pointer for the caller.
6687 *ppstgOpen = (IStorage*)newStorage;
6689 end:
6690 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6691 return hr;
6694 /******************************************************************************
6695 * StgCreateDocfileOnILockBytes [OLE32.@]
6697 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6698 ILockBytes *plkbyt,
6699 DWORD grfMode,
6700 DWORD reserved,
6701 IStorage** ppstgOpen)
6703 StorageBaseImpl* newStorage = 0;
6704 HRESULT hr = S_OK;
6706 if ((ppstgOpen == 0) || (plkbyt == 0))
6707 return STG_E_INVALIDPOINTER;
6710 * Allocate and initialize the new IStorage object.
6712 hr = Storage_Construct(
6715 plkbyt,
6716 grfMode,
6717 FALSE,
6718 TRUE,
6719 &newStorage);
6721 if (FAILED(hr))
6723 return hr;
6727 * Get an "out" pointer for the caller.
6729 *ppstgOpen = (IStorage*)newStorage;
6731 return hr;
6734 /******************************************************************************
6735 * StgOpenStorageOnILockBytes [OLE32.@]
6737 HRESULT WINAPI StgOpenStorageOnILockBytes(
6738 ILockBytes *plkbyt,
6739 IStorage *pstgPriority,
6740 DWORD grfMode,
6741 SNB snbExclude,
6742 DWORD reserved,
6743 IStorage **ppstgOpen)
6745 StorageBaseImpl* newStorage = 0;
6746 HRESULT hr = S_OK;
6748 if ((plkbyt == 0) || (ppstgOpen == 0))
6749 return STG_E_INVALIDPOINTER;
6751 if ( FAILED( validateSTGM(grfMode) ))
6752 return STG_E_INVALIDFLAG;
6754 *ppstgOpen = 0;
6757 * Allocate and initialize the new IStorage object.
6759 hr = Storage_Construct(
6762 plkbyt,
6763 grfMode,
6764 FALSE,
6765 FALSE,
6766 &newStorage);
6768 if (FAILED(hr))
6770 return hr;
6774 * Get an "out" pointer for the caller.
6776 *ppstgOpen = (IStorage*)newStorage;
6778 return hr;
6781 /******************************************************************************
6782 * StgSetTimes [ole32.@]
6783 * StgSetTimes [OLE32.@]
6787 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6788 FILETIME const *patime, FILETIME const *pmtime)
6790 IStorage *stg = NULL;
6791 HRESULT r;
6793 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6795 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6796 0, 0, &stg);
6797 if( SUCCEEDED(r) )
6799 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6800 IStorage_Release(stg);
6803 return r;
6806 /******************************************************************************
6807 * StgIsStorageILockBytes [OLE32.@]
6809 * Determines if the ILockBytes contains a storage object.
6811 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6813 BYTE sig[8];
6814 ULARGE_INTEGER offset;
6816 offset.u.HighPart = 0;
6817 offset.u.LowPart = 0;
6819 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6821 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6822 return S_OK;
6824 return S_FALSE;
6827 /******************************************************************************
6828 * WriteClassStg [OLE32.@]
6830 * This method will store the specified CLSID in the specified storage object
6832 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6834 HRESULT hRes;
6836 if(!pStg)
6837 return E_INVALIDARG;
6839 if(!rclsid)
6840 return STG_E_INVALIDPOINTER;
6842 hRes = IStorage_SetClass(pStg, rclsid);
6844 return hRes;
6847 /***********************************************************************
6848 * ReadClassStg (OLE32.@)
6850 * This method reads the CLSID previously written to a storage object with
6851 * the WriteClassStg.
6853 * PARAMS
6854 * pstg [I] IStorage pointer
6855 * pclsid [O] Pointer to where the CLSID is written
6857 * RETURNS
6858 * Success: S_OK.
6859 * Failure: HRESULT code.
6861 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6863 STATSTG pstatstg;
6864 HRESULT hRes;
6866 TRACE("(%p, %p)\n", pstg, pclsid);
6868 if(!pstg || !pclsid)
6869 return E_INVALIDARG;
6872 * read a STATSTG structure (contains the clsid) from the storage
6874 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6876 if(SUCCEEDED(hRes))
6877 *pclsid=pstatstg.clsid;
6879 return hRes;
6882 /***********************************************************************
6883 * OleLoadFromStream (OLE32.@)
6885 * This function loads an object from stream
6887 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6889 CLSID clsid;
6890 HRESULT res;
6891 LPPERSISTSTREAM xstm;
6893 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6895 res=ReadClassStm(pStm,&clsid);
6896 if (FAILED(res))
6897 return res;
6898 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6899 if (FAILED(res))
6900 return res;
6901 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6902 if (FAILED(res)) {
6903 IUnknown_Release((IUnknown*)*ppvObj);
6904 return res;
6906 res=IPersistStream_Load(xstm,pStm);
6907 IPersistStream_Release(xstm);
6908 /* FIXME: all refcounts ok at this point? I think they should be:
6909 * pStm : unchanged
6910 * ppvObj : 1
6911 * xstm : 0 (released)
6913 return res;
6916 /***********************************************************************
6917 * OleSaveToStream (OLE32.@)
6919 * This function saves an object with the IPersistStream interface on it
6920 * to the specified stream.
6922 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6925 CLSID clsid;
6926 HRESULT res;
6928 TRACE("(%p,%p)\n",pPStm,pStm);
6930 res=IPersistStream_GetClassID(pPStm,&clsid);
6932 if (SUCCEEDED(res)){
6934 res=WriteClassStm(pStm,&clsid);
6936 if (SUCCEEDED(res))
6938 res=IPersistStream_Save(pPStm,pStm,TRUE);
6941 TRACE("Finished Save\n");
6942 return res;
6945 /****************************************************************************
6946 * This method validate a STGM parameter that can contain the values below
6948 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6949 * The stgm values contained in 0xffff0000 are bitmasks.
6951 * STGM_DIRECT 0x00000000
6952 * STGM_TRANSACTED 0x00010000
6953 * STGM_SIMPLE 0x08000000
6955 * STGM_READ 0x00000000
6956 * STGM_WRITE 0x00000001
6957 * STGM_READWRITE 0x00000002
6959 * STGM_SHARE_DENY_NONE 0x00000040
6960 * STGM_SHARE_DENY_READ 0x00000030
6961 * STGM_SHARE_DENY_WRITE 0x00000020
6962 * STGM_SHARE_EXCLUSIVE 0x00000010
6964 * STGM_PRIORITY 0x00040000
6965 * STGM_DELETEONRELEASE 0x04000000
6967 * STGM_CREATE 0x00001000
6968 * STGM_CONVERT 0x00020000
6969 * STGM_FAILIFTHERE 0x00000000
6971 * STGM_NOSCRATCH 0x00100000
6972 * STGM_NOSNAPSHOT 0x00200000
6974 static HRESULT validateSTGM(DWORD stgm)
6976 DWORD access = STGM_ACCESS_MODE(stgm);
6977 DWORD share = STGM_SHARE_MODE(stgm);
6978 DWORD create = STGM_CREATE_MODE(stgm);
6980 if (stgm&~STGM_KNOWN_FLAGS)
6982 ERR("unknown flags %08x\n", stgm);
6983 return E_FAIL;
6986 switch (access)
6988 case STGM_READ:
6989 case STGM_WRITE:
6990 case STGM_READWRITE:
6991 break;
6992 default:
6993 return E_FAIL;
6996 switch (share)
6998 case STGM_SHARE_DENY_NONE:
6999 case STGM_SHARE_DENY_READ:
7000 case STGM_SHARE_DENY_WRITE:
7001 case STGM_SHARE_EXCLUSIVE:
7002 break;
7003 default:
7004 return E_FAIL;
7007 switch (create)
7009 case STGM_CREATE:
7010 case STGM_FAILIFTHERE:
7011 break;
7012 default:
7013 return E_FAIL;
7017 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7019 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7020 return E_FAIL;
7023 * STGM_CREATE | STGM_CONVERT
7024 * if both are false, STGM_FAILIFTHERE is set to TRUE
7026 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7027 return E_FAIL;
7030 * STGM_NOSCRATCH requires STGM_TRANSACTED
7032 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7033 return E_FAIL;
7036 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7037 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7039 if ( (stgm & STGM_NOSNAPSHOT) &&
7040 (!(stgm & STGM_TRANSACTED) ||
7041 share == STGM_SHARE_EXCLUSIVE ||
7042 share == STGM_SHARE_DENY_WRITE) )
7043 return E_FAIL;
7045 return S_OK;
7048 /****************************************************************************
7049 * GetShareModeFromSTGM
7051 * This method will return a share mode flag from a STGM value.
7052 * The STGM value is assumed valid.
7054 static DWORD GetShareModeFromSTGM(DWORD stgm)
7056 switch (STGM_SHARE_MODE(stgm))
7058 case STGM_SHARE_DENY_NONE:
7059 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7060 case STGM_SHARE_DENY_READ:
7061 return FILE_SHARE_WRITE;
7062 case STGM_SHARE_DENY_WRITE:
7063 return FILE_SHARE_READ;
7064 case STGM_SHARE_EXCLUSIVE:
7065 return 0;
7067 ERR("Invalid share mode!\n");
7068 assert(0);
7069 return 0;
7072 /****************************************************************************
7073 * GetAccessModeFromSTGM
7075 * This method will return an access mode flag from a STGM value.
7076 * The STGM value is assumed valid.
7078 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7080 switch (STGM_ACCESS_MODE(stgm))
7082 case STGM_READ:
7083 return GENERIC_READ;
7084 case STGM_WRITE:
7085 case STGM_READWRITE:
7086 return GENERIC_READ | GENERIC_WRITE;
7088 ERR("Invalid access mode!\n");
7089 assert(0);
7090 return 0;
7093 /****************************************************************************
7094 * GetCreationModeFromSTGM
7096 * This method will return a creation mode flag from a STGM value.
7097 * The STGM value is assumed valid.
7099 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7101 switch(STGM_CREATE_MODE(stgm))
7103 case STGM_CREATE:
7104 return CREATE_ALWAYS;
7105 case STGM_CONVERT:
7106 FIXME("STGM_CONVERT not implemented!\n");
7107 return CREATE_NEW;
7108 case STGM_FAILIFTHERE:
7109 return CREATE_NEW;
7111 ERR("Invalid create mode!\n");
7112 assert(0);
7113 return 0;
7117 /*************************************************************************
7118 * OLECONVERT_LoadOLE10 [Internal]
7120 * Loads the OLE10 STREAM to memory
7122 * PARAMS
7123 * pOleStream [I] The OLESTREAM
7124 * pData [I] Data Structure for the OLESTREAM Data
7126 * RETURNS
7127 * Success: S_OK
7128 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7129 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7131 * NOTES
7132 * This function is used by OleConvertOLESTREAMToIStorage only.
7134 * Memory allocated for pData must be freed by the caller
7136 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7138 DWORD dwSize;
7139 HRESULT hRes = S_OK;
7140 int nTryCnt=0;
7141 int max_try = 6;
7143 pData->pData = NULL;
7144 pData->pstrOleObjFileName = NULL;
7146 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7148 /* Get the OleID */
7149 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7150 if(dwSize != sizeof(pData->dwOleID))
7152 hRes = CONVERT10_E_OLESTREAM_GET;
7154 else if(pData->dwOleID != OLESTREAM_ID)
7156 hRes = CONVERT10_E_OLESTREAM_FMT;
7158 else
7160 hRes = S_OK;
7161 break;
7165 if(hRes == S_OK)
7167 /* Get the TypeID... more info needed for this field */
7168 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7169 if(dwSize != sizeof(pData->dwTypeID))
7171 hRes = CONVERT10_E_OLESTREAM_GET;
7174 if(hRes == S_OK)
7176 if(pData->dwTypeID != 0)
7178 /* Get the length of the OleTypeName */
7179 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7180 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7182 hRes = CONVERT10_E_OLESTREAM_GET;
7185 if(hRes == S_OK)
7187 if(pData->dwOleTypeNameLength > 0)
7189 /* Get the OleTypeName */
7190 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7191 if(dwSize != pData->dwOleTypeNameLength)
7193 hRes = CONVERT10_E_OLESTREAM_GET;
7197 if(bStrem1)
7199 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7200 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7202 hRes = CONVERT10_E_OLESTREAM_GET;
7204 if(hRes == S_OK)
7206 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7207 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7208 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7209 if(pData->pstrOleObjFileName)
7211 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7212 if(dwSize != pData->dwOleObjFileNameLength)
7214 hRes = CONVERT10_E_OLESTREAM_GET;
7217 else
7218 hRes = CONVERT10_E_OLESTREAM_GET;
7221 else
7223 /* Get the Width of the Metafile */
7224 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7225 if(dwSize != sizeof(pData->dwMetaFileWidth))
7227 hRes = CONVERT10_E_OLESTREAM_GET;
7229 if(hRes == S_OK)
7231 /* Get the Height of the Metafile */
7232 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7233 if(dwSize != sizeof(pData->dwMetaFileHeight))
7235 hRes = CONVERT10_E_OLESTREAM_GET;
7239 if(hRes == S_OK)
7241 /* Get the Length of the Data */
7242 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7243 if(dwSize != sizeof(pData->dwDataLength))
7245 hRes = CONVERT10_E_OLESTREAM_GET;
7249 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7251 if(!bStrem1) /* if it is a second OLE stream data */
7253 pData->dwDataLength -= 8;
7254 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7255 if(dwSize != sizeof(pData->strUnknown))
7257 hRes = CONVERT10_E_OLESTREAM_GET;
7261 if(hRes == S_OK)
7263 if(pData->dwDataLength > 0)
7265 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7267 /* Get Data (ex. IStorage, Metafile, or BMP) */
7268 if(pData->pData)
7270 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7271 if(dwSize != pData->dwDataLength)
7273 hRes = CONVERT10_E_OLESTREAM_GET;
7276 else
7278 hRes = CONVERT10_E_OLESTREAM_GET;
7284 return hRes;
7287 /*************************************************************************
7288 * OLECONVERT_SaveOLE10 [Internal]
7290 * Saves the OLE10 STREAM From memory
7292 * PARAMS
7293 * pData [I] Data Structure for the OLESTREAM Data
7294 * pOleStream [I] The OLESTREAM to save
7296 * RETURNS
7297 * Success: S_OK
7298 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7300 * NOTES
7301 * This function is used by OleConvertIStorageToOLESTREAM only.
7304 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7306 DWORD dwSize;
7307 HRESULT hRes = S_OK;
7310 /* Set the OleID */
7311 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7312 if(dwSize != sizeof(pData->dwOleID))
7314 hRes = CONVERT10_E_OLESTREAM_PUT;
7317 if(hRes == S_OK)
7319 /* Set the TypeID */
7320 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7321 if(dwSize != sizeof(pData->dwTypeID))
7323 hRes = CONVERT10_E_OLESTREAM_PUT;
7327 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7329 /* Set the Length of the OleTypeName */
7330 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7331 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7333 hRes = CONVERT10_E_OLESTREAM_PUT;
7336 if(hRes == S_OK)
7338 if(pData->dwOleTypeNameLength > 0)
7340 /* Set the OleTypeName */
7341 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7342 if(dwSize != pData->dwOleTypeNameLength)
7344 hRes = CONVERT10_E_OLESTREAM_PUT;
7349 if(hRes == S_OK)
7351 /* Set the width of the Metafile */
7352 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7353 if(dwSize != sizeof(pData->dwMetaFileWidth))
7355 hRes = CONVERT10_E_OLESTREAM_PUT;
7359 if(hRes == S_OK)
7361 /* Set the height of the Metafile */
7362 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7363 if(dwSize != sizeof(pData->dwMetaFileHeight))
7365 hRes = CONVERT10_E_OLESTREAM_PUT;
7369 if(hRes == S_OK)
7371 /* Set the length of the Data */
7372 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7373 if(dwSize != sizeof(pData->dwDataLength))
7375 hRes = CONVERT10_E_OLESTREAM_PUT;
7379 if(hRes == S_OK)
7381 if(pData->dwDataLength > 0)
7383 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7384 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7385 if(dwSize != pData->dwDataLength)
7387 hRes = CONVERT10_E_OLESTREAM_PUT;
7392 return hRes;
7395 /*************************************************************************
7396 * OLECONVERT_GetOLE20FromOLE10[Internal]
7398 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7399 * opens it, and copies the content to the dest IStorage for
7400 * OleConvertOLESTREAMToIStorage
7403 * PARAMS
7404 * pDestStorage [I] The IStorage to copy the data to
7405 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7406 * nBufferLength [I] The size of the buffer
7408 * RETURNS
7409 * Nothing
7411 * NOTES
7415 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7417 HRESULT hRes;
7418 HANDLE hFile;
7419 IStorage *pTempStorage;
7420 DWORD dwNumOfBytesWritten;
7421 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7422 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7424 /* Create a temp File */
7425 GetTempPathW(MAX_PATH, wstrTempDir);
7426 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7427 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7429 if(hFile != INVALID_HANDLE_VALUE)
7431 /* Write IStorage Data to File */
7432 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7433 CloseHandle(hFile);
7435 /* Open and copy temp storage to the Dest Storage */
7436 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7437 if(hRes == S_OK)
7439 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7440 IStorage_Release(pTempStorage);
7442 DeleteFileW(wstrTempFile);
7447 /*************************************************************************
7448 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7450 * Saves the OLE10 STREAM From memory
7452 * PARAMS
7453 * pStorage [I] The Src IStorage to copy
7454 * pData [I] The Dest Memory to write to.
7456 * RETURNS
7457 * The size in bytes allocated for pData
7459 * NOTES
7460 * Memory allocated for pData must be freed by the caller
7462 * Used by OleConvertIStorageToOLESTREAM only.
7465 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7467 HANDLE hFile;
7468 HRESULT hRes;
7469 DWORD nDataLength = 0;
7470 IStorage *pTempStorage;
7471 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7472 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7474 *pData = NULL;
7476 /* Create temp Storage */
7477 GetTempPathW(MAX_PATH, wstrTempDir);
7478 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7479 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7481 if(hRes == S_OK)
7483 /* Copy Src Storage to the Temp Storage */
7484 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7485 IStorage_Release(pTempStorage);
7487 /* Open Temp Storage as a file and copy to memory */
7488 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7489 if(hFile != INVALID_HANDLE_VALUE)
7491 nDataLength = GetFileSize(hFile, NULL);
7492 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7493 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7494 CloseHandle(hFile);
7496 DeleteFileW(wstrTempFile);
7498 return nDataLength;
7501 /*************************************************************************
7502 * OLECONVERT_CreateOleStream [Internal]
7504 * Creates the "\001OLE" stream in the IStorage if necessary.
7506 * PARAMS
7507 * pStorage [I] Dest storage to create the stream in
7509 * RETURNS
7510 * Nothing
7512 * NOTES
7513 * This function is used by OleConvertOLESTREAMToIStorage only.
7515 * This stream is still unknown, MS Word seems to have extra data
7516 * but since the data is stored in the OLESTREAM there should be
7517 * no need to recreate the stream. If the stream is manually
7518 * deleted it will create it with this default data.
7521 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7523 HRESULT hRes;
7524 IStream *pStream;
7525 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7526 BYTE pOleStreamHeader [] =
7528 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7529 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7530 0x00, 0x00, 0x00, 0x00
7533 /* Create stream if not present */
7534 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7535 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7537 if(hRes == S_OK)
7539 /* Write default Data */
7540 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7541 IStream_Release(pStream);
7545 /* write a string to a stream, preceded by its length */
7546 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7548 HRESULT r;
7549 LPSTR str;
7550 DWORD len = 0;
7552 if( string )
7553 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7554 r = IStream_Write( stm, &len, sizeof(len), NULL);
7555 if( FAILED( r ) )
7556 return r;
7557 if(len == 0)
7558 return r;
7559 str = CoTaskMemAlloc( len );
7560 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7561 r = IStream_Write( stm, str, len, NULL);
7562 CoTaskMemFree( str );
7563 return r;
7566 /* read a string preceded by its length from a stream */
7567 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7569 HRESULT r;
7570 DWORD len, count = 0;
7571 LPSTR str;
7572 LPWSTR wstr;
7574 r = IStream_Read( stm, &len, sizeof(len), &count );
7575 if( FAILED( r ) )
7576 return r;
7577 if( count != sizeof(len) )
7578 return E_OUTOFMEMORY;
7580 TRACE("%d bytes\n",len);
7582 str = CoTaskMemAlloc( len );
7583 if( !str )
7584 return E_OUTOFMEMORY;
7585 count = 0;
7586 r = IStream_Read( stm, str, len, &count );
7587 if( FAILED( r ) )
7588 return r;
7589 if( count != len )
7591 CoTaskMemFree( str );
7592 return E_OUTOFMEMORY;
7595 TRACE("Read string %s\n",debugstr_an(str,len));
7597 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7598 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7599 if( wstr )
7600 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7601 CoTaskMemFree( str );
7603 *string = wstr;
7605 return r;
7609 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7610 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7612 IStream *pstm;
7613 HRESULT r = S_OK;
7614 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7616 static const BYTE unknown1[12] =
7617 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7618 0xFF, 0xFF, 0xFF, 0xFF};
7619 static const BYTE unknown2[16] =
7620 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7621 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7623 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7624 debugstr_w(lpszUserType), debugstr_w(szClipName),
7625 debugstr_w(szProgIDName));
7627 /* Create a CompObj stream */
7628 r = IStorage_CreateStream(pstg, szwStreamName,
7629 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7630 if( FAILED (r) )
7631 return r;
7633 /* Write CompObj Structure to stream */
7634 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7636 if( SUCCEEDED( r ) )
7637 r = WriteClassStm( pstm, clsid );
7639 if( SUCCEEDED( r ) )
7640 r = STREAM_WriteString( pstm, lpszUserType );
7641 if( SUCCEEDED( r ) )
7642 r = STREAM_WriteString( pstm, szClipName );
7643 if( SUCCEEDED( r ) )
7644 r = STREAM_WriteString( pstm, szProgIDName );
7645 if( SUCCEEDED( r ) )
7646 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7648 IStream_Release( pstm );
7650 return r;
7653 /***********************************************************************
7654 * WriteFmtUserTypeStg (OLE32.@)
7656 HRESULT WINAPI WriteFmtUserTypeStg(
7657 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7659 HRESULT r;
7660 WCHAR szwClipName[0x40];
7661 CLSID clsid = CLSID_NULL;
7662 LPWSTR wstrProgID = NULL;
7663 DWORD n;
7665 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7667 /* get the clipboard format name */
7668 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7669 szwClipName[n]=0;
7671 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7673 /* FIXME: There's room to save a CLSID and its ProgID, but
7674 the CLSID is not looked up in the registry and in all the
7675 tests I wrote it was CLSID_NULL. Where does it come from?
7678 /* get the real program ID. This may fail, but that's fine */
7679 ProgIDFromCLSID(&clsid, &wstrProgID);
7681 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7683 r = STORAGE_WriteCompObj( pstg, &clsid,
7684 lpszUserType, szwClipName, wstrProgID );
7686 CoTaskMemFree(wstrProgID);
7688 return r;
7692 /******************************************************************************
7693 * ReadFmtUserTypeStg [OLE32.@]
7695 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7697 HRESULT r;
7698 IStream *stm = 0;
7699 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7700 unsigned char unknown1[12];
7701 unsigned char unknown2[16];
7702 DWORD count;
7703 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7704 CLSID clsid;
7706 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7708 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7709 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7710 if( FAILED ( r ) )
7712 WARN("Failed to open stream r = %08x\n", r);
7713 return r;
7716 /* read the various parts of the structure */
7717 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7718 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7719 goto end;
7720 r = ReadClassStm( stm, &clsid );
7721 if( FAILED( r ) )
7722 goto end;
7724 r = STREAM_ReadString( stm, &szCLSIDName );
7725 if( FAILED( r ) )
7726 goto end;
7728 r = STREAM_ReadString( stm, &szOleTypeName );
7729 if( FAILED( r ) )
7730 goto end;
7732 r = STREAM_ReadString( stm, &szProgIDName );
7733 if( FAILED( r ) )
7734 goto end;
7736 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7737 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7738 goto end;
7740 /* ok, success... now we just need to store what we found */
7741 if( pcf )
7742 *pcf = RegisterClipboardFormatW( szOleTypeName );
7743 CoTaskMemFree( szOleTypeName );
7745 if( lplpszUserType )
7746 *lplpszUserType = szCLSIDName;
7747 CoTaskMemFree( szProgIDName );
7749 end:
7750 IStream_Release( stm );
7752 return r;
7756 /*************************************************************************
7757 * OLECONVERT_CreateCompObjStream [Internal]
7759 * Creates a "\001CompObj" is the destination IStorage if necessary.
7761 * PARAMS
7762 * pStorage [I] The dest IStorage to create the CompObj Stream
7763 * if necessary.
7764 * strOleTypeName [I] The ProgID
7766 * RETURNS
7767 * Success: S_OK
7768 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7770 * NOTES
7771 * This function is used by OleConvertOLESTREAMToIStorage only.
7773 * The stream data is stored in the OLESTREAM and there should be
7774 * no need to recreate the stream. If the stream is manually
7775 * deleted it will attempt to create it by querying the registry.
7779 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7781 IStream *pStream;
7782 HRESULT hStorageRes, hRes = S_OK;
7783 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7784 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7785 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7787 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7788 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7790 /* Initialize the CompObj structure */
7791 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7792 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7793 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7796 /* Create a CompObj stream if it doesn't exist */
7797 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7798 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7799 if(hStorageRes == S_OK)
7801 /* copy the OleTypeName to the compobj struct */
7802 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7803 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7805 /* copy the OleTypeName to the compobj struct */
7806 /* Note: in the test made, these were Identical */
7807 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7808 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7810 /* Get the CLSID */
7811 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7812 bufferW, OLESTREAM_MAX_STR_LEN );
7813 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7815 if(hRes == S_OK)
7817 HKEY hKey;
7818 LONG hErr;
7819 /* Get the CLSID Default Name from the Registry */
7820 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7821 if(hErr == ERROR_SUCCESS)
7823 char strTemp[OLESTREAM_MAX_STR_LEN];
7824 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7825 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7826 if(hErr == ERROR_SUCCESS)
7828 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7830 RegCloseKey(hKey);
7834 /* Write CompObj Structure to stream */
7835 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7837 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7839 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7840 if(IStorageCompObj.dwCLSIDNameLength > 0)
7842 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7844 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7845 if(IStorageCompObj.dwOleTypeNameLength > 0)
7847 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7849 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7850 if(IStorageCompObj.dwProgIDNameLength > 0)
7852 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7854 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7855 IStream_Release(pStream);
7857 return hRes;
7861 /*************************************************************************
7862 * OLECONVERT_CreateOlePresStream[Internal]
7864 * Creates the "\002OlePres000" Stream with the Metafile data
7866 * PARAMS
7867 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7868 * dwExtentX [I] Width of the Metafile
7869 * dwExtentY [I] Height of the Metafile
7870 * pData [I] Metafile data
7871 * dwDataLength [I] Size of the Metafile data
7873 * RETURNS
7874 * Success: S_OK
7875 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7877 * NOTES
7878 * This function is used by OleConvertOLESTREAMToIStorage only.
7881 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7883 HRESULT hRes;
7884 IStream *pStream;
7885 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7886 BYTE pOlePresStreamHeader [] =
7888 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7889 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7890 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7891 0x00, 0x00, 0x00, 0x00
7894 BYTE pOlePresStreamHeaderEmpty [] =
7896 0x00, 0x00, 0x00, 0x00,
7897 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7898 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7899 0x00, 0x00, 0x00, 0x00
7902 /* Create the OlePres000 Stream */
7903 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7904 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7906 if(hRes == S_OK)
7908 DWORD nHeaderSize;
7909 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7911 memset(&OlePres, 0, sizeof(OlePres));
7912 /* Do we have any metafile data to save */
7913 if(dwDataLength > 0)
7915 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7916 nHeaderSize = sizeof(pOlePresStreamHeader);
7918 else
7920 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7921 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7923 /* Set width and height of the metafile */
7924 OlePres.dwExtentX = dwExtentX;
7925 OlePres.dwExtentY = -dwExtentY;
7927 /* Set Data and Length */
7928 if(dwDataLength > sizeof(METAFILEPICT16))
7930 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7931 OlePres.pData = &(pData[8]);
7933 /* Save OlePres000 Data to Stream */
7934 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7935 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7936 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7937 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7938 if(OlePres.dwSize > 0)
7940 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7942 IStream_Release(pStream);
7946 /*************************************************************************
7947 * OLECONVERT_CreateOle10NativeStream [Internal]
7949 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7951 * PARAMS
7952 * pStorage [I] Dest storage to create the stream in
7953 * pData [I] Ole10 Native Data (ex. bmp)
7954 * dwDataLength [I] Size of the Ole10 Native Data
7956 * RETURNS
7957 * Nothing
7959 * NOTES
7960 * This function is used by OleConvertOLESTREAMToIStorage only.
7962 * Might need to verify the data and return appropriate error message
7965 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7967 HRESULT hRes;
7968 IStream *pStream;
7969 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7971 /* Create the Ole10Native Stream */
7972 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7973 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7975 if(hRes == S_OK)
7977 /* Write info to stream */
7978 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7979 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7980 IStream_Release(pStream);
7985 /*************************************************************************
7986 * OLECONVERT_GetOLE10ProgID [Internal]
7988 * Finds the ProgID (or OleTypeID) from the IStorage
7990 * PARAMS
7991 * pStorage [I] The Src IStorage to get the ProgID
7992 * strProgID [I] the ProgID string to get
7993 * dwSize [I] the size of the string
7995 * RETURNS
7996 * Success: S_OK
7997 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7999 * NOTES
8000 * This function is used by OleConvertIStorageToOLESTREAM only.
8004 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8006 HRESULT hRes;
8007 IStream *pStream;
8008 LARGE_INTEGER iSeekPos;
8009 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8010 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8012 /* Open the CompObj Stream */
8013 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8014 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8015 if(hRes == S_OK)
8018 /*Get the OleType from the CompObj Stream */
8019 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8020 iSeekPos.u.HighPart = 0;
8022 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8023 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8024 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8025 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8026 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8027 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8028 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8030 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8031 if(*dwSize > 0)
8033 IStream_Read(pStream, strProgID, *dwSize, NULL);
8035 IStream_Release(pStream);
8037 else
8039 STATSTG stat;
8040 LPOLESTR wstrProgID;
8042 /* Get the OleType from the registry */
8043 REFCLSID clsid = &(stat.clsid);
8044 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8045 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8046 if(hRes == S_OK)
8048 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8052 return hRes;
8055 /*************************************************************************
8056 * OLECONVERT_GetOle10PresData [Internal]
8058 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8060 * PARAMS
8061 * pStorage [I] Src IStroage
8062 * pOleStream [I] Dest OleStream Mem Struct
8064 * RETURNS
8065 * Nothing
8067 * NOTES
8068 * This function is used by OleConvertIStorageToOLESTREAM only.
8070 * Memory allocated for pData must be freed by the caller
8074 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8077 HRESULT hRes;
8078 IStream *pStream;
8079 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8081 /* Initialize Default data for OLESTREAM */
8082 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8083 pOleStreamData[0].dwTypeID = 2;
8084 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8085 pOleStreamData[1].dwTypeID = 0;
8086 pOleStreamData[0].dwMetaFileWidth = 0;
8087 pOleStreamData[0].dwMetaFileHeight = 0;
8088 pOleStreamData[0].pData = NULL;
8089 pOleStreamData[1].pData = NULL;
8091 /* Open Ole10Native Stream */
8092 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8093 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8094 if(hRes == S_OK)
8097 /* Read Size and Data */
8098 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8099 if(pOleStreamData->dwDataLength > 0)
8101 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8102 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8104 IStream_Release(pStream);
8110 /*************************************************************************
8111 * OLECONVERT_GetOle20PresData[Internal]
8113 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8115 * PARAMS
8116 * pStorage [I] Src IStroage
8117 * pOleStreamData [I] Dest OleStream Mem Struct
8119 * RETURNS
8120 * Nothing
8122 * NOTES
8123 * This function is used by OleConvertIStorageToOLESTREAM only.
8125 * Memory allocated for pData must be freed by the caller
8127 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8129 HRESULT hRes;
8130 IStream *pStream;
8131 OLECONVERT_ISTORAGE_OLEPRES olePress;
8132 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8134 /* Initialize Default data for OLESTREAM */
8135 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8136 pOleStreamData[0].dwTypeID = 2;
8137 pOleStreamData[0].dwMetaFileWidth = 0;
8138 pOleStreamData[0].dwMetaFileHeight = 0;
8139 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8140 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8141 pOleStreamData[1].dwTypeID = 0;
8142 pOleStreamData[1].dwOleTypeNameLength = 0;
8143 pOleStreamData[1].strOleTypeName[0] = 0;
8144 pOleStreamData[1].dwMetaFileWidth = 0;
8145 pOleStreamData[1].dwMetaFileHeight = 0;
8146 pOleStreamData[1].pData = NULL;
8147 pOleStreamData[1].dwDataLength = 0;
8150 /* Open OlePress000 stream */
8151 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8152 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8153 if(hRes == S_OK)
8155 LARGE_INTEGER iSeekPos;
8156 METAFILEPICT16 MetaFilePict;
8157 static const char strMetafilePictName[] = "METAFILEPICT";
8159 /* Set the TypeID for a Metafile */
8160 pOleStreamData[1].dwTypeID = 5;
8162 /* Set the OleTypeName to Metafile */
8163 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8164 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8166 iSeekPos.u.HighPart = 0;
8167 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8169 /* Get Presentation Data */
8170 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8171 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8172 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8173 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8175 /*Set width and Height */
8176 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8177 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8178 if(olePress.dwSize > 0)
8180 /* Set Length */
8181 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8183 /* Set MetaFilePict struct */
8184 MetaFilePict.mm = 8;
8185 MetaFilePict.xExt = olePress.dwExtentX;
8186 MetaFilePict.yExt = olePress.dwExtentY;
8187 MetaFilePict.hMF = 0;
8189 /* Get Metafile Data */
8190 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8191 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8192 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8194 IStream_Release(pStream);
8198 /*************************************************************************
8199 * OleConvertOLESTREAMToIStorage [OLE32.@]
8201 * Read info on MSDN
8203 * TODO
8204 * DVTARGETDEVICE parameter is not handled
8205 * Still unsure of some mem fields for OLE 10 Stream
8206 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8207 * and "\001OLE" streams
8210 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8211 LPOLESTREAM pOleStream,
8212 LPSTORAGE pstg,
8213 const DVTARGETDEVICE* ptd)
8215 int i;
8216 HRESULT hRes=S_OK;
8217 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8219 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8221 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8223 if(ptd != NULL)
8225 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8228 if(pstg == NULL || pOleStream == NULL)
8230 hRes = E_INVALIDARG;
8233 if(hRes == S_OK)
8235 /* Load the OLESTREAM to Memory */
8236 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8239 if(hRes == S_OK)
8241 /* Load the OLESTREAM to Memory (part 2)*/
8242 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8245 if(hRes == S_OK)
8248 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8250 /* Do we have the IStorage Data in the OLESTREAM */
8251 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8253 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8254 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8256 else
8258 /* It must be an original OLE 1.0 source */
8259 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8262 else
8264 /* It must be an original OLE 1.0 source */
8265 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8268 /* Create CompObj Stream if necessary */
8269 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8270 if(hRes == S_OK)
8272 /*Create the Ole Stream if necessary */
8273 OLECONVERT_CreateOleStream(pstg);
8278 /* Free allocated memory */
8279 for(i=0; i < 2; i++)
8281 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8282 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8283 pOleStreamData[i].pstrOleObjFileName = NULL;
8285 return hRes;
8288 /*************************************************************************
8289 * OleConvertIStorageToOLESTREAM [OLE32.@]
8291 * Read info on MSDN
8293 * Read info on MSDN
8295 * TODO
8296 * Still unsure of some mem fields for OLE 10 Stream
8297 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8298 * and "\001OLE" streams.
8301 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8302 LPSTORAGE pstg,
8303 LPOLESTREAM pOleStream)
8305 int i;
8306 HRESULT hRes = S_OK;
8307 IStream *pStream;
8308 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8309 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8311 TRACE("%p %p\n", pstg, pOleStream);
8313 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8315 if(pstg == NULL || pOleStream == NULL)
8317 hRes = E_INVALIDARG;
8319 if(hRes == S_OK)
8321 /* Get the ProgID */
8322 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8323 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8325 if(hRes == S_OK)
8327 /* Was it originally Ole10 */
8328 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8329 if(hRes == S_OK)
8331 IStream_Release(pStream);
8332 /* Get Presentation Data for Ole10Native */
8333 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8335 else
8337 /* Get Presentation Data (OLE20) */
8338 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8341 /* Save OLESTREAM */
8342 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8343 if(hRes == S_OK)
8345 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8350 /* Free allocated memory */
8351 for(i=0; i < 2; i++)
8353 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8356 return hRes;
8359 /***********************************************************************
8360 * GetConvertStg (OLE32.@)
8362 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8363 FIXME("unimplemented stub!\n");
8364 return E_FAIL;
8367 /******************************************************************************
8368 * StgIsStorageFile [OLE32.@]
8369 * Verify if the file contains a storage object
8371 * PARAMS
8372 * fn [ I] Filename
8374 * RETURNS
8375 * S_OK if file has magic bytes as a storage object
8376 * S_FALSE if file is not storage
8378 HRESULT WINAPI
8379 StgIsStorageFile(LPCOLESTR fn)
8381 HANDLE hf;
8382 BYTE magic[8];
8383 DWORD bytes_read;
8385 TRACE("%s\n", debugstr_w(fn));
8386 hf = CreateFileW(fn, GENERIC_READ,
8387 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8388 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8390 if (hf == INVALID_HANDLE_VALUE)
8391 return STG_E_FILENOTFOUND;
8393 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8395 WARN(" unable to read file\n");
8396 CloseHandle(hf);
8397 return S_FALSE;
8400 CloseHandle(hf);
8402 if (bytes_read != 8) {
8403 TRACE(" too short\n");
8404 return S_FALSE;
8407 if (!memcmp(magic,STORAGE_magic,8)) {
8408 TRACE(" -> YES\n");
8409 return S_OK;
8412 TRACE(" -> Invalid header.\n");
8413 return S_FALSE;
8416 /***********************************************************************
8417 * WriteClassStm (OLE32.@)
8419 * Writes a CLSID to a stream.
8421 * PARAMS
8422 * pStm [I] Stream to write to.
8423 * rclsid [I] CLSID to write.
8425 * RETURNS
8426 * Success: S_OK.
8427 * Failure: HRESULT code.
8429 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8431 TRACE("(%p,%p)\n",pStm,rclsid);
8433 if (!pStm || !rclsid)
8434 return E_INVALIDARG;
8436 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8439 /***********************************************************************
8440 * ReadClassStm (OLE32.@)
8442 * Reads a CLSID from a stream.
8444 * PARAMS
8445 * pStm [I] Stream to read from.
8446 * rclsid [O] CLSID to read.
8448 * RETURNS
8449 * Success: S_OK.
8450 * Failure: HRESULT code.
8452 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8454 ULONG nbByte;
8455 HRESULT res;
8457 TRACE("(%p,%p)\n",pStm,pclsid);
8459 if (!pStm || !pclsid)
8460 return E_INVALIDARG;
8462 /* clear the output args */
8463 *pclsid = CLSID_NULL;
8465 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8467 if (FAILED(res))
8468 return res;
8470 if (nbByte != sizeof(CLSID))
8471 return STG_E_READFAULT;
8472 else
8473 return S_OK;