ole32: Remove an unnecessary special case in StorageBaseImpl_CopyTo.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob2d2c3d621c640353051d17861a8b6a6bad4da855
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 */
262 * The current implementation of the IEnumSTATSTGImpl class uses a stack
263 * to walk the directory entries to get the content of a storage. This stack
264 * is implemented by the following 3 data members
266 ULONG stackSize;
267 ULONG stackMaxSize;
268 DirRef* stackToVisit;
270 #define ENUMSTATSGT_SIZE_INCREMENT 10
274 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
275 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
276 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, DirRef nodeToPush);
277 static DirRef IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
279 /************************************************************************
280 ** Block Functions
283 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
285 if (index == 0xffffffff)
286 index = 0;
287 else
288 index ++;
290 return index * BIG_BLOCK_SIZE;
293 /************************************************************************
294 ** Storage32BaseImpl implementation
296 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
297 ULARGE_INTEGER offset,
298 void* buffer,
299 ULONG size,
300 ULONG* bytesRead)
302 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
305 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
306 ULARGE_INTEGER offset,
307 const void* buffer,
308 const ULONG size,
309 ULONG* bytesWritten)
311 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
314 /************************************************************************
315 * Storage32BaseImpl_QueryInterface (IUnknown)
317 * This method implements the common QueryInterface for all IStorage32
318 * implementations contained in this file.
320 * See Windows documentation for more details on IUnknown methods.
322 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
323 IStorage* iface,
324 REFIID riid,
325 void** ppvObject)
327 StorageBaseImpl *This = (StorageBaseImpl *)iface;
329 if ( (This==0) || (ppvObject==0) )
330 return E_INVALIDARG;
332 *ppvObject = 0;
334 if (IsEqualGUID(&IID_IUnknown, riid) ||
335 IsEqualGUID(&IID_IStorage, riid))
337 *ppvObject = This;
339 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
341 *ppvObject = &This->pssVtbl;
344 if ((*ppvObject)==0)
345 return E_NOINTERFACE;
347 IStorage_AddRef(iface);
349 return S_OK;
352 /************************************************************************
353 * Storage32BaseImpl_AddRef (IUnknown)
355 * This method implements the common AddRef for all IStorage32
356 * implementations contained in this file.
358 * See Windows documentation for more details on IUnknown methods.
360 static ULONG WINAPI StorageBaseImpl_AddRef(
361 IStorage* iface)
363 StorageBaseImpl *This = (StorageBaseImpl *)iface;
364 ULONG ref = InterlockedIncrement(&This->ref);
366 TRACE("(%p) AddRef to %d\n", This, ref);
368 return ref;
371 /************************************************************************
372 * Storage32BaseImpl_Release (IUnknown)
374 * This method implements the common Release for all IStorage32
375 * implementations contained in this file.
377 * See Windows documentation for more details on IUnknown methods.
379 static ULONG WINAPI StorageBaseImpl_Release(
380 IStorage* iface)
382 StorageBaseImpl *This = (StorageBaseImpl *)iface;
384 ULONG ref = InterlockedDecrement(&This->ref);
386 TRACE("(%p) ReleaseRef to %d\n", This, ref);
388 if (ref == 0)
391 * Since we are using a system of base-classes, we want to call the
392 * destructor of the appropriate derived class. To do this, we are
393 * using virtual functions to implement the destructor.
395 StorageBaseImpl_Destroy(This);
398 return ref;
401 /************************************************************************
402 * Storage32BaseImpl_OpenStream (IStorage)
404 * This method will open the specified stream object from the current storage.
406 * See Windows documentation for more details on IStorage methods.
408 static HRESULT WINAPI StorageBaseImpl_OpenStream(
409 IStorage* iface,
410 const OLECHAR* pwcsName, /* [string][in] */
411 void* reserved1, /* [unique][in] */
412 DWORD grfMode, /* [in] */
413 DWORD reserved2, /* [in] */
414 IStream** ppstm) /* [out] */
416 StorageBaseImpl *This = (StorageBaseImpl *)iface;
417 StgStreamImpl* newStream;
418 DirEntry currentEntry;
419 DirRef streamEntryRef;
420 HRESULT res = STG_E_UNKNOWN;
422 TRACE("(%p, %s, %p, %x, %d, %p)\n",
423 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
425 if ( (pwcsName==NULL) || (ppstm==0) )
427 res = E_INVALIDARG;
428 goto end;
431 *ppstm = NULL;
433 if ( FAILED( validateSTGM(grfMode) ) ||
434 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
436 res = STG_E_INVALIDFLAG;
437 goto end;
441 * As documented.
443 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
445 res = STG_E_INVALIDFUNCTION;
446 goto end;
449 if (This->reverted)
451 res = STG_E_REVERTED;
452 goto end;
456 * Check that we're compatible with the parent's storage mode, but
457 * only if we are not in transacted mode
459 if(!(This->openFlags & STGM_TRANSACTED)) {
460 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
462 res = STG_E_ACCESSDENIED;
463 goto end;
468 * Search for the element with the given name
470 streamEntryRef = findElement(
471 This,
472 This->storageDirEntry,
473 pwcsName,
474 &currentEntry);
477 * If it was found, construct the stream object and return a pointer to it.
479 if ( (streamEntryRef!=DIRENTRY_NULL) &&
480 (currentEntry.stgType==STGTY_STREAM) )
482 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
484 /* A single stream cannot be opened a second time. */
485 res = STG_E_ACCESSDENIED;
486 goto end;
489 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
491 if (newStream!=0)
493 newStream->grfMode = grfMode;
494 *ppstm = (IStream*)newStream;
496 IStream_AddRef(*ppstm);
498 res = S_OK;
499 goto end;
502 res = E_OUTOFMEMORY;
503 goto end;
506 res = STG_E_FILENOTFOUND;
508 end:
509 if (res == S_OK)
510 TRACE("<-- IStream %p\n", *ppstm);
511 TRACE("<-- %08x\n", res);
512 return res;
515 /************************************************************************
516 * Storage32BaseImpl_OpenStorage (IStorage)
518 * This method will open a new storage object from the current storage.
520 * See Windows documentation for more details on IStorage methods.
522 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
523 IStorage* iface,
524 const OLECHAR* pwcsName, /* [string][unique][in] */
525 IStorage* pstgPriority, /* [unique][in] */
526 DWORD grfMode, /* [in] */
527 SNB snbExclude, /* [unique][in] */
528 DWORD reserved, /* [in] */
529 IStorage** ppstg) /* [out] */
531 StorageBaseImpl *This = (StorageBaseImpl *)iface;
532 StorageInternalImpl* newStorage;
533 StorageBaseImpl* newTransactedStorage;
534 DirEntry currentEntry;
535 DirRef storageEntryRef;
536 HRESULT res = STG_E_UNKNOWN;
538 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
539 iface, debugstr_w(pwcsName), pstgPriority,
540 grfMode, snbExclude, reserved, ppstg);
542 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
544 res = E_INVALIDARG;
545 goto end;
548 if (This->openFlags & STGM_SIMPLE)
550 res = STG_E_INVALIDFUNCTION;
551 goto end;
554 /* as documented */
555 if (snbExclude != NULL)
557 res = STG_E_INVALIDPARAMETER;
558 goto end;
561 if ( FAILED( validateSTGM(grfMode) ))
563 res = STG_E_INVALIDFLAG;
564 goto end;
568 * As documented.
570 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
571 (grfMode & STGM_DELETEONRELEASE) ||
572 (grfMode & STGM_PRIORITY) )
574 res = STG_E_INVALIDFUNCTION;
575 goto end;
578 if (This->reverted)
579 return STG_E_REVERTED;
582 * Check that we're compatible with the parent's storage mode,
583 * but only if we are not transacted
585 if(!(This->openFlags & STGM_TRANSACTED)) {
586 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
588 res = STG_E_ACCESSDENIED;
589 goto end;
593 *ppstg = NULL;
595 storageEntryRef = findElement(
596 This,
597 This->storageDirEntry,
598 pwcsName,
599 &currentEntry);
601 if ( (storageEntryRef!=DIRENTRY_NULL) &&
602 (currentEntry.stgType==STGTY_STORAGE) )
604 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
606 /* A single storage cannot be opened a second time. */
607 res = STG_E_ACCESSDENIED;
608 goto end;
611 newStorage = StorageInternalImpl_Construct(
612 This,
613 grfMode,
614 storageEntryRef);
616 if (newStorage != 0)
618 if (grfMode & STGM_TRANSACTED)
620 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
622 if (FAILED(res))
624 HeapFree(GetProcessHeap(), 0, newStorage);
625 goto end;
628 *ppstg = (IStorage*)newTransactedStorage;
630 else
632 *ppstg = (IStorage*)newStorage;
635 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
637 res = S_OK;
638 goto end;
641 res = STG_E_INSUFFICIENTMEMORY;
642 goto end;
645 res = STG_E_FILENOTFOUND;
647 end:
648 TRACE("<-- %08x\n", res);
649 return res;
652 /************************************************************************
653 * Storage32BaseImpl_EnumElements (IStorage)
655 * This method will create an enumerator object that can be used to
656 * retrieve information about all the elements in the storage object.
658 * See Windows documentation for more details on IStorage methods.
660 static HRESULT WINAPI StorageBaseImpl_EnumElements(
661 IStorage* iface,
662 DWORD reserved1, /* [in] */
663 void* reserved2, /* [size_is][unique][in] */
664 DWORD reserved3, /* [in] */
665 IEnumSTATSTG** ppenum) /* [out] */
667 StorageBaseImpl *This = (StorageBaseImpl *)iface;
668 IEnumSTATSTGImpl* newEnum;
670 TRACE("(%p, %d, %p, %d, %p)\n",
671 iface, reserved1, reserved2, reserved3, ppenum);
673 if ( (This==0) || (ppenum==0))
674 return E_INVALIDARG;
676 if (This->reverted)
677 return STG_E_REVERTED;
679 newEnum = IEnumSTATSTGImpl_Construct(
680 This,
681 This->storageDirEntry);
683 if (newEnum!=0)
685 *ppenum = (IEnumSTATSTG*)newEnum;
687 IEnumSTATSTG_AddRef(*ppenum);
689 return S_OK;
692 return E_OUTOFMEMORY;
695 /************************************************************************
696 * Storage32BaseImpl_Stat (IStorage)
698 * This method will retrieve information about this storage object.
700 * See Windows documentation for more details on IStorage methods.
702 static HRESULT WINAPI StorageBaseImpl_Stat(
703 IStorage* iface,
704 STATSTG* pstatstg, /* [out] */
705 DWORD grfStatFlag) /* [in] */
707 StorageBaseImpl *This = (StorageBaseImpl *)iface;
708 DirEntry currentEntry;
709 HRESULT res = STG_E_UNKNOWN;
711 TRACE("(%p, %p, %x)\n",
712 iface, pstatstg, grfStatFlag);
714 if ( (This==0) || (pstatstg==0))
716 res = E_INVALIDARG;
717 goto end;
720 if (This->reverted)
722 res = STG_E_REVERTED;
723 goto end;
726 res = StorageBaseImpl_ReadDirEntry(
727 This,
728 This->storageDirEntry,
729 &currentEntry);
731 if (SUCCEEDED(res))
733 StorageUtl_CopyDirEntryToSTATSTG(
734 This,
735 pstatstg,
736 &currentEntry,
737 grfStatFlag);
739 pstatstg->grfMode = This->openFlags;
740 pstatstg->grfStateBits = This->stateBits;
743 end:
744 if (res == S_OK)
746 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);
748 TRACE("<-- %08x\n", res);
749 return res;
752 /************************************************************************
753 * Storage32BaseImpl_RenameElement (IStorage)
755 * This method will rename the specified element.
757 * See Windows documentation for more details on IStorage methods.
759 static HRESULT WINAPI StorageBaseImpl_RenameElement(
760 IStorage* iface,
761 const OLECHAR* pwcsOldName, /* [in] */
762 const OLECHAR* pwcsNewName) /* [in] */
764 StorageBaseImpl *This = (StorageBaseImpl *)iface;
765 DirEntry currentEntry;
766 DirRef currentEntryRef;
768 TRACE("(%p, %s, %s)\n",
769 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
771 if (This->reverted)
772 return STG_E_REVERTED;
774 currentEntryRef = findElement(This,
775 This->storageDirEntry,
776 pwcsNewName,
777 &currentEntry);
779 if (currentEntryRef != DIRENTRY_NULL)
782 * There is already an element with the new name
784 return STG_E_FILEALREADYEXISTS;
788 * Search for the old element name
790 currentEntryRef = findElement(This,
791 This->storageDirEntry,
792 pwcsOldName,
793 &currentEntry);
795 if (currentEntryRef != DIRENTRY_NULL)
797 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
798 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
800 WARN("Element is already open; cannot rename.\n");
801 return STG_E_ACCESSDENIED;
804 /* Remove the element from its current position in the tree */
805 removeFromTree(This, This->storageDirEntry,
806 currentEntryRef);
808 /* Change the name of the element */
809 strcpyW(currentEntry.name, pwcsNewName);
811 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
812 &currentEntry);
814 /* Insert the element in a new position in the tree */
815 insertIntoTree(This, This->storageDirEntry,
816 currentEntryRef);
818 else
821 * There is no element with the old name
823 return STG_E_FILENOTFOUND;
826 return S_OK;
829 /************************************************************************
830 * Storage32BaseImpl_CreateStream (IStorage)
832 * This method will create a stream object within this storage
834 * See Windows documentation for more details on IStorage methods.
836 static HRESULT WINAPI StorageBaseImpl_CreateStream(
837 IStorage* iface,
838 const OLECHAR* pwcsName, /* [string][in] */
839 DWORD grfMode, /* [in] */
840 DWORD reserved1, /* [in] */
841 DWORD reserved2, /* [in] */
842 IStream** ppstm) /* [out] */
844 StorageBaseImpl *This = (StorageBaseImpl *)iface;
845 StgStreamImpl* newStream;
846 DirEntry currentEntry, newStreamEntry;
847 DirRef currentEntryRef, newStreamEntryRef;
849 TRACE("(%p, %s, %x, %d, %d, %p)\n",
850 iface, debugstr_w(pwcsName), grfMode,
851 reserved1, reserved2, ppstm);
853 if (ppstm == 0)
854 return STG_E_INVALIDPOINTER;
856 if (pwcsName == 0)
857 return STG_E_INVALIDNAME;
859 if (reserved1 || reserved2)
860 return STG_E_INVALIDPARAMETER;
862 if ( FAILED( validateSTGM(grfMode) ))
863 return STG_E_INVALIDFLAG;
865 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
866 return STG_E_INVALIDFLAG;
868 if (This->reverted)
869 return STG_E_REVERTED;
872 * As documented.
874 if ((grfMode & STGM_DELETEONRELEASE) ||
875 (grfMode & STGM_TRANSACTED))
876 return STG_E_INVALIDFUNCTION;
878 /* Can't create a stream on read-only storage */
879 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
880 return STG_E_ACCESSDENIED;
883 * Check that we're compatible with the parent's storage mode
884 * if not in transacted mode
886 if(!(This->openFlags & STGM_TRANSACTED)) {
887 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
888 return STG_E_ACCESSDENIED;
891 if(This->openFlags & STGM_SIMPLE)
892 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
894 *ppstm = 0;
896 currentEntryRef = findElement(This,
897 This->storageDirEntry,
898 pwcsName,
899 &currentEntry);
901 if (currentEntryRef != DIRENTRY_NULL)
904 * An element with this name already exists
906 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
908 IStorage_DestroyElement(iface, pwcsName);
910 else
911 return STG_E_FILEALREADYEXISTS;
913 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
915 WARN("read-only storage\n");
916 return STG_E_ACCESSDENIED;
920 * memset the empty entry
922 memset(&newStreamEntry, 0, sizeof(DirEntry));
924 newStreamEntry.sizeOfNameString =
925 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
927 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
928 return STG_E_INVALIDNAME;
930 strcpyW(newStreamEntry.name, pwcsName);
932 newStreamEntry.stgType = STGTY_STREAM;
933 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
934 newStreamEntry.size.u.LowPart = 0;
935 newStreamEntry.size.u.HighPart = 0;
937 newStreamEntry.leftChild = DIRENTRY_NULL;
938 newStreamEntry.rightChild = DIRENTRY_NULL;
939 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
941 /* call CoFileTime to get the current time
942 newStreamEntry.ctime
943 newStreamEntry.mtime
946 /* newStreamEntry.clsid */
949 * Create an entry with the new data
951 StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
953 * Insert the new entry in the parent storage's tree.
955 insertIntoTree(
956 This,
957 This->storageDirEntry,
958 newStreamEntryRef);
961 * Open the stream to return it.
963 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
965 if (newStream != 0)
967 *ppstm = (IStream*)newStream;
969 IStream_AddRef(*ppstm);
971 else
973 return STG_E_INSUFFICIENTMEMORY;
976 return S_OK;
979 /************************************************************************
980 * Storage32BaseImpl_SetClass (IStorage)
982 * This method will write the specified CLSID in the directory entry of this
983 * storage.
985 * See Windows documentation for more details on IStorage methods.
987 static HRESULT WINAPI StorageBaseImpl_SetClass(
988 IStorage* iface,
989 REFCLSID clsid) /* [in] */
991 StorageBaseImpl *This = (StorageBaseImpl *)iface;
992 HRESULT hRes;
993 DirEntry currentEntry;
995 TRACE("(%p, %p)\n", iface, clsid);
997 if (This->reverted)
998 return STG_E_REVERTED;
1000 hRes = StorageBaseImpl_ReadDirEntry(This,
1001 This->storageDirEntry,
1002 &currentEntry);
1003 if (SUCCEEDED(hRes))
1005 currentEntry.clsid = *clsid;
1007 hRes = StorageBaseImpl_WriteDirEntry(This,
1008 This->storageDirEntry,
1009 &currentEntry);
1012 return hRes;
1015 /************************************************************************
1016 ** Storage32Impl implementation
1019 /************************************************************************
1020 * Storage32BaseImpl_CreateStorage (IStorage)
1022 * This method will create the storage object within the provided storage.
1024 * See Windows documentation for more details on IStorage methods.
1026 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1027 IStorage* iface,
1028 const OLECHAR *pwcsName, /* [string][in] */
1029 DWORD grfMode, /* [in] */
1030 DWORD reserved1, /* [in] */
1031 DWORD reserved2, /* [in] */
1032 IStorage **ppstg) /* [out] */
1034 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1036 DirEntry currentEntry;
1037 DirEntry newEntry;
1038 DirRef currentEntryRef;
1039 DirRef newEntryRef;
1040 HRESULT hr;
1042 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1043 iface, debugstr_w(pwcsName), grfMode,
1044 reserved1, reserved2, ppstg);
1046 if (ppstg == 0)
1047 return STG_E_INVALIDPOINTER;
1049 if (This->openFlags & STGM_SIMPLE)
1051 return STG_E_INVALIDFUNCTION;
1054 if (pwcsName == 0)
1055 return STG_E_INVALIDNAME;
1057 *ppstg = NULL;
1059 if ( FAILED( validateSTGM(grfMode) ) ||
1060 (grfMode & STGM_DELETEONRELEASE) )
1062 WARN("bad grfMode: 0x%x\n", grfMode);
1063 return STG_E_INVALIDFLAG;
1066 if (This->reverted)
1067 return STG_E_REVERTED;
1070 * Check that we're compatible with the parent's storage mode
1072 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1074 WARN("access denied\n");
1075 return STG_E_ACCESSDENIED;
1078 currentEntryRef = findElement(This,
1079 This->storageDirEntry,
1080 pwcsName,
1081 &currentEntry);
1083 if (currentEntryRef != DIRENTRY_NULL)
1086 * An element with this name already exists
1088 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1089 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1091 hr = IStorage_DestroyElement(iface, pwcsName);
1092 if (FAILED(hr))
1093 return hr;
1095 else
1097 WARN("file already exists\n");
1098 return STG_E_FILEALREADYEXISTS;
1101 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1103 WARN("read-only storage\n");
1104 return STG_E_ACCESSDENIED;
1107 memset(&newEntry, 0, sizeof(DirEntry));
1109 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1111 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1113 FIXME("name too long\n");
1114 return STG_E_INVALIDNAME;
1117 strcpyW(newEntry.name, pwcsName);
1119 newEntry.stgType = STGTY_STORAGE;
1120 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1121 newEntry.size.u.LowPart = 0;
1122 newEntry.size.u.HighPart = 0;
1124 newEntry.leftChild = DIRENTRY_NULL;
1125 newEntry.rightChild = DIRENTRY_NULL;
1126 newEntry.dirRootEntry = DIRENTRY_NULL;
1128 /* call CoFileTime to get the current time
1129 newEntry.ctime
1130 newEntry.mtime
1133 /* newEntry.clsid */
1136 * Create a new directory entry for the storage
1138 StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1141 * Insert the new directory entry into the parent storage's tree
1143 insertIntoTree(
1144 This,
1145 This->storageDirEntry,
1146 newEntryRef);
1149 * Open it to get a pointer to return.
1151 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1153 if( (hr != S_OK) || (*ppstg == NULL))
1155 return hr;
1159 return S_OK;
1163 /***************************************************************************
1165 * Internal Method
1167 * Reserve a directory entry in the file and initialize it.
1169 static HRESULT StorageImpl_CreateDirEntry(
1170 StorageBaseImpl *base,
1171 const DirEntry *newData,
1172 DirRef *index)
1174 StorageImpl *storage = (StorageImpl*)base;
1175 ULONG currentEntryIndex = 0;
1176 ULONG newEntryIndex = DIRENTRY_NULL;
1177 HRESULT hr = S_OK;
1178 BYTE currentData[RAW_DIRENTRY_SIZE];
1179 WORD sizeOfNameString;
1183 hr = StorageImpl_ReadRawDirEntry(storage,
1184 currentEntryIndex,
1185 currentData);
1187 if (SUCCEEDED(hr))
1189 StorageUtl_ReadWord(
1190 currentData,
1191 OFFSET_PS_NAMELENGTH,
1192 &sizeOfNameString);
1194 if (sizeOfNameString == 0)
1197 * The entry exists and is available, we found it.
1199 newEntryIndex = currentEntryIndex;
1202 else
1205 * We exhausted the directory entries, we will create more space below
1207 newEntryIndex = currentEntryIndex;
1209 currentEntryIndex++;
1211 } while (newEntryIndex == DIRENTRY_NULL);
1214 * grow the directory stream
1216 if (FAILED(hr))
1218 BYTE emptyData[RAW_DIRENTRY_SIZE];
1219 ULARGE_INTEGER newSize;
1220 ULONG entryIndex;
1221 ULONG lastEntry = 0;
1222 ULONG blockCount = 0;
1225 * obtain the new count of blocks in the directory stream
1227 blockCount = BlockChainStream_GetCount(
1228 storage->rootBlockChain)+1;
1231 * initialize the size used by the directory stream
1233 newSize.u.HighPart = 0;
1234 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1237 * add a block to the directory stream
1239 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1242 * memset the empty entry in order to initialize the unused newly
1243 * created entries
1245 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1248 * initialize them
1250 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1252 for(
1253 entryIndex = newEntryIndex + 1;
1254 entryIndex < lastEntry;
1255 entryIndex++)
1257 StorageImpl_WriteRawDirEntry(
1258 storage,
1259 entryIndex,
1260 emptyData);
1264 UpdateRawDirEntry(currentData, newData);
1266 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1268 if (SUCCEEDED(hr))
1269 *index = newEntryIndex;
1271 return hr;
1274 /***************************************************************************
1276 * Internal Method
1278 * Mark a directory entry in the file as free.
1280 static HRESULT StorageImpl_DestroyDirEntry(
1281 StorageBaseImpl *base,
1282 DirRef index)
1284 HRESULT hr;
1285 BYTE emptyData[RAW_DIRENTRY_SIZE];
1286 StorageImpl *storage = (StorageImpl*)base;
1288 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1290 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1292 return hr;
1296 /***************************************************************************
1298 * Internal Method
1300 * Destroy an entry, its attached data, and all entries reachable from it.
1302 static HRESULT DestroyReachableEntries(
1303 StorageBaseImpl *base,
1304 DirRef index)
1306 HRESULT hr = S_OK;
1307 DirEntry data;
1308 ULARGE_INTEGER zero;
1310 zero.QuadPart = 0;
1312 if (index != DIRENTRY_NULL)
1314 hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1316 if (SUCCEEDED(hr))
1317 hr = DestroyReachableEntries(base, data.dirRootEntry);
1319 if (SUCCEEDED(hr))
1320 hr = DestroyReachableEntries(base, data.leftChild);
1322 if (SUCCEEDED(hr))
1323 hr = DestroyReachableEntries(base, data.rightChild);
1325 if (SUCCEEDED(hr))
1326 hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1328 if (SUCCEEDED(hr))
1329 hr = StorageBaseImpl_DestroyDirEntry(base, index);
1332 return hr;
1336 /****************************************************************************
1338 * Internal Method
1340 * Case insensitive comparison of DirEntry.name by first considering
1341 * their size.
1343 * Returns <0 when name1 < name2
1344 * >0 when name1 > name2
1345 * 0 when name1 == name2
1347 static LONG entryNameCmp(
1348 const OLECHAR *name1,
1349 const OLECHAR *name2)
1351 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1353 if (diff == 0)
1356 * We compare the string themselves only when they are of the same length
1358 diff = lstrcmpiW( name1, name2);
1361 return diff;
1364 /****************************************************************************
1366 * Internal Method
1368 * Add a directory entry to a storage
1370 static HRESULT insertIntoTree(
1371 StorageBaseImpl *This,
1372 DirRef parentStorageIndex,
1373 DirRef newEntryIndex)
1375 DirEntry currentEntry;
1376 DirEntry newEntry;
1379 * Read the inserted entry
1381 StorageBaseImpl_ReadDirEntry(This,
1382 newEntryIndex,
1383 &newEntry);
1386 * Read the storage entry
1388 StorageBaseImpl_ReadDirEntry(This,
1389 parentStorageIndex,
1390 &currentEntry);
1392 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1395 * The root storage contains some element, therefore, start the research
1396 * for the appropriate location.
1398 BOOL found = 0;
1399 DirRef current, next, previous, currentEntryId;
1402 * Keep a reference to the root of the storage's element tree
1404 currentEntryId = currentEntry.dirRootEntry;
1407 * Read
1409 StorageBaseImpl_ReadDirEntry(This,
1410 currentEntry.dirRootEntry,
1411 &currentEntry);
1413 previous = currentEntry.leftChild;
1414 next = currentEntry.rightChild;
1415 current = currentEntryId;
1417 while (found == 0)
1419 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1421 if (diff < 0)
1423 if (previous != DIRENTRY_NULL)
1425 StorageBaseImpl_ReadDirEntry(This,
1426 previous,
1427 &currentEntry);
1428 current = previous;
1430 else
1432 currentEntry.leftChild = newEntryIndex;
1433 StorageBaseImpl_WriteDirEntry(This,
1434 current,
1435 &currentEntry);
1436 found = 1;
1439 else if (diff > 0)
1441 if (next != DIRENTRY_NULL)
1443 StorageBaseImpl_ReadDirEntry(This,
1444 next,
1445 &currentEntry);
1446 current = next;
1448 else
1450 currentEntry.rightChild = newEntryIndex;
1451 StorageBaseImpl_WriteDirEntry(This,
1452 current,
1453 &currentEntry);
1454 found = 1;
1457 else
1460 * Trying to insert an item with the same name in the
1461 * subtree structure.
1463 return STG_E_FILEALREADYEXISTS;
1466 previous = currentEntry.leftChild;
1467 next = currentEntry.rightChild;
1470 else
1473 * The storage is empty, make the new entry the root of its element tree
1475 currentEntry.dirRootEntry = newEntryIndex;
1476 StorageBaseImpl_WriteDirEntry(This,
1477 parentStorageIndex,
1478 &currentEntry);
1481 return S_OK;
1484 /****************************************************************************
1486 * Internal Method
1488 * Find and read the element of a storage with the given name.
1490 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1491 const OLECHAR *name, DirEntry *data)
1493 DirRef currentEntry;
1495 /* Read the storage entry to find the root of the tree. */
1496 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1498 currentEntry = data->dirRootEntry;
1500 while (currentEntry != DIRENTRY_NULL)
1502 LONG cmp;
1504 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1506 cmp = entryNameCmp(name, data->name);
1508 if (cmp == 0)
1509 /* found it */
1510 break;
1512 else if (cmp < 0)
1513 currentEntry = data->leftChild;
1515 else if (cmp > 0)
1516 currentEntry = data->rightChild;
1519 return currentEntry;
1522 /****************************************************************************
1524 * Internal Method
1526 * Find and read the binary tree parent of the element with the given name.
1528 * If there is no such element, find a place where it could be inserted and
1529 * return STG_E_FILENOTFOUND.
1531 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1532 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1533 ULONG *relation)
1535 DirRef childEntry;
1536 DirEntry childData;
1538 /* Read the storage entry to find the root of the tree. */
1539 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1541 *parentEntry = storageEntry;
1542 *relation = DIRENTRY_RELATION_DIR;
1544 childEntry = parentData->dirRootEntry;
1546 while (childEntry != DIRENTRY_NULL)
1548 LONG cmp;
1550 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1552 cmp = entryNameCmp(childName, childData.name);
1554 if (cmp == 0)
1555 /* found it */
1556 break;
1558 else if (cmp < 0)
1560 *parentData = childData;
1561 *parentEntry = childEntry;
1562 *relation = DIRENTRY_RELATION_PREVIOUS;
1564 childEntry = parentData->leftChild;
1567 else if (cmp > 0)
1569 *parentData = childData;
1570 *parentEntry = childEntry;
1571 *relation = DIRENTRY_RELATION_NEXT;
1573 childEntry = parentData->rightChild;
1577 if (childEntry == DIRENTRY_NULL)
1578 return STG_E_FILENOTFOUND;
1579 else
1580 return S_OK;
1584 /*************************************************************************
1585 * CopyTo (IStorage)
1587 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1588 IStorage* iface,
1589 DWORD ciidExclude, /* [in] */
1590 const IID* rgiidExclude, /* [size_is][unique][in] */
1591 SNB snbExclude, /* [unique][in] */
1592 IStorage* pstgDest) /* [unique][in] */
1594 IEnumSTATSTG *elements = 0;
1595 STATSTG curElement, strStat;
1596 HRESULT hr;
1597 IStorage *pstgTmp, *pstgChild;
1598 IStream *pstrTmp, *pstrChild;
1599 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1600 int i;
1602 TRACE("(%p, %d, %p, %p, %p)\n",
1603 iface, ciidExclude, rgiidExclude,
1604 snbExclude, pstgDest);
1606 if ( pstgDest == 0 )
1607 return STG_E_INVALIDPOINTER;
1610 * Enumerate the elements
1612 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1614 if ( hr != S_OK )
1615 return hr;
1618 * set the class ID
1620 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1621 IStorage_SetClass( pstgDest, &curElement.clsid );
1623 for(i = 0; i < ciidExclude; ++i)
1625 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1626 skip_storage = TRUE;
1627 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1628 skip_stream = TRUE;
1629 else
1630 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1636 * Obtain the next element
1638 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1640 if ( hr == S_FALSE )
1642 hr = S_OK; /* done, every element has been copied */
1643 break;
1646 if ( snbExclude )
1648 WCHAR **snb = snbExclude;
1649 skip = FALSE;
1650 while ( *snb != NULL && !skip )
1652 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1653 skip = TRUE;
1654 ++snb;
1658 if ( skip )
1659 goto cleanup;
1661 if (curElement.type == STGTY_STORAGE)
1663 if(skip_storage)
1664 goto cleanup;
1667 * open child source storage
1669 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1670 STGM_READ|STGM_SHARE_EXCLUSIVE,
1671 NULL, 0, &pstgChild );
1673 if (hr != S_OK)
1674 goto cleanup;
1677 * create a new storage in destination storage
1679 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1680 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1681 0, 0,
1682 &pstgTmp );
1684 * if it already exist, don't create a new one use this one
1686 if (hr == STG_E_FILEALREADYEXISTS)
1688 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1689 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1690 NULL, 0, &pstgTmp );
1693 if (hr != S_OK)
1694 goto cleanup;
1698 * do the copy recursively
1700 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1701 NULL, pstgTmp );
1703 IStorage_Release( pstgTmp );
1704 IStorage_Release( pstgChild );
1706 else if (curElement.type == STGTY_STREAM)
1708 if(skip_stream)
1709 goto cleanup;
1712 * create a new stream in destination storage. If the stream already
1713 * exist, it will be deleted and a new one will be created.
1715 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1716 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1717 0, 0, &pstrTmp );
1719 if (hr != S_OK)
1720 goto cleanup;
1723 * open child stream storage
1725 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1726 STGM_READ|STGM_SHARE_EXCLUSIVE,
1727 0, &pstrChild );
1729 if (hr != S_OK)
1730 goto cleanup;
1733 * Get the size of the source stream
1735 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1738 * Set the size of the destination stream.
1740 IStream_SetSize(pstrTmp, strStat.cbSize);
1743 * do the copy
1745 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1746 NULL, NULL );
1748 IStream_Release( pstrTmp );
1749 IStream_Release( pstrChild );
1751 else
1753 WARN("unknown element type: %d\n", curElement.type);
1756 cleanup:
1757 CoTaskMemFree(curElement.pwcsName);
1758 } while (hr == S_OK);
1761 * Clean-up
1763 IEnumSTATSTG_Release(elements);
1765 return hr;
1768 /*************************************************************************
1769 * MoveElementTo (IStorage)
1771 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1772 IStorage* iface,
1773 const OLECHAR *pwcsName, /* [string][in] */
1774 IStorage *pstgDest, /* [unique][in] */
1775 const OLECHAR *pwcsNewName,/* [string][in] */
1776 DWORD grfFlags) /* [in] */
1778 FIXME("(%p %s %p %s %u): stub\n", iface,
1779 debugstr_w(pwcsName), pstgDest,
1780 debugstr_w(pwcsNewName), grfFlags);
1781 return E_NOTIMPL;
1784 /*************************************************************************
1785 * Commit (IStorage)
1787 * Ensures that any changes made to a storage object open in transacted mode
1788 * are reflected in the parent storage
1790 * NOTES
1791 * Wine doesn't implement transacted mode, which seems to be a basic
1792 * optimization, so we can ignore this stub for now.
1794 static HRESULT WINAPI StorageImpl_Commit(
1795 IStorage* iface,
1796 DWORD grfCommitFlags)/* [in] */
1798 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1799 return S_OK;
1802 /*************************************************************************
1803 * Revert (IStorage)
1805 * Discard all changes that have been made since the last commit operation
1807 static HRESULT WINAPI StorageImpl_Revert(
1808 IStorage* iface)
1810 FIXME("(%p): stub\n", iface);
1811 return E_NOTIMPL;
1814 /*************************************************************************
1815 * DestroyElement (IStorage)
1817 * Strategy: This implementation is built this way for simplicity not for speed.
1818 * I always delete the topmost element of the enumeration and adjust
1819 * the deleted element pointer all the time. This takes longer to
1820 * do but allow to reinvoke DestroyElement whenever we encounter a
1821 * storage object. The optimisation resides in the usage of another
1822 * enumeration strategy that would give all the leaves of a storage
1823 * first. (postfix order)
1825 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1826 IStorage* iface,
1827 const OLECHAR *pwcsName)/* [string][in] */
1829 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1831 HRESULT hr = S_OK;
1832 DirEntry entryToDelete;
1833 DirRef entryToDeleteRef;
1835 TRACE("(%p, %s)\n",
1836 iface, debugstr_w(pwcsName));
1838 if (pwcsName==NULL)
1839 return STG_E_INVALIDPOINTER;
1841 if (This->reverted)
1842 return STG_E_REVERTED;
1844 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1845 return STG_E_ACCESSDENIED;
1847 entryToDeleteRef = findElement(
1848 This,
1849 This->storageDirEntry,
1850 pwcsName,
1851 &entryToDelete);
1853 if ( entryToDeleteRef == DIRENTRY_NULL )
1855 return STG_E_FILENOTFOUND;
1858 if ( entryToDelete.stgType == STGTY_STORAGE )
1860 hr = deleteStorageContents(
1861 This,
1862 entryToDeleteRef,
1863 entryToDelete);
1865 else if ( entryToDelete.stgType == STGTY_STREAM )
1867 hr = deleteStreamContents(
1868 This,
1869 entryToDeleteRef,
1870 entryToDelete);
1873 if (hr!=S_OK)
1874 return hr;
1877 * Remove the entry from its parent storage
1879 hr = removeFromTree(
1880 This,
1881 This->storageDirEntry,
1882 entryToDeleteRef);
1885 * Invalidate the entry
1887 if (SUCCEEDED(hr))
1888 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1890 return hr;
1894 /******************************************************************************
1895 * Internal stream list handlers
1898 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1900 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1901 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1904 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1906 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1907 list_remove(&(strm->StrmListEntry));
1910 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1912 StgStreamImpl *strm;
1914 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1916 if (strm->dirEntry == streamEntry)
1918 return TRUE;
1922 return FALSE;
1925 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1927 StorageInternalImpl *childstg;
1929 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1931 if (childstg->base.storageDirEntry == storageEntry)
1933 return TRUE;
1937 return FALSE;
1940 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1942 struct list *cur, *cur2;
1943 StgStreamImpl *strm=NULL;
1944 StorageInternalImpl *childstg=NULL;
1946 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1947 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1948 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1949 strm->parentStorage = NULL;
1950 list_remove(cur);
1953 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1954 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1955 StorageBaseImpl_Invalidate( &childstg->base );
1958 if (stg->transactedChild)
1960 StorageBaseImpl_Invalidate(stg->transactedChild);
1962 stg->transactedChild = NULL;
1967 /*********************************************************************
1969 * Internal Method
1971 * Delete the contents of a storage entry.
1974 static HRESULT deleteStorageContents(
1975 StorageBaseImpl *parentStorage,
1976 DirRef indexToDelete,
1977 DirEntry entryDataToDelete)
1979 IEnumSTATSTG *elements = 0;
1980 IStorage *childStorage = 0;
1981 STATSTG currentElement;
1982 HRESULT hr;
1983 HRESULT destroyHr = S_OK;
1984 StorageInternalImpl *stg, *stg2;
1986 /* Invalidate any open storage objects. */
1987 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1989 if (stg->base.storageDirEntry == indexToDelete)
1991 StorageBaseImpl_Invalidate(&stg->base);
1996 * Open the storage and enumerate it
1998 hr = StorageBaseImpl_OpenStorage(
1999 (IStorage*)parentStorage,
2000 entryDataToDelete.name,
2002 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2005 &childStorage);
2007 if (hr != S_OK)
2009 return hr;
2013 * Enumerate the elements
2015 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2020 * Obtain the next element
2022 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2023 if (hr==S_OK)
2025 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2027 CoTaskMemFree(currentElement.pwcsName);
2031 * We need to Reset the enumeration every time because we delete elements
2032 * and the enumeration could be invalid
2034 IEnumSTATSTG_Reset(elements);
2036 } while ((hr == S_OK) && (destroyHr == S_OK));
2038 IStorage_Release(childStorage);
2039 IEnumSTATSTG_Release(elements);
2041 return destroyHr;
2044 /*********************************************************************
2046 * Internal Method
2048 * Perform the deletion of a stream's data
2051 static HRESULT deleteStreamContents(
2052 StorageBaseImpl *parentStorage,
2053 DirRef indexToDelete,
2054 DirEntry entryDataToDelete)
2056 IStream *pis;
2057 HRESULT hr;
2058 ULARGE_INTEGER size;
2059 StgStreamImpl *strm, *strm2;
2061 /* Invalidate any open stream objects. */
2062 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2064 if (strm->dirEntry == indexToDelete)
2066 TRACE("Stream deleted %p\n", strm);
2067 strm->parentStorage = NULL;
2068 list_remove(&strm->StrmListEntry);
2072 size.u.HighPart = 0;
2073 size.u.LowPart = 0;
2075 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2076 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2078 if (hr!=S_OK)
2080 return(hr);
2084 * Zap the stream
2086 hr = IStream_SetSize(pis, size);
2088 if(hr != S_OK)
2090 return hr;
2094 * Release the stream object.
2096 IStream_Release(pis);
2098 return S_OK;
2101 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2103 switch (relation)
2105 case DIRENTRY_RELATION_PREVIOUS:
2106 entry->leftChild = new_target;
2107 break;
2108 case DIRENTRY_RELATION_NEXT:
2109 entry->rightChild = new_target;
2110 break;
2111 case DIRENTRY_RELATION_DIR:
2112 entry->dirRootEntry = new_target;
2113 break;
2114 default:
2115 assert(0);
2119 /*************************************************************************
2121 * Internal Method
2123 * This method removes a directory entry from its parent storage tree without
2124 * freeing any resources attached to it.
2126 static HRESULT removeFromTree(
2127 StorageBaseImpl *This,
2128 DirRef parentStorageIndex,
2129 DirRef deletedIndex)
2131 HRESULT hr = S_OK;
2132 DirEntry entryToDelete;
2133 DirEntry parentEntry;
2134 DirRef parentEntryRef;
2135 ULONG typeOfRelation;
2137 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2139 if (hr != S_OK)
2140 return hr;
2143 * Find the element that links to the one we want to delete.
2145 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2146 &parentEntry, &parentEntryRef, &typeOfRelation);
2148 if (hr != S_OK)
2149 return hr;
2151 if (entryToDelete.leftChild != DIRENTRY_NULL)
2154 * Replace the deleted entry with its left child
2156 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2158 hr = StorageBaseImpl_WriteDirEntry(
2159 This,
2160 parentEntryRef,
2161 &parentEntry);
2162 if(FAILED(hr))
2164 return hr;
2167 if (entryToDelete.rightChild != DIRENTRY_NULL)
2170 * We need to reinsert the right child somewhere. We already know it and
2171 * its children are greater than everything in the left tree, so we
2172 * insert it at the rightmost point in the left tree.
2174 DirRef newRightChildParent = entryToDelete.leftChild;
2175 DirEntry newRightChildParentEntry;
2179 hr = StorageBaseImpl_ReadDirEntry(
2180 This,
2181 newRightChildParent,
2182 &newRightChildParentEntry);
2183 if (FAILED(hr))
2185 return hr;
2188 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2189 newRightChildParent = newRightChildParentEntry.rightChild;
2190 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2192 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2194 hr = StorageBaseImpl_WriteDirEntry(
2195 This,
2196 newRightChildParent,
2197 &newRightChildParentEntry);
2198 if (FAILED(hr))
2200 return hr;
2204 else
2207 * Replace the deleted entry with its right child
2209 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2211 hr = StorageBaseImpl_WriteDirEntry(
2212 This,
2213 parentEntryRef,
2214 &parentEntry);
2215 if(FAILED(hr))
2217 return hr;
2221 return hr;
2225 /******************************************************************************
2226 * SetElementTimes (IStorage)
2228 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2229 IStorage* iface,
2230 const OLECHAR *pwcsName,/* [string][in] */
2231 const FILETIME *pctime, /* [in] */
2232 const FILETIME *patime, /* [in] */
2233 const FILETIME *pmtime) /* [in] */
2235 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2236 return S_OK;
2239 /******************************************************************************
2240 * SetStateBits (IStorage)
2242 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2243 IStorage* iface,
2244 DWORD grfStateBits,/* [in] */
2245 DWORD grfMask) /* [in] */
2247 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2249 if (This->reverted)
2250 return STG_E_REVERTED;
2252 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2253 return S_OK;
2256 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2257 DirRef index, const DirEntry *data)
2259 StorageImpl *This = (StorageImpl*)base;
2260 return StorageImpl_WriteDirEntry(This, index, data);
2263 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2264 DirRef index, DirEntry *data)
2266 StorageImpl *This = (StorageImpl*)base;
2267 return StorageImpl_ReadDirEntry(This, index, data);
2270 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2271 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2273 StorageImpl *This = (StorageImpl*)base;
2274 DirEntry data;
2275 HRESULT hr;
2276 ULONG bytesToRead;
2278 hr = StorageImpl_ReadDirEntry(This, index, &data);
2279 if (FAILED(hr)) return hr;
2281 if (data.size.QuadPart == 0)
2283 *bytesRead = 0;
2284 return S_OK;
2287 if (offset.QuadPart + size > data.size.QuadPart)
2289 bytesToRead = data.size.QuadPart - offset.QuadPart;
2291 else
2293 bytesToRead = size;
2296 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2298 SmallBlockChainStream *stream;
2300 stream = SmallBlockChainStream_Construct(This, NULL, index);
2301 if (!stream) return E_OUTOFMEMORY;
2303 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2305 SmallBlockChainStream_Destroy(stream);
2307 return hr;
2309 else
2311 BlockChainStream *stream;
2313 stream = BlockChainStream_Construct(This, NULL, index);
2314 if (!stream) return E_OUTOFMEMORY;
2316 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2318 BlockChainStream_Destroy(stream);
2320 return hr;
2324 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2325 ULARGE_INTEGER newsize)
2327 StorageImpl *This = (StorageImpl*)base;
2328 DirEntry data;
2329 HRESULT hr;
2330 SmallBlockChainStream *smallblock=NULL;
2331 BlockChainStream *bigblock=NULL;
2333 hr = StorageImpl_ReadDirEntry(This, index, &data);
2334 if (FAILED(hr)) return hr;
2336 /* In simple mode keep the stream size above the small block limit */
2337 if (This->base.openFlags & STGM_SIMPLE)
2338 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2340 if (data.size.QuadPart == newsize.QuadPart)
2341 return S_OK;
2343 /* Create a block chain object of the appropriate type */
2344 if (data.size.QuadPart == 0)
2346 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2348 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2349 if (!smallblock) return E_OUTOFMEMORY;
2351 else
2353 bigblock = BlockChainStream_Construct(This, NULL, index);
2354 if (!bigblock) return E_OUTOFMEMORY;
2357 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2359 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2360 if (!smallblock) return E_OUTOFMEMORY;
2362 else
2364 bigblock = BlockChainStream_Construct(This, NULL, index);
2365 if (!bigblock) return E_OUTOFMEMORY;
2368 /* Change the block chain type if necessary. */
2369 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2371 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2372 if (!bigblock)
2374 SmallBlockChainStream_Destroy(smallblock);
2375 return E_FAIL;
2378 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2380 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, &bigblock);
2381 if (!smallblock)
2383 BlockChainStream_Destroy(bigblock);
2384 return E_FAIL;
2388 /* Set the size of the block chain. */
2389 if (smallblock)
2391 SmallBlockChainStream_SetSize(smallblock, newsize);
2392 SmallBlockChainStream_Destroy(smallblock);
2394 else
2396 BlockChainStream_SetSize(bigblock, newsize);
2397 BlockChainStream_Destroy(bigblock);
2400 /* Set the size in the directory entry. */
2401 hr = StorageImpl_ReadDirEntry(This, index, &data);
2402 if (SUCCEEDED(hr))
2404 data.size = newsize;
2406 hr = StorageImpl_WriteDirEntry(This, index, &data);
2408 return hr;
2411 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2412 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2414 StorageImpl *This = (StorageImpl*)base;
2415 DirEntry data;
2416 HRESULT hr;
2417 ULARGE_INTEGER newSize;
2419 hr = StorageImpl_ReadDirEntry(This, index, &data);
2420 if (FAILED(hr)) return hr;
2422 /* Grow the stream if necessary */
2423 newSize.QuadPart = 0;
2424 newSize.QuadPart = offset.QuadPart + size;
2426 if (newSize.QuadPart > data.size.QuadPart)
2428 hr = StorageImpl_StreamSetSize(base, index, newSize);
2429 if (FAILED(hr))
2430 return hr;
2432 data.size = newSize;
2435 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2437 SmallBlockChainStream *stream;
2439 stream = SmallBlockChainStream_Construct(This, NULL, index);
2440 if (!stream) return E_OUTOFMEMORY;
2442 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2444 SmallBlockChainStream_Destroy(stream);
2446 return hr;
2448 else
2450 BlockChainStream *stream;
2452 stream = BlockChainStream_Construct(This, NULL, index);
2453 if (!stream) return E_OUTOFMEMORY;
2455 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2457 BlockChainStream_Destroy(stream);
2459 return hr;
2464 * Virtual function table for the IStorage32Impl class.
2466 static const IStorageVtbl Storage32Impl_Vtbl =
2468 StorageBaseImpl_QueryInterface,
2469 StorageBaseImpl_AddRef,
2470 StorageBaseImpl_Release,
2471 StorageBaseImpl_CreateStream,
2472 StorageBaseImpl_OpenStream,
2473 StorageBaseImpl_CreateStorage,
2474 StorageBaseImpl_OpenStorage,
2475 StorageBaseImpl_CopyTo,
2476 StorageBaseImpl_MoveElementTo,
2477 StorageImpl_Commit,
2478 StorageImpl_Revert,
2479 StorageBaseImpl_EnumElements,
2480 StorageBaseImpl_DestroyElement,
2481 StorageBaseImpl_RenameElement,
2482 StorageBaseImpl_SetElementTimes,
2483 StorageBaseImpl_SetClass,
2484 StorageBaseImpl_SetStateBits,
2485 StorageBaseImpl_Stat
2488 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2490 StorageImpl_Destroy,
2491 StorageImpl_Invalidate,
2492 StorageImpl_CreateDirEntry,
2493 StorageImpl_BaseWriteDirEntry,
2494 StorageImpl_BaseReadDirEntry,
2495 StorageImpl_DestroyDirEntry,
2496 StorageImpl_StreamReadAt,
2497 StorageImpl_StreamWriteAt,
2498 StorageImpl_StreamSetSize
2501 static HRESULT StorageImpl_Construct(
2502 HANDLE hFile,
2503 LPCOLESTR pwcsName,
2504 ILockBytes* pLkbyt,
2505 DWORD openFlags,
2506 BOOL fileBased,
2507 BOOL create,
2508 StorageImpl** result)
2510 StorageImpl* This;
2511 HRESULT hr = S_OK;
2512 DirEntry currentEntry;
2513 DirRef currentEntryRef;
2514 WCHAR fullpath[MAX_PATH];
2516 if ( FAILED( validateSTGM(openFlags) ))
2517 return STG_E_INVALIDFLAG;
2519 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2520 if (!This)
2521 return E_OUTOFMEMORY;
2523 memset(This, 0, sizeof(StorageImpl));
2525 list_init(&This->base.strmHead);
2527 list_init(&This->base.storageHead);
2529 This->base.lpVtbl = &Storage32Impl_Vtbl;
2530 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2531 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2532 This->base.openFlags = (openFlags & ~STGM_CREATE);
2533 This->base.ref = 1;
2534 This->base.create = create;
2536 This->base.reverted = 0;
2538 This->hFile = hFile;
2540 if(pwcsName) {
2541 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2543 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2545 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2546 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2547 if (!This->pwcsName)
2549 hr = STG_E_INSUFFICIENTMEMORY;
2550 goto end;
2552 strcpyW(This->pwcsName, fullpath);
2553 This->base.filename = This->pwcsName;
2557 * Initialize the big block cache.
2559 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2560 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2561 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2562 pLkbyt,
2563 openFlags,
2564 This->bigBlockSize,
2565 fileBased);
2567 if (This->bigBlockFile == 0)
2569 hr = E_FAIL;
2570 goto end;
2573 if (create)
2575 ULARGE_INTEGER size;
2576 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2579 * Initialize all header variables:
2580 * - The big block depot consists of one block and it is at block 0
2581 * - The directory table starts at block 1
2582 * - There is no small block depot
2584 memset( This->bigBlockDepotStart,
2585 BLOCK_UNUSED,
2586 sizeof(This->bigBlockDepotStart));
2588 This->bigBlockDepotCount = 1;
2589 This->bigBlockDepotStart[0] = 0;
2590 This->rootStartBlock = 1;
2591 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2592 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2593 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2594 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2595 This->extBigBlockDepotCount = 0;
2597 StorageImpl_SaveFileHeader(This);
2600 * Add one block for the big block depot and one block for the directory table
2602 size.u.HighPart = 0;
2603 size.u.LowPart = This->bigBlockSize * 3;
2604 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2607 * Initialize the big block depot
2609 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2610 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2611 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2612 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2614 else
2617 * Load the header for the file.
2619 hr = StorageImpl_LoadFileHeader(This);
2621 if (FAILED(hr))
2623 goto end;
2628 * There is no block depot cached yet.
2630 This->indexBlockDepotCached = 0xFFFFFFFF;
2633 * Start searching for free blocks with block 0.
2635 This->prevFreeBlock = 0;
2638 * Create the block chain abstractions.
2640 if(!(This->rootBlockChain =
2641 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2643 hr = STG_E_READFAULT;
2644 goto end;
2647 if(!(This->smallBlockDepotChain =
2648 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2649 DIRENTRY_NULL)))
2651 hr = STG_E_READFAULT;
2652 goto end;
2656 * Write the root storage entry (memory only)
2658 if (create)
2660 DirEntry rootEntry;
2662 * Initialize the directory table
2664 memset(&rootEntry, 0, sizeof(rootEntry));
2665 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2666 sizeof(rootEntry.name)/sizeof(WCHAR) );
2667 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2668 rootEntry.stgType = STGTY_ROOT;
2669 rootEntry.leftChild = DIRENTRY_NULL;
2670 rootEntry.rightChild = DIRENTRY_NULL;
2671 rootEntry.dirRootEntry = DIRENTRY_NULL;
2672 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2673 rootEntry.size.u.HighPart = 0;
2674 rootEntry.size.u.LowPart = 0;
2676 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2680 * Find the ID of the root storage.
2682 currentEntryRef = 0;
2686 hr = StorageImpl_ReadDirEntry(
2687 This,
2688 currentEntryRef,
2689 &currentEntry);
2691 if (SUCCEEDED(hr))
2693 if ( (currentEntry.sizeOfNameString != 0 ) &&
2694 (currentEntry.stgType == STGTY_ROOT) )
2696 This->base.storageDirEntry = currentEntryRef;
2700 currentEntryRef++;
2702 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2704 if (FAILED(hr))
2706 hr = STG_E_READFAULT;
2707 goto end;
2711 * Create the block chain abstraction for the small block root chain.
2713 if(!(This->smallBlockRootChain =
2714 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2716 hr = STG_E_READFAULT;
2719 end:
2720 if (FAILED(hr))
2722 IStorage_Release((IStorage*)This);
2723 *result = NULL;
2725 else
2726 *result = This;
2728 return hr;
2731 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2733 StorageImpl *This = (StorageImpl*) iface;
2735 StorageBaseImpl_DeleteAll(&This->base);
2737 This->base.reverted = 1;
2740 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2742 StorageImpl *This = (StorageImpl*) iface;
2743 TRACE("(%p)\n", This);
2745 StorageImpl_Invalidate(iface);
2747 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2749 BlockChainStream_Destroy(This->smallBlockRootChain);
2750 BlockChainStream_Destroy(This->rootBlockChain);
2751 BlockChainStream_Destroy(This->smallBlockDepotChain);
2753 if (This->bigBlockFile)
2754 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2755 HeapFree(GetProcessHeap(), 0, This);
2758 /******************************************************************************
2759 * Storage32Impl_GetNextFreeBigBlock
2761 * Returns the index of the next free big block.
2762 * If the big block depot is filled, this method will enlarge it.
2765 static ULONG StorageImpl_GetNextFreeBigBlock(
2766 StorageImpl* This)
2768 ULONG depotBlockIndexPos;
2769 BYTE depotBuffer[BIG_BLOCK_SIZE];
2770 BOOL success;
2771 ULONG depotBlockOffset;
2772 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2773 ULONG nextBlockIndex = BLOCK_SPECIAL;
2774 int depotIndex = 0;
2775 ULONG freeBlock = BLOCK_UNUSED;
2777 depotIndex = This->prevFreeBlock / blocksPerDepot;
2778 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2781 * Scan the entire big block depot until we find a block marked free
2783 while (nextBlockIndex != BLOCK_UNUSED)
2785 if (depotIndex < COUNT_BBDEPOTINHEADER)
2787 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2790 * Grow the primary depot.
2792 if (depotBlockIndexPos == BLOCK_UNUSED)
2794 depotBlockIndexPos = depotIndex*blocksPerDepot;
2797 * Add a block depot.
2799 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2800 This->bigBlockDepotCount++;
2801 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2804 * Flag it as a block depot.
2806 StorageImpl_SetNextBlockInChain(This,
2807 depotBlockIndexPos,
2808 BLOCK_SPECIAL);
2810 /* Save new header information.
2812 StorageImpl_SaveFileHeader(This);
2815 else
2817 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2819 if (depotBlockIndexPos == BLOCK_UNUSED)
2822 * Grow the extended depot.
2824 ULONG extIndex = BLOCK_UNUSED;
2825 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2826 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2828 if (extBlockOffset == 0)
2830 /* We need an extended block.
2832 extIndex = Storage32Impl_AddExtBlockDepot(This);
2833 This->extBigBlockDepotCount++;
2834 depotBlockIndexPos = extIndex + 1;
2836 else
2837 depotBlockIndexPos = depotIndex * blocksPerDepot;
2840 * Add a block depot and mark it in the extended block.
2842 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2843 This->bigBlockDepotCount++;
2844 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2846 /* Flag the block depot.
2848 StorageImpl_SetNextBlockInChain(This,
2849 depotBlockIndexPos,
2850 BLOCK_SPECIAL);
2852 /* If necessary, flag the extended depot block.
2854 if (extIndex != BLOCK_UNUSED)
2855 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2857 /* Save header information.
2859 StorageImpl_SaveFileHeader(This);
2863 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2865 if (success)
2867 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2868 ( nextBlockIndex != BLOCK_UNUSED))
2870 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2872 if (nextBlockIndex == BLOCK_UNUSED)
2874 freeBlock = (depotIndex * blocksPerDepot) +
2875 (depotBlockOffset/sizeof(ULONG));
2878 depotBlockOffset += sizeof(ULONG);
2882 depotIndex++;
2883 depotBlockOffset = 0;
2887 * make sure that the block physically exists before using it
2889 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2891 This->prevFreeBlock = freeBlock;
2893 return freeBlock;
2896 /******************************************************************************
2897 * Storage32Impl_AddBlockDepot
2899 * This will create a depot block, essentially it is a block initialized
2900 * to BLOCK_UNUSEDs.
2902 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2904 BYTE blockBuffer[BIG_BLOCK_SIZE];
2907 * Initialize blocks as free
2909 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2910 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2913 /******************************************************************************
2914 * Storage32Impl_GetExtDepotBlock
2916 * Returns the index of the block that corresponds to the specified depot
2917 * index. This method is only for depot indexes equal or greater than
2918 * COUNT_BBDEPOTINHEADER.
2920 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2922 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2923 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2924 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2925 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2926 ULONG blockIndex = BLOCK_UNUSED;
2927 ULONG extBlockIndex = This->extBigBlockDepotStart;
2929 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2931 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2932 return BLOCK_UNUSED;
2934 while (extBlockCount > 0)
2936 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2937 extBlockCount--;
2940 if (extBlockIndex != BLOCK_UNUSED)
2941 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2942 extBlockOffset * sizeof(ULONG), &blockIndex);
2944 return blockIndex;
2947 /******************************************************************************
2948 * Storage32Impl_SetExtDepotBlock
2950 * Associates the specified block index to the specified depot index.
2951 * This method is only for depot indexes equal or greater than
2952 * COUNT_BBDEPOTINHEADER.
2954 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2956 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2957 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2958 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2959 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2960 ULONG extBlockIndex = This->extBigBlockDepotStart;
2962 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2964 while (extBlockCount > 0)
2966 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2967 extBlockCount--;
2970 if (extBlockIndex != BLOCK_UNUSED)
2972 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2973 extBlockOffset * sizeof(ULONG),
2974 blockIndex);
2978 /******************************************************************************
2979 * Storage32Impl_AddExtBlockDepot
2981 * Creates an extended depot block.
2983 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2985 ULONG numExtBlocks = This->extBigBlockDepotCount;
2986 ULONG nextExtBlock = This->extBigBlockDepotStart;
2987 BYTE depotBuffer[BIG_BLOCK_SIZE];
2988 ULONG index = BLOCK_UNUSED;
2989 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2990 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2991 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2993 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2994 blocksPerDepotBlock;
2996 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2999 * The first extended block.
3001 This->extBigBlockDepotStart = index;
3003 else
3005 unsigned int i;
3007 * Follow the chain to the last one.
3009 for (i = 0; i < (numExtBlocks - 1); i++)
3011 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3015 * Add the new extended block to the chain.
3017 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3018 index);
3022 * Initialize this block.
3024 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3025 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3027 return index;
3030 /******************************************************************************
3031 * Storage32Impl_FreeBigBlock
3033 * This method will flag the specified block as free in the big block depot.
3035 static void StorageImpl_FreeBigBlock(
3036 StorageImpl* This,
3037 ULONG blockIndex)
3039 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3041 if (blockIndex < This->prevFreeBlock)
3042 This->prevFreeBlock = blockIndex;
3045 /************************************************************************
3046 * Storage32Impl_GetNextBlockInChain
3048 * This method will retrieve the block index of the next big block in
3049 * in the chain.
3051 * Params: This - Pointer to the Storage object.
3052 * blockIndex - Index of the block to retrieve the chain
3053 * for.
3054 * nextBlockIndex - receives the return value.
3056 * Returns: This method returns the index of the next block in the chain.
3057 * It will return the constants:
3058 * BLOCK_SPECIAL - If the block given was not part of a
3059 * chain.
3060 * BLOCK_END_OF_CHAIN - If the block given was the last in
3061 * a chain.
3062 * BLOCK_UNUSED - If the block given was not past of a chain
3063 * and is available.
3064 * BLOCK_EXTBBDEPOT - This block is part of the extended
3065 * big block depot.
3067 * See Windows documentation for more details on IStorage methods.
3069 static HRESULT StorageImpl_GetNextBlockInChain(
3070 StorageImpl* This,
3071 ULONG blockIndex,
3072 ULONG* nextBlockIndex)
3074 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3075 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3076 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3077 BYTE depotBuffer[BIG_BLOCK_SIZE];
3078 BOOL success;
3079 ULONG depotBlockIndexPos;
3080 int index;
3082 *nextBlockIndex = BLOCK_SPECIAL;
3084 if(depotBlockCount >= This->bigBlockDepotCount)
3086 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3087 This->bigBlockDepotCount);
3088 return STG_E_READFAULT;
3092 * Cache the currently accessed depot block.
3094 if (depotBlockCount != This->indexBlockDepotCached)
3096 This->indexBlockDepotCached = depotBlockCount;
3098 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3100 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3102 else
3105 * We have to look in the extended depot.
3107 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3110 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3112 if (!success)
3113 return STG_E_READFAULT;
3115 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
3117 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3118 This->blockDepotCached[index] = *nextBlockIndex;
3122 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3124 return S_OK;
3127 /******************************************************************************
3128 * Storage32Impl_GetNextExtendedBlock
3130 * Given an extended block this method will return the next extended block.
3132 * NOTES:
3133 * The last ULONG of an extended block is the block index of the next
3134 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3135 * depot.
3137 * Return values:
3138 * - The index of the next extended block
3139 * - BLOCK_UNUSED: there is no next extended block.
3140 * - Any other return values denotes failure.
3142 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3144 ULONG nextBlockIndex = BLOCK_SPECIAL;
3145 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3147 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3148 &nextBlockIndex);
3150 return nextBlockIndex;
3153 /******************************************************************************
3154 * Storage32Impl_SetNextBlockInChain
3156 * This method will write the index of the specified block's next block
3157 * in the big block depot.
3159 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3160 * do the following
3162 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3163 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3164 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3167 static void StorageImpl_SetNextBlockInChain(
3168 StorageImpl* This,
3169 ULONG blockIndex,
3170 ULONG nextBlock)
3172 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3173 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3174 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3175 ULONG depotBlockIndexPos;
3177 assert(depotBlockCount < This->bigBlockDepotCount);
3178 assert(blockIndex != nextBlock);
3180 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3182 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3184 else
3187 * We have to look in the extended depot.
3189 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3192 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3193 nextBlock);
3195 * Update the cached block depot, if necessary.
3197 if (depotBlockCount == This->indexBlockDepotCached)
3199 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3203 /******************************************************************************
3204 * Storage32Impl_LoadFileHeader
3206 * This method will read in the file header, i.e. big block index -1.
3208 static HRESULT StorageImpl_LoadFileHeader(
3209 StorageImpl* This)
3211 HRESULT hr = STG_E_FILENOTFOUND;
3212 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3213 BOOL success;
3214 int index;
3216 TRACE("\n");
3218 * Get a pointer to the big block of data containing the header.
3220 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3223 * Extract the information from the header.
3225 if (success)
3228 * Check for the "magic number" signature and return an error if it is not
3229 * found.
3231 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3233 return STG_E_OLDFORMAT;
3236 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3238 return STG_E_INVALIDHEADER;
3241 StorageUtl_ReadWord(
3242 headerBigBlock,
3243 OFFSET_BIGBLOCKSIZEBITS,
3244 &This->bigBlockSizeBits);
3246 StorageUtl_ReadWord(
3247 headerBigBlock,
3248 OFFSET_SMALLBLOCKSIZEBITS,
3249 &This->smallBlockSizeBits);
3251 StorageUtl_ReadDWord(
3252 headerBigBlock,
3253 OFFSET_BBDEPOTCOUNT,
3254 &This->bigBlockDepotCount);
3256 StorageUtl_ReadDWord(
3257 headerBigBlock,
3258 OFFSET_ROOTSTARTBLOCK,
3259 &This->rootStartBlock);
3261 StorageUtl_ReadDWord(
3262 headerBigBlock,
3263 OFFSET_SBDEPOTSTART,
3264 &This->smallBlockDepotStart);
3266 StorageUtl_ReadDWord(
3267 headerBigBlock,
3268 OFFSET_EXTBBDEPOTSTART,
3269 &This->extBigBlockDepotStart);
3271 StorageUtl_ReadDWord(
3272 headerBigBlock,
3273 OFFSET_EXTBBDEPOTCOUNT,
3274 &This->extBigBlockDepotCount);
3276 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3278 StorageUtl_ReadDWord(
3279 headerBigBlock,
3280 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3281 &(This->bigBlockDepotStart[index]));
3285 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3287 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3288 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3291 * Right now, the code is making some assumptions about the size of the
3292 * blocks, just make sure they are what we're expecting.
3294 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3295 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3297 WARN("Broken OLE storage file\n");
3298 hr = STG_E_INVALIDHEADER;
3300 else
3301 hr = S_OK;
3304 return hr;
3307 /******************************************************************************
3308 * Storage32Impl_SaveFileHeader
3310 * This method will save to the file the header, i.e. big block -1.
3312 static void StorageImpl_SaveFileHeader(
3313 StorageImpl* This)
3315 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3316 int index;
3317 BOOL success;
3320 * Get a pointer to the big block of data containing the header.
3322 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3325 * If the block read failed, the file is probably new.
3327 if (!success)
3330 * Initialize for all unknown fields.
3332 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3335 * Initialize the magic number.
3337 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3340 * And a bunch of things we don't know what they mean
3342 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3343 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3344 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3345 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3349 * Write the information to the header.
3351 StorageUtl_WriteWord(
3352 headerBigBlock,
3353 OFFSET_BIGBLOCKSIZEBITS,
3354 This->bigBlockSizeBits);
3356 StorageUtl_WriteWord(
3357 headerBigBlock,
3358 OFFSET_SMALLBLOCKSIZEBITS,
3359 This->smallBlockSizeBits);
3361 StorageUtl_WriteDWord(
3362 headerBigBlock,
3363 OFFSET_BBDEPOTCOUNT,
3364 This->bigBlockDepotCount);
3366 StorageUtl_WriteDWord(
3367 headerBigBlock,
3368 OFFSET_ROOTSTARTBLOCK,
3369 This->rootStartBlock);
3371 StorageUtl_WriteDWord(
3372 headerBigBlock,
3373 OFFSET_SBDEPOTSTART,
3374 This->smallBlockDepotStart);
3376 StorageUtl_WriteDWord(
3377 headerBigBlock,
3378 OFFSET_SBDEPOTCOUNT,
3379 This->smallBlockDepotChain ?
3380 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3382 StorageUtl_WriteDWord(
3383 headerBigBlock,
3384 OFFSET_EXTBBDEPOTSTART,
3385 This->extBigBlockDepotStart);
3387 StorageUtl_WriteDWord(
3388 headerBigBlock,
3389 OFFSET_EXTBBDEPOTCOUNT,
3390 This->extBigBlockDepotCount);
3392 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3394 StorageUtl_WriteDWord(
3395 headerBigBlock,
3396 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3397 (This->bigBlockDepotStart[index]));
3401 * Write the big block back to the file.
3403 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3406 /******************************************************************************
3407 * StorageImpl_ReadRawDirEntry
3409 * This method will read the raw data from a directory entry in the file.
3411 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3413 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3415 ULARGE_INTEGER offset;
3416 HRESULT hr;
3417 ULONG bytesRead;
3419 offset.u.HighPart = 0;
3420 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3422 hr = BlockChainStream_ReadAt(
3423 This->rootBlockChain,
3424 offset,
3425 RAW_DIRENTRY_SIZE,
3426 buffer,
3427 &bytesRead);
3429 return hr;
3432 /******************************************************************************
3433 * StorageImpl_WriteRawDirEntry
3435 * This method will write the raw data from a directory entry in the file.
3437 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3439 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3441 ULARGE_INTEGER offset;
3442 HRESULT hr;
3443 ULONG bytesRead;
3445 offset.u.HighPart = 0;
3446 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3448 hr = BlockChainStream_WriteAt(
3449 This->rootBlockChain,
3450 offset,
3451 RAW_DIRENTRY_SIZE,
3452 buffer,
3453 &bytesRead);
3455 return hr;
3458 /******************************************************************************
3459 * UpdateRawDirEntry
3461 * Update raw directory entry data from the fields in newData.
3463 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3465 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3467 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3469 memcpy(
3470 buffer + OFFSET_PS_NAME,
3471 newData->name,
3472 DIRENTRY_NAME_BUFFER_LEN );
3474 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3476 StorageUtl_WriteWord(
3477 buffer,
3478 OFFSET_PS_NAMELENGTH,
3479 newData->sizeOfNameString);
3481 StorageUtl_WriteDWord(
3482 buffer,
3483 OFFSET_PS_LEFTCHILD,
3484 newData->leftChild);
3486 StorageUtl_WriteDWord(
3487 buffer,
3488 OFFSET_PS_RIGHTCHILD,
3489 newData->rightChild);
3491 StorageUtl_WriteDWord(
3492 buffer,
3493 OFFSET_PS_DIRROOT,
3494 newData->dirRootEntry);
3496 StorageUtl_WriteGUID(
3497 buffer,
3498 OFFSET_PS_GUID,
3499 &newData->clsid);
3501 StorageUtl_WriteDWord(
3502 buffer,
3503 OFFSET_PS_CTIMELOW,
3504 newData->ctime.dwLowDateTime);
3506 StorageUtl_WriteDWord(
3507 buffer,
3508 OFFSET_PS_CTIMEHIGH,
3509 newData->ctime.dwHighDateTime);
3511 StorageUtl_WriteDWord(
3512 buffer,
3513 OFFSET_PS_MTIMELOW,
3514 newData->mtime.dwLowDateTime);
3516 StorageUtl_WriteDWord(
3517 buffer,
3518 OFFSET_PS_MTIMEHIGH,
3519 newData->ctime.dwHighDateTime);
3521 StorageUtl_WriteDWord(
3522 buffer,
3523 OFFSET_PS_STARTBLOCK,
3524 newData->startingBlock);
3526 StorageUtl_WriteDWord(
3527 buffer,
3528 OFFSET_PS_SIZE,
3529 newData->size.u.LowPart);
3532 /******************************************************************************
3533 * Storage32Impl_ReadDirEntry
3535 * This method will read the specified directory entry.
3537 HRESULT StorageImpl_ReadDirEntry(
3538 StorageImpl* This,
3539 DirRef index,
3540 DirEntry* buffer)
3542 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3543 HRESULT readRes;
3545 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3547 if (SUCCEEDED(readRes))
3549 memset(buffer->name, 0, sizeof(buffer->name));
3550 memcpy(
3551 buffer->name,
3552 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3553 DIRENTRY_NAME_BUFFER_LEN );
3554 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3556 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3558 StorageUtl_ReadWord(
3559 currentEntry,
3560 OFFSET_PS_NAMELENGTH,
3561 &buffer->sizeOfNameString);
3563 StorageUtl_ReadDWord(
3564 currentEntry,
3565 OFFSET_PS_LEFTCHILD,
3566 &buffer->leftChild);
3568 StorageUtl_ReadDWord(
3569 currentEntry,
3570 OFFSET_PS_RIGHTCHILD,
3571 &buffer->rightChild);
3573 StorageUtl_ReadDWord(
3574 currentEntry,
3575 OFFSET_PS_DIRROOT,
3576 &buffer->dirRootEntry);
3578 StorageUtl_ReadGUID(
3579 currentEntry,
3580 OFFSET_PS_GUID,
3581 &buffer->clsid);
3583 StorageUtl_ReadDWord(
3584 currentEntry,
3585 OFFSET_PS_CTIMELOW,
3586 &buffer->ctime.dwLowDateTime);
3588 StorageUtl_ReadDWord(
3589 currentEntry,
3590 OFFSET_PS_CTIMEHIGH,
3591 &buffer->ctime.dwHighDateTime);
3593 StorageUtl_ReadDWord(
3594 currentEntry,
3595 OFFSET_PS_MTIMELOW,
3596 &buffer->mtime.dwLowDateTime);
3598 StorageUtl_ReadDWord(
3599 currentEntry,
3600 OFFSET_PS_MTIMEHIGH,
3601 &buffer->mtime.dwHighDateTime);
3603 StorageUtl_ReadDWord(
3604 currentEntry,
3605 OFFSET_PS_STARTBLOCK,
3606 &buffer->startingBlock);
3608 StorageUtl_ReadDWord(
3609 currentEntry,
3610 OFFSET_PS_SIZE,
3611 &buffer->size.u.LowPart);
3613 buffer->size.u.HighPart = 0;
3616 return readRes;
3619 /*********************************************************************
3620 * Write the specified directory entry to the file
3622 HRESULT StorageImpl_WriteDirEntry(
3623 StorageImpl* This,
3624 DirRef index,
3625 const DirEntry* buffer)
3627 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3628 HRESULT writeRes;
3630 UpdateRawDirEntry(currentEntry, buffer);
3632 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3633 return writeRes;
3636 static BOOL StorageImpl_ReadBigBlock(
3637 StorageImpl* This,
3638 ULONG blockIndex,
3639 void* buffer)
3641 ULARGE_INTEGER ulOffset;
3642 DWORD read;
3644 ulOffset.u.HighPart = 0;
3645 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3647 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3648 return (read == This->bigBlockSize);
3651 static BOOL StorageImpl_ReadDWordFromBigBlock(
3652 StorageImpl* This,
3653 ULONG blockIndex,
3654 ULONG offset,
3655 DWORD* value)
3657 ULARGE_INTEGER ulOffset;
3658 DWORD read;
3659 DWORD tmp;
3661 ulOffset.u.HighPart = 0;
3662 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3663 ulOffset.u.LowPart += offset;
3665 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3666 *value = lendian32toh(tmp);
3667 return (read == sizeof(DWORD));
3670 static BOOL StorageImpl_WriteBigBlock(
3671 StorageImpl* This,
3672 ULONG blockIndex,
3673 const void* buffer)
3675 ULARGE_INTEGER ulOffset;
3676 DWORD wrote;
3678 ulOffset.u.HighPart = 0;
3679 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3681 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3682 return (wrote == This->bigBlockSize);
3685 static BOOL StorageImpl_WriteDWordToBigBlock(
3686 StorageImpl* This,
3687 ULONG blockIndex,
3688 ULONG offset,
3689 DWORD value)
3691 ULARGE_INTEGER ulOffset;
3692 DWORD wrote;
3694 ulOffset.u.HighPart = 0;
3695 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3696 ulOffset.u.LowPart += offset;
3698 value = htole32(value);
3699 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3700 return (wrote == sizeof(DWORD));
3703 /******************************************************************************
3704 * Storage32Impl_SmallBlocksToBigBlocks
3706 * This method will convert a small block chain to a big block chain.
3707 * The small block chain will be destroyed.
3709 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3710 StorageImpl* This,
3711 SmallBlockChainStream** ppsbChain)
3713 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3714 ULARGE_INTEGER size, offset;
3715 ULONG cbRead, cbWritten;
3716 ULARGE_INTEGER cbTotalRead;
3717 DirRef streamEntryRef;
3718 HRESULT resWrite = S_OK;
3719 HRESULT resRead;
3720 DirEntry streamEntry;
3721 BYTE *buffer;
3722 BlockChainStream *bbTempChain = NULL;
3723 BlockChainStream *bigBlockChain = NULL;
3726 * Create a temporary big block chain that doesn't have
3727 * an associated directory entry. This temporary chain will be
3728 * used to copy data from small blocks to big blocks.
3730 bbTempChain = BlockChainStream_Construct(This,
3731 &bbHeadOfChain,
3732 DIRENTRY_NULL);
3733 if(!bbTempChain) return NULL;
3735 * Grow the big block chain.
3737 size = SmallBlockChainStream_GetSize(*ppsbChain);
3738 BlockChainStream_SetSize(bbTempChain, size);
3741 * Copy the contents of the small block chain to the big block chain
3742 * by small block size increments.
3744 offset.u.LowPart = 0;
3745 offset.u.HighPart = 0;
3746 cbTotalRead.QuadPart = 0;
3748 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3751 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3752 offset,
3753 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3754 buffer,
3755 &cbRead);
3756 if (FAILED(resRead))
3757 break;
3759 if (cbRead > 0)
3761 cbTotalRead.QuadPart += cbRead;
3763 resWrite = BlockChainStream_WriteAt(bbTempChain,
3764 offset,
3765 cbRead,
3766 buffer,
3767 &cbWritten);
3769 if (FAILED(resWrite))
3770 break;
3772 offset.u.LowPart += cbRead;
3774 } while (cbTotalRead.QuadPart < size.QuadPart);
3775 HeapFree(GetProcessHeap(),0,buffer);
3777 size.u.HighPart = 0;
3778 size.u.LowPart = 0;
3780 if (FAILED(resRead) || FAILED(resWrite))
3782 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3783 BlockChainStream_SetSize(bbTempChain, size);
3784 BlockChainStream_Destroy(bbTempChain);
3785 return NULL;
3789 * Destroy the small block chain.
3791 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3792 SmallBlockChainStream_SetSize(*ppsbChain, size);
3793 SmallBlockChainStream_Destroy(*ppsbChain);
3794 *ppsbChain = 0;
3797 * Change the directory entry. This chain is now a big block chain
3798 * and it doesn't reside in the small blocks chain anymore.
3800 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3802 streamEntry.startingBlock = bbHeadOfChain;
3804 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3807 * Destroy the temporary entryless big block chain.
3808 * Create a new big block chain associated with this entry.
3810 BlockChainStream_Destroy(bbTempChain);
3811 bigBlockChain = BlockChainStream_Construct(This,
3812 NULL,
3813 streamEntryRef);
3815 return bigBlockChain;
3818 /******************************************************************************
3819 * Storage32Impl_BigBlocksToSmallBlocks
3821 * This method will convert a big block chain to a small block chain.
3822 * The big block chain will be destroyed on success.
3824 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3825 StorageImpl* This,
3826 BlockChainStream** ppbbChain)
3828 ULARGE_INTEGER size, offset, cbTotalRead;
3829 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3830 DirRef streamEntryRef;
3831 HRESULT resWrite = S_OK, resRead;
3832 DirEntry streamEntry;
3833 BYTE* buffer;
3834 SmallBlockChainStream* sbTempChain;
3836 TRACE("%p %p\n", This, ppbbChain);
3838 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3839 DIRENTRY_NULL);
3841 if(!sbTempChain)
3842 return NULL;
3844 size = BlockChainStream_GetSize(*ppbbChain);
3845 SmallBlockChainStream_SetSize(sbTempChain, size);
3847 offset.u.HighPart = 0;
3848 offset.u.LowPart = 0;
3849 cbTotalRead.QuadPart = 0;
3850 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3853 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3854 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3855 buffer, &cbRead);
3857 if(FAILED(resRead))
3858 break;
3860 if(cbRead > 0)
3862 cbTotalRead.QuadPart += cbRead;
3864 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3865 cbRead, buffer, &cbWritten);
3867 if(FAILED(resWrite))
3868 break;
3870 offset.u.LowPart += cbRead;
3872 }while(cbTotalRead.QuadPart < size.QuadPart);
3873 HeapFree(GetProcessHeap(), 0, buffer);
3875 size.u.HighPart = 0;
3876 size.u.LowPart = 0;
3878 if(FAILED(resRead) || FAILED(resWrite))
3880 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3881 SmallBlockChainStream_SetSize(sbTempChain, size);
3882 SmallBlockChainStream_Destroy(sbTempChain);
3883 return NULL;
3886 /* destroy the original big block chain */
3887 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3888 BlockChainStream_SetSize(*ppbbChain, size);
3889 BlockChainStream_Destroy(*ppbbChain);
3890 *ppbbChain = NULL;
3892 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3893 streamEntry.startingBlock = sbHeadOfChain;
3894 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3896 SmallBlockChainStream_Destroy(sbTempChain);
3897 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3900 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
3902 HRESULT hr;
3903 DirEntry parentData, snapshotData;
3905 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
3906 0, (IStorage**)snapshot);
3908 if (SUCCEEDED(hr))
3910 hr = StorageBaseImpl_ReadDirEntry(original,
3911 original->storageDirEntry, &parentData);
3913 if (SUCCEEDED(hr))
3914 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
3915 (*snapshot)->storageDirEntry, &snapshotData);
3917 if (SUCCEEDED(hr))
3919 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
3920 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
3921 snapshotData.stgType = parentData.stgType;
3922 snapshotData.clsid = parentData.clsid;
3923 snapshotData.ctime = parentData.ctime;
3924 snapshotData.mtime = parentData.mtime;
3925 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
3926 (*snapshot)->storageDirEntry, &snapshotData);
3929 if (SUCCEEDED(hr))
3930 hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
3931 (IStorage*)(*snapshot));
3933 if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
3936 return hr;
3939 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
3940 IStorage* iface,
3941 DWORD grfCommitFlags) /* [in] */
3943 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
3944 HRESULT hr;
3945 DirEntry data, tempStorageData, snapshotRootData;
3946 DirRef tempStorageEntry, oldDirRoot;
3947 StorageInternalImpl *tempStorage;
3949 TRACE("(%p,%x)\n", iface, grfCommitFlags);
3951 /* Cannot commit a read-only transacted storage */
3952 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
3953 return STG_E_ACCESSDENIED;
3955 /* To prevent data loss, we create the new structure in the file before we
3956 * delete the old one, so that in case of errors the old data is intact. We
3957 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
3958 * needed in the rare situation where we have just enough free disk space to
3959 * overwrite the existing data. */
3961 /* Create an orphaned storage in the parent for the new directory structure. */
3962 memset(&data, 0, sizeof(data));
3963 data.name[0] = 'D';
3964 data.sizeOfNameString = 1;
3965 data.stgType = STGTY_STORAGE;
3966 data.leftChild = DIRENTRY_NULL;
3967 data.rightChild = DIRENTRY_NULL;
3968 data.dirRootEntry = DIRENTRY_NULL;
3969 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
3971 if (FAILED(hr)) return hr;
3973 tempStorage = StorageInternalImpl_Construct(This->transactedParent,
3974 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
3975 if (tempStorage)
3977 hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
3978 (IStorage*)tempStorage);
3980 list_init(&tempStorage->ParentListEntry);
3982 IStorage_Release((IStorage*) tempStorage);
3984 else
3985 hr = E_OUTOFMEMORY;
3987 if (FAILED(hr))
3989 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
3990 return hr;
3993 /* Update the storage to use the new data in one step. */
3994 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
3995 This->transactedParent->storageDirEntry, &data);
3997 if (SUCCEEDED(hr))
3999 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4000 tempStorageEntry, &tempStorageData);
4003 if (SUCCEEDED(hr))
4005 hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4006 This->snapshot->storageDirEntry, &snapshotRootData);
4009 if (SUCCEEDED(hr))
4011 oldDirRoot = data.dirRootEntry;
4012 data.dirRootEntry = tempStorageData.dirRootEntry;
4013 data.clsid = snapshotRootData.clsid;
4014 data.ctime = snapshotRootData.ctime;
4015 data.mtime = snapshotRootData.mtime;
4017 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4018 This->transactedParent->storageDirEntry, &data);
4021 if (SUCCEEDED(hr))
4023 /* Destroy the old now-orphaned data. */
4024 DestroyReachableEntries(This->transactedParent, oldDirRoot);
4025 StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4027 else
4029 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4032 return hr;
4035 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4036 IStorage* iface)
4038 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4039 StorageBaseImpl *newSnapshot;
4040 HRESULT hr;
4042 TRACE("(%p)\n", iface);
4044 /* Create a new copy of the parent data. */
4045 hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4046 if (FAILED(hr)) return hr;
4048 /* Destroy the open objects. */
4049 StorageBaseImpl_DeleteAll(&This->base);
4051 /* Replace our current snapshot. */
4052 IStorage_Release((IStorage*)This->snapshot);
4053 This->snapshot = newSnapshot;
4055 return S_OK;
4058 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4060 if (!This->reverted)
4062 TRACE("Storage invalidated (stg=%p)\n", This);
4064 This->reverted = 1;
4066 StorageBaseImpl_DeleteAll(This);
4070 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4072 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4074 TransactedSnapshotImpl_Invalidate(iface);
4076 IStorage_Release((IStorage*)This->transactedParent);
4078 IStorage_Release((IStorage*)This->snapshot);
4080 HeapFree(GetProcessHeap(), 0, This);
4083 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4084 const DirEntry *newData, DirRef *index)
4086 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4088 return StorageBaseImpl_CreateDirEntry(This->snapshot,
4089 newData, index);
4092 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4093 DirRef index, const DirEntry *data)
4095 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4097 return StorageBaseImpl_WriteDirEntry(This->snapshot,
4098 index, data);
4101 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4102 DirRef index, DirEntry *data)
4104 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4106 return StorageBaseImpl_ReadDirEntry(This->snapshot,
4107 index, data);
4110 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4111 DirRef index)
4113 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4115 return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4116 index);
4119 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4120 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4122 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4124 return StorageBaseImpl_StreamReadAt(This->snapshot,
4125 index, offset, size, buffer, bytesRead);
4128 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4129 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4131 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4133 return StorageBaseImpl_StreamWriteAt(This->snapshot,
4134 index, offset, size, buffer, bytesWritten);
4137 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4138 DirRef index, ULARGE_INTEGER newsize)
4140 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4142 return StorageBaseImpl_StreamSetSize(This->snapshot,
4143 index, newsize);
4146 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4148 StorageBaseImpl_QueryInterface,
4149 StorageBaseImpl_AddRef,
4150 StorageBaseImpl_Release,
4151 StorageBaseImpl_CreateStream,
4152 StorageBaseImpl_OpenStream,
4153 StorageBaseImpl_CreateStorage,
4154 StorageBaseImpl_OpenStorage,
4155 StorageBaseImpl_CopyTo,
4156 StorageBaseImpl_MoveElementTo,
4157 TransactedSnapshotImpl_Commit,
4158 TransactedSnapshotImpl_Revert,
4159 StorageBaseImpl_EnumElements,
4160 StorageBaseImpl_DestroyElement,
4161 StorageBaseImpl_RenameElement,
4162 StorageBaseImpl_SetElementTimes,
4163 StorageBaseImpl_SetClass,
4164 StorageBaseImpl_SetStateBits,
4165 StorageBaseImpl_Stat
4168 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4170 TransactedSnapshotImpl_Destroy,
4171 TransactedSnapshotImpl_Invalidate,
4172 TransactedSnapshotImpl_CreateDirEntry,
4173 TransactedSnapshotImpl_WriteDirEntry,
4174 TransactedSnapshotImpl_ReadDirEntry,
4175 TransactedSnapshotImpl_DestroyDirEntry,
4176 TransactedSnapshotImpl_StreamReadAt,
4177 TransactedSnapshotImpl_StreamWriteAt,
4178 TransactedSnapshotImpl_StreamSetSize
4181 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4182 TransactedSnapshotImpl** result)
4184 HRESULT hr;
4186 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4187 if (*result)
4189 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4191 /* This is OK because the property set storage functions use the IStorage functions. */
4192 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4194 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4196 list_init(&(*result)->base.strmHead);
4198 list_init(&(*result)->base.storageHead);
4200 (*result)->base.ref = 1;
4202 (*result)->base.openFlags = parentStorage->openFlags;
4204 (*result)->base.filename = parentStorage->filename;
4206 /* Create a new temporary storage to act as the snapshot */
4207 hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4209 if (SUCCEEDED(hr))
4211 (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4213 /* parentStorage already has 1 reference, which we take over here. */
4214 (*result)->transactedParent = parentStorage;
4216 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4219 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4221 return hr;
4223 else
4224 return E_OUTOFMEMORY;
4227 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4228 StorageBaseImpl** result)
4230 static int fixme=0;
4232 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4234 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4237 return TransactedSnapshotImpl_Construct(parentStorage,
4238 (TransactedSnapshotImpl**)result);
4241 static HRESULT Storage_Construct(
4242 HANDLE hFile,
4243 LPCOLESTR pwcsName,
4244 ILockBytes* pLkbyt,
4245 DWORD openFlags,
4246 BOOL fileBased,
4247 BOOL create,
4248 StorageBaseImpl** result)
4250 StorageImpl *newStorage;
4251 StorageBaseImpl *newTransactedStorage;
4252 HRESULT hr;
4254 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, &newStorage);
4255 if (FAILED(hr)) goto end;
4257 if (openFlags & STGM_TRANSACTED)
4259 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4260 if (FAILED(hr))
4261 IStorage_Release((IStorage*)newStorage);
4262 else
4263 *result = newTransactedStorage;
4265 else
4266 *result = &newStorage->base;
4268 end:
4269 return hr;
4272 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4274 StorageInternalImpl* This = (StorageInternalImpl*) base;
4276 if (!This->base.reverted)
4278 TRACE("Storage invalidated (stg=%p)\n", This);
4280 This->base.reverted = 1;
4282 This->parentStorage = NULL;
4284 StorageBaseImpl_DeleteAll(&This->base);
4286 list_remove(&This->ParentListEntry);
4290 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4292 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4294 StorageInternalImpl_Invalidate(&This->base);
4296 HeapFree(GetProcessHeap(), 0, This);
4299 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4300 const DirEntry *newData, DirRef *index)
4302 StorageInternalImpl* This = (StorageInternalImpl*) base;
4304 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4305 newData, index);
4308 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4309 DirRef index, const DirEntry *data)
4311 StorageInternalImpl* This = (StorageInternalImpl*) base;
4313 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4314 index, data);
4317 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4318 DirRef index, DirEntry *data)
4320 StorageInternalImpl* This = (StorageInternalImpl*) base;
4322 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4323 index, data);
4326 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4327 DirRef index)
4329 StorageInternalImpl* This = (StorageInternalImpl*) base;
4331 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4332 index);
4335 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4336 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4338 StorageInternalImpl* This = (StorageInternalImpl*) base;
4340 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4341 index, offset, size, buffer, bytesRead);
4344 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4345 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4347 StorageInternalImpl* This = (StorageInternalImpl*) base;
4349 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4350 index, offset, size, buffer, bytesWritten);
4353 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4354 DirRef index, ULARGE_INTEGER newsize)
4356 StorageInternalImpl* This = (StorageInternalImpl*) base;
4358 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4359 index, newsize);
4362 /******************************************************************************
4364 ** Storage32InternalImpl_Commit
4367 static HRESULT WINAPI StorageInternalImpl_Commit(
4368 IStorage* iface,
4369 DWORD grfCommitFlags) /* [in] */
4371 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4372 return S_OK;
4375 /******************************************************************************
4377 ** Storage32InternalImpl_Revert
4380 static HRESULT WINAPI StorageInternalImpl_Revert(
4381 IStorage* iface)
4383 FIXME("(%p): stub\n", iface);
4384 return S_OK;
4387 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4389 IStorage_Release((IStorage*)This->parentStorage);
4390 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
4391 HeapFree(GetProcessHeap(), 0, This);
4394 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4395 IEnumSTATSTG* iface,
4396 REFIID riid,
4397 void** ppvObject)
4399 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4401 if (ppvObject==0)
4402 return E_INVALIDARG;
4404 *ppvObject = 0;
4406 if (IsEqualGUID(&IID_IUnknown, riid) ||
4407 IsEqualGUID(&IID_IEnumSTATSTG, riid))
4409 *ppvObject = This;
4410 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4411 return S_OK;
4414 return E_NOINTERFACE;
4417 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
4418 IEnumSTATSTG* iface)
4420 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4421 return InterlockedIncrement(&This->ref);
4424 static ULONG WINAPI IEnumSTATSTGImpl_Release(
4425 IEnumSTATSTG* iface)
4427 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4429 ULONG newRef;
4431 newRef = InterlockedDecrement(&This->ref);
4433 if (newRef==0)
4435 IEnumSTATSTGImpl_Destroy(This);
4438 return newRef;
4441 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4442 IEnumSTATSTG* iface,
4443 ULONG celt,
4444 STATSTG* rgelt,
4445 ULONG* pceltFetched)
4447 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4449 DirEntry currentEntry;
4450 STATSTG* currentReturnStruct = rgelt;
4451 ULONG objectFetched = 0;
4452 DirRef currentSearchNode;
4454 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4455 return E_INVALIDARG;
4458 * To avoid the special case, get another pointer to a ULONG value if
4459 * the caller didn't supply one.
4461 if (pceltFetched==0)
4462 pceltFetched = &objectFetched;
4465 * Start the iteration, we will iterate until we hit the end of the
4466 * linked list or until we hit the number of items to iterate through
4468 *pceltFetched = 0;
4471 * Start with the node at the top of the stack.
4473 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4475 while ( ( *pceltFetched < celt) &&
4476 ( currentSearchNode!=DIRENTRY_NULL) )
4479 * Remove the top node from the stack
4481 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4484 * Read the entry from the storage.
4486 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4487 currentSearchNode,
4488 &currentEntry);
4491 * Copy the information to the return buffer.
4493 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4494 currentReturnStruct,
4495 &currentEntry,
4496 STATFLAG_DEFAULT);
4499 * Step to the next item in the iteration
4501 (*pceltFetched)++;
4502 currentReturnStruct++;
4505 * Push the next search node in the search stack.
4507 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
4510 * continue the iteration.
4512 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4515 if (*pceltFetched == celt)
4516 return S_OK;
4518 return S_FALSE;
4522 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4523 IEnumSTATSTG* iface,
4524 ULONG celt)
4526 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4528 DirEntry currentEntry;
4529 ULONG objectFetched = 0;
4530 DirRef currentSearchNode;
4533 * Start with the node at the top of the stack.
4535 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4537 while ( (objectFetched < celt) &&
4538 (currentSearchNode!=DIRENTRY_NULL) )
4541 * Remove the top node from the stack
4543 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4546 * Read the entry from the storage.
4548 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4549 currentSearchNode,
4550 &currentEntry);
4553 * Step to the next item in the iteration
4555 objectFetched++;
4558 * Push the next search node in the search stack.
4560 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
4563 * continue the iteration.
4565 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4568 if (objectFetched == celt)
4569 return S_OK;
4571 return S_FALSE;
4574 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4575 IEnumSTATSTG* iface)
4577 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4579 DirEntry storageEntry;
4580 HRESULT hr;
4583 * Re-initialize the search stack to an empty stack
4585 This->stackSize = 0;
4588 * Read the storage entry from the top-level storage.
4590 hr = StorageBaseImpl_ReadDirEntry(
4591 This->parentStorage,
4592 This->storageDirEntry,
4593 &storageEntry);
4595 if (SUCCEEDED(hr))
4597 assert(storageEntry.sizeOfNameString!=0);
4600 * Push the search node in the search stack.
4602 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.dirRootEntry);
4605 return hr;
4608 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4609 IEnumSTATSTG* iface,
4610 IEnumSTATSTG** ppenum)
4612 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4614 IEnumSTATSTGImpl* newClone;
4617 * Perform a sanity check on the parameters.
4619 if (ppenum==0)
4620 return E_INVALIDARG;
4622 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4623 This->storageDirEntry);
4627 * The new clone enumeration must point to the same current node as
4628 * the ole one.
4630 newClone->stackSize = This->stackSize ;
4631 newClone->stackMaxSize = This->stackMaxSize ;
4632 newClone->stackToVisit =
4633 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
4635 memcpy(
4636 newClone->stackToVisit,
4637 This->stackToVisit,
4638 sizeof(DirRef) * newClone->stackSize);
4640 *ppenum = (IEnumSTATSTG*)newClone;
4643 * Don't forget to nail down a reference to the clone before
4644 * returning it.
4646 IEnumSTATSTGImpl_AddRef(*ppenum);
4648 return S_OK;
4651 static void IEnumSTATSTGImpl_PushSearchNode(
4652 IEnumSTATSTGImpl* This,
4653 DirRef nodeToPush)
4655 DirEntry storageEntry;
4656 HRESULT hr;
4659 * First, make sure we're not trying to push an unexisting node.
4661 if (nodeToPush==DIRENTRY_NULL)
4662 return;
4665 * First push the node to the stack
4667 if (This->stackSize == This->stackMaxSize)
4669 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4671 This->stackToVisit = HeapReAlloc(
4672 GetProcessHeap(),
4674 This->stackToVisit,
4675 sizeof(DirRef) * This->stackMaxSize);
4678 This->stackToVisit[This->stackSize] = nodeToPush;
4679 This->stackSize++;
4682 * Read the storage entry from the top-level storage.
4684 hr = StorageBaseImpl_ReadDirEntry(
4685 This->parentStorage,
4686 nodeToPush,
4687 &storageEntry);
4689 if (SUCCEEDED(hr))
4691 assert(storageEntry.sizeOfNameString!=0);
4694 * Push the previous search node in the search stack.
4696 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.leftChild);
4700 static DirRef IEnumSTATSTGImpl_PopSearchNode(
4701 IEnumSTATSTGImpl* This,
4702 BOOL remove)
4704 DirRef topNode;
4706 if (This->stackSize == 0)
4707 return DIRENTRY_NULL;
4709 topNode = This->stackToVisit[This->stackSize-1];
4711 if (remove)
4712 This->stackSize--;
4714 return topNode;
4718 * Virtual function table for the IEnumSTATSTGImpl class.
4720 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4722 IEnumSTATSTGImpl_QueryInterface,
4723 IEnumSTATSTGImpl_AddRef,
4724 IEnumSTATSTGImpl_Release,
4725 IEnumSTATSTGImpl_Next,
4726 IEnumSTATSTGImpl_Skip,
4727 IEnumSTATSTGImpl_Reset,
4728 IEnumSTATSTGImpl_Clone
4731 /******************************************************************************
4732 ** IEnumSTATSTGImpl implementation
4735 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4736 StorageBaseImpl* parentStorage,
4737 DirRef storageDirEntry)
4739 IEnumSTATSTGImpl* newEnumeration;
4741 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4743 if (newEnumeration!=0)
4746 * Set-up the virtual function table and reference count.
4748 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4749 newEnumeration->ref = 0;
4752 * We want to nail-down the reference to the storage in case the
4753 * enumeration out-lives the storage in the client application.
4755 newEnumeration->parentStorage = parentStorage;
4756 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4758 newEnumeration->storageDirEntry = storageDirEntry;
4761 * Initialize the search stack
4763 newEnumeration->stackSize = 0;
4764 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4765 newEnumeration->stackToVisit =
4766 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef)*ENUMSTATSGT_SIZE_INCREMENT);
4769 * Make sure the current node of the iterator is the first one.
4771 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4774 return newEnumeration;
4778 * Virtual function table for the Storage32InternalImpl class.
4780 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4782 StorageBaseImpl_QueryInterface,
4783 StorageBaseImpl_AddRef,
4784 StorageBaseImpl_Release,
4785 StorageBaseImpl_CreateStream,
4786 StorageBaseImpl_OpenStream,
4787 StorageBaseImpl_CreateStorage,
4788 StorageBaseImpl_OpenStorage,
4789 StorageBaseImpl_CopyTo,
4790 StorageBaseImpl_MoveElementTo,
4791 StorageInternalImpl_Commit,
4792 StorageInternalImpl_Revert,
4793 StorageBaseImpl_EnumElements,
4794 StorageBaseImpl_DestroyElement,
4795 StorageBaseImpl_RenameElement,
4796 StorageBaseImpl_SetElementTimes,
4797 StorageBaseImpl_SetClass,
4798 StorageBaseImpl_SetStateBits,
4799 StorageBaseImpl_Stat
4802 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4804 StorageInternalImpl_Destroy,
4805 StorageInternalImpl_Invalidate,
4806 StorageInternalImpl_CreateDirEntry,
4807 StorageInternalImpl_WriteDirEntry,
4808 StorageInternalImpl_ReadDirEntry,
4809 StorageInternalImpl_DestroyDirEntry,
4810 StorageInternalImpl_StreamReadAt,
4811 StorageInternalImpl_StreamWriteAt,
4812 StorageInternalImpl_StreamSetSize
4815 /******************************************************************************
4816 ** Storage32InternalImpl implementation
4819 static StorageInternalImpl* StorageInternalImpl_Construct(
4820 StorageBaseImpl* parentStorage,
4821 DWORD openFlags,
4822 DirRef storageDirEntry)
4824 StorageInternalImpl* newStorage;
4826 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4828 if (newStorage!=0)
4830 list_init(&newStorage->base.strmHead);
4832 list_init(&newStorage->base.storageHead);
4835 * Initialize the virtual function table.
4837 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4838 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4839 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4841 newStorage->base.reverted = 0;
4843 newStorage->base.ref = 1;
4845 newStorage->parentStorage = parentStorage;
4848 * Keep a reference to the directory entry of this storage
4850 newStorage->base.storageDirEntry = storageDirEntry;
4852 newStorage->base.create = 0;
4854 return newStorage;
4857 return 0;
4860 /******************************************************************************
4861 ** StorageUtl implementation
4864 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4866 WORD tmp;
4868 memcpy(&tmp, buffer+offset, sizeof(WORD));
4869 *value = lendian16toh(tmp);
4872 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4874 value = htole16(value);
4875 memcpy(buffer+offset, &value, sizeof(WORD));
4878 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4880 DWORD tmp;
4882 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4883 *value = lendian32toh(tmp);
4886 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4888 value = htole32(value);
4889 memcpy(buffer+offset, &value, sizeof(DWORD));
4892 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4893 ULARGE_INTEGER* value)
4895 #ifdef WORDS_BIGENDIAN
4896 ULARGE_INTEGER tmp;
4898 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4899 value->u.LowPart = htole32(tmp.u.HighPart);
4900 value->u.HighPart = htole32(tmp.u.LowPart);
4901 #else
4902 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4903 #endif
4906 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4907 const ULARGE_INTEGER *value)
4909 #ifdef WORDS_BIGENDIAN
4910 ULARGE_INTEGER tmp;
4912 tmp.u.LowPart = htole32(value->u.HighPart);
4913 tmp.u.HighPart = htole32(value->u.LowPart);
4914 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4915 #else
4916 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4917 #endif
4920 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4922 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4923 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4924 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4926 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4929 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4931 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4932 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4933 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4935 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4938 void StorageUtl_CopyDirEntryToSTATSTG(
4939 StorageBaseImpl* storage,
4940 STATSTG* destination,
4941 const DirEntry* source,
4942 int statFlags)
4944 LPCWSTR entryName;
4946 if (source->stgType == STGTY_ROOT)
4948 /* replace the name of root entry (often "Root Entry") by the file name */
4949 entryName = storage->filename;
4951 else
4953 entryName = source->name;
4957 * The copy of the string occurs only when the flag is not set
4959 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4960 (entryName == NULL) ||
4961 (entryName[0] == 0) )
4963 destination->pwcsName = 0;
4965 else
4967 destination->pwcsName =
4968 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4970 strcpyW(destination->pwcsName, entryName);
4973 switch (source->stgType)
4975 case STGTY_STORAGE:
4976 case STGTY_ROOT:
4977 destination->type = STGTY_STORAGE;
4978 break;
4979 case STGTY_STREAM:
4980 destination->type = STGTY_STREAM;
4981 break;
4982 default:
4983 destination->type = STGTY_STREAM;
4984 break;
4987 destination->cbSize = source->size;
4989 currentReturnStruct->mtime = {0}; TODO
4990 currentReturnStruct->ctime = {0};
4991 currentReturnStruct->atime = {0};
4993 destination->grfMode = 0;
4994 destination->grfLocksSupported = 0;
4995 destination->clsid = source->clsid;
4996 destination->grfStateBits = 0;
4997 destination->reserved = 0;
5000 /******************************************************************************
5001 ** BlockChainStream implementation
5004 BlockChainStream* BlockChainStream_Construct(
5005 StorageImpl* parentStorage,
5006 ULONG* headOfStreamPlaceHolder,
5007 DirRef dirEntry)
5009 BlockChainStream* newStream;
5010 ULONG blockIndex;
5012 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5014 newStream->parentStorage = parentStorage;
5015 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5016 newStream->ownerDirEntry = dirEntry;
5017 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
5018 newStream->tailIndex = BLOCK_END_OF_CHAIN;
5019 newStream->numBlocks = 0;
5021 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
5023 while (blockIndex != BLOCK_END_OF_CHAIN)
5025 newStream->numBlocks++;
5026 newStream->tailIndex = blockIndex;
5028 if(FAILED(StorageImpl_GetNextBlockInChain(
5029 parentStorage,
5030 blockIndex,
5031 &blockIndex)))
5033 HeapFree(GetProcessHeap(), 0, newStream);
5034 return NULL;
5038 return newStream;
5041 void BlockChainStream_Destroy(BlockChainStream* This)
5043 HeapFree(GetProcessHeap(), 0, This);
5046 /******************************************************************************
5047 * BlockChainStream_GetHeadOfChain
5049 * Returns the head of this stream chain.
5050 * Some special chains don't have directory entries, their heads are kept in
5051 * This->headOfStreamPlaceHolder.
5054 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5056 DirEntry chainEntry;
5057 HRESULT hr;
5059 if (This->headOfStreamPlaceHolder != 0)
5060 return *(This->headOfStreamPlaceHolder);
5062 if (This->ownerDirEntry != DIRENTRY_NULL)
5064 hr = StorageImpl_ReadDirEntry(
5065 This->parentStorage,
5066 This->ownerDirEntry,
5067 &chainEntry);
5069 if (SUCCEEDED(hr))
5071 return chainEntry.startingBlock;
5075 return BLOCK_END_OF_CHAIN;
5078 /******************************************************************************
5079 * BlockChainStream_GetCount
5081 * Returns the number of blocks that comprises this chain.
5082 * This is not the size of the stream as the last block may not be full!
5085 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5087 ULONG blockIndex;
5088 ULONG count = 0;
5090 blockIndex = BlockChainStream_GetHeadOfChain(This);
5092 while (blockIndex != BLOCK_END_OF_CHAIN)
5094 count++;
5096 if(FAILED(StorageImpl_GetNextBlockInChain(
5097 This->parentStorage,
5098 blockIndex,
5099 &blockIndex)))
5100 return 0;
5103 return count;
5106 /******************************************************************************
5107 * BlockChainStream_ReadAt
5109 * Reads a specified number of bytes from this chain at the specified offset.
5110 * bytesRead may be NULL.
5111 * Failure will be returned if the specified number of bytes has not been read.
5113 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5114 ULARGE_INTEGER offset,
5115 ULONG size,
5116 void* buffer,
5117 ULONG* bytesRead)
5119 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5120 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5121 ULONG bytesToReadInBuffer;
5122 ULONG blockIndex;
5123 BYTE* bufferWalker;
5125 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5128 * Find the first block in the stream that contains part of the buffer.
5130 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5131 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5132 (blockNoInSequence < This->lastBlockNoInSequence) )
5134 blockIndex = BlockChainStream_GetHeadOfChain(This);
5135 This->lastBlockNoInSequence = blockNoInSequence;
5137 else
5139 ULONG temp = blockNoInSequence;
5141 blockIndex = This->lastBlockNoInSequenceIndex;
5142 blockNoInSequence -= This->lastBlockNoInSequence;
5143 This->lastBlockNoInSequence = temp;
5146 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5148 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5149 return STG_E_DOCFILECORRUPT;
5150 blockNoInSequence--;
5153 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5154 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5156 This->lastBlockNoInSequenceIndex = blockIndex;
5159 * Start reading the buffer.
5161 *bytesRead = 0;
5162 bufferWalker = buffer;
5164 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5166 ULARGE_INTEGER ulOffset;
5167 DWORD bytesReadAt;
5169 * Calculate how many bytes we can copy from this big block.
5171 bytesToReadInBuffer =
5172 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5174 TRACE("block %i\n",blockIndex);
5175 ulOffset.u.HighPart = 0;
5176 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5177 offsetInBlock;
5179 StorageImpl_ReadAt(This->parentStorage,
5180 ulOffset,
5181 bufferWalker,
5182 bytesToReadInBuffer,
5183 &bytesReadAt);
5185 * Step to the next big block.
5187 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5188 return STG_E_DOCFILECORRUPT;
5190 bufferWalker += bytesReadAt;
5191 size -= bytesReadAt;
5192 *bytesRead += bytesReadAt;
5193 offsetInBlock = 0; /* There is no offset on the next block */
5195 if (bytesToReadInBuffer != bytesReadAt)
5196 break;
5199 return (size == 0) ? S_OK : STG_E_READFAULT;
5202 /******************************************************************************
5203 * BlockChainStream_WriteAt
5205 * Writes the specified number of bytes to this chain at the specified offset.
5206 * Will fail if not all specified number of bytes have been written.
5208 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5209 ULARGE_INTEGER offset,
5210 ULONG size,
5211 const void* buffer,
5212 ULONG* bytesWritten)
5214 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5215 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5216 ULONG bytesToWrite;
5217 ULONG blockIndex;
5218 const BYTE* bufferWalker;
5221 * Find the first block in the stream that contains part of the buffer.
5223 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5224 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5225 (blockNoInSequence < This->lastBlockNoInSequence) )
5227 blockIndex = BlockChainStream_GetHeadOfChain(This);
5228 This->lastBlockNoInSequence = blockNoInSequence;
5230 else
5232 ULONG temp = blockNoInSequence;
5234 blockIndex = This->lastBlockNoInSequenceIndex;
5235 blockNoInSequence -= This->lastBlockNoInSequence;
5236 This->lastBlockNoInSequence = temp;
5239 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5241 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5242 &blockIndex)))
5243 return STG_E_DOCFILECORRUPT;
5244 blockNoInSequence--;
5247 This->lastBlockNoInSequenceIndex = blockIndex;
5249 /* BlockChainStream_SetSize should have already been called to ensure we have
5250 * enough blocks in the chain to write into */
5251 if (blockIndex == BLOCK_END_OF_CHAIN)
5253 ERR("not enough blocks in chain to write data\n");
5254 return STG_E_DOCFILECORRUPT;
5257 *bytesWritten = 0;
5258 bufferWalker = buffer;
5260 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5262 ULARGE_INTEGER ulOffset;
5263 DWORD bytesWrittenAt;
5265 * Calculate how many bytes we can copy from this big block.
5267 bytesToWrite =
5268 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5270 TRACE("block %i\n",blockIndex);
5271 ulOffset.u.HighPart = 0;
5272 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5273 offsetInBlock;
5275 StorageImpl_WriteAt(This->parentStorage,
5276 ulOffset,
5277 bufferWalker,
5278 bytesToWrite,
5279 &bytesWrittenAt);
5282 * Step to the next big block.
5284 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5285 &blockIndex)))
5286 return STG_E_DOCFILECORRUPT;
5288 bufferWalker += bytesWrittenAt;
5289 size -= bytesWrittenAt;
5290 *bytesWritten += bytesWrittenAt;
5291 offsetInBlock = 0; /* There is no offset on the next block */
5293 if (bytesWrittenAt != bytesToWrite)
5294 break;
5297 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5300 /******************************************************************************
5301 * BlockChainStream_Shrink
5303 * Shrinks this chain in the big block depot.
5305 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5306 ULARGE_INTEGER newSize)
5308 ULONG blockIndex, extraBlock;
5309 ULONG numBlocks;
5310 ULONG count = 1;
5313 * Reset the last accessed block cache.
5315 This->lastBlockNoInSequence = 0xFFFFFFFF;
5316 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5319 * Figure out how many blocks are needed to contain the new size
5321 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5323 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5324 numBlocks++;
5326 blockIndex = BlockChainStream_GetHeadOfChain(This);
5329 * Go to the new end of chain
5331 while (count < numBlocks)
5333 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5334 &blockIndex)))
5335 return FALSE;
5336 count++;
5339 /* Get the next block before marking the new end */
5340 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5341 &extraBlock)))
5342 return FALSE;
5344 /* Mark the new end of chain */
5345 StorageImpl_SetNextBlockInChain(
5346 This->parentStorage,
5347 blockIndex,
5348 BLOCK_END_OF_CHAIN);
5350 This->tailIndex = blockIndex;
5351 This->numBlocks = numBlocks;
5354 * Mark the extra blocks as free
5356 while (extraBlock != BLOCK_END_OF_CHAIN)
5358 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5359 &blockIndex)))
5360 return FALSE;
5361 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5362 extraBlock = blockIndex;
5365 return TRUE;
5368 /******************************************************************************
5369 * BlockChainStream_Enlarge
5371 * Grows this chain in the big block depot.
5373 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5374 ULARGE_INTEGER newSize)
5376 ULONG blockIndex, currentBlock;
5377 ULONG newNumBlocks;
5378 ULONG oldNumBlocks = 0;
5380 blockIndex = BlockChainStream_GetHeadOfChain(This);
5383 * Empty chain. Create the head.
5385 if (blockIndex == BLOCK_END_OF_CHAIN)
5387 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5388 StorageImpl_SetNextBlockInChain(This->parentStorage,
5389 blockIndex,
5390 BLOCK_END_OF_CHAIN);
5392 if (This->headOfStreamPlaceHolder != 0)
5394 *(This->headOfStreamPlaceHolder) = blockIndex;
5396 else
5398 DirEntry chainEntry;
5399 assert(This->ownerDirEntry != DIRENTRY_NULL);
5401 StorageImpl_ReadDirEntry(
5402 This->parentStorage,
5403 This->ownerDirEntry,
5404 &chainEntry);
5406 chainEntry.startingBlock = blockIndex;
5408 StorageImpl_WriteDirEntry(
5409 This->parentStorage,
5410 This->ownerDirEntry,
5411 &chainEntry);
5414 This->tailIndex = blockIndex;
5415 This->numBlocks = 1;
5419 * Figure out how many blocks are needed to contain this stream
5421 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5423 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5424 newNumBlocks++;
5427 * Go to the current end of chain
5429 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5431 currentBlock = blockIndex;
5433 while (blockIndex != BLOCK_END_OF_CHAIN)
5435 This->numBlocks++;
5436 currentBlock = blockIndex;
5438 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5439 &blockIndex)))
5440 return FALSE;
5443 This->tailIndex = currentBlock;
5446 currentBlock = This->tailIndex;
5447 oldNumBlocks = This->numBlocks;
5450 * Add new blocks to the chain
5452 if (oldNumBlocks < newNumBlocks)
5454 while (oldNumBlocks < newNumBlocks)
5456 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5458 StorageImpl_SetNextBlockInChain(
5459 This->parentStorage,
5460 currentBlock,
5461 blockIndex);
5463 StorageImpl_SetNextBlockInChain(
5464 This->parentStorage,
5465 blockIndex,
5466 BLOCK_END_OF_CHAIN);
5468 currentBlock = blockIndex;
5469 oldNumBlocks++;
5472 This->tailIndex = blockIndex;
5473 This->numBlocks = newNumBlocks;
5476 return TRUE;
5479 /******************************************************************************
5480 * BlockChainStream_SetSize
5482 * Sets the size of this stream. The big block depot will be updated.
5483 * The file will grow if we grow the chain.
5485 * TODO: Free the actual blocks in the file when we shrink the chain.
5486 * Currently, the blocks are still in the file. So the file size
5487 * doesn't shrink even if we shrink streams.
5489 BOOL BlockChainStream_SetSize(
5490 BlockChainStream* This,
5491 ULARGE_INTEGER newSize)
5493 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5495 if (newSize.u.LowPart == size.u.LowPart)
5496 return TRUE;
5498 if (newSize.u.LowPart < size.u.LowPart)
5500 BlockChainStream_Shrink(This, newSize);
5502 else
5504 BlockChainStream_Enlarge(This, newSize);
5507 return TRUE;
5510 /******************************************************************************
5511 * BlockChainStream_GetSize
5513 * Returns the size of this chain.
5514 * Will return the block count if this chain doesn't have a directory entry.
5516 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5518 DirEntry chainEntry;
5520 if(This->headOfStreamPlaceHolder == NULL)
5523 * This chain has a directory entry so use the size value from there.
5525 StorageImpl_ReadDirEntry(
5526 This->parentStorage,
5527 This->ownerDirEntry,
5528 &chainEntry);
5530 return chainEntry.size;
5532 else
5535 * this chain is a chain that does not have a directory entry, figure out the
5536 * size by making the product number of used blocks times the
5537 * size of them
5539 ULARGE_INTEGER result;
5540 result.u.HighPart = 0;
5542 result.u.LowPart =
5543 BlockChainStream_GetCount(This) *
5544 This->parentStorage->bigBlockSize;
5546 return result;
5550 /******************************************************************************
5551 ** SmallBlockChainStream implementation
5554 SmallBlockChainStream* SmallBlockChainStream_Construct(
5555 StorageImpl* parentStorage,
5556 ULONG* headOfStreamPlaceHolder,
5557 DirRef dirEntry)
5559 SmallBlockChainStream* newStream;
5561 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5563 newStream->parentStorage = parentStorage;
5564 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5565 newStream->ownerDirEntry = dirEntry;
5567 return newStream;
5570 void SmallBlockChainStream_Destroy(
5571 SmallBlockChainStream* This)
5573 HeapFree(GetProcessHeap(), 0, This);
5576 /******************************************************************************
5577 * SmallBlockChainStream_GetHeadOfChain
5579 * Returns the head of this chain of small blocks.
5581 static ULONG SmallBlockChainStream_GetHeadOfChain(
5582 SmallBlockChainStream* This)
5584 DirEntry chainEntry;
5585 HRESULT hr;
5587 if (This->headOfStreamPlaceHolder != NULL)
5588 return *(This->headOfStreamPlaceHolder);
5590 if (This->ownerDirEntry)
5592 hr = StorageImpl_ReadDirEntry(
5593 This->parentStorage,
5594 This->ownerDirEntry,
5595 &chainEntry);
5597 if (SUCCEEDED(hr))
5599 return chainEntry.startingBlock;
5604 return BLOCK_END_OF_CHAIN;
5607 /******************************************************************************
5608 * SmallBlockChainStream_GetNextBlockInChain
5610 * Returns the index of the next small block in this chain.
5612 * Return Values:
5613 * - BLOCK_END_OF_CHAIN: end of this chain
5614 * - BLOCK_UNUSED: small block 'blockIndex' is free
5616 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5617 SmallBlockChainStream* This,
5618 ULONG blockIndex,
5619 ULONG* nextBlockInChain)
5621 ULARGE_INTEGER offsetOfBlockInDepot;
5622 DWORD buffer;
5623 ULONG bytesRead;
5624 HRESULT res;
5626 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5628 offsetOfBlockInDepot.u.HighPart = 0;
5629 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5632 * Read those bytes in the buffer from the small block file.
5634 res = BlockChainStream_ReadAt(
5635 This->parentStorage->smallBlockDepotChain,
5636 offsetOfBlockInDepot,
5637 sizeof(DWORD),
5638 &buffer,
5639 &bytesRead);
5641 if (SUCCEEDED(res))
5643 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5644 return S_OK;
5647 return res;
5650 /******************************************************************************
5651 * SmallBlockChainStream_SetNextBlockInChain
5653 * Writes the index of the next block of the specified block in the small
5654 * block depot.
5655 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5656 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5658 static void SmallBlockChainStream_SetNextBlockInChain(
5659 SmallBlockChainStream* This,
5660 ULONG blockIndex,
5661 ULONG nextBlock)
5663 ULARGE_INTEGER offsetOfBlockInDepot;
5664 DWORD buffer;
5665 ULONG bytesWritten;
5667 offsetOfBlockInDepot.u.HighPart = 0;
5668 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5670 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5673 * Read those bytes in the buffer from the small block file.
5675 BlockChainStream_WriteAt(
5676 This->parentStorage->smallBlockDepotChain,
5677 offsetOfBlockInDepot,
5678 sizeof(DWORD),
5679 &buffer,
5680 &bytesWritten);
5683 /******************************************************************************
5684 * SmallBlockChainStream_FreeBlock
5686 * Flag small block 'blockIndex' as free in the small block depot.
5688 static void SmallBlockChainStream_FreeBlock(
5689 SmallBlockChainStream* This,
5690 ULONG blockIndex)
5692 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5695 /******************************************************************************
5696 * SmallBlockChainStream_GetNextFreeBlock
5698 * Returns the index of a free small block. The small block depot will be
5699 * enlarged if necessary. The small block chain will also be enlarged if
5700 * necessary.
5702 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5703 SmallBlockChainStream* This)
5705 ULARGE_INTEGER offsetOfBlockInDepot;
5706 DWORD buffer;
5707 ULONG bytesRead;
5708 ULONG blockIndex = 0;
5709 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5710 HRESULT res = S_OK;
5711 ULONG smallBlocksPerBigBlock;
5713 offsetOfBlockInDepot.u.HighPart = 0;
5716 * Scan the small block depot for a free block
5718 while (nextBlockIndex != BLOCK_UNUSED)
5720 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5722 res = BlockChainStream_ReadAt(
5723 This->parentStorage->smallBlockDepotChain,
5724 offsetOfBlockInDepot,
5725 sizeof(DWORD),
5726 &buffer,
5727 &bytesRead);
5730 * If we run out of space for the small block depot, enlarge it
5732 if (SUCCEEDED(res))
5734 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5736 if (nextBlockIndex != BLOCK_UNUSED)
5737 blockIndex++;
5739 else
5741 ULONG count =
5742 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5744 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5745 ULONG nextBlock, newsbdIndex;
5746 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5748 nextBlock = sbdIndex;
5749 while (nextBlock != BLOCK_END_OF_CHAIN)
5751 sbdIndex = nextBlock;
5752 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5755 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5756 if (sbdIndex != BLOCK_END_OF_CHAIN)
5757 StorageImpl_SetNextBlockInChain(
5758 This->parentStorage,
5759 sbdIndex,
5760 newsbdIndex);
5762 StorageImpl_SetNextBlockInChain(
5763 This->parentStorage,
5764 newsbdIndex,
5765 BLOCK_END_OF_CHAIN);
5768 * Initialize all the small blocks to free
5770 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5771 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5773 if (count == 0)
5776 * We have just created the small block depot.
5778 DirEntry rootEntry;
5779 ULONG sbStartIndex;
5782 * Save it in the header
5784 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5785 StorageImpl_SaveFileHeader(This->parentStorage);
5788 * And allocate the first big block that will contain small blocks
5790 sbStartIndex =
5791 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5793 StorageImpl_SetNextBlockInChain(
5794 This->parentStorage,
5795 sbStartIndex,
5796 BLOCK_END_OF_CHAIN);
5798 StorageImpl_ReadDirEntry(
5799 This->parentStorage,
5800 This->parentStorage->base.storageDirEntry,
5801 &rootEntry);
5803 rootEntry.startingBlock = sbStartIndex;
5804 rootEntry.size.u.HighPart = 0;
5805 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5807 StorageImpl_WriteDirEntry(
5808 This->parentStorage,
5809 This->parentStorage->base.storageDirEntry,
5810 &rootEntry);
5812 else
5813 StorageImpl_SaveFileHeader(This->parentStorage);
5817 smallBlocksPerBigBlock =
5818 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5821 * Verify if we have to allocate big blocks to contain small blocks
5823 if (blockIndex % smallBlocksPerBigBlock == 0)
5825 DirEntry rootEntry;
5826 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5828 StorageImpl_ReadDirEntry(
5829 This->parentStorage,
5830 This->parentStorage->base.storageDirEntry,
5831 &rootEntry);
5833 if (rootEntry.size.u.LowPart <
5834 (blocksRequired * This->parentStorage->bigBlockSize))
5836 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5838 BlockChainStream_SetSize(
5839 This->parentStorage->smallBlockRootChain,
5840 rootEntry.size);
5842 StorageImpl_WriteDirEntry(
5843 This->parentStorage,
5844 This->parentStorage->base.storageDirEntry,
5845 &rootEntry);
5849 return blockIndex;
5852 /******************************************************************************
5853 * SmallBlockChainStream_ReadAt
5855 * Reads a specified number of bytes from this chain at the specified offset.
5856 * bytesRead may be NULL.
5857 * Failure will be returned if the specified number of bytes has not been read.
5859 HRESULT SmallBlockChainStream_ReadAt(
5860 SmallBlockChainStream* This,
5861 ULARGE_INTEGER offset,
5862 ULONG size,
5863 void* buffer,
5864 ULONG* bytesRead)
5866 HRESULT rc = S_OK;
5867 ULARGE_INTEGER offsetInBigBlockFile;
5868 ULONG blockNoInSequence =
5869 offset.u.LowPart / This->parentStorage->smallBlockSize;
5871 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5872 ULONG bytesToReadInBuffer;
5873 ULONG blockIndex;
5874 ULONG bytesReadFromBigBlockFile;
5875 BYTE* bufferWalker;
5878 * This should never happen on a small block file.
5880 assert(offset.u.HighPart==0);
5883 * Find the first block in the stream that contains part of the buffer.
5885 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5887 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5889 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5890 if(FAILED(rc))
5891 return rc;
5892 blockNoInSequence--;
5896 * Start reading the buffer.
5898 *bytesRead = 0;
5899 bufferWalker = buffer;
5901 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5904 * Calculate how many bytes we can copy from this small block.
5906 bytesToReadInBuffer =
5907 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5910 * Calculate the offset of the small block in the small block file.
5912 offsetInBigBlockFile.u.HighPart = 0;
5913 offsetInBigBlockFile.u.LowPart =
5914 blockIndex * This->parentStorage->smallBlockSize;
5916 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5919 * Read those bytes in the buffer from the small block file.
5920 * The small block has already been identified so it shouldn't fail
5921 * unless the file is corrupt.
5923 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5924 offsetInBigBlockFile,
5925 bytesToReadInBuffer,
5926 bufferWalker,
5927 &bytesReadFromBigBlockFile);
5929 if (FAILED(rc))
5930 return rc;
5933 * Step to the next big block.
5935 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5936 if(FAILED(rc))
5937 return STG_E_DOCFILECORRUPT;
5939 bufferWalker += bytesReadFromBigBlockFile;
5940 size -= bytesReadFromBigBlockFile;
5941 *bytesRead += bytesReadFromBigBlockFile;
5942 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5945 return (size == 0) ? S_OK : STG_E_READFAULT;
5948 /******************************************************************************
5949 * SmallBlockChainStream_WriteAt
5951 * Writes the specified number of bytes to this chain at the specified offset.
5952 * Will fail if not all specified number of bytes have been written.
5954 HRESULT SmallBlockChainStream_WriteAt(
5955 SmallBlockChainStream* This,
5956 ULARGE_INTEGER offset,
5957 ULONG size,
5958 const void* buffer,
5959 ULONG* bytesWritten)
5961 ULARGE_INTEGER offsetInBigBlockFile;
5962 ULONG blockNoInSequence =
5963 offset.u.LowPart / This->parentStorage->smallBlockSize;
5965 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5966 ULONG bytesToWriteInBuffer;
5967 ULONG blockIndex;
5968 ULONG bytesWrittenToBigBlockFile;
5969 const BYTE* bufferWalker;
5970 HRESULT res;
5973 * This should never happen on a small block file.
5975 assert(offset.u.HighPart==0);
5978 * Find the first block in the stream that contains part of the buffer.
5980 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5982 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5984 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5985 return STG_E_DOCFILECORRUPT;
5986 blockNoInSequence--;
5990 * Start writing the buffer.
5992 *bytesWritten = 0;
5993 bufferWalker = buffer;
5994 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5997 * Calculate how many bytes we can copy to this small block.
5999 bytesToWriteInBuffer =
6000 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6003 * Calculate the offset of the small block in the small block file.
6005 offsetInBigBlockFile.u.HighPart = 0;
6006 offsetInBigBlockFile.u.LowPart =
6007 blockIndex * This->parentStorage->smallBlockSize;
6009 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6012 * Write those bytes in the buffer to the small block file.
6014 res = BlockChainStream_WriteAt(
6015 This->parentStorage->smallBlockRootChain,
6016 offsetInBigBlockFile,
6017 bytesToWriteInBuffer,
6018 bufferWalker,
6019 &bytesWrittenToBigBlockFile);
6020 if (FAILED(res))
6021 return res;
6024 * Step to the next big block.
6026 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6027 &blockIndex)))
6028 return FALSE;
6029 bufferWalker += bytesWrittenToBigBlockFile;
6030 size -= bytesWrittenToBigBlockFile;
6031 *bytesWritten += bytesWrittenToBigBlockFile;
6032 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6035 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6038 /******************************************************************************
6039 * SmallBlockChainStream_Shrink
6041 * Shrinks this chain in the small block depot.
6043 static BOOL SmallBlockChainStream_Shrink(
6044 SmallBlockChainStream* This,
6045 ULARGE_INTEGER newSize)
6047 ULONG blockIndex, extraBlock;
6048 ULONG numBlocks;
6049 ULONG count = 0;
6051 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6053 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6054 numBlocks++;
6056 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6059 * Go to the new end of chain
6061 while (count < numBlocks)
6063 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6064 &blockIndex)))
6065 return FALSE;
6066 count++;
6070 * If the count is 0, we have a special case, the head of the chain was
6071 * just freed.
6073 if (count == 0)
6075 DirEntry chainEntry;
6077 StorageImpl_ReadDirEntry(This->parentStorage,
6078 This->ownerDirEntry,
6079 &chainEntry);
6081 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6083 StorageImpl_WriteDirEntry(This->parentStorage,
6084 This->ownerDirEntry,
6085 &chainEntry);
6088 * We start freeing the chain at the head block.
6090 extraBlock = blockIndex;
6092 else
6094 /* Get the next block before marking the new end */
6095 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6096 &extraBlock)))
6097 return FALSE;
6099 /* Mark the new end of chain */
6100 SmallBlockChainStream_SetNextBlockInChain(
6101 This,
6102 blockIndex,
6103 BLOCK_END_OF_CHAIN);
6107 * Mark the extra blocks as free
6109 while (extraBlock != BLOCK_END_OF_CHAIN)
6111 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6112 &blockIndex)))
6113 return FALSE;
6114 SmallBlockChainStream_FreeBlock(This, extraBlock);
6115 extraBlock = blockIndex;
6118 return TRUE;
6121 /******************************************************************************
6122 * SmallBlockChainStream_Enlarge
6124 * Grows this chain in the small block depot.
6126 static BOOL SmallBlockChainStream_Enlarge(
6127 SmallBlockChainStream* This,
6128 ULARGE_INTEGER newSize)
6130 ULONG blockIndex, currentBlock;
6131 ULONG newNumBlocks;
6132 ULONG oldNumBlocks = 0;
6134 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6137 * Empty chain. Create the head.
6139 if (blockIndex == BLOCK_END_OF_CHAIN)
6141 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6142 SmallBlockChainStream_SetNextBlockInChain(
6143 This,
6144 blockIndex,
6145 BLOCK_END_OF_CHAIN);
6147 if (This->headOfStreamPlaceHolder != NULL)
6149 *(This->headOfStreamPlaceHolder) = blockIndex;
6151 else
6153 DirEntry chainEntry;
6155 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6156 &chainEntry);
6158 chainEntry.startingBlock = blockIndex;
6160 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6161 &chainEntry);
6165 currentBlock = blockIndex;
6168 * Figure out how many blocks are needed to contain this stream
6170 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6172 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6173 newNumBlocks++;
6176 * Go to the current end of chain
6178 while (blockIndex != BLOCK_END_OF_CHAIN)
6180 oldNumBlocks++;
6181 currentBlock = blockIndex;
6182 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6183 return FALSE;
6187 * Add new blocks to the chain
6189 while (oldNumBlocks < newNumBlocks)
6191 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6192 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6194 SmallBlockChainStream_SetNextBlockInChain(
6195 This,
6196 blockIndex,
6197 BLOCK_END_OF_CHAIN);
6199 currentBlock = blockIndex;
6200 oldNumBlocks++;
6203 return TRUE;
6206 /******************************************************************************
6207 * SmallBlockChainStream_SetSize
6209 * Sets the size of this stream.
6210 * The file will grow if we grow the chain.
6212 * TODO: Free the actual blocks in the file when we shrink the chain.
6213 * Currently, the blocks are still in the file. So the file size
6214 * doesn't shrink even if we shrink streams.
6216 BOOL SmallBlockChainStream_SetSize(
6217 SmallBlockChainStream* This,
6218 ULARGE_INTEGER newSize)
6220 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6222 if (newSize.u.LowPart == size.u.LowPart)
6223 return TRUE;
6225 if (newSize.u.LowPart < size.u.LowPart)
6227 SmallBlockChainStream_Shrink(This, newSize);
6229 else
6231 SmallBlockChainStream_Enlarge(This, newSize);
6234 return TRUE;
6237 /******************************************************************************
6238 * SmallBlockChainStream_GetCount
6240 * Returns the number of small blocks that comprises this chain.
6241 * This is not the size of the stream as the last block may not be full!
6244 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6246 ULONG blockIndex;
6247 ULONG count = 0;
6249 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6251 while(blockIndex != BLOCK_END_OF_CHAIN)
6253 count++;
6255 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6256 blockIndex, &blockIndex)))
6257 return 0;
6260 return count;
6263 /******************************************************************************
6264 * SmallBlockChainStream_GetSize
6266 * Returns the size of this chain.
6268 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6270 DirEntry chainEntry;
6272 if(This->headOfStreamPlaceHolder != NULL)
6274 ULARGE_INTEGER result;
6275 result.u.HighPart = 0;
6277 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6278 This->parentStorage->smallBlockSize;
6280 return result;
6283 StorageImpl_ReadDirEntry(
6284 This->parentStorage,
6285 This->ownerDirEntry,
6286 &chainEntry);
6288 return chainEntry.size;
6291 /******************************************************************************
6292 * StgCreateDocfile [OLE32.@]
6293 * Creates a new compound file storage object
6295 * PARAMS
6296 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6297 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6298 * reserved [ ?] unused?, usually 0
6299 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6301 * RETURNS
6302 * S_OK if the file was successfully created
6303 * some STG_E_ value if error
6304 * NOTES
6305 * if pwcsName is NULL, create file with new unique name
6306 * the function can returns
6307 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6308 * (unrealized now)
6310 HRESULT WINAPI StgCreateDocfile(
6311 LPCOLESTR pwcsName,
6312 DWORD grfMode,
6313 DWORD reserved,
6314 IStorage **ppstgOpen)
6316 StorageBaseImpl* newStorage = 0;
6317 HANDLE hFile = INVALID_HANDLE_VALUE;
6318 HRESULT hr = STG_E_INVALIDFLAG;
6319 DWORD shareMode;
6320 DWORD accessMode;
6321 DWORD creationMode;
6322 DWORD fileAttributes;
6323 WCHAR tempFileName[MAX_PATH];
6325 TRACE("(%s, %x, %d, %p)\n",
6326 debugstr_w(pwcsName), grfMode,
6327 reserved, ppstgOpen);
6329 if (ppstgOpen == 0)
6330 return STG_E_INVALIDPOINTER;
6331 if (reserved != 0)
6332 return STG_E_INVALIDPARAMETER;
6334 /* if no share mode given then DENY_NONE is the default */
6335 if (STGM_SHARE_MODE(grfMode) == 0)
6336 grfMode |= STGM_SHARE_DENY_NONE;
6338 if ( FAILED( validateSTGM(grfMode) ))
6339 goto end;
6341 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6342 switch(STGM_ACCESS_MODE(grfMode))
6344 case STGM_WRITE:
6345 case STGM_READWRITE:
6346 break;
6347 default:
6348 goto end;
6351 /* in direct mode, can only use SHARE_EXCLUSIVE */
6352 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6353 goto end;
6355 /* but in transacted mode, any share mode is valid */
6358 * Generate a unique name.
6360 if (pwcsName == 0)
6362 WCHAR tempPath[MAX_PATH];
6363 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6365 memset(tempPath, 0, sizeof(tempPath));
6366 memset(tempFileName, 0, sizeof(tempFileName));
6368 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6369 tempPath[0] = '.';
6371 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6372 pwcsName = tempFileName;
6373 else
6375 hr = STG_E_INSUFFICIENTMEMORY;
6376 goto end;
6379 creationMode = TRUNCATE_EXISTING;
6381 else
6383 creationMode = GetCreationModeFromSTGM(grfMode);
6387 * Interpret the STGM value grfMode
6389 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6390 accessMode = GetAccessModeFromSTGM(grfMode);
6392 if (grfMode & STGM_DELETEONRELEASE)
6393 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6394 else
6395 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6397 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6398 FIXME("Storage share mode not implemented.\n");
6400 if (grfMode & STGM_TRANSACTED)
6401 FIXME("Transacted mode not implemented.\n");
6403 *ppstgOpen = 0;
6405 hFile = CreateFileW(pwcsName,
6406 accessMode,
6407 shareMode,
6408 NULL,
6409 creationMode,
6410 fileAttributes,
6413 if (hFile == INVALID_HANDLE_VALUE)
6415 if(GetLastError() == ERROR_FILE_EXISTS)
6416 hr = STG_E_FILEALREADYEXISTS;
6417 else
6418 hr = E_FAIL;
6419 goto end;
6423 * Allocate and initialize the new IStorage32object.
6425 hr = Storage_Construct(
6426 hFile,
6427 pwcsName,
6428 NULL,
6429 grfMode,
6430 TRUE,
6431 TRUE,
6432 &newStorage);
6434 if (FAILED(hr))
6436 goto end;
6440 * Get an "out" pointer for the caller.
6442 *ppstgOpen = (IStorage*)newStorage;
6444 end:
6445 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6447 return hr;
6450 /******************************************************************************
6451 * StgCreateStorageEx [OLE32.@]
6453 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6455 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6456 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6458 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6460 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6461 return STG_E_INVALIDPARAMETER;
6464 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6466 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6467 return STG_E_INVALIDPARAMETER;
6470 if (stgfmt == STGFMT_FILE)
6472 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6473 return STG_E_INVALIDPARAMETER;
6476 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6478 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6479 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
6482 ERR("Invalid stgfmt argument\n");
6483 return STG_E_INVALIDPARAMETER;
6486 /******************************************************************************
6487 * StgCreatePropSetStg [OLE32.@]
6489 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6490 IPropertySetStorage **ppPropSetStg)
6492 HRESULT hr;
6494 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6495 if (reserved)
6496 hr = STG_E_INVALIDPARAMETER;
6497 else
6498 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6499 (void**)ppPropSetStg);
6500 return hr;
6503 /******************************************************************************
6504 * StgOpenStorageEx [OLE32.@]
6506 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6508 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6509 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6511 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6513 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6514 return STG_E_INVALIDPARAMETER;
6517 switch (stgfmt)
6519 case STGFMT_FILE:
6520 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6521 return STG_E_INVALIDPARAMETER;
6523 case STGFMT_STORAGE:
6524 break;
6526 case STGFMT_DOCFILE:
6527 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6529 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6530 return STG_E_INVALIDPARAMETER;
6532 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6533 break;
6535 case STGFMT_ANY:
6536 WARN("STGFMT_ANY assuming storage\n");
6537 break;
6539 default:
6540 return STG_E_INVALIDPARAMETER;
6543 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6547 /******************************************************************************
6548 * StgOpenStorage [OLE32.@]
6550 HRESULT WINAPI StgOpenStorage(
6551 const OLECHAR *pwcsName,
6552 IStorage *pstgPriority,
6553 DWORD grfMode,
6554 SNB snbExclude,
6555 DWORD reserved,
6556 IStorage **ppstgOpen)
6558 StorageBaseImpl* newStorage = 0;
6559 HRESULT hr = S_OK;
6560 HANDLE hFile = 0;
6561 DWORD shareMode;
6562 DWORD accessMode;
6564 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6565 debugstr_w(pwcsName), pstgPriority, grfMode,
6566 snbExclude, reserved, ppstgOpen);
6568 if (pwcsName == 0)
6570 hr = STG_E_INVALIDNAME;
6571 goto end;
6574 if (ppstgOpen == 0)
6576 hr = STG_E_INVALIDPOINTER;
6577 goto end;
6580 if (reserved)
6582 hr = STG_E_INVALIDPARAMETER;
6583 goto end;
6586 if (grfMode & STGM_PRIORITY)
6588 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6589 return STG_E_INVALIDFLAG;
6590 if (grfMode & STGM_DELETEONRELEASE)
6591 return STG_E_INVALIDFUNCTION;
6592 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6593 return STG_E_INVALIDFLAG;
6594 grfMode &= ~0xf0; /* remove the existing sharing mode */
6595 grfMode |= STGM_SHARE_DENY_NONE;
6597 /* STGM_PRIORITY stops other IStorage objects on the same file from
6598 * committing until the STGM_PRIORITY IStorage is closed. it also
6599 * stops non-transacted mode StgOpenStorage calls with write access from
6600 * succeeding. obviously, both of these cannot be achieved through just
6601 * file share flags */
6602 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6606 * Validate the sharing mode
6608 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6609 switch(STGM_SHARE_MODE(grfMode))
6611 case STGM_SHARE_EXCLUSIVE:
6612 case STGM_SHARE_DENY_WRITE:
6613 break;
6614 default:
6615 hr = STG_E_INVALIDFLAG;
6616 goto end;
6619 if ( FAILED( validateSTGM(grfMode) ) ||
6620 (grfMode&STGM_CREATE))
6622 hr = STG_E_INVALIDFLAG;
6623 goto end;
6626 /* shared reading requires transacted mode */
6627 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6628 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6629 !(grfMode&STGM_TRANSACTED) )
6631 hr = STG_E_INVALIDFLAG;
6632 goto end;
6636 * Interpret the STGM value grfMode
6638 shareMode = GetShareModeFromSTGM(grfMode);
6639 accessMode = GetAccessModeFromSTGM(grfMode);
6641 *ppstgOpen = 0;
6643 hFile = CreateFileW( pwcsName,
6644 accessMode,
6645 shareMode,
6646 NULL,
6647 OPEN_EXISTING,
6648 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6651 if (hFile==INVALID_HANDLE_VALUE)
6653 DWORD last_error = GetLastError();
6655 hr = E_FAIL;
6657 switch (last_error)
6659 case ERROR_FILE_NOT_FOUND:
6660 hr = STG_E_FILENOTFOUND;
6661 break;
6663 case ERROR_PATH_NOT_FOUND:
6664 hr = STG_E_PATHNOTFOUND;
6665 break;
6667 case ERROR_ACCESS_DENIED:
6668 case ERROR_WRITE_PROTECT:
6669 hr = STG_E_ACCESSDENIED;
6670 break;
6672 case ERROR_SHARING_VIOLATION:
6673 hr = STG_E_SHAREVIOLATION;
6674 break;
6676 default:
6677 hr = E_FAIL;
6680 goto end;
6684 * Refuse to open the file if it's too small to be a structured storage file
6685 * FIXME: verify the file when reading instead of here
6687 if (GetFileSize(hFile, NULL) < 0x100)
6689 CloseHandle(hFile);
6690 hr = STG_E_FILEALREADYEXISTS;
6691 goto end;
6695 * Allocate and initialize the new IStorage32object.
6697 hr = Storage_Construct(
6698 hFile,
6699 pwcsName,
6700 NULL,
6701 grfMode,
6702 TRUE,
6703 FALSE,
6704 &newStorage);
6706 if (FAILED(hr))
6709 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6711 if(hr == STG_E_INVALIDHEADER)
6712 hr = STG_E_FILEALREADYEXISTS;
6713 goto end;
6717 * Get an "out" pointer for the caller.
6719 *ppstgOpen = (IStorage*)newStorage;
6721 end:
6722 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6723 return hr;
6726 /******************************************************************************
6727 * StgCreateDocfileOnILockBytes [OLE32.@]
6729 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6730 ILockBytes *plkbyt,
6731 DWORD grfMode,
6732 DWORD reserved,
6733 IStorage** ppstgOpen)
6735 StorageBaseImpl* newStorage = 0;
6736 HRESULT hr = S_OK;
6738 if ((ppstgOpen == 0) || (plkbyt == 0))
6739 return STG_E_INVALIDPOINTER;
6742 * Allocate and initialize the new IStorage object.
6744 hr = Storage_Construct(
6747 plkbyt,
6748 grfMode,
6749 FALSE,
6750 TRUE,
6751 &newStorage);
6753 if (FAILED(hr))
6755 return hr;
6759 * Get an "out" pointer for the caller.
6761 *ppstgOpen = (IStorage*)newStorage;
6763 return hr;
6766 /******************************************************************************
6767 * StgOpenStorageOnILockBytes [OLE32.@]
6769 HRESULT WINAPI StgOpenStorageOnILockBytes(
6770 ILockBytes *plkbyt,
6771 IStorage *pstgPriority,
6772 DWORD grfMode,
6773 SNB snbExclude,
6774 DWORD reserved,
6775 IStorage **ppstgOpen)
6777 StorageBaseImpl* newStorage = 0;
6778 HRESULT hr = S_OK;
6780 if ((plkbyt == 0) || (ppstgOpen == 0))
6781 return STG_E_INVALIDPOINTER;
6783 if ( FAILED( validateSTGM(grfMode) ))
6784 return STG_E_INVALIDFLAG;
6786 *ppstgOpen = 0;
6789 * Allocate and initialize the new IStorage object.
6791 hr = Storage_Construct(
6794 plkbyt,
6795 grfMode,
6796 FALSE,
6797 FALSE,
6798 &newStorage);
6800 if (FAILED(hr))
6802 return hr;
6806 * Get an "out" pointer for the caller.
6808 *ppstgOpen = (IStorage*)newStorage;
6810 return hr;
6813 /******************************************************************************
6814 * StgSetTimes [ole32.@]
6815 * StgSetTimes [OLE32.@]
6819 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6820 FILETIME const *patime, FILETIME const *pmtime)
6822 IStorage *stg = NULL;
6823 HRESULT r;
6825 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6827 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6828 0, 0, &stg);
6829 if( SUCCEEDED(r) )
6831 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6832 IStorage_Release(stg);
6835 return r;
6838 /******************************************************************************
6839 * StgIsStorageILockBytes [OLE32.@]
6841 * Determines if the ILockBytes contains a storage object.
6843 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6845 BYTE sig[8];
6846 ULARGE_INTEGER offset;
6848 offset.u.HighPart = 0;
6849 offset.u.LowPart = 0;
6851 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6853 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6854 return S_OK;
6856 return S_FALSE;
6859 /******************************************************************************
6860 * WriteClassStg [OLE32.@]
6862 * This method will store the specified CLSID in the specified storage object
6864 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6866 HRESULT hRes;
6868 if(!pStg)
6869 return E_INVALIDARG;
6871 if(!rclsid)
6872 return STG_E_INVALIDPOINTER;
6874 hRes = IStorage_SetClass(pStg, rclsid);
6876 return hRes;
6879 /***********************************************************************
6880 * ReadClassStg (OLE32.@)
6882 * This method reads the CLSID previously written to a storage object with
6883 * the WriteClassStg.
6885 * PARAMS
6886 * pstg [I] IStorage pointer
6887 * pclsid [O] Pointer to where the CLSID is written
6889 * RETURNS
6890 * Success: S_OK.
6891 * Failure: HRESULT code.
6893 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6895 STATSTG pstatstg;
6896 HRESULT hRes;
6898 TRACE("(%p, %p)\n", pstg, pclsid);
6900 if(!pstg || !pclsid)
6901 return E_INVALIDARG;
6904 * read a STATSTG structure (contains the clsid) from the storage
6906 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6908 if(SUCCEEDED(hRes))
6909 *pclsid=pstatstg.clsid;
6911 return hRes;
6914 /***********************************************************************
6915 * OleLoadFromStream (OLE32.@)
6917 * This function loads an object from stream
6919 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6921 CLSID clsid;
6922 HRESULT res;
6923 LPPERSISTSTREAM xstm;
6925 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6927 res=ReadClassStm(pStm,&clsid);
6928 if (FAILED(res))
6929 return res;
6930 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6931 if (FAILED(res))
6932 return res;
6933 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6934 if (FAILED(res)) {
6935 IUnknown_Release((IUnknown*)*ppvObj);
6936 return res;
6938 res=IPersistStream_Load(xstm,pStm);
6939 IPersistStream_Release(xstm);
6940 /* FIXME: all refcounts ok at this point? I think they should be:
6941 * pStm : unchanged
6942 * ppvObj : 1
6943 * xstm : 0 (released)
6945 return res;
6948 /***********************************************************************
6949 * OleSaveToStream (OLE32.@)
6951 * This function saves an object with the IPersistStream interface on it
6952 * to the specified stream.
6954 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6957 CLSID clsid;
6958 HRESULT res;
6960 TRACE("(%p,%p)\n",pPStm,pStm);
6962 res=IPersistStream_GetClassID(pPStm,&clsid);
6964 if (SUCCEEDED(res)){
6966 res=WriteClassStm(pStm,&clsid);
6968 if (SUCCEEDED(res))
6970 res=IPersistStream_Save(pPStm,pStm,TRUE);
6973 TRACE("Finished Save\n");
6974 return res;
6977 /****************************************************************************
6978 * This method validate a STGM parameter that can contain the values below
6980 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6981 * The stgm values contained in 0xffff0000 are bitmasks.
6983 * STGM_DIRECT 0x00000000
6984 * STGM_TRANSACTED 0x00010000
6985 * STGM_SIMPLE 0x08000000
6987 * STGM_READ 0x00000000
6988 * STGM_WRITE 0x00000001
6989 * STGM_READWRITE 0x00000002
6991 * STGM_SHARE_DENY_NONE 0x00000040
6992 * STGM_SHARE_DENY_READ 0x00000030
6993 * STGM_SHARE_DENY_WRITE 0x00000020
6994 * STGM_SHARE_EXCLUSIVE 0x00000010
6996 * STGM_PRIORITY 0x00040000
6997 * STGM_DELETEONRELEASE 0x04000000
6999 * STGM_CREATE 0x00001000
7000 * STGM_CONVERT 0x00020000
7001 * STGM_FAILIFTHERE 0x00000000
7003 * STGM_NOSCRATCH 0x00100000
7004 * STGM_NOSNAPSHOT 0x00200000
7006 static HRESULT validateSTGM(DWORD stgm)
7008 DWORD access = STGM_ACCESS_MODE(stgm);
7009 DWORD share = STGM_SHARE_MODE(stgm);
7010 DWORD create = STGM_CREATE_MODE(stgm);
7012 if (stgm&~STGM_KNOWN_FLAGS)
7014 ERR("unknown flags %08x\n", stgm);
7015 return E_FAIL;
7018 switch (access)
7020 case STGM_READ:
7021 case STGM_WRITE:
7022 case STGM_READWRITE:
7023 break;
7024 default:
7025 return E_FAIL;
7028 switch (share)
7030 case STGM_SHARE_DENY_NONE:
7031 case STGM_SHARE_DENY_READ:
7032 case STGM_SHARE_DENY_WRITE:
7033 case STGM_SHARE_EXCLUSIVE:
7034 break;
7035 default:
7036 return E_FAIL;
7039 switch (create)
7041 case STGM_CREATE:
7042 case STGM_FAILIFTHERE:
7043 break;
7044 default:
7045 return E_FAIL;
7049 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7051 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7052 return E_FAIL;
7055 * STGM_CREATE | STGM_CONVERT
7056 * if both are false, STGM_FAILIFTHERE is set to TRUE
7058 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7059 return E_FAIL;
7062 * STGM_NOSCRATCH requires STGM_TRANSACTED
7064 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7065 return E_FAIL;
7068 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7069 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7071 if ( (stgm & STGM_NOSNAPSHOT) &&
7072 (!(stgm & STGM_TRANSACTED) ||
7073 share == STGM_SHARE_EXCLUSIVE ||
7074 share == STGM_SHARE_DENY_WRITE) )
7075 return E_FAIL;
7077 return S_OK;
7080 /****************************************************************************
7081 * GetShareModeFromSTGM
7083 * This method will return a share mode flag from a STGM value.
7084 * The STGM value is assumed valid.
7086 static DWORD GetShareModeFromSTGM(DWORD stgm)
7088 switch (STGM_SHARE_MODE(stgm))
7090 case STGM_SHARE_DENY_NONE:
7091 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7092 case STGM_SHARE_DENY_READ:
7093 return FILE_SHARE_WRITE;
7094 case STGM_SHARE_DENY_WRITE:
7095 return FILE_SHARE_READ;
7096 case STGM_SHARE_EXCLUSIVE:
7097 return 0;
7099 ERR("Invalid share mode!\n");
7100 assert(0);
7101 return 0;
7104 /****************************************************************************
7105 * GetAccessModeFromSTGM
7107 * This method will return an access mode flag from a STGM value.
7108 * The STGM value is assumed valid.
7110 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7112 switch (STGM_ACCESS_MODE(stgm))
7114 case STGM_READ:
7115 return GENERIC_READ;
7116 case STGM_WRITE:
7117 case STGM_READWRITE:
7118 return GENERIC_READ | GENERIC_WRITE;
7120 ERR("Invalid access mode!\n");
7121 assert(0);
7122 return 0;
7125 /****************************************************************************
7126 * GetCreationModeFromSTGM
7128 * This method will return a creation mode flag from a STGM value.
7129 * The STGM value is assumed valid.
7131 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7133 switch(STGM_CREATE_MODE(stgm))
7135 case STGM_CREATE:
7136 return CREATE_ALWAYS;
7137 case STGM_CONVERT:
7138 FIXME("STGM_CONVERT not implemented!\n");
7139 return CREATE_NEW;
7140 case STGM_FAILIFTHERE:
7141 return CREATE_NEW;
7143 ERR("Invalid create mode!\n");
7144 assert(0);
7145 return 0;
7149 /*************************************************************************
7150 * OLECONVERT_LoadOLE10 [Internal]
7152 * Loads the OLE10 STREAM to memory
7154 * PARAMS
7155 * pOleStream [I] The OLESTREAM
7156 * pData [I] Data Structure for the OLESTREAM Data
7158 * RETURNS
7159 * Success: S_OK
7160 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7161 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7163 * NOTES
7164 * This function is used by OleConvertOLESTREAMToIStorage only.
7166 * Memory allocated for pData must be freed by the caller
7168 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7170 DWORD dwSize;
7171 HRESULT hRes = S_OK;
7172 int nTryCnt=0;
7173 int max_try = 6;
7175 pData->pData = NULL;
7176 pData->pstrOleObjFileName = NULL;
7178 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7180 /* Get the OleID */
7181 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7182 if(dwSize != sizeof(pData->dwOleID))
7184 hRes = CONVERT10_E_OLESTREAM_GET;
7186 else if(pData->dwOleID != OLESTREAM_ID)
7188 hRes = CONVERT10_E_OLESTREAM_FMT;
7190 else
7192 hRes = S_OK;
7193 break;
7197 if(hRes == S_OK)
7199 /* Get the TypeID... more info needed for this field */
7200 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7201 if(dwSize != sizeof(pData->dwTypeID))
7203 hRes = CONVERT10_E_OLESTREAM_GET;
7206 if(hRes == S_OK)
7208 if(pData->dwTypeID != 0)
7210 /* Get the length of the OleTypeName */
7211 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7212 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7214 hRes = CONVERT10_E_OLESTREAM_GET;
7217 if(hRes == S_OK)
7219 if(pData->dwOleTypeNameLength > 0)
7221 /* Get the OleTypeName */
7222 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7223 if(dwSize != pData->dwOleTypeNameLength)
7225 hRes = CONVERT10_E_OLESTREAM_GET;
7229 if(bStrem1)
7231 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7232 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7234 hRes = CONVERT10_E_OLESTREAM_GET;
7236 if(hRes == S_OK)
7238 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7239 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7240 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7241 if(pData->pstrOleObjFileName)
7243 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7244 if(dwSize != pData->dwOleObjFileNameLength)
7246 hRes = CONVERT10_E_OLESTREAM_GET;
7249 else
7250 hRes = CONVERT10_E_OLESTREAM_GET;
7253 else
7255 /* Get the Width of the Metafile */
7256 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7257 if(dwSize != sizeof(pData->dwMetaFileWidth))
7259 hRes = CONVERT10_E_OLESTREAM_GET;
7261 if(hRes == S_OK)
7263 /* Get the Height of the Metafile */
7264 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7265 if(dwSize != sizeof(pData->dwMetaFileHeight))
7267 hRes = CONVERT10_E_OLESTREAM_GET;
7271 if(hRes == S_OK)
7273 /* Get the Length of the Data */
7274 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7275 if(dwSize != sizeof(pData->dwDataLength))
7277 hRes = CONVERT10_E_OLESTREAM_GET;
7281 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7283 if(!bStrem1) /* if it is a second OLE stream data */
7285 pData->dwDataLength -= 8;
7286 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7287 if(dwSize != sizeof(pData->strUnknown))
7289 hRes = CONVERT10_E_OLESTREAM_GET;
7293 if(hRes == S_OK)
7295 if(pData->dwDataLength > 0)
7297 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7299 /* Get Data (ex. IStorage, Metafile, or BMP) */
7300 if(pData->pData)
7302 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7303 if(dwSize != pData->dwDataLength)
7305 hRes = CONVERT10_E_OLESTREAM_GET;
7308 else
7310 hRes = CONVERT10_E_OLESTREAM_GET;
7316 return hRes;
7319 /*************************************************************************
7320 * OLECONVERT_SaveOLE10 [Internal]
7322 * Saves the OLE10 STREAM From memory
7324 * PARAMS
7325 * pData [I] Data Structure for the OLESTREAM Data
7326 * pOleStream [I] The OLESTREAM to save
7328 * RETURNS
7329 * Success: S_OK
7330 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7332 * NOTES
7333 * This function is used by OleConvertIStorageToOLESTREAM only.
7336 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7338 DWORD dwSize;
7339 HRESULT hRes = S_OK;
7342 /* Set the OleID */
7343 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7344 if(dwSize != sizeof(pData->dwOleID))
7346 hRes = CONVERT10_E_OLESTREAM_PUT;
7349 if(hRes == S_OK)
7351 /* Set the TypeID */
7352 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7353 if(dwSize != sizeof(pData->dwTypeID))
7355 hRes = CONVERT10_E_OLESTREAM_PUT;
7359 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7361 /* Set the Length of the OleTypeName */
7362 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7363 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7365 hRes = CONVERT10_E_OLESTREAM_PUT;
7368 if(hRes == S_OK)
7370 if(pData->dwOleTypeNameLength > 0)
7372 /* Set the OleTypeName */
7373 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7374 if(dwSize != pData->dwOleTypeNameLength)
7376 hRes = CONVERT10_E_OLESTREAM_PUT;
7381 if(hRes == S_OK)
7383 /* Set the width of the Metafile */
7384 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7385 if(dwSize != sizeof(pData->dwMetaFileWidth))
7387 hRes = CONVERT10_E_OLESTREAM_PUT;
7391 if(hRes == S_OK)
7393 /* Set the height of the Metafile */
7394 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7395 if(dwSize != sizeof(pData->dwMetaFileHeight))
7397 hRes = CONVERT10_E_OLESTREAM_PUT;
7401 if(hRes == S_OK)
7403 /* Set the length of the Data */
7404 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7405 if(dwSize != sizeof(pData->dwDataLength))
7407 hRes = CONVERT10_E_OLESTREAM_PUT;
7411 if(hRes == S_OK)
7413 if(pData->dwDataLength > 0)
7415 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7416 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7417 if(dwSize != pData->dwDataLength)
7419 hRes = CONVERT10_E_OLESTREAM_PUT;
7424 return hRes;
7427 /*************************************************************************
7428 * OLECONVERT_GetOLE20FromOLE10[Internal]
7430 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7431 * opens it, and copies the content to the dest IStorage for
7432 * OleConvertOLESTREAMToIStorage
7435 * PARAMS
7436 * pDestStorage [I] The IStorage to copy the data to
7437 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7438 * nBufferLength [I] The size of the buffer
7440 * RETURNS
7441 * Nothing
7443 * NOTES
7447 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7449 HRESULT hRes;
7450 HANDLE hFile;
7451 IStorage *pTempStorage;
7452 DWORD dwNumOfBytesWritten;
7453 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7454 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7456 /* Create a temp File */
7457 GetTempPathW(MAX_PATH, wstrTempDir);
7458 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7459 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7461 if(hFile != INVALID_HANDLE_VALUE)
7463 /* Write IStorage Data to File */
7464 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7465 CloseHandle(hFile);
7467 /* Open and copy temp storage to the Dest Storage */
7468 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7469 if(hRes == S_OK)
7471 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7472 IStorage_Release(pTempStorage);
7474 DeleteFileW(wstrTempFile);
7479 /*************************************************************************
7480 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7482 * Saves the OLE10 STREAM From memory
7484 * PARAMS
7485 * pStorage [I] The Src IStorage to copy
7486 * pData [I] The Dest Memory to write to.
7488 * RETURNS
7489 * The size in bytes allocated for pData
7491 * NOTES
7492 * Memory allocated for pData must be freed by the caller
7494 * Used by OleConvertIStorageToOLESTREAM only.
7497 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7499 HANDLE hFile;
7500 HRESULT hRes;
7501 DWORD nDataLength = 0;
7502 IStorage *pTempStorage;
7503 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7504 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7506 *pData = NULL;
7508 /* Create temp Storage */
7509 GetTempPathW(MAX_PATH, wstrTempDir);
7510 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7511 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7513 if(hRes == S_OK)
7515 /* Copy Src Storage to the Temp Storage */
7516 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7517 IStorage_Release(pTempStorage);
7519 /* Open Temp Storage as a file and copy to memory */
7520 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7521 if(hFile != INVALID_HANDLE_VALUE)
7523 nDataLength = GetFileSize(hFile, NULL);
7524 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7525 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7526 CloseHandle(hFile);
7528 DeleteFileW(wstrTempFile);
7530 return nDataLength;
7533 /*************************************************************************
7534 * OLECONVERT_CreateOleStream [Internal]
7536 * Creates the "\001OLE" stream in the IStorage if necessary.
7538 * PARAMS
7539 * pStorage [I] Dest storage to create the stream in
7541 * RETURNS
7542 * Nothing
7544 * NOTES
7545 * This function is used by OleConvertOLESTREAMToIStorage only.
7547 * This stream is still unknown, MS Word seems to have extra data
7548 * but since the data is stored in the OLESTREAM there should be
7549 * no need to recreate the stream. If the stream is manually
7550 * deleted it will create it with this default data.
7553 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7555 HRESULT hRes;
7556 IStream *pStream;
7557 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7558 BYTE pOleStreamHeader [] =
7560 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7561 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7562 0x00, 0x00, 0x00, 0x00
7565 /* Create stream if not present */
7566 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7567 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7569 if(hRes == S_OK)
7571 /* Write default Data */
7572 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7573 IStream_Release(pStream);
7577 /* write a string to a stream, preceded by its length */
7578 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7580 HRESULT r;
7581 LPSTR str;
7582 DWORD len = 0;
7584 if( string )
7585 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7586 r = IStream_Write( stm, &len, sizeof(len), NULL);
7587 if( FAILED( r ) )
7588 return r;
7589 if(len == 0)
7590 return r;
7591 str = CoTaskMemAlloc( len );
7592 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7593 r = IStream_Write( stm, str, len, NULL);
7594 CoTaskMemFree( str );
7595 return r;
7598 /* read a string preceded by its length from a stream */
7599 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7601 HRESULT r;
7602 DWORD len, count = 0;
7603 LPSTR str;
7604 LPWSTR wstr;
7606 r = IStream_Read( stm, &len, sizeof(len), &count );
7607 if( FAILED( r ) )
7608 return r;
7609 if( count != sizeof(len) )
7610 return E_OUTOFMEMORY;
7612 TRACE("%d bytes\n",len);
7614 str = CoTaskMemAlloc( len );
7615 if( !str )
7616 return E_OUTOFMEMORY;
7617 count = 0;
7618 r = IStream_Read( stm, str, len, &count );
7619 if( FAILED( r ) )
7620 return r;
7621 if( count != len )
7623 CoTaskMemFree( str );
7624 return E_OUTOFMEMORY;
7627 TRACE("Read string %s\n",debugstr_an(str,len));
7629 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7630 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7631 if( wstr )
7632 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7633 CoTaskMemFree( str );
7635 *string = wstr;
7637 return r;
7641 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7642 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7644 IStream *pstm;
7645 HRESULT r = S_OK;
7646 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7648 static const BYTE unknown1[12] =
7649 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7650 0xFF, 0xFF, 0xFF, 0xFF};
7651 static const BYTE unknown2[16] =
7652 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7653 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7655 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7656 debugstr_w(lpszUserType), debugstr_w(szClipName),
7657 debugstr_w(szProgIDName));
7659 /* Create a CompObj stream */
7660 r = IStorage_CreateStream(pstg, szwStreamName,
7661 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7662 if( FAILED (r) )
7663 return r;
7665 /* Write CompObj Structure to stream */
7666 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7668 if( SUCCEEDED( r ) )
7669 r = WriteClassStm( pstm, clsid );
7671 if( SUCCEEDED( r ) )
7672 r = STREAM_WriteString( pstm, lpszUserType );
7673 if( SUCCEEDED( r ) )
7674 r = STREAM_WriteString( pstm, szClipName );
7675 if( SUCCEEDED( r ) )
7676 r = STREAM_WriteString( pstm, szProgIDName );
7677 if( SUCCEEDED( r ) )
7678 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7680 IStream_Release( pstm );
7682 return r;
7685 /***********************************************************************
7686 * WriteFmtUserTypeStg (OLE32.@)
7688 HRESULT WINAPI WriteFmtUserTypeStg(
7689 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7691 HRESULT r;
7692 WCHAR szwClipName[0x40];
7693 CLSID clsid = CLSID_NULL;
7694 LPWSTR wstrProgID = NULL;
7695 DWORD n;
7697 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7699 /* get the clipboard format name */
7700 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7701 szwClipName[n]=0;
7703 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7705 /* FIXME: There's room to save a CLSID and its ProgID, but
7706 the CLSID is not looked up in the registry and in all the
7707 tests I wrote it was CLSID_NULL. Where does it come from?
7710 /* get the real program ID. This may fail, but that's fine */
7711 ProgIDFromCLSID(&clsid, &wstrProgID);
7713 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7715 r = STORAGE_WriteCompObj( pstg, &clsid,
7716 lpszUserType, szwClipName, wstrProgID );
7718 CoTaskMemFree(wstrProgID);
7720 return r;
7724 /******************************************************************************
7725 * ReadFmtUserTypeStg [OLE32.@]
7727 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7729 HRESULT r;
7730 IStream *stm = 0;
7731 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7732 unsigned char unknown1[12];
7733 unsigned char unknown2[16];
7734 DWORD count;
7735 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7736 CLSID clsid;
7738 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7740 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7741 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7742 if( FAILED ( r ) )
7744 WARN("Failed to open stream r = %08x\n", r);
7745 return r;
7748 /* read the various parts of the structure */
7749 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7750 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7751 goto end;
7752 r = ReadClassStm( stm, &clsid );
7753 if( FAILED( r ) )
7754 goto end;
7756 r = STREAM_ReadString( stm, &szCLSIDName );
7757 if( FAILED( r ) )
7758 goto end;
7760 r = STREAM_ReadString( stm, &szOleTypeName );
7761 if( FAILED( r ) )
7762 goto end;
7764 r = STREAM_ReadString( stm, &szProgIDName );
7765 if( FAILED( r ) )
7766 goto end;
7768 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7769 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7770 goto end;
7772 /* ok, success... now we just need to store what we found */
7773 if( pcf )
7774 *pcf = RegisterClipboardFormatW( szOleTypeName );
7775 CoTaskMemFree( szOleTypeName );
7777 if( lplpszUserType )
7778 *lplpszUserType = szCLSIDName;
7779 CoTaskMemFree( szProgIDName );
7781 end:
7782 IStream_Release( stm );
7784 return r;
7788 /*************************************************************************
7789 * OLECONVERT_CreateCompObjStream [Internal]
7791 * Creates a "\001CompObj" is the destination IStorage if necessary.
7793 * PARAMS
7794 * pStorage [I] The dest IStorage to create the CompObj Stream
7795 * if necessary.
7796 * strOleTypeName [I] The ProgID
7798 * RETURNS
7799 * Success: S_OK
7800 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7802 * NOTES
7803 * This function is used by OleConvertOLESTREAMToIStorage only.
7805 * The stream data is stored in the OLESTREAM and there should be
7806 * no need to recreate the stream. If the stream is manually
7807 * deleted it will attempt to create it by querying the registry.
7811 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7813 IStream *pStream;
7814 HRESULT hStorageRes, hRes = S_OK;
7815 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7816 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7817 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7819 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7820 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7822 /* Initialize the CompObj structure */
7823 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7824 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7825 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7828 /* Create a CompObj stream if it doesn't exist */
7829 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7830 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7831 if(hStorageRes == S_OK)
7833 /* copy the OleTypeName to the compobj struct */
7834 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7835 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7837 /* copy the OleTypeName to the compobj struct */
7838 /* Note: in the test made, these were Identical */
7839 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7840 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7842 /* Get the CLSID */
7843 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7844 bufferW, OLESTREAM_MAX_STR_LEN );
7845 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7847 if(hRes == S_OK)
7849 HKEY hKey;
7850 LONG hErr;
7851 /* Get the CLSID Default Name from the Registry */
7852 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7853 if(hErr == ERROR_SUCCESS)
7855 char strTemp[OLESTREAM_MAX_STR_LEN];
7856 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7857 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7858 if(hErr == ERROR_SUCCESS)
7860 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7862 RegCloseKey(hKey);
7866 /* Write CompObj Structure to stream */
7867 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7869 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7871 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7872 if(IStorageCompObj.dwCLSIDNameLength > 0)
7874 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7876 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7877 if(IStorageCompObj.dwOleTypeNameLength > 0)
7879 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7881 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7882 if(IStorageCompObj.dwProgIDNameLength > 0)
7884 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7886 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7887 IStream_Release(pStream);
7889 return hRes;
7893 /*************************************************************************
7894 * OLECONVERT_CreateOlePresStream[Internal]
7896 * Creates the "\002OlePres000" Stream with the Metafile data
7898 * PARAMS
7899 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7900 * dwExtentX [I] Width of the Metafile
7901 * dwExtentY [I] Height of the Metafile
7902 * pData [I] Metafile data
7903 * dwDataLength [I] Size of the Metafile data
7905 * RETURNS
7906 * Success: S_OK
7907 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7909 * NOTES
7910 * This function is used by OleConvertOLESTREAMToIStorage only.
7913 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7915 HRESULT hRes;
7916 IStream *pStream;
7917 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7918 BYTE pOlePresStreamHeader [] =
7920 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7921 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7922 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7923 0x00, 0x00, 0x00, 0x00
7926 BYTE pOlePresStreamHeaderEmpty [] =
7928 0x00, 0x00, 0x00, 0x00,
7929 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7930 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7931 0x00, 0x00, 0x00, 0x00
7934 /* Create the OlePres000 Stream */
7935 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7936 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7938 if(hRes == S_OK)
7940 DWORD nHeaderSize;
7941 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7943 memset(&OlePres, 0, sizeof(OlePres));
7944 /* Do we have any metafile data to save */
7945 if(dwDataLength > 0)
7947 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7948 nHeaderSize = sizeof(pOlePresStreamHeader);
7950 else
7952 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7953 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7955 /* Set width and height of the metafile */
7956 OlePres.dwExtentX = dwExtentX;
7957 OlePres.dwExtentY = -dwExtentY;
7959 /* Set Data and Length */
7960 if(dwDataLength > sizeof(METAFILEPICT16))
7962 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7963 OlePres.pData = &(pData[8]);
7965 /* Save OlePres000 Data to Stream */
7966 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7967 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7968 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7969 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7970 if(OlePres.dwSize > 0)
7972 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7974 IStream_Release(pStream);
7978 /*************************************************************************
7979 * OLECONVERT_CreateOle10NativeStream [Internal]
7981 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7983 * PARAMS
7984 * pStorage [I] Dest storage to create the stream in
7985 * pData [I] Ole10 Native Data (ex. bmp)
7986 * dwDataLength [I] Size of the Ole10 Native Data
7988 * RETURNS
7989 * Nothing
7991 * NOTES
7992 * This function is used by OleConvertOLESTREAMToIStorage only.
7994 * Might need to verify the data and return appropriate error message
7997 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7999 HRESULT hRes;
8000 IStream *pStream;
8001 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8003 /* Create the Ole10Native Stream */
8004 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8005 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8007 if(hRes == S_OK)
8009 /* Write info to stream */
8010 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8011 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8012 IStream_Release(pStream);
8017 /*************************************************************************
8018 * OLECONVERT_GetOLE10ProgID [Internal]
8020 * Finds the ProgID (or OleTypeID) from the IStorage
8022 * PARAMS
8023 * pStorage [I] The Src IStorage to get the ProgID
8024 * strProgID [I] the ProgID string to get
8025 * dwSize [I] the size of the string
8027 * RETURNS
8028 * Success: S_OK
8029 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8031 * NOTES
8032 * This function is used by OleConvertIStorageToOLESTREAM only.
8036 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8038 HRESULT hRes;
8039 IStream *pStream;
8040 LARGE_INTEGER iSeekPos;
8041 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8042 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8044 /* Open the CompObj Stream */
8045 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8046 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8047 if(hRes == S_OK)
8050 /*Get the OleType from the CompObj Stream */
8051 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8052 iSeekPos.u.HighPart = 0;
8054 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8055 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8056 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8057 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8058 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8059 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8060 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8062 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8063 if(*dwSize > 0)
8065 IStream_Read(pStream, strProgID, *dwSize, NULL);
8067 IStream_Release(pStream);
8069 else
8071 STATSTG stat;
8072 LPOLESTR wstrProgID;
8074 /* Get the OleType from the registry */
8075 REFCLSID clsid = &(stat.clsid);
8076 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8077 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8078 if(hRes == S_OK)
8080 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8084 return hRes;
8087 /*************************************************************************
8088 * OLECONVERT_GetOle10PresData [Internal]
8090 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8092 * PARAMS
8093 * pStorage [I] Src IStroage
8094 * pOleStream [I] Dest OleStream Mem Struct
8096 * RETURNS
8097 * Nothing
8099 * NOTES
8100 * This function is used by OleConvertIStorageToOLESTREAM only.
8102 * Memory allocated for pData must be freed by the caller
8106 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8109 HRESULT hRes;
8110 IStream *pStream;
8111 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8113 /* Initialize Default data for OLESTREAM */
8114 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8115 pOleStreamData[0].dwTypeID = 2;
8116 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8117 pOleStreamData[1].dwTypeID = 0;
8118 pOleStreamData[0].dwMetaFileWidth = 0;
8119 pOleStreamData[0].dwMetaFileHeight = 0;
8120 pOleStreamData[0].pData = NULL;
8121 pOleStreamData[1].pData = NULL;
8123 /* Open Ole10Native Stream */
8124 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8125 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8126 if(hRes == S_OK)
8129 /* Read Size and Data */
8130 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8131 if(pOleStreamData->dwDataLength > 0)
8133 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8134 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8136 IStream_Release(pStream);
8142 /*************************************************************************
8143 * OLECONVERT_GetOle20PresData[Internal]
8145 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8147 * PARAMS
8148 * pStorage [I] Src IStroage
8149 * pOleStreamData [I] Dest OleStream Mem Struct
8151 * RETURNS
8152 * Nothing
8154 * NOTES
8155 * This function is used by OleConvertIStorageToOLESTREAM only.
8157 * Memory allocated for pData must be freed by the caller
8159 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8161 HRESULT hRes;
8162 IStream *pStream;
8163 OLECONVERT_ISTORAGE_OLEPRES olePress;
8164 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8166 /* Initialize Default data for OLESTREAM */
8167 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8168 pOleStreamData[0].dwTypeID = 2;
8169 pOleStreamData[0].dwMetaFileWidth = 0;
8170 pOleStreamData[0].dwMetaFileHeight = 0;
8171 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8172 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8173 pOleStreamData[1].dwTypeID = 0;
8174 pOleStreamData[1].dwOleTypeNameLength = 0;
8175 pOleStreamData[1].strOleTypeName[0] = 0;
8176 pOleStreamData[1].dwMetaFileWidth = 0;
8177 pOleStreamData[1].dwMetaFileHeight = 0;
8178 pOleStreamData[1].pData = NULL;
8179 pOleStreamData[1].dwDataLength = 0;
8182 /* Open OlePress000 stream */
8183 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8184 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8185 if(hRes == S_OK)
8187 LARGE_INTEGER iSeekPos;
8188 METAFILEPICT16 MetaFilePict;
8189 static const char strMetafilePictName[] = "METAFILEPICT";
8191 /* Set the TypeID for a Metafile */
8192 pOleStreamData[1].dwTypeID = 5;
8194 /* Set the OleTypeName to Metafile */
8195 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8196 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8198 iSeekPos.u.HighPart = 0;
8199 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8201 /* Get Presentation Data */
8202 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8203 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8204 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8205 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8207 /*Set width and Height */
8208 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8209 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8210 if(olePress.dwSize > 0)
8212 /* Set Length */
8213 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8215 /* Set MetaFilePict struct */
8216 MetaFilePict.mm = 8;
8217 MetaFilePict.xExt = olePress.dwExtentX;
8218 MetaFilePict.yExt = olePress.dwExtentY;
8219 MetaFilePict.hMF = 0;
8221 /* Get Metafile Data */
8222 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8223 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8224 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8226 IStream_Release(pStream);
8230 /*************************************************************************
8231 * OleConvertOLESTREAMToIStorage [OLE32.@]
8233 * Read info on MSDN
8235 * TODO
8236 * DVTARGETDEVICE parameter is not handled
8237 * Still unsure of some mem fields for OLE 10 Stream
8238 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8239 * and "\001OLE" streams
8242 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8243 LPOLESTREAM pOleStream,
8244 LPSTORAGE pstg,
8245 const DVTARGETDEVICE* ptd)
8247 int i;
8248 HRESULT hRes=S_OK;
8249 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8251 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8253 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8255 if(ptd != NULL)
8257 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8260 if(pstg == NULL || pOleStream == NULL)
8262 hRes = E_INVALIDARG;
8265 if(hRes == S_OK)
8267 /* Load the OLESTREAM to Memory */
8268 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8271 if(hRes == S_OK)
8273 /* Load the OLESTREAM to Memory (part 2)*/
8274 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8277 if(hRes == S_OK)
8280 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8282 /* Do we have the IStorage Data in the OLESTREAM */
8283 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8285 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8286 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8288 else
8290 /* It must be an original OLE 1.0 source */
8291 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8294 else
8296 /* It must be an original OLE 1.0 source */
8297 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8300 /* Create CompObj Stream if necessary */
8301 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8302 if(hRes == S_OK)
8304 /*Create the Ole Stream if necessary */
8305 OLECONVERT_CreateOleStream(pstg);
8310 /* Free allocated memory */
8311 for(i=0; i < 2; i++)
8313 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8314 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8315 pOleStreamData[i].pstrOleObjFileName = NULL;
8317 return hRes;
8320 /*************************************************************************
8321 * OleConvertIStorageToOLESTREAM [OLE32.@]
8323 * Read info on MSDN
8325 * Read info on MSDN
8327 * TODO
8328 * Still unsure of some mem fields for OLE 10 Stream
8329 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8330 * and "\001OLE" streams.
8333 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8334 LPSTORAGE pstg,
8335 LPOLESTREAM pOleStream)
8337 int i;
8338 HRESULT hRes = S_OK;
8339 IStream *pStream;
8340 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8341 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8343 TRACE("%p %p\n", pstg, pOleStream);
8345 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8347 if(pstg == NULL || pOleStream == NULL)
8349 hRes = E_INVALIDARG;
8351 if(hRes == S_OK)
8353 /* Get the ProgID */
8354 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8355 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8357 if(hRes == S_OK)
8359 /* Was it originally Ole10 */
8360 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8361 if(hRes == S_OK)
8363 IStream_Release(pStream);
8364 /* Get Presentation Data for Ole10Native */
8365 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8367 else
8369 /* Get Presentation Data (OLE20) */
8370 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8373 /* Save OLESTREAM */
8374 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8375 if(hRes == S_OK)
8377 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8382 /* Free allocated memory */
8383 for(i=0; i < 2; i++)
8385 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8388 return hRes;
8391 /***********************************************************************
8392 * GetConvertStg (OLE32.@)
8394 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8395 FIXME("unimplemented stub!\n");
8396 return E_FAIL;
8399 /******************************************************************************
8400 * StgIsStorageFile [OLE32.@]
8401 * Verify if the file contains a storage object
8403 * PARAMS
8404 * fn [ I] Filename
8406 * RETURNS
8407 * S_OK if file has magic bytes as a storage object
8408 * S_FALSE if file is not storage
8410 HRESULT WINAPI
8411 StgIsStorageFile(LPCOLESTR fn)
8413 HANDLE hf;
8414 BYTE magic[8];
8415 DWORD bytes_read;
8417 TRACE("%s\n", debugstr_w(fn));
8418 hf = CreateFileW(fn, GENERIC_READ,
8419 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8420 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8422 if (hf == INVALID_HANDLE_VALUE)
8423 return STG_E_FILENOTFOUND;
8425 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8427 WARN(" unable to read file\n");
8428 CloseHandle(hf);
8429 return S_FALSE;
8432 CloseHandle(hf);
8434 if (bytes_read != 8) {
8435 WARN(" too short\n");
8436 return S_FALSE;
8439 if (!memcmp(magic,STORAGE_magic,8)) {
8440 WARN(" -> YES\n");
8441 return S_OK;
8444 WARN(" -> Invalid header.\n");
8445 return S_FALSE;
8448 /***********************************************************************
8449 * WriteClassStm (OLE32.@)
8451 * Writes a CLSID to a stream.
8453 * PARAMS
8454 * pStm [I] Stream to write to.
8455 * rclsid [I] CLSID to write.
8457 * RETURNS
8458 * Success: S_OK.
8459 * Failure: HRESULT code.
8461 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8463 TRACE("(%p,%p)\n",pStm,rclsid);
8465 if (!pStm || !rclsid)
8466 return E_INVALIDARG;
8468 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8471 /***********************************************************************
8472 * ReadClassStm (OLE32.@)
8474 * Reads a CLSID from a stream.
8476 * PARAMS
8477 * pStm [I] Stream to read from.
8478 * rclsid [O] CLSID to read.
8480 * RETURNS
8481 * Success: S_OK.
8482 * Failure: HRESULT code.
8484 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8486 ULONG nbByte;
8487 HRESULT res;
8489 TRACE("(%p,%p)\n",pStm,pclsid);
8491 if (!pStm || !pclsid)
8492 return E_INVALIDARG;
8494 /* clear the output args */
8495 *pclsid = CLSID_NULL;
8497 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8499 if (FAILED(res))
8500 return res;
8502 if (nbByte != sizeof(CLSID))
8503 return STG_E_READFAULT;
8504 else
8505 return S_OK;