push 5b1efc32b5a8acb1d5b5e60584746392dd0c436e
[wine/hacks.git] / dlls / ole32 / storage32.c
blob479c2a54b8a4bd6d1ebd66cdd3cffc7e4db83ff5
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 continue;
1661 if (curElement.type == STGTY_STORAGE)
1663 if(skip_storage)
1664 continue;
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 break;
1677 * Check if destination storage is not a child of the source
1678 * storage, which will cause an infinite loop
1680 if (pstgChild == pstgDest)
1682 IEnumSTATSTG_Release(elements);
1684 return STG_E_ACCESSDENIED;
1688 * create a new storage in destination storage
1690 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1691 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1692 0, 0,
1693 &pstgTmp );
1695 * if it already exist, don't create a new one use this one
1697 if (hr == STG_E_FILEALREADYEXISTS)
1699 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1700 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1701 NULL, 0, &pstgTmp );
1704 if (hr != S_OK)
1705 break;
1709 * do the copy recursively
1711 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1712 NULL, pstgTmp );
1714 IStorage_Release( pstgTmp );
1715 IStorage_Release( pstgChild );
1717 else if (curElement.type == STGTY_STREAM)
1719 if(skip_stream)
1720 continue;
1723 * create a new stream in destination storage. If the stream already
1724 * exist, it will be deleted and a new one will be created.
1726 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1727 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1728 0, 0, &pstrTmp );
1730 if (hr != S_OK)
1731 break;
1734 * open child stream storage
1736 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1737 STGM_READ|STGM_SHARE_EXCLUSIVE,
1738 0, &pstrChild );
1740 if (hr != S_OK)
1741 break;
1744 * Get the size of the source stream
1746 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1749 * Set the size of the destination stream.
1751 IStream_SetSize(pstrTmp, strStat.cbSize);
1754 * do the copy
1756 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1757 NULL, NULL );
1759 IStream_Release( pstrTmp );
1760 IStream_Release( pstrChild );
1762 else
1764 WARN("unknown element type: %d\n", curElement.type);
1767 } while (hr == S_OK);
1770 * Clean-up
1772 IEnumSTATSTG_Release(elements);
1774 return hr;
1777 /*************************************************************************
1778 * MoveElementTo (IStorage)
1780 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1781 IStorage* iface,
1782 const OLECHAR *pwcsName, /* [string][in] */
1783 IStorage *pstgDest, /* [unique][in] */
1784 const OLECHAR *pwcsNewName,/* [string][in] */
1785 DWORD grfFlags) /* [in] */
1787 FIXME("(%p %s %p %s %u): stub\n", iface,
1788 debugstr_w(pwcsName), pstgDest,
1789 debugstr_w(pwcsNewName), grfFlags);
1790 return E_NOTIMPL;
1793 /*************************************************************************
1794 * Commit (IStorage)
1796 * Ensures that any changes made to a storage object open in transacted mode
1797 * are reflected in the parent storage
1799 * NOTES
1800 * Wine doesn't implement transacted mode, which seems to be a basic
1801 * optimization, so we can ignore this stub for now.
1803 static HRESULT WINAPI StorageImpl_Commit(
1804 IStorage* iface,
1805 DWORD grfCommitFlags)/* [in] */
1807 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1808 return S_OK;
1811 /*************************************************************************
1812 * Revert (IStorage)
1814 * Discard all changes that have been made since the last commit operation
1816 static HRESULT WINAPI StorageImpl_Revert(
1817 IStorage* iface)
1819 FIXME("(%p): stub\n", iface);
1820 return E_NOTIMPL;
1823 /*************************************************************************
1824 * DestroyElement (IStorage)
1826 * Strategy: This implementation is built this way for simplicity not for speed.
1827 * I always delete the topmost element of the enumeration and adjust
1828 * the deleted element pointer all the time. This takes longer to
1829 * do but allow to reinvoke DestroyElement whenever we encounter a
1830 * storage object. The optimisation resides in the usage of another
1831 * enumeration strategy that would give all the leaves of a storage
1832 * first. (postfix order)
1834 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1835 IStorage* iface,
1836 const OLECHAR *pwcsName)/* [string][in] */
1838 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1840 HRESULT hr = S_OK;
1841 DirEntry entryToDelete;
1842 DirRef entryToDeleteRef;
1844 TRACE("(%p, %s)\n",
1845 iface, debugstr_w(pwcsName));
1847 if (pwcsName==NULL)
1848 return STG_E_INVALIDPOINTER;
1850 if (This->reverted)
1851 return STG_E_REVERTED;
1853 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1854 return STG_E_ACCESSDENIED;
1856 entryToDeleteRef = findElement(
1857 This,
1858 This->storageDirEntry,
1859 pwcsName,
1860 &entryToDelete);
1862 if ( entryToDeleteRef == DIRENTRY_NULL )
1864 return STG_E_FILENOTFOUND;
1867 if ( entryToDelete.stgType == STGTY_STORAGE )
1869 hr = deleteStorageContents(
1870 This,
1871 entryToDeleteRef,
1872 entryToDelete);
1874 else if ( entryToDelete.stgType == STGTY_STREAM )
1876 hr = deleteStreamContents(
1877 This,
1878 entryToDeleteRef,
1879 entryToDelete);
1882 if (hr!=S_OK)
1883 return hr;
1886 * Remove the entry from its parent storage
1888 hr = removeFromTree(
1889 This,
1890 This->storageDirEntry,
1891 entryToDeleteRef);
1894 * Invalidate the entry
1896 if (SUCCEEDED(hr))
1897 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1899 return hr;
1903 /******************************************************************************
1904 * Internal stream list handlers
1907 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1909 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1910 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1913 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1915 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1916 list_remove(&(strm->StrmListEntry));
1919 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1921 StgStreamImpl *strm;
1923 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1925 if (strm->dirEntry == streamEntry)
1927 return TRUE;
1931 return FALSE;
1934 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1936 StorageInternalImpl *childstg;
1938 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1940 if (childstg->base.storageDirEntry == storageEntry)
1942 return TRUE;
1946 return FALSE;
1949 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1951 struct list *cur, *cur2;
1952 StgStreamImpl *strm=NULL;
1953 StorageInternalImpl *childstg=NULL;
1955 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1956 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1957 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1958 strm->parentStorage = NULL;
1959 list_remove(cur);
1962 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1963 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1964 StorageBaseImpl_Invalidate( &childstg->base );
1967 if (stg->transactedChild)
1969 StorageBaseImpl_Invalidate(stg->transactedChild);
1971 stg->transactedChild = NULL;
1976 /*********************************************************************
1978 * Internal Method
1980 * Delete the contents of a storage entry.
1983 static HRESULT deleteStorageContents(
1984 StorageBaseImpl *parentStorage,
1985 DirRef indexToDelete,
1986 DirEntry entryDataToDelete)
1988 IEnumSTATSTG *elements = 0;
1989 IStorage *childStorage = 0;
1990 STATSTG currentElement;
1991 HRESULT hr;
1992 HRESULT destroyHr = S_OK;
1993 StorageInternalImpl *stg, *stg2;
1995 /* Invalidate any open storage objects. */
1996 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1998 if (stg->base.storageDirEntry == indexToDelete)
2000 StorageBaseImpl_Invalidate(&stg->base);
2005 * Open the storage and enumerate it
2007 hr = StorageBaseImpl_OpenStorage(
2008 (IStorage*)parentStorage,
2009 entryDataToDelete.name,
2011 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2014 &childStorage);
2016 if (hr != S_OK)
2018 return hr;
2022 * Enumerate the elements
2024 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2029 * Obtain the next element
2031 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2032 if (hr==S_OK)
2034 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2036 CoTaskMemFree(currentElement.pwcsName);
2040 * We need to Reset the enumeration every time because we delete elements
2041 * and the enumeration could be invalid
2043 IEnumSTATSTG_Reset(elements);
2045 } while ((hr == S_OK) && (destroyHr == S_OK));
2047 IStorage_Release(childStorage);
2048 IEnumSTATSTG_Release(elements);
2050 return destroyHr;
2053 /*********************************************************************
2055 * Internal Method
2057 * Perform the deletion of a stream's data
2060 static HRESULT deleteStreamContents(
2061 StorageBaseImpl *parentStorage,
2062 DirRef indexToDelete,
2063 DirEntry entryDataToDelete)
2065 IStream *pis;
2066 HRESULT hr;
2067 ULARGE_INTEGER size;
2068 StgStreamImpl *strm, *strm2;
2070 /* Invalidate any open stream objects. */
2071 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2073 if (strm->dirEntry == indexToDelete)
2075 TRACE("Stream deleted %p\n", strm);
2076 strm->parentStorage = NULL;
2077 list_remove(&strm->StrmListEntry);
2081 size.u.HighPart = 0;
2082 size.u.LowPart = 0;
2084 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2085 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2087 if (hr!=S_OK)
2089 return(hr);
2093 * Zap the stream
2095 hr = IStream_SetSize(pis, size);
2097 if(hr != S_OK)
2099 return hr;
2103 * Release the stream object.
2105 IStream_Release(pis);
2107 return S_OK;
2110 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2112 switch (relation)
2114 case DIRENTRY_RELATION_PREVIOUS:
2115 entry->leftChild = new_target;
2116 break;
2117 case DIRENTRY_RELATION_NEXT:
2118 entry->rightChild = new_target;
2119 break;
2120 case DIRENTRY_RELATION_DIR:
2121 entry->dirRootEntry = new_target;
2122 break;
2123 default:
2124 assert(0);
2128 /*************************************************************************
2130 * Internal Method
2132 * This method removes a directory entry from its parent storage tree without
2133 * freeing any resources attached to it.
2135 static HRESULT removeFromTree(
2136 StorageBaseImpl *This,
2137 DirRef parentStorageIndex,
2138 DirRef deletedIndex)
2140 HRESULT hr = S_OK;
2141 DirEntry entryToDelete;
2142 DirEntry parentEntry;
2143 DirRef parentEntryRef;
2144 ULONG typeOfRelation;
2146 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2148 if (hr != S_OK)
2149 return hr;
2152 * Find the element that links to the one we want to delete.
2154 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2155 &parentEntry, &parentEntryRef, &typeOfRelation);
2157 if (hr != S_OK)
2158 return hr;
2160 if (entryToDelete.leftChild != DIRENTRY_NULL)
2163 * Replace the deleted entry with its left child
2165 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2167 hr = StorageBaseImpl_WriteDirEntry(
2168 This,
2169 parentEntryRef,
2170 &parentEntry);
2171 if(FAILED(hr))
2173 return hr;
2176 if (entryToDelete.rightChild != DIRENTRY_NULL)
2179 * We need to reinsert the right child somewhere. We already know it and
2180 * its children are greater than everything in the left tree, so we
2181 * insert it at the rightmost point in the left tree.
2183 DirRef newRightChildParent = entryToDelete.leftChild;
2184 DirEntry newRightChildParentEntry;
2188 hr = StorageBaseImpl_ReadDirEntry(
2189 This,
2190 newRightChildParent,
2191 &newRightChildParentEntry);
2192 if (FAILED(hr))
2194 return hr;
2197 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2198 newRightChildParent = newRightChildParentEntry.rightChild;
2199 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2201 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2203 hr = StorageBaseImpl_WriteDirEntry(
2204 This,
2205 newRightChildParent,
2206 &newRightChildParentEntry);
2207 if (FAILED(hr))
2209 return hr;
2213 else
2216 * Replace the deleted entry with its right child
2218 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2220 hr = StorageBaseImpl_WriteDirEntry(
2221 This,
2222 parentEntryRef,
2223 &parentEntry);
2224 if(FAILED(hr))
2226 return hr;
2230 return hr;
2234 /******************************************************************************
2235 * SetElementTimes (IStorage)
2237 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2238 IStorage* iface,
2239 const OLECHAR *pwcsName,/* [string][in] */
2240 const FILETIME *pctime, /* [in] */
2241 const FILETIME *patime, /* [in] */
2242 const FILETIME *pmtime) /* [in] */
2244 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2245 return S_OK;
2248 /******************************************************************************
2249 * SetStateBits (IStorage)
2251 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2252 IStorage* iface,
2253 DWORD grfStateBits,/* [in] */
2254 DWORD grfMask) /* [in] */
2256 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2258 if (This->reverted)
2259 return STG_E_REVERTED;
2261 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2262 return S_OK;
2265 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2266 DirRef index, const DirEntry *data)
2268 StorageImpl *This = (StorageImpl*)base;
2269 return StorageImpl_WriteDirEntry(This, index, data);
2272 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2273 DirRef index, DirEntry *data)
2275 StorageImpl *This = (StorageImpl*)base;
2276 return StorageImpl_ReadDirEntry(This, index, data);
2279 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2280 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2282 StorageImpl *This = (StorageImpl*)base;
2283 DirEntry data;
2284 HRESULT hr;
2285 ULONG bytesToRead;
2287 hr = StorageImpl_ReadDirEntry(This, index, &data);
2288 if (FAILED(hr)) return hr;
2290 if (data.size.QuadPart == 0)
2292 *bytesRead = 0;
2293 return S_OK;
2296 if (offset.QuadPart + size > data.size.QuadPart)
2298 bytesToRead = data.size.QuadPart - offset.QuadPart;
2300 else
2302 bytesToRead = size;
2305 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2307 SmallBlockChainStream *stream;
2309 stream = SmallBlockChainStream_Construct(This, NULL, index);
2310 if (!stream) return E_OUTOFMEMORY;
2312 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2314 SmallBlockChainStream_Destroy(stream);
2316 return hr;
2318 else
2320 BlockChainStream *stream;
2322 stream = BlockChainStream_Construct(This, NULL, index);
2323 if (!stream) return E_OUTOFMEMORY;
2325 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2327 BlockChainStream_Destroy(stream);
2329 return hr;
2333 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2334 ULARGE_INTEGER newsize)
2336 StorageImpl *This = (StorageImpl*)base;
2337 DirEntry data;
2338 HRESULT hr;
2339 SmallBlockChainStream *smallblock=NULL;
2340 BlockChainStream *bigblock=NULL;
2342 hr = StorageImpl_ReadDirEntry(This, index, &data);
2343 if (FAILED(hr)) return hr;
2345 /* In simple mode keep the stream size above the small block limit */
2346 if (This->base.openFlags & STGM_SIMPLE)
2347 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2349 if (data.size.QuadPart == newsize.QuadPart)
2350 return S_OK;
2352 /* Create a block chain object of the appropriate type */
2353 if (data.size.QuadPart == 0)
2355 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2357 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2358 if (!smallblock) return E_OUTOFMEMORY;
2360 else
2362 bigblock = BlockChainStream_Construct(This, NULL, index);
2363 if (!bigblock) return E_OUTOFMEMORY;
2366 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2368 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2369 if (!smallblock) return E_OUTOFMEMORY;
2371 else
2373 bigblock = BlockChainStream_Construct(This, NULL, index);
2374 if (!bigblock) return E_OUTOFMEMORY;
2377 /* Change the block chain type if necessary. */
2378 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2380 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2381 if (!bigblock)
2383 SmallBlockChainStream_Destroy(smallblock);
2384 return E_FAIL;
2387 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2389 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, &bigblock);
2390 if (!smallblock)
2392 BlockChainStream_Destroy(bigblock);
2393 return E_FAIL;
2397 /* Set the size of the block chain. */
2398 if (smallblock)
2400 SmallBlockChainStream_SetSize(smallblock, newsize);
2401 SmallBlockChainStream_Destroy(smallblock);
2403 else
2405 BlockChainStream_SetSize(bigblock, newsize);
2406 BlockChainStream_Destroy(bigblock);
2409 /* Set the size in the directory entry. */
2410 hr = StorageImpl_ReadDirEntry(This, index, &data);
2411 if (SUCCEEDED(hr))
2413 data.size = newsize;
2415 hr = StorageImpl_WriteDirEntry(This, index, &data);
2417 return hr;
2420 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2421 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2423 StorageImpl *This = (StorageImpl*)base;
2424 DirEntry data;
2425 HRESULT hr;
2426 ULARGE_INTEGER newSize;
2428 hr = StorageImpl_ReadDirEntry(This, index, &data);
2429 if (FAILED(hr)) return hr;
2431 /* Grow the stream if necessary */
2432 newSize.QuadPart = 0;
2433 newSize.QuadPart = offset.QuadPart + size;
2435 if (newSize.QuadPart > data.size.QuadPart)
2437 hr = StorageImpl_StreamSetSize(base, index, newSize);
2438 if (FAILED(hr))
2439 return hr;
2441 data.size = newSize;
2444 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2446 SmallBlockChainStream *stream;
2448 stream = SmallBlockChainStream_Construct(This, NULL, index);
2449 if (!stream) return E_OUTOFMEMORY;
2451 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2453 SmallBlockChainStream_Destroy(stream);
2455 return hr;
2457 else
2459 BlockChainStream *stream;
2461 stream = BlockChainStream_Construct(This, NULL, index);
2462 if (!stream) return E_OUTOFMEMORY;
2464 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2466 BlockChainStream_Destroy(stream);
2468 return hr;
2473 * Virtual function table for the IStorage32Impl class.
2475 static const IStorageVtbl Storage32Impl_Vtbl =
2477 StorageBaseImpl_QueryInterface,
2478 StorageBaseImpl_AddRef,
2479 StorageBaseImpl_Release,
2480 StorageBaseImpl_CreateStream,
2481 StorageBaseImpl_OpenStream,
2482 StorageBaseImpl_CreateStorage,
2483 StorageBaseImpl_OpenStorage,
2484 StorageBaseImpl_CopyTo,
2485 StorageBaseImpl_MoveElementTo,
2486 StorageImpl_Commit,
2487 StorageImpl_Revert,
2488 StorageBaseImpl_EnumElements,
2489 StorageBaseImpl_DestroyElement,
2490 StorageBaseImpl_RenameElement,
2491 StorageBaseImpl_SetElementTimes,
2492 StorageBaseImpl_SetClass,
2493 StorageBaseImpl_SetStateBits,
2494 StorageBaseImpl_Stat
2497 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2499 StorageImpl_Destroy,
2500 StorageImpl_Invalidate,
2501 StorageImpl_CreateDirEntry,
2502 StorageImpl_BaseWriteDirEntry,
2503 StorageImpl_BaseReadDirEntry,
2504 StorageImpl_DestroyDirEntry,
2505 StorageImpl_StreamReadAt,
2506 StorageImpl_StreamWriteAt,
2507 StorageImpl_StreamSetSize
2510 static HRESULT StorageImpl_Construct(
2511 HANDLE hFile,
2512 LPCOLESTR pwcsName,
2513 ILockBytes* pLkbyt,
2514 DWORD openFlags,
2515 BOOL fileBased,
2516 BOOL create,
2517 StorageImpl** result)
2519 StorageImpl* This;
2520 HRESULT hr = S_OK;
2521 DirEntry currentEntry;
2522 DirRef currentEntryRef;
2523 WCHAR fullpath[MAX_PATH];
2525 if ( FAILED( validateSTGM(openFlags) ))
2526 return STG_E_INVALIDFLAG;
2528 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2529 if (!This)
2530 return E_OUTOFMEMORY;
2532 memset(This, 0, sizeof(StorageImpl));
2534 list_init(&This->base.strmHead);
2536 list_init(&This->base.storageHead);
2538 This->base.lpVtbl = &Storage32Impl_Vtbl;
2539 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2540 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2541 This->base.openFlags = (openFlags & ~STGM_CREATE);
2542 This->base.ref = 1;
2543 This->base.create = create;
2545 This->base.reverted = 0;
2547 This->hFile = hFile;
2549 if(pwcsName) {
2550 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2552 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2554 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2555 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2556 if (!This->pwcsName)
2558 hr = STG_E_INSUFFICIENTMEMORY;
2559 goto end;
2561 strcpyW(This->pwcsName, fullpath);
2562 This->base.filename = This->pwcsName;
2566 * Initialize the big block cache.
2568 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2569 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2570 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2571 pLkbyt,
2572 openFlags,
2573 This->bigBlockSize,
2574 fileBased);
2576 if (This->bigBlockFile == 0)
2578 hr = E_FAIL;
2579 goto end;
2582 if (create)
2584 ULARGE_INTEGER size;
2585 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2588 * Initialize all header variables:
2589 * - The big block depot consists of one block and it is at block 0
2590 * - The directory table starts at block 1
2591 * - There is no small block depot
2593 memset( This->bigBlockDepotStart,
2594 BLOCK_UNUSED,
2595 sizeof(This->bigBlockDepotStart));
2597 This->bigBlockDepotCount = 1;
2598 This->bigBlockDepotStart[0] = 0;
2599 This->rootStartBlock = 1;
2600 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2601 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2602 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2603 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2604 This->extBigBlockDepotCount = 0;
2606 StorageImpl_SaveFileHeader(This);
2609 * Add one block for the big block depot and one block for the directory table
2611 size.u.HighPart = 0;
2612 size.u.LowPart = This->bigBlockSize * 3;
2613 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2616 * Initialize the big block depot
2618 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2619 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2620 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2621 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2623 else
2626 * Load the header for the file.
2628 hr = StorageImpl_LoadFileHeader(This);
2630 if (FAILED(hr))
2632 goto end;
2637 * There is no block depot cached yet.
2639 This->indexBlockDepotCached = 0xFFFFFFFF;
2642 * Start searching for free blocks with block 0.
2644 This->prevFreeBlock = 0;
2647 * Create the block chain abstractions.
2649 if(!(This->rootBlockChain =
2650 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2652 hr = STG_E_READFAULT;
2653 goto end;
2656 if(!(This->smallBlockDepotChain =
2657 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2658 DIRENTRY_NULL)))
2660 hr = STG_E_READFAULT;
2661 goto end;
2665 * Write the root storage entry (memory only)
2667 if (create)
2669 DirEntry rootEntry;
2671 * Initialize the directory table
2673 memset(&rootEntry, 0, sizeof(rootEntry));
2674 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2675 sizeof(rootEntry.name)/sizeof(WCHAR) );
2676 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2677 rootEntry.stgType = STGTY_ROOT;
2678 rootEntry.leftChild = DIRENTRY_NULL;
2679 rootEntry.rightChild = DIRENTRY_NULL;
2680 rootEntry.dirRootEntry = DIRENTRY_NULL;
2681 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2682 rootEntry.size.u.HighPart = 0;
2683 rootEntry.size.u.LowPart = 0;
2685 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2689 * Find the ID of the root storage.
2691 currentEntryRef = 0;
2695 hr = StorageImpl_ReadDirEntry(
2696 This,
2697 currentEntryRef,
2698 &currentEntry);
2700 if (SUCCEEDED(hr))
2702 if ( (currentEntry.sizeOfNameString != 0 ) &&
2703 (currentEntry.stgType == STGTY_ROOT) )
2705 This->base.storageDirEntry = currentEntryRef;
2709 currentEntryRef++;
2711 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2713 if (FAILED(hr))
2715 hr = STG_E_READFAULT;
2716 goto end;
2720 * Create the block chain abstraction for the small block root chain.
2722 if(!(This->smallBlockRootChain =
2723 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2725 hr = STG_E_READFAULT;
2728 end:
2729 if (FAILED(hr))
2731 IStorage_Release((IStorage*)This);
2732 *result = NULL;
2734 else
2735 *result = This;
2737 return hr;
2740 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2742 StorageImpl *This = (StorageImpl*) iface;
2744 StorageBaseImpl_DeleteAll(&This->base);
2746 This->base.reverted = 1;
2749 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2751 StorageImpl *This = (StorageImpl*) iface;
2752 TRACE("(%p)\n", This);
2754 StorageImpl_Invalidate(iface);
2756 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2758 BlockChainStream_Destroy(This->smallBlockRootChain);
2759 BlockChainStream_Destroy(This->rootBlockChain);
2760 BlockChainStream_Destroy(This->smallBlockDepotChain);
2762 if (This->bigBlockFile)
2763 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2764 HeapFree(GetProcessHeap(), 0, This);
2767 /******************************************************************************
2768 * Storage32Impl_GetNextFreeBigBlock
2770 * Returns the index of the next free big block.
2771 * If the big block depot is filled, this method will enlarge it.
2774 static ULONG StorageImpl_GetNextFreeBigBlock(
2775 StorageImpl* This)
2777 ULONG depotBlockIndexPos;
2778 BYTE depotBuffer[BIG_BLOCK_SIZE];
2779 BOOL success;
2780 ULONG depotBlockOffset;
2781 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2782 ULONG nextBlockIndex = BLOCK_SPECIAL;
2783 int depotIndex = 0;
2784 ULONG freeBlock = BLOCK_UNUSED;
2786 depotIndex = This->prevFreeBlock / blocksPerDepot;
2787 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2790 * Scan the entire big block depot until we find a block marked free
2792 while (nextBlockIndex != BLOCK_UNUSED)
2794 if (depotIndex < COUNT_BBDEPOTINHEADER)
2796 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2799 * Grow the primary depot.
2801 if (depotBlockIndexPos == BLOCK_UNUSED)
2803 depotBlockIndexPos = depotIndex*blocksPerDepot;
2806 * Add a block depot.
2808 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2809 This->bigBlockDepotCount++;
2810 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2813 * Flag it as a block depot.
2815 StorageImpl_SetNextBlockInChain(This,
2816 depotBlockIndexPos,
2817 BLOCK_SPECIAL);
2819 /* Save new header information.
2821 StorageImpl_SaveFileHeader(This);
2824 else
2826 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2828 if (depotBlockIndexPos == BLOCK_UNUSED)
2831 * Grow the extended depot.
2833 ULONG extIndex = BLOCK_UNUSED;
2834 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2835 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2837 if (extBlockOffset == 0)
2839 /* We need an extended block.
2841 extIndex = Storage32Impl_AddExtBlockDepot(This);
2842 This->extBigBlockDepotCount++;
2843 depotBlockIndexPos = extIndex + 1;
2845 else
2846 depotBlockIndexPos = depotIndex * blocksPerDepot;
2849 * Add a block depot and mark it in the extended block.
2851 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2852 This->bigBlockDepotCount++;
2853 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2855 /* Flag the block depot.
2857 StorageImpl_SetNextBlockInChain(This,
2858 depotBlockIndexPos,
2859 BLOCK_SPECIAL);
2861 /* If necessary, flag the extended depot block.
2863 if (extIndex != BLOCK_UNUSED)
2864 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2866 /* Save header information.
2868 StorageImpl_SaveFileHeader(This);
2872 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2874 if (success)
2876 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2877 ( nextBlockIndex != BLOCK_UNUSED))
2879 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2881 if (nextBlockIndex == BLOCK_UNUSED)
2883 freeBlock = (depotIndex * blocksPerDepot) +
2884 (depotBlockOffset/sizeof(ULONG));
2887 depotBlockOffset += sizeof(ULONG);
2891 depotIndex++;
2892 depotBlockOffset = 0;
2896 * make sure that the block physically exists before using it
2898 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2900 This->prevFreeBlock = freeBlock;
2902 return freeBlock;
2905 /******************************************************************************
2906 * Storage32Impl_AddBlockDepot
2908 * This will create a depot block, essentially it is a block initialized
2909 * to BLOCK_UNUSEDs.
2911 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2913 BYTE blockBuffer[BIG_BLOCK_SIZE];
2916 * Initialize blocks as free
2918 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2919 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2922 /******************************************************************************
2923 * Storage32Impl_GetExtDepotBlock
2925 * Returns the index of the block that corresponds to the specified depot
2926 * index. This method is only for depot indexes equal or greater than
2927 * COUNT_BBDEPOTINHEADER.
2929 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2931 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2932 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2933 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2934 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2935 ULONG blockIndex = BLOCK_UNUSED;
2936 ULONG extBlockIndex = This->extBigBlockDepotStart;
2938 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2940 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2941 return BLOCK_UNUSED;
2943 while (extBlockCount > 0)
2945 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2946 extBlockCount--;
2949 if (extBlockIndex != BLOCK_UNUSED)
2950 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2951 extBlockOffset * sizeof(ULONG), &blockIndex);
2953 return blockIndex;
2956 /******************************************************************************
2957 * Storage32Impl_SetExtDepotBlock
2959 * Associates the specified block index to the specified depot index.
2960 * This method is only for depot indexes equal or greater than
2961 * COUNT_BBDEPOTINHEADER.
2963 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2965 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2966 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2967 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2968 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2969 ULONG extBlockIndex = This->extBigBlockDepotStart;
2971 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2973 while (extBlockCount > 0)
2975 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2976 extBlockCount--;
2979 if (extBlockIndex != BLOCK_UNUSED)
2981 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2982 extBlockOffset * sizeof(ULONG),
2983 blockIndex);
2987 /******************************************************************************
2988 * Storage32Impl_AddExtBlockDepot
2990 * Creates an extended depot block.
2992 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2994 ULONG numExtBlocks = This->extBigBlockDepotCount;
2995 ULONG nextExtBlock = This->extBigBlockDepotStart;
2996 BYTE depotBuffer[BIG_BLOCK_SIZE];
2997 ULONG index = BLOCK_UNUSED;
2998 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2999 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3000 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3002 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3003 blocksPerDepotBlock;
3005 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3008 * The first extended block.
3010 This->extBigBlockDepotStart = index;
3012 else
3014 unsigned int i;
3016 * Follow the chain to the last one.
3018 for (i = 0; i < (numExtBlocks - 1); i++)
3020 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3024 * Add the new extended block to the chain.
3026 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3027 index);
3031 * Initialize this block.
3033 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3034 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3036 return index;
3039 /******************************************************************************
3040 * Storage32Impl_FreeBigBlock
3042 * This method will flag the specified block as free in the big block depot.
3044 static void StorageImpl_FreeBigBlock(
3045 StorageImpl* This,
3046 ULONG blockIndex)
3048 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3050 if (blockIndex < This->prevFreeBlock)
3051 This->prevFreeBlock = blockIndex;
3054 /************************************************************************
3055 * Storage32Impl_GetNextBlockInChain
3057 * This method will retrieve the block index of the next big block in
3058 * in the chain.
3060 * Params: This - Pointer to the Storage object.
3061 * blockIndex - Index of the block to retrieve the chain
3062 * for.
3063 * nextBlockIndex - receives the return value.
3065 * Returns: This method returns the index of the next block in the chain.
3066 * It will return the constants:
3067 * BLOCK_SPECIAL - If the block given was not part of a
3068 * chain.
3069 * BLOCK_END_OF_CHAIN - If the block given was the last in
3070 * a chain.
3071 * BLOCK_UNUSED - If the block given was not past of a chain
3072 * and is available.
3073 * BLOCK_EXTBBDEPOT - This block is part of the extended
3074 * big block depot.
3076 * See Windows documentation for more details on IStorage methods.
3078 static HRESULT StorageImpl_GetNextBlockInChain(
3079 StorageImpl* This,
3080 ULONG blockIndex,
3081 ULONG* nextBlockIndex)
3083 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3084 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3085 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3086 BYTE depotBuffer[BIG_BLOCK_SIZE];
3087 BOOL success;
3088 ULONG depotBlockIndexPos;
3089 int index;
3091 *nextBlockIndex = BLOCK_SPECIAL;
3093 if(depotBlockCount >= This->bigBlockDepotCount)
3095 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3096 This->bigBlockDepotCount);
3097 return STG_E_READFAULT;
3101 * Cache the currently accessed depot block.
3103 if (depotBlockCount != This->indexBlockDepotCached)
3105 This->indexBlockDepotCached = depotBlockCount;
3107 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3109 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3111 else
3114 * We have to look in the extended depot.
3116 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3119 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3121 if (!success)
3122 return STG_E_READFAULT;
3124 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
3126 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3127 This->blockDepotCached[index] = *nextBlockIndex;
3131 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3133 return S_OK;
3136 /******************************************************************************
3137 * Storage32Impl_GetNextExtendedBlock
3139 * Given an extended block this method will return the next extended block.
3141 * NOTES:
3142 * The last ULONG of an extended block is the block index of the next
3143 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3144 * depot.
3146 * Return values:
3147 * - The index of the next extended block
3148 * - BLOCK_UNUSED: there is no next extended block.
3149 * - Any other return values denotes failure.
3151 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3153 ULONG nextBlockIndex = BLOCK_SPECIAL;
3154 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3156 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3157 &nextBlockIndex);
3159 return nextBlockIndex;
3162 /******************************************************************************
3163 * Storage32Impl_SetNextBlockInChain
3165 * This method will write the index of the specified block's next block
3166 * in the big block depot.
3168 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3169 * do the following
3171 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3172 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3173 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3176 static void StorageImpl_SetNextBlockInChain(
3177 StorageImpl* This,
3178 ULONG blockIndex,
3179 ULONG nextBlock)
3181 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3182 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3183 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3184 ULONG depotBlockIndexPos;
3186 assert(depotBlockCount < This->bigBlockDepotCount);
3187 assert(blockIndex != nextBlock);
3189 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3191 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3193 else
3196 * We have to look in the extended depot.
3198 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3201 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3202 nextBlock);
3204 * Update the cached block depot, if necessary.
3206 if (depotBlockCount == This->indexBlockDepotCached)
3208 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3212 /******************************************************************************
3213 * Storage32Impl_LoadFileHeader
3215 * This method will read in the file header, i.e. big block index -1.
3217 static HRESULT StorageImpl_LoadFileHeader(
3218 StorageImpl* This)
3220 HRESULT hr = STG_E_FILENOTFOUND;
3221 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3222 BOOL success;
3223 int index;
3225 TRACE("\n");
3227 * Get a pointer to the big block of data containing the header.
3229 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3232 * Extract the information from the header.
3234 if (success)
3237 * Check for the "magic number" signature and return an error if it is not
3238 * found.
3240 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3242 return STG_E_OLDFORMAT;
3245 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3247 return STG_E_INVALIDHEADER;
3250 StorageUtl_ReadWord(
3251 headerBigBlock,
3252 OFFSET_BIGBLOCKSIZEBITS,
3253 &This->bigBlockSizeBits);
3255 StorageUtl_ReadWord(
3256 headerBigBlock,
3257 OFFSET_SMALLBLOCKSIZEBITS,
3258 &This->smallBlockSizeBits);
3260 StorageUtl_ReadDWord(
3261 headerBigBlock,
3262 OFFSET_BBDEPOTCOUNT,
3263 &This->bigBlockDepotCount);
3265 StorageUtl_ReadDWord(
3266 headerBigBlock,
3267 OFFSET_ROOTSTARTBLOCK,
3268 &This->rootStartBlock);
3270 StorageUtl_ReadDWord(
3271 headerBigBlock,
3272 OFFSET_SBDEPOTSTART,
3273 &This->smallBlockDepotStart);
3275 StorageUtl_ReadDWord(
3276 headerBigBlock,
3277 OFFSET_EXTBBDEPOTSTART,
3278 &This->extBigBlockDepotStart);
3280 StorageUtl_ReadDWord(
3281 headerBigBlock,
3282 OFFSET_EXTBBDEPOTCOUNT,
3283 &This->extBigBlockDepotCount);
3285 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3287 StorageUtl_ReadDWord(
3288 headerBigBlock,
3289 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3290 &(This->bigBlockDepotStart[index]));
3294 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3296 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3297 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3300 * Right now, the code is making some assumptions about the size of the
3301 * blocks, just make sure they are what we're expecting.
3303 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3304 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3306 WARN("Broken OLE storage file\n");
3307 hr = STG_E_INVALIDHEADER;
3309 else
3310 hr = S_OK;
3313 return hr;
3316 /******************************************************************************
3317 * Storage32Impl_SaveFileHeader
3319 * This method will save to the file the header, i.e. big block -1.
3321 static void StorageImpl_SaveFileHeader(
3322 StorageImpl* This)
3324 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3325 int index;
3326 BOOL success;
3329 * Get a pointer to the big block of data containing the header.
3331 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3334 * If the block read failed, the file is probably new.
3336 if (!success)
3339 * Initialize for all unknown fields.
3341 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3344 * Initialize the magic number.
3346 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3349 * And a bunch of things we don't know what they mean
3351 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3352 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3353 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3354 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3358 * Write the information to the header.
3360 StorageUtl_WriteWord(
3361 headerBigBlock,
3362 OFFSET_BIGBLOCKSIZEBITS,
3363 This->bigBlockSizeBits);
3365 StorageUtl_WriteWord(
3366 headerBigBlock,
3367 OFFSET_SMALLBLOCKSIZEBITS,
3368 This->smallBlockSizeBits);
3370 StorageUtl_WriteDWord(
3371 headerBigBlock,
3372 OFFSET_BBDEPOTCOUNT,
3373 This->bigBlockDepotCount);
3375 StorageUtl_WriteDWord(
3376 headerBigBlock,
3377 OFFSET_ROOTSTARTBLOCK,
3378 This->rootStartBlock);
3380 StorageUtl_WriteDWord(
3381 headerBigBlock,
3382 OFFSET_SBDEPOTSTART,
3383 This->smallBlockDepotStart);
3385 StorageUtl_WriteDWord(
3386 headerBigBlock,
3387 OFFSET_SBDEPOTCOUNT,
3388 This->smallBlockDepotChain ?
3389 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3391 StorageUtl_WriteDWord(
3392 headerBigBlock,
3393 OFFSET_EXTBBDEPOTSTART,
3394 This->extBigBlockDepotStart);
3396 StorageUtl_WriteDWord(
3397 headerBigBlock,
3398 OFFSET_EXTBBDEPOTCOUNT,
3399 This->extBigBlockDepotCount);
3401 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3403 StorageUtl_WriteDWord(
3404 headerBigBlock,
3405 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3406 (This->bigBlockDepotStart[index]));
3410 * Write the big block back to the file.
3412 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3415 /******************************************************************************
3416 * StorageImpl_ReadRawDirEntry
3418 * This method will read the raw data from a directory entry in the file.
3420 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3422 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3424 ULARGE_INTEGER offset;
3425 HRESULT hr;
3426 ULONG bytesRead;
3428 offset.u.HighPart = 0;
3429 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3431 hr = BlockChainStream_ReadAt(
3432 This->rootBlockChain,
3433 offset,
3434 RAW_DIRENTRY_SIZE,
3435 buffer,
3436 &bytesRead);
3438 return hr;
3441 /******************************************************************************
3442 * StorageImpl_WriteRawDirEntry
3444 * This method will write the raw data from a directory entry in the file.
3446 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3448 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3450 ULARGE_INTEGER offset;
3451 HRESULT hr;
3452 ULONG bytesRead;
3454 offset.u.HighPart = 0;
3455 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3457 hr = BlockChainStream_WriteAt(
3458 This->rootBlockChain,
3459 offset,
3460 RAW_DIRENTRY_SIZE,
3461 buffer,
3462 &bytesRead);
3464 return hr;
3467 /******************************************************************************
3468 * UpdateRawDirEntry
3470 * Update raw directory entry data from the fields in newData.
3472 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3474 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3476 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3478 memcpy(
3479 buffer + OFFSET_PS_NAME,
3480 newData->name,
3481 DIRENTRY_NAME_BUFFER_LEN );
3483 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3485 StorageUtl_WriteWord(
3486 buffer,
3487 OFFSET_PS_NAMELENGTH,
3488 newData->sizeOfNameString);
3490 StorageUtl_WriteDWord(
3491 buffer,
3492 OFFSET_PS_LEFTCHILD,
3493 newData->leftChild);
3495 StorageUtl_WriteDWord(
3496 buffer,
3497 OFFSET_PS_RIGHTCHILD,
3498 newData->rightChild);
3500 StorageUtl_WriteDWord(
3501 buffer,
3502 OFFSET_PS_DIRROOT,
3503 newData->dirRootEntry);
3505 StorageUtl_WriteGUID(
3506 buffer,
3507 OFFSET_PS_GUID,
3508 &newData->clsid);
3510 StorageUtl_WriteDWord(
3511 buffer,
3512 OFFSET_PS_CTIMELOW,
3513 newData->ctime.dwLowDateTime);
3515 StorageUtl_WriteDWord(
3516 buffer,
3517 OFFSET_PS_CTIMEHIGH,
3518 newData->ctime.dwHighDateTime);
3520 StorageUtl_WriteDWord(
3521 buffer,
3522 OFFSET_PS_MTIMELOW,
3523 newData->mtime.dwLowDateTime);
3525 StorageUtl_WriteDWord(
3526 buffer,
3527 OFFSET_PS_MTIMEHIGH,
3528 newData->ctime.dwHighDateTime);
3530 StorageUtl_WriteDWord(
3531 buffer,
3532 OFFSET_PS_STARTBLOCK,
3533 newData->startingBlock);
3535 StorageUtl_WriteDWord(
3536 buffer,
3537 OFFSET_PS_SIZE,
3538 newData->size.u.LowPart);
3541 /******************************************************************************
3542 * Storage32Impl_ReadDirEntry
3544 * This method will read the specified directory entry.
3546 HRESULT StorageImpl_ReadDirEntry(
3547 StorageImpl* This,
3548 DirRef index,
3549 DirEntry* buffer)
3551 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3552 HRESULT readRes;
3554 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3556 if (SUCCEEDED(readRes))
3558 memset(buffer->name, 0, sizeof(buffer->name));
3559 memcpy(
3560 buffer->name,
3561 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3562 DIRENTRY_NAME_BUFFER_LEN );
3563 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3565 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3567 StorageUtl_ReadWord(
3568 currentEntry,
3569 OFFSET_PS_NAMELENGTH,
3570 &buffer->sizeOfNameString);
3572 StorageUtl_ReadDWord(
3573 currentEntry,
3574 OFFSET_PS_LEFTCHILD,
3575 &buffer->leftChild);
3577 StorageUtl_ReadDWord(
3578 currentEntry,
3579 OFFSET_PS_RIGHTCHILD,
3580 &buffer->rightChild);
3582 StorageUtl_ReadDWord(
3583 currentEntry,
3584 OFFSET_PS_DIRROOT,
3585 &buffer->dirRootEntry);
3587 StorageUtl_ReadGUID(
3588 currentEntry,
3589 OFFSET_PS_GUID,
3590 &buffer->clsid);
3592 StorageUtl_ReadDWord(
3593 currentEntry,
3594 OFFSET_PS_CTIMELOW,
3595 &buffer->ctime.dwLowDateTime);
3597 StorageUtl_ReadDWord(
3598 currentEntry,
3599 OFFSET_PS_CTIMEHIGH,
3600 &buffer->ctime.dwHighDateTime);
3602 StorageUtl_ReadDWord(
3603 currentEntry,
3604 OFFSET_PS_MTIMELOW,
3605 &buffer->mtime.dwLowDateTime);
3607 StorageUtl_ReadDWord(
3608 currentEntry,
3609 OFFSET_PS_MTIMEHIGH,
3610 &buffer->mtime.dwHighDateTime);
3612 StorageUtl_ReadDWord(
3613 currentEntry,
3614 OFFSET_PS_STARTBLOCK,
3615 &buffer->startingBlock);
3617 StorageUtl_ReadDWord(
3618 currentEntry,
3619 OFFSET_PS_SIZE,
3620 &buffer->size.u.LowPart);
3622 buffer->size.u.HighPart = 0;
3625 return readRes;
3628 /*********************************************************************
3629 * Write the specified directory entry to the file
3631 HRESULT StorageImpl_WriteDirEntry(
3632 StorageImpl* This,
3633 DirRef index,
3634 const DirEntry* buffer)
3636 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3637 HRESULT writeRes;
3639 UpdateRawDirEntry(currentEntry, buffer);
3641 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3642 return writeRes;
3645 static BOOL StorageImpl_ReadBigBlock(
3646 StorageImpl* This,
3647 ULONG blockIndex,
3648 void* buffer)
3650 ULARGE_INTEGER ulOffset;
3651 DWORD read;
3653 ulOffset.u.HighPart = 0;
3654 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3656 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3657 return (read == This->bigBlockSize);
3660 static BOOL StorageImpl_ReadDWordFromBigBlock(
3661 StorageImpl* This,
3662 ULONG blockIndex,
3663 ULONG offset,
3664 DWORD* value)
3666 ULARGE_INTEGER ulOffset;
3667 DWORD read;
3668 DWORD tmp;
3670 ulOffset.u.HighPart = 0;
3671 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3672 ulOffset.u.LowPart += offset;
3674 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3675 *value = lendian32toh(tmp);
3676 return (read == sizeof(DWORD));
3679 static BOOL StorageImpl_WriteBigBlock(
3680 StorageImpl* This,
3681 ULONG blockIndex,
3682 const void* buffer)
3684 ULARGE_INTEGER ulOffset;
3685 DWORD wrote;
3687 ulOffset.u.HighPart = 0;
3688 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3690 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3691 return (wrote == This->bigBlockSize);
3694 static BOOL StorageImpl_WriteDWordToBigBlock(
3695 StorageImpl* This,
3696 ULONG blockIndex,
3697 ULONG offset,
3698 DWORD value)
3700 ULARGE_INTEGER ulOffset;
3701 DWORD wrote;
3703 ulOffset.u.HighPart = 0;
3704 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3705 ulOffset.u.LowPart += offset;
3707 value = htole32(value);
3708 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3709 return (wrote == sizeof(DWORD));
3712 /******************************************************************************
3713 * Storage32Impl_SmallBlocksToBigBlocks
3715 * This method will convert a small block chain to a big block chain.
3716 * The small block chain will be destroyed.
3718 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3719 StorageImpl* This,
3720 SmallBlockChainStream** ppsbChain)
3722 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3723 ULARGE_INTEGER size, offset;
3724 ULONG cbRead, cbWritten;
3725 ULARGE_INTEGER cbTotalRead;
3726 DirRef streamEntryRef;
3727 HRESULT resWrite = S_OK;
3728 HRESULT resRead;
3729 DirEntry streamEntry;
3730 BYTE *buffer;
3731 BlockChainStream *bbTempChain = NULL;
3732 BlockChainStream *bigBlockChain = NULL;
3735 * Create a temporary big block chain that doesn't have
3736 * an associated directory entry. This temporary chain will be
3737 * used to copy data from small blocks to big blocks.
3739 bbTempChain = BlockChainStream_Construct(This,
3740 &bbHeadOfChain,
3741 DIRENTRY_NULL);
3742 if(!bbTempChain) return NULL;
3744 * Grow the big block chain.
3746 size = SmallBlockChainStream_GetSize(*ppsbChain);
3747 BlockChainStream_SetSize(bbTempChain, size);
3750 * Copy the contents of the small block chain to the big block chain
3751 * by small block size increments.
3753 offset.u.LowPart = 0;
3754 offset.u.HighPart = 0;
3755 cbTotalRead.QuadPart = 0;
3757 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3760 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3761 offset,
3762 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3763 buffer,
3764 &cbRead);
3765 if (FAILED(resRead))
3766 break;
3768 if (cbRead > 0)
3770 cbTotalRead.QuadPart += cbRead;
3772 resWrite = BlockChainStream_WriteAt(bbTempChain,
3773 offset,
3774 cbRead,
3775 buffer,
3776 &cbWritten);
3778 if (FAILED(resWrite))
3779 break;
3781 offset.u.LowPart += cbRead;
3783 } while (cbTotalRead.QuadPart < size.QuadPart);
3784 HeapFree(GetProcessHeap(),0,buffer);
3786 size.u.HighPart = 0;
3787 size.u.LowPart = 0;
3789 if (FAILED(resRead) || FAILED(resWrite))
3791 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3792 BlockChainStream_SetSize(bbTempChain, size);
3793 BlockChainStream_Destroy(bbTempChain);
3794 return NULL;
3798 * Destroy the small block chain.
3800 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3801 SmallBlockChainStream_SetSize(*ppsbChain, size);
3802 SmallBlockChainStream_Destroy(*ppsbChain);
3803 *ppsbChain = 0;
3806 * Change the directory entry. This chain is now a big block chain
3807 * and it doesn't reside in the small blocks chain anymore.
3809 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3811 streamEntry.startingBlock = bbHeadOfChain;
3813 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3816 * Destroy the temporary entryless big block chain.
3817 * Create a new big block chain associated with this entry.
3819 BlockChainStream_Destroy(bbTempChain);
3820 bigBlockChain = BlockChainStream_Construct(This,
3821 NULL,
3822 streamEntryRef);
3824 return bigBlockChain;
3827 /******************************************************************************
3828 * Storage32Impl_BigBlocksToSmallBlocks
3830 * This method will convert a big block chain to a small block chain.
3831 * The big block chain will be destroyed on success.
3833 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3834 StorageImpl* This,
3835 BlockChainStream** ppbbChain)
3837 ULARGE_INTEGER size, offset, cbTotalRead;
3838 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3839 DirRef streamEntryRef;
3840 HRESULT resWrite = S_OK, resRead;
3841 DirEntry streamEntry;
3842 BYTE* buffer;
3843 SmallBlockChainStream* sbTempChain;
3845 TRACE("%p %p\n", This, ppbbChain);
3847 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3848 DIRENTRY_NULL);
3850 if(!sbTempChain)
3851 return NULL;
3853 size = BlockChainStream_GetSize(*ppbbChain);
3854 SmallBlockChainStream_SetSize(sbTempChain, size);
3856 offset.u.HighPart = 0;
3857 offset.u.LowPart = 0;
3858 cbTotalRead.QuadPart = 0;
3859 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3862 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3863 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3864 buffer, &cbRead);
3866 if(FAILED(resRead))
3867 break;
3869 if(cbRead > 0)
3871 cbTotalRead.QuadPart += cbRead;
3873 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3874 cbRead, buffer, &cbWritten);
3876 if(FAILED(resWrite))
3877 break;
3879 offset.u.LowPart += cbRead;
3881 }while(cbTotalRead.QuadPart < size.QuadPart);
3882 HeapFree(GetProcessHeap(), 0, buffer);
3884 size.u.HighPart = 0;
3885 size.u.LowPart = 0;
3887 if(FAILED(resRead) || FAILED(resWrite))
3889 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3890 SmallBlockChainStream_SetSize(sbTempChain, size);
3891 SmallBlockChainStream_Destroy(sbTempChain);
3892 return NULL;
3895 /* destroy the original big block chain */
3896 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3897 BlockChainStream_SetSize(*ppbbChain, size);
3898 BlockChainStream_Destroy(*ppbbChain);
3899 *ppbbChain = NULL;
3901 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3902 streamEntry.startingBlock = sbHeadOfChain;
3903 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3905 SmallBlockChainStream_Destroy(sbTempChain);
3906 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3909 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
3911 HRESULT hr;
3912 DirEntry parentData, snapshotData;
3914 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
3915 0, (IStorage**)snapshot);
3917 if (SUCCEEDED(hr))
3919 hr = StorageBaseImpl_ReadDirEntry(original,
3920 original->storageDirEntry, &parentData);
3922 if (SUCCEEDED(hr))
3923 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
3924 (*snapshot)->storageDirEntry, &snapshotData);
3926 if (SUCCEEDED(hr))
3928 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
3929 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
3930 snapshotData.stgType = parentData.stgType;
3931 snapshotData.clsid = parentData.clsid;
3932 snapshotData.ctime = parentData.ctime;
3933 snapshotData.mtime = parentData.mtime;
3934 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
3935 (*snapshot)->storageDirEntry, &snapshotData);
3938 if (SUCCEEDED(hr))
3939 hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
3940 (IStorage*)(*snapshot));
3942 if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
3945 return hr;
3948 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
3949 IStorage* iface,
3950 DWORD grfCommitFlags) /* [in] */
3952 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
3953 HRESULT hr;
3954 DirEntry data, tempStorageData, snapshotRootData;
3955 DirRef tempStorageEntry, oldDirRoot;
3956 StorageInternalImpl *tempStorage;
3958 TRACE("(%p,%x)\n", iface, grfCommitFlags);
3960 /* Cannot commit a read-only transacted storage */
3961 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
3962 return STG_E_ACCESSDENIED;
3964 /* To prevent data loss, we create the new structure in the file before we
3965 * delete the old one, so that in case of errors the old data is intact. We
3966 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
3967 * needed in the rare situation where we have just enough free disk space to
3968 * overwrite the existing data. */
3970 /* Create an orphaned storage in the parent for the new directory structure. */
3971 memset(&data, 0, sizeof(data));
3972 data.name[0] = 'D';
3973 data.sizeOfNameString = 1;
3974 data.stgType = STGTY_STORAGE;
3975 data.leftChild = DIRENTRY_NULL;
3976 data.rightChild = DIRENTRY_NULL;
3977 data.dirRootEntry = DIRENTRY_NULL;
3978 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
3980 if (FAILED(hr)) return hr;
3982 tempStorage = StorageInternalImpl_Construct(This->transactedParent,
3983 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
3984 if (tempStorage)
3986 hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
3987 (IStorage*)tempStorage);
3989 list_init(&tempStorage->ParentListEntry);
3991 IStorage_Release((IStorage*) tempStorage);
3993 else
3994 hr = E_OUTOFMEMORY;
3996 if (FAILED(hr))
3998 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
3999 return hr;
4002 /* Update the storage to use the new data in one step. */
4003 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4004 This->transactedParent->storageDirEntry, &data);
4006 if (SUCCEEDED(hr))
4008 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4009 tempStorageEntry, &tempStorageData);
4012 if (SUCCEEDED(hr))
4014 hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4015 This->snapshot->storageDirEntry, &snapshotRootData);
4018 if (SUCCEEDED(hr))
4020 oldDirRoot = data.dirRootEntry;
4021 data.dirRootEntry = tempStorageData.dirRootEntry;
4022 data.clsid = snapshotRootData.clsid;
4023 data.ctime = snapshotRootData.ctime;
4024 data.mtime = snapshotRootData.mtime;
4026 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4027 This->transactedParent->storageDirEntry, &data);
4030 if (SUCCEEDED(hr))
4032 /* Destroy the old now-orphaned data. */
4033 DestroyReachableEntries(This->transactedParent, oldDirRoot);
4034 StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4036 else
4038 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4041 return hr;
4044 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4045 IStorage* iface)
4047 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4048 StorageBaseImpl *newSnapshot;
4049 HRESULT hr;
4051 TRACE("(%p)\n", iface);
4053 /* Create a new copy of the parent data. */
4054 hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4055 if (FAILED(hr)) return hr;
4057 /* Destroy the open objects. */
4058 StorageBaseImpl_DeleteAll(&This->base);
4060 /* Replace our current snapshot. */
4061 IStorage_Release((IStorage*)This->snapshot);
4062 This->snapshot = newSnapshot;
4064 return S_OK;
4067 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4069 if (!This->reverted)
4071 TRACE("Storage invalidated (stg=%p)\n", This);
4073 This->reverted = 1;
4075 StorageBaseImpl_DeleteAll(This);
4079 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4081 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4083 TransactedSnapshotImpl_Invalidate(iface);
4085 IStorage_Release((IStorage*)This->transactedParent);
4087 IStorage_Release((IStorage*)This->snapshot);
4089 HeapFree(GetProcessHeap(), 0, This);
4092 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4093 const DirEntry *newData, DirRef *index)
4095 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4097 return StorageBaseImpl_CreateDirEntry(This->snapshot,
4098 newData, index);
4101 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4102 DirRef index, const DirEntry *data)
4104 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4106 return StorageBaseImpl_WriteDirEntry(This->snapshot,
4107 index, data);
4110 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4111 DirRef index, DirEntry *data)
4113 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4115 return StorageBaseImpl_ReadDirEntry(This->snapshot,
4116 index, data);
4119 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4120 DirRef index)
4122 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4124 return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4125 index);
4128 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4129 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4131 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4133 return StorageBaseImpl_StreamReadAt(This->snapshot,
4134 index, offset, size, buffer, bytesRead);
4137 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4138 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4140 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4142 return StorageBaseImpl_StreamWriteAt(This->snapshot,
4143 index, offset, size, buffer, bytesWritten);
4146 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4147 DirRef index, ULARGE_INTEGER newsize)
4149 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4151 return StorageBaseImpl_StreamSetSize(This->snapshot,
4152 index, newsize);
4155 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4157 StorageBaseImpl_QueryInterface,
4158 StorageBaseImpl_AddRef,
4159 StorageBaseImpl_Release,
4160 StorageBaseImpl_CreateStream,
4161 StorageBaseImpl_OpenStream,
4162 StorageBaseImpl_CreateStorage,
4163 StorageBaseImpl_OpenStorage,
4164 StorageBaseImpl_CopyTo,
4165 StorageBaseImpl_MoveElementTo,
4166 TransactedSnapshotImpl_Commit,
4167 TransactedSnapshotImpl_Revert,
4168 StorageBaseImpl_EnumElements,
4169 StorageBaseImpl_DestroyElement,
4170 StorageBaseImpl_RenameElement,
4171 StorageBaseImpl_SetElementTimes,
4172 StorageBaseImpl_SetClass,
4173 StorageBaseImpl_SetStateBits,
4174 StorageBaseImpl_Stat
4177 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4179 TransactedSnapshotImpl_Destroy,
4180 TransactedSnapshotImpl_Invalidate,
4181 TransactedSnapshotImpl_CreateDirEntry,
4182 TransactedSnapshotImpl_WriteDirEntry,
4183 TransactedSnapshotImpl_ReadDirEntry,
4184 TransactedSnapshotImpl_DestroyDirEntry,
4185 TransactedSnapshotImpl_StreamReadAt,
4186 TransactedSnapshotImpl_StreamWriteAt,
4187 TransactedSnapshotImpl_StreamSetSize
4190 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4191 TransactedSnapshotImpl** result)
4193 HRESULT hr;
4195 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4196 if (*result)
4198 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4200 /* This is OK because the property set storage functions use the IStorage functions. */
4201 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4203 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4205 list_init(&(*result)->base.strmHead);
4207 list_init(&(*result)->base.storageHead);
4209 (*result)->base.ref = 1;
4211 (*result)->base.openFlags = parentStorage->openFlags;
4213 (*result)->base.filename = parentStorage->filename;
4215 /* Create a new temporary storage to act as the snapshot */
4216 hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4218 if (SUCCEEDED(hr))
4220 (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4222 /* parentStorage already has 1 reference, which we take over here. */
4223 (*result)->transactedParent = parentStorage;
4225 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4228 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4230 return hr;
4232 else
4233 return E_OUTOFMEMORY;
4236 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4237 StorageBaseImpl** result)
4239 static int fixme=0;
4241 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4243 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4246 return TransactedSnapshotImpl_Construct(parentStorage,
4247 (TransactedSnapshotImpl**)result);
4250 static HRESULT Storage_Construct(
4251 HANDLE hFile,
4252 LPCOLESTR pwcsName,
4253 ILockBytes* pLkbyt,
4254 DWORD openFlags,
4255 BOOL fileBased,
4256 BOOL create,
4257 StorageBaseImpl** result)
4259 StorageImpl *newStorage;
4260 StorageBaseImpl *newTransactedStorage;
4261 HRESULT hr;
4263 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, &newStorage);
4264 if (FAILED(hr)) goto end;
4266 if (openFlags & STGM_TRANSACTED)
4268 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4269 if (FAILED(hr))
4270 IStorage_Release((IStorage*)newStorage);
4271 else
4272 *result = newTransactedStorage;
4274 else
4275 *result = &newStorage->base;
4277 end:
4278 return hr;
4281 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4283 StorageInternalImpl* This = (StorageInternalImpl*) base;
4285 if (!This->base.reverted)
4287 TRACE("Storage invalidated (stg=%p)\n", This);
4289 This->base.reverted = 1;
4291 This->parentStorage = NULL;
4293 StorageBaseImpl_DeleteAll(&This->base);
4295 list_remove(&This->ParentListEntry);
4299 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4301 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4303 StorageInternalImpl_Invalidate(&This->base);
4305 HeapFree(GetProcessHeap(), 0, This);
4308 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4309 const DirEntry *newData, DirRef *index)
4311 StorageInternalImpl* This = (StorageInternalImpl*) base;
4313 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4314 newData, index);
4317 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4318 DirRef index, const DirEntry *data)
4320 StorageInternalImpl* This = (StorageInternalImpl*) base;
4322 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4323 index, data);
4326 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4327 DirRef index, DirEntry *data)
4329 StorageInternalImpl* This = (StorageInternalImpl*) base;
4331 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4332 index, data);
4335 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4336 DirRef index)
4338 StorageInternalImpl* This = (StorageInternalImpl*) base;
4340 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4341 index);
4344 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4345 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4347 StorageInternalImpl* This = (StorageInternalImpl*) base;
4349 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4350 index, offset, size, buffer, bytesRead);
4353 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4354 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4356 StorageInternalImpl* This = (StorageInternalImpl*) base;
4358 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4359 index, offset, size, buffer, bytesWritten);
4362 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4363 DirRef index, ULARGE_INTEGER newsize)
4365 StorageInternalImpl* This = (StorageInternalImpl*) base;
4367 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4368 index, newsize);
4371 /******************************************************************************
4373 ** Storage32InternalImpl_Commit
4376 static HRESULT WINAPI StorageInternalImpl_Commit(
4377 IStorage* iface,
4378 DWORD grfCommitFlags) /* [in] */
4380 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4381 return S_OK;
4384 /******************************************************************************
4386 ** Storage32InternalImpl_Revert
4389 static HRESULT WINAPI StorageInternalImpl_Revert(
4390 IStorage* iface)
4392 FIXME("(%p): stub\n", iface);
4393 return S_OK;
4396 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4398 IStorage_Release((IStorage*)This->parentStorage);
4399 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
4400 HeapFree(GetProcessHeap(), 0, This);
4403 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4404 IEnumSTATSTG* iface,
4405 REFIID riid,
4406 void** ppvObject)
4408 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4410 if (ppvObject==0)
4411 return E_INVALIDARG;
4413 *ppvObject = 0;
4415 if (IsEqualGUID(&IID_IUnknown, riid) ||
4416 IsEqualGUID(&IID_IEnumSTATSTG, riid))
4418 *ppvObject = This;
4419 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4420 return S_OK;
4423 return E_NOINTERFACE;
4426 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
4427 IEnumSTATSTG* iface)
4429 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4430 return InterlockedIncrement(&This->ref);
4433 static ULONG WINAPI IEnumSTATSTGImpl_Release(
4434 IEnumSTATSTG* iface)
4436 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4438 ULONG newRef;
4440 newRef = InterlockedDecrement(&This->ref);
4442 if (newRef==0)
4444 IEnumSTATSTGImpl_Destroy(This);
4447 return newRef;
4450 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4451 IEnumSTATSTG* iface,
4452 ULONG celt,
4453 STATSTG* rgelt,
4454 ULONG* pceltFetched)
4456 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4458 DirEntry currentEntry;
4459 STATSTG* currentReturnStruct = rgelt;
4460 ULONG objectFetched = 0;
4461 DirRef currentSearchNode;
4463 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4464 return E_INVALIDARG;
4467 * To avoid the special case, get another pointer to a ULONG value if
4468 * the caller didn't supply one.
4470 if (pceltFetched==0)
4471 pceltFetched = &objectFetched;
4474 * Start the iteration, we will iterate until we hit the end of the
4475 * linked list or until we hit the number of items to iterate through
4477 *pceltFetched = 0;
4480 * Start with the node at the top of the stack.
4482 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4484 while ( ( *pceltFetched < celt) &&
4485 ( currentSearchNode!=DIRENTRY_NULL) )
4488 * Remove the top node from the stack
4490 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4493 * Read the entry from the storage.
4495 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4496 currentSearchNode,
4497 &currentEntry);
4500 * Copy the information to the return buffer.
4502 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4503 currentReturnStruct,
4504 &currentEntry,
4505 STATFLAG_DEFAULT);
4508 * Step to the next item in the iteration
4510 (*pceltFetched)++;
4511 currentReturnStruct++;
4514 * Push the next search node in the search stack.
4516 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
4519 * continue the iteration.
4521 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4524 if (*pceltFetched == celt)
4525 return S_OK;
4527 return S_FALSE;
4531 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4532 IEnumSTATSTG* iface,
4533 ULONG celt)
4535 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4537 DirEntry currentEntry;
4538 ULONG objectFetched = 0;
4539 DirRef currentSearchNode;
4542 * Start with the node at the top of the stack.
4544 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4546 while ( (objectFetched < celt) &&
4547 (currentSearchNode!=DIRENTRY_NULL) )
4550 * Remove the top node from the stack
4552 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4555 * Read the entry from the storage.
4557 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4558 currentSearchNode,
4559 &currentEntry);
4562 * Step to the next item in the iteration
4564 objectFetched++;
4567 * Push the next search node in the search stack.
4569 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
4572 * continue the iteration.
4574 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4577 if (objectFetched == celt)
4578 return S_OK;
4580 return S_FALSE;
4583 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4584 IEnumSTATSTG* iface)
4586 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4588 DirEntry storageEntry;
4589 HRESULT hr;
4592 * Re-initialize the search stack to an empty stack
4594 This->stackSize = 0;
4597 * Read the storage entry from the top-level storage.
4599 hr = StorageBaseImpl_ReadDirEntry(
4600 This->parentStorage,
4601 This->storageDirEntry,
4602 &storageEntry);
4604 if (SUCCEEDED(hr))
4606 assert(storageEntry.sizeOfNameString!=0);
4609 * Push the search node in the search stack.
4611 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.dirRootEntry);
4614 return hr;
4617 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4618 IEnumSTATSTG* iface,
4619 IEnumSTATSTG** ppenum)
4621 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4623 IEnumSTATSTGImpl* newClone;
4626 * Perform a sanity check on the parameters.
4628 if (ppenum==0)
4629 return E_INVALIDARG;
4631 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4632 This->storageDirEntry);
4636 * The new clone enumeration must point to the same current node as
4637 * the ole one.
4639 newClone->stackSize = This->stackSize ;
4640 newClone->stackMaxSize = This->stackMaxSize ;
4641 newClone->stackToVisit =
4642 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
4644 memcpy(
4645 newClone->stackToVisit,
4646 This->stackToVisit,
4647 sizeof(DirRef) * newClone->stackSize);
4649 *ppenum = (IEnumSTATSTG*)newClone;
4652 * Don't forget to nail down a reference to the clone before
4653 * returning it.
4655 IEnumSTATSTGImpl_AddRef(*ppenum);
4657 return S_OK;
4660 static void IEnumSTATSTGImpl_PushSearchNode(
4661 IEnumSTATSTGImpl* This,
4662 DirRef nodeToPush)
4664 DirEntry storageEntry;
4665 HRESULT hr;
4668 * First, make sure we're not trying to push an unexisting node.
4670 if (nodeToPush==DIRENTRY_NULL)
4671 return;
4674 * First push the node to the stack
4676 if (This->stackSize == This->stackMaxSize)
4678 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4680 This->stackToVisit = HeapReAlloc(
4681 GetProcessHeap(),
4683 This->stackToVisit,
4684 sizeof(DirRef) * This->stackMaxSize);
4687 This->stackToVisit[This->stackSize] = nodeToPush;
4688 This->stackSize++;
4691 * Read the storage entry from the top-level storage.
4693 hr = StorageBaseImpl_ReadDirEntry(
4694 This->parentStorage,
4695 nodeToPush,
4696 &storageEntry);
4698 if (SUCCEEDED(hr))
4700 assert(storageEntry.sizeOfNameString!=0);
4703 * Push the previous search node in the search stack.
4705 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.leftChild);
4709 static DirRef IEnumSTATSTGImpl_PopSearchNode(
4710 IEnumSTATSTGImpl* This,
4711 BOOL remove)
4713 DirRef topNode;
4715 if (This->stackSize == 0)
4716 return DIRENTRY_NULL;
4718 topNode = This->stackToVisit[This->stackSize-1];
4720 if (remove)
4721 This->stackSize--;
4723 return topNode;
4727 * Virtual function table for the IEnumSTATSTGImpl class.
4729 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4731 IEnumSTATSTGImpl_QueryInterface,
4732 IEnumSTATSTGImpl_AddRef,
4733 IEnumSTATSTGImpl_Release,
4734 IEnumSTATSTGImpl_Next,
4735 IEnumSTATSTGImpl_Skip,
4736 IEnumSTATSTGImpl_Reset,
4737 IEnumSTATSTGImpl_Clone
4740 /******************************************************************************
4741 ** IEnumSTATSTGImpl implementation
4744 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4745 StorageBaseImpl* parentStorage,
4746 DirRef storageDirEntry)
4748 IEnumSTATSTGImpl* newEnumeration;
4750 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4752 if (newEnumeration!=0)
4755 * Set-up the virtual function table and reference count.
4757 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4758 newEnumeration->ref = 0;
4761 * We want to nail-down the reference to the storage in case the
4762 * enumeration out-lives the storage in the client application.
4764 newEnumeration->parentStorage = parentStorage;
4765 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4767 newEnumeration->storageDirEntry = storageDirEntry;
4770 * Initialize the search stack
4772 newEnumeration->stackSize = 0;
4773 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4774 newEnumeration->stackToVisit =
4775 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef)*ENUMSTATSGT_SIZE_INCREMENT);
4778 * Make sure the current node of the iterator is the first one.
4780 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4783 return newEnumeration;
4787 * Virtual function table for the Storage32InternalImpl class.
4789 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4791 StorageBaseImpl_QueryInterface,
4792 StorageBaseImpl_AddRef,
4793 StorageBaseImpl_Release,
4794 StorageBaseImpl_CreateStream,
4795 StorageBaseImpl_OpenStream,
4796 StorageBaseImpl_CreateStorage,
4797 StorageBaseImpl_OpenStorage,
4798 StorageBaseImpl_CopyTo,
4799 StorageBaseImpl_MoveElementTo,
4800 StorageInternalImpl_Commit,
4801 StorageInternalImpl_Revert,
4802 StorageBaseImpl_EnumElements,
4803 StorageBaseImpl_DestroyElement,
4804 StorageBaseImpl_RenameElement,
4805 StorageBaseImpl_SetElementTimes,
4806 StorageBaseImpl_SetClass,
4807 StorageBaseImpl_SetStateBits,
4808 StorageBaseImpl_Stat
4811 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4813 StorageInternalImpl_Destroy,
4814 StorageInternalImpl_Invalidate,
4815 StorageInternalImpl_CreateDirEntry,
4816 StorageInternalImpl_WriteDirEntry,
4817 StorageInternalImpl_ReadDirEntry,
4818 StorageInternalImpl_DestroyDirEntry,
4819 StorageInternalImpl_StreamReadAt,
4820 StorageInternalImpl_StreamWriteAt,
4821 StorageInternalImpl_StreamSetSize
4824 /******************************************************************************
4825 ** Storage32InternalImpl implementation
4828 static StorageInternalImpl* StorageInternalImpl_Construct(
4829 StorageBaseImpl* parentStorage,
4830 DWORD openFlags,
4831 DirRef storageDirEntry)
4833 StorageInternalImpl* newStorage;
4835 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4837 if (newStorage!=0)
4839 list_init(&newStorage->base.strmHead);
4841 list_init(&newStorage->base.storageHead);
4844 * Initialize the virtual function table.
4846 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4847 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4848 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4850 newStorage->base.reverted = 0;
4852 newStorage->base.ref = 1;
4854 newStorage->parentStorage = parentStorage;
4857 * Keep a reference to the directory entry of this storage
4859 newStorage->base.storageDirEntry = storageDirEntry;
4861 newStorage->base.create = 0;
4863 return newStorage;
4866 return 0;
4869 /******************************************************************************
4870 ** StorageUtl implementation
4873 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4875 WORD tmp;
4877 memcpy(&tmp, buffer+offset, sizeof(WORD));
4878 *value = lendian16toh(tmp);
4881 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4883 value = htole16(value);
4884 memcpy(buffer+offset, &value, sizeof(WORD));
4887 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4889 DWORD tmp;
4891 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4892 *value = lendian32toh(tmp);
4895 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4897 value = htole32(value);
4898 memcpy(buffer+offset, &value, sizeof(DWORD));
4901 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4902 ULARGE_INTEGER* value)
4904 #ifdef WORDS_BIGENDIAN
4905 ULARGE_INTEGER tmp;
4907 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4908 value->u.LowPart = htole32(tmp.u.HighPart);
4909 value->u.HighPart = htole32(tmp.u.LowPart);
4910 #else
4911 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4912 #endif
4915 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4916 const ULARGE_INTEGER *value)
4918 #ifdef WORDS_BIGENDIAN
4919 ULARGE_INTEGER tmp;
4921 tmp.u.LowPart = htole32(value->u.HighPart);
4922 tmp.u.HighPart = htole32(value->u.LowPart);
4923 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4924 #else
4925 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4926 #endif
4929 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4931 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4932 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4933 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4935 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4938 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4940 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4941 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4942 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4944 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4947 void StorageUtl_CopyDirEntryToSTATSTG(
4948 StorageBaseImpl* storage,
4949 STATSTG* destination,
4950 const DirEntry* source,
4951 int statFlags)
4953 LPCWSTR entryName;
4955 if (source->stgType == STGTY_ROOT)
4957 /* replace the name of root entry (often "Root Entry") by the file name */
4958 entryName = storage->filename;
4960 else
4962 entryName = source->name;
4966 * The copy of the string occurs only when the flag is not set
4968 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4969 (entryName == NULL) ||
4970 (entryName[0] == 0) )
4972 destination->pwcsName = 0;
4974 else
4976 destination->pwcsName =
4977 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4979 strcpyW(destination->pwcsName, entryName);
4982 switch (source->stgType)
4984 case STGTY_STORAGE:
4985 case STGTY_ROOT:
4986 destination->type = STGTY_STORAGE;
4987 break;
4988 case STGTY_STREAM:
4989 destination->type = STGTY_STREAM;
4990 break;
4991 default:
4992 destination->type = STGTY_STREAM;
4993 break;
4996 destination->cbSize = source->size;
4998 currentReturnStruct->mtime = {0}; TODO
4999 currentReturnStruct->ctime = {0};
5000 currentReturnStruct->atime = {0};
5002 destination->grfMode = 0;
5003 destination->grfLocksSupported = 0;
5004 destination->clsid = source->clsid;
5005 destination->grfStateBits = 0;
5006 destination->reserved = 0;
5009 /******************************************************************************
5010 ** BlockChainStream implementation
5013 BlockChainStream* BlockChainStream_Construct(
5014 StorageImpl* parentStorage,
5015 ULONG* headOfStreamPlaceHolder,
5016 DirRef dirEntry)
5018 BlockChainStream* newStream;
5019 ULONG blockIndex;
5021 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5023 newStream->parentStorage = parentStorage;
5024 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5025 newStream->ownerDirEntry = dirEntry;
5026 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
5027 newStream->tailIndex = BLOCK_END_OF_CHAIN;
5028 newStream->numBlocks = 0;
5030 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
5032 while (blockIndex != BLOCK_END_OF_CHAIN)
5034 newStream->numBlocks++;
5035 newStream->tailIndex = blockIndex;
5037 if(FAILED(StorageImpl_GetNextBlockInChain(
5038 parentStorage,
5039 blockIndex,
5040 &blockIndex)))
5042 HeapFree(GetProcessHeap(), 0, newStream);
5043 return NULL;
5047 return newStream;
5050 void BlockChainStream_Destroy(BlockChainStream* This)
5052 HeapFree(GetProcessHeap(), 0, This);
5055 /******************************************************************************
5056 * BlockChainStream_GetHeadOfChain
5058 * Returns the head of this stream chain.
5059 * Some special chains don't have directory entries, their heads are kept in
5060 * This->headOfStreamPlaceHolder.
5063 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5065 DirEntry chainEntry;
5066 HRESULT hr;
5068 if (This->headOfStreamPlaceHolder != 0)
5069 return *(This->headOfStreamPlaceHolder);
5071 if (This->ownerDirEntry != DIRENTRY_NULL)
5073 hr = StorageImpl_ReadDirEntry(
5074 This->parentStorage,
5075 This->ownerDirEntry,
5076 &chainEntry);
5078 if (SUCCEEDED(hr))
5080 return chainEntry.startingBlock;
5084 return BLOCK_END_OF_CHAIN;
5087 /******************************************************************************
5088 * BlockChainStream_GetCount
5090 * Returns the number of blocks that comprises this chain.
5091 * This is not the size of the stream as the last block may not be full!
5094 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5096 ULONG blockIndex;
5097 ULONG count = 0;
5099 blockIndex = BlockChainStream_GetHeadOfChain(This);
5101 while (blockIndex != BLOCK_END_OF_CHAIN)
5103 count++;
5105 if(FAILED(StorageImpl_GetNextBlockInChain(
5106 This->parentStorage,
5107 blockIndex,
5108 &blockIndex)))
5109 return 0;
5112 return count;
5115 /******************************************************************************
5116 * BlockChainStream_ReadAt
5118 * Reads a specified number of bytes from this chain at the specified offset.
5119 * bytesRead may be NULL.
5120 * Failure will be returned if the specified number of bytes has not been read.
5122 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5123 ULARGE_INTEGER offset,
5124 ULONG size,
5125 void* buffer,
5126 ULONG* bytesRead)
5128 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5129 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5130 ULONG bytesToReadInBuffer;
5131 ULONG blockIndex;
5132 BYTE* bufferWalker;
5134 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5137 * Find the first block in the stream that contains part of the buffer.
5139 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5140 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5141 (blockNoInSequence < This->lastBlockNoInSequence) )
5143 blockIndex = BlockChainStream_GetHeadOfChain(This);
5144 This->lastBlockNoInSequence = blockNoInSequence;
5146 else
5148 ULONG temp = blockNoInSequence;
5150 blockIndex = This->lastBlockNoInSequenceIndex;
5151 blockNoInSequence -= This->lastBlockNoInSequence;
5152 This->lastBlockNoInSequence = temp;
5155 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5157 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5158 return STG_E_DOCFILECORRUPT;
5159 blockNoInSequence--;
5162 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5163 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5165 This->lastBlockNoInSequenceIndex = blockIndex;
5168 * Start reading the buffer.
5170 *bytesRead = 0;
5171 bufferWalker = buffer;
5173 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5175 ULARGE_INTEGER ulOffset;
5176 DWORD bytesReadAt;
5178 * Calculate how many bytes we can copy from this big block.
5180 bytesToReadInBuffer =
5181 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5183 TRACE("block %i\n",blockIndex);
5184 ulOffset.u.HighPart = 0;
5185 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5186 offsetInBlock;
5188 StorageImpl_ReadAt(This->parentStorage,
5189 ulOffset,
5190 bufferWalker,
5191 bytesToReadInBuffer,
5192 &bytesReadAt);
5194 * Step to the next big block.
5196 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5197 return STG_E_DOCFILECORRUPT;
5199 bufferWalker += bytesReadAt;
5200 size -= bytesReadAt;
5201 *bytesRead += bytesReadAt;
5202 offsetInBlock = 0; /* There is no offset on the next block */
5204 if (bytesToReadInBuffer != bytesReadAt)
5205 break;
5208 return (size == 0) ? S_OK : STG_E_READFAULT;
5211 /******************************************************************************
5212 * BlockChainStream_WriteAt
5214 * Writes the specified number of bytes to this chain at the specified offset.
5215 * Will fail if not all specified number of bytes have been written.
5217 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5218 ULARGE_INTEGER offset,
5219 ULONG size,
5220 const void* buffer,
5221 ULONG* bytesWritten)
5223 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5224 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5225 ULONG bytesToWrite;
5226 ULONG blockIndex;
5227 const BYTE* bufferWalker;
5230 * Find the first block in the stream that contains part of the buffer.
5232 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5233 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5234 (blockNoInSequence < This->lastBlockNoInSequence) )
5236 blockIndex = BlockChainStream_GetHeadOfChain(This);
5237 This->lastBlockNoInSequence = blockNoInSequence;
5239 else
5241 ULONG temp = blockNoInSequence;
5243 blockIndex = This->lastBlockNoInSequenceIndex;
5244 blockNoInSequence -= This->lastBlockNoInSequence;
5245 This->lastBlockNoInSequence = temp;
5248 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5250 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5251 &blockIndex)))
5252 return STG_E_DOCFILECORRUPT;
5253 blockNoInSequence--;
5256 This->lastBlockNoInSequenceIndex = blockIndex;
5258 /* BlockChainStream_SetSize should have already been called to ensure we have
5259 * enough blocks in the chain to write into */
5260 if (blockIndex == BLOCK_END_OF_CHAIN)
5262 ERR("not enough blocks in chain to write data\n");
5263 return STG_E_DOCFILECORRUPT;
5266 *bytesWritten = 0;
5267 bufferWalker = buffer;
5269 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5271 ULARGE_INTEGER ulOffset;
5272 DWORD bytesWrittenAt;
5274 * Calculate how many bytes we can copy from this big block.
5276 bytesToWrite =
5277 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5279 TRACE("block %i\n",blockIndex);
5280 ulOffset.u.HighPart = 0;
5281 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
5282 offsetInBlock;
5284 StorageImpl_WriteAt(This->parentStorage,
5285 ulOffset,
5286 bufferWalker,
5287 bytesToWrite,
5288 &bytesWrittenAt);
5291 * Step to the next big block.
5293 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5294 &blockIndex)))
5295 return STG_E_DOCFILECORRUPT;
5297 bufferWalker += bytesWrittenAt;
5298 size -= bytesWrittenAt;
5299 *bytesWritten += bytesWrittenAt;
5300 offsetInBlock = 0; /* There is no offset on the next block */
5302 if (bytesWrittenAt != bytesToWrite)
5303 break;
5306 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5309 /******************************************************************************
5310 * BlockChainStream_Shrink
5312 * Shrinks this chain in the big block depot.
5314 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5315 ULARGE_INTEGER newSize)
5317 ULONG blockIndex, extraBlock;
5318 ULONG numBlocks;
5319 ULONG count = 1;
5322 * Reset the last accessed block cache.
5324 This->lastBlockNoInSequence = 0xFFFFFFFF;
5325 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5328 * Figure out how many blocks are needed to contain the new size
5330 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5332 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5333 numBlocks++;
5335 blockIndex = BlockChainStream_GetHeadOfChain(This);
5338 * Go to the new end of chain
5340 while (count < numBlocks)
5342 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5343 &blockIndex)))
5344 return FALSE;
5345 count++;
5348 /* Get the next block before marking the new end */
5349 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5350 &extraBlock)))
5351 return FALSE;
5353 /* Mark the new end of chain */
5354 StorageImpl_SetNextBlockInChain(
5355 This->parentStorage,
5356 blockIndex,
5357 BLOCK_END_OF_CHAIN);
5359 This->tailIndex = blockIndex;
5360 This->numBlocks = numBlocks;
5363 * Mark the extra blocks as free
5365 while (extraBlock != BLOCK_END_OF_CHAIN)
5367 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5368 &blockIndex)))
5369 return FALSE;
5370 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5371 extraBlock = blockIndex;
5374 return TRUE;
5377 /******************************************************************************
5378 * BlockChainStream_Enlarge
5380 * Grows this chain in the big block depot.
5382 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5383 ULARGE_INTEGER newSize)
5385 ULONG blockIndex, currentBlock;
5386 ULONG newNumBlocks;
5387 ULONG oldNumBlocks = 0;
5389 blockIndex = BlockChainStream_GetHeadOfChain(This);
5392 * Empty chain. Create the head.
5394 if (blockIndex == BLOCK_END_OF_CHAIN)
5396 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5397 StorageImpl_SetNextBlockInChain(This->parentStorage,
5398 blockIndex,
5399 BLOCK_END_OF_CHAIN);
5401 if (This->headOfStreamPlaceHolder != 0)
5403 *(This->headOfStreamPlaceHolder) = blockIndex;
5405 else
5407 DirEntry chainEntry;
5408 assert(This->ownerDirEntry != DIRENTRY_NULL);
5410 StorageImpl_ReadDirEntry(
5411 This->parentStorage,
5412 This->ownerDirEntry,
5413 &chainEntry);
5415 chainEntry.startingBlock = blockIndex;
5417 StorageImpl_WriteDirEntry(
5418 This->parentStorage,
5419 This->ownerDirEntry,
5420 &chainEntry);
5423 This->tailIndex = blockIndex;
5424 This->numBlocks = 1;
5428 * Figure out how many blocks are needed to contain this stream
5430 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5432 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5433 newNumBlocks++;
5436 * Go to the current end of chain
5438 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5440 currentBlock = blockIndex;
5442 while (blockIndex != BLOCK_END_OF_CHAIN)
5444 This->numBlocks++;
5445 currentBlock = blockIndex;
5447 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5448 &blockIndex)))
5449 return FALSE;
5452 This->tailIndex = currentBlock;
5455 currentBlock = This->tailIndex;
5456 oldNumBlocks = This->numBlocks;
5459 * Add new blocks to the chain
5461 if (oldNumBlocks < newNumBlocks)
5463 while (oldNumBlocks < newNumBlocks)
5465 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5467 StorageImpl_SetNextBlockInChain(
5468 This->parentStorage,
5469 currentBlock,
5470 blockIndex);
5472 StorageImpl_SetNextBlockInChain(
5473 This->parentStorage,
5474 blockIndex,
5475 BLOCK_END_OF_CHAIN);
5477 currentBlock = blockIndex;
5478 oldNumBlocks++;
5481 This->tailIndex = blockIndex;
5482 This->numBlocks = newNumBlocks;
5485 return TRUE;
5488 /******************************************************************************
5489 * BlockChainStream_SetSize
5491 * Sets the size of this stream. The big block depot will be updated.
5492 * The file will grow if we grow the chain.
5494 * TODO: Free the actual blocks in the file when we shrink the chain.
5495 * Currently, the blocks are still in the file. So the file size
5496 * doesn't shrink even if we shrink streams.
5498 BOOL BlockChainStream_SetSize(
5499 BlockChainStream* This,
5500 ULARGE_INTEGER newSize)
5502 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5504 if (newSize.u.LowPart == size.u.LowPart)
5505 return TRUE;
5507 if (newSize.u.LowPart < size.u.LowPart)
5509 BlockChainStream_Shrink(This, newSize);
5511 else
5513 BlockChainStream_Enlarge(This, newSize);
5516 return TRUE;
5519 /******************************************************************************
5520 * BlockChainStream_GetSize
5522 * Returns the size of this chain.
5523 * Will return the block count if this chain doesn't have a directory entry.
5525 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5527 DirEntry chainEntry;
5529 if(This->headOfStreamPlaceHolder == NULL)
5532 * This chain has a directory entry so use the size value from there.
5534 StorageImpl_ReadDirEntry(
5535 This->parentStorage,
5536 This->ownerDirEntry,
5537 &chainEntry);
5539 return chainEntry.size;
5541 else
5544 * this chain is a chain that does not have a directory entry, figure out the
5545 * size by making the product number of used blocks times the
5546 * size of them
5548 ULARGE_INTEGER result;
5549 result.u.HighPart = 0;
5551 result.u.LowPart =
5552 BlockChainStream_GetCount(This) *
5553 This->parentStorage->bigBlockSize;
5555 return result;
5559 /******************************************************************************
5560 ** SmallBlockChainStream implementation
5563 SmallBlockChainStream* SmallBlockChainStream_Construct(
5564 StorageImpl* parentStorage,
5565 ULONG* headOfStreamPlaceHolder,
5566 DirRef dirEntry)
5568 SmallBlockChainStream* newStream;
5570 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5572 newStream->parentStorage = parentStorage;
5573 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5574 newStream->ownerDirEntry = dirEntry;
5576 return newStream;
5579 void SmallBlockChainStream_Destroy(
5580 SmallBlockChainStream* This)
5582 HeapFree(GetProcessHeap(), 0, This);
5585 /******************************************************************************
5586 * SmallBlockChainStream_GetHeadOfChain
5588 * Returns the head of this chain of small blocks.
5590 static ULONG SmallBlockChainStream_GetHeadOfChain(
5591 SmallBlockChainStream* This)
5593 DirEntry chainEntry;
5594 HRESULT hr;
5596 if (This->headOfStreamPlaceHolder != NULL)
5597 return *(This->headOfStreamPlaceHolder);
5599 if (This->ownerDirEntry)
5601 hr = StorageImpl_ReadDirEntry(
5602 This->parentStorage,
5603 This->ownerDirEntry,
5604 &chainEntry);
5606 if (SUCCEEDED(hr))
5608 return chainEntry.startingBlock;
5613 return BLOCK_END_OF_CHAIN;
5616 /******************************************************************************
5617 * SmallBlockChainStream_GetNextBlockInChain
5619 * Returns the index of the next small block in this chain.
5621 * Return Values:
5622 * - BLOCK_END_OF_CHAIN: end of this chain
5623 * - BLOCK_UNUSED: small block 'blockIndex' is free
5625 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5626 SmallBlockChainStream* This,
5627 ULONG blockIndex,
5628 ULONG* nextBlockInChain)
5630 ULARGE_INTEGER offsetOfBlockInDepot;
5631 DWORD buffer;
5632 ULONG bytesRead;
5633 HRESULT res;
5635 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5637 offsetOfBlockInDepot.u.HighPart = 0;
5638 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5641 * Read those bytes in the buffer from the small block file.
5643 res = BlockChainStream_ReadAt(
5644 This->parentStorage->smallBlockDepotChain,
5645 offsetOfBlockInDepot,
5646 sizeof(DWORD),
5647 &buffer,
5648 &bytesRead);
5650 if (SUCCEEDED(res))
5652 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5653 return S_OK;
5656 return res;
5659 /******************************************************************************
5660 * SmallBlockChainStream_SetNextBlockInChain
5662 * Writes the index of the next block of the specified block in the small
5663 * block depot.
5664 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5665 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5667 static void SmallBlockChainStream_SetNextBlockInChain(
5668 SmallBlockChainStream* This,
5669 ULONG blockIndex,
5670 ULONG nextBlock)
5672 ULARGE_INTEGER offsetOfBlockInDepot;
5673 DWORD buffer;
5674 ULONG bytesWritten;
5676 offsetOfBlockInDepot.u.HighPart = 0;
5677 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5679 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5682 * Read those bytes in the buffer from the small block file.
5684 BlockChainStream_WriteAt(
5685 This->parentStorage->smallBlockDepotChain,
5686 offsetOfBlockInDepot,
5687 sizeof(DWORD),
5688 &buffer,
5689 &bytesWritten);
5692 /******************************************************************************
5693 * SmallBlockChainStream_FreeBlock
5695 * Flag small block 'blockIndex' as free in the small block depot.
5697 static void SmallBlockChainStream_FreeBlock(
5698 SmallBlockChainStream* This,
5699 ULONG blockIndex)
5701 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5704 /******************************************************************************
5705 * SmallBlockChainStream_GetNextFreeBlock
5707 * Returns the index of a free small block. The small block depot will be
5708 * enlarged if necessary. The small block chain will also be enlarged if
5709 * necessary.
5711 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5712 SmallBlockChainStream* This)
5714 ULARGE_INTEGER offsetOfBlockInDepot;
5715 DWORD buffer;
5716 ULONG bytesRead;
5717 ULONG blockIndex = 0;
5718 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5719 HRESULT res = S_OK;
5720 ULONG smallBlocksPerBigBlock;
5722 offsetOfBlockInDepot.u.HighPart = 0;
5725 * Scan the small block depot for a free block
5727 while (nextBlockIndex != BLOCK_UNUSED)
5729 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5731 res = BlockChainStream_ReadAt(
5732 This->parentStorage->smallBlockDepotChain,
5733 offsetOfBlockInDepot,
5734 sizeof(DWORD),
5735 &buffer,
5736 &bytesRead);
5739 * If we run out of space for the small block depot, enlarge it
5741 if (SUCCEEDED(res))
5743 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5745 if (nextBlockIndex != BLOCK_UNUSED)
5746 blockIndex++;
5748 else
5750 ULONG count =
5751 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5753 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5754 ULONG nextBlock, newsbdIndex;
5755 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5757 nextBlock = sbdIndex;
5758 while (nextBlock != BLOCK_END_OF_CHAIN)
5760 sbdIndex = nextBlock;
5761 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5764 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5765 if (sbdIndex != BLOCK_END_OF_CHAIN)
5766 StorageImpl_SetNextBlockInChain(
5767 This->parentStorage,
5768 sbdIndex,
5769 newsbdIndex);
5771 StorageImpl_SetNextBlockInChain(
5772 This->parentStorage,
5773 newsbdIndex,
5774 BLOCK_END_OF_CHAIN);
5777 * Initialize all the small blocks to free
5779 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5780 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5782 if (count == 0)
5785 * We have just created the small block depot.
5787 DirEntry rootEntry;
5788 ULONG sbStartIndex;
5791 * Save it in the header
5793 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5794 StorageImpl_SaveFileHeader(This->parentStorage);
5797 * And allocate the first big block that will contain small blocks
5799 sbStartIndex =
5800 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5802 StorageImpl_SetNextBlockInChain(
5803 This->parentStorage,
5804 sbStartIndex,
5805 BLOCK_END_OF_CHAIN);
5807 StorageImpl_ReadDirEntry(
5808 This->parentStorage,
5809 This->parentStorage->base.storageDirEntry,
5810 &rootEntry);
5812 rootEntry.startingBlock = sbStartIndex;
5813 rootEntry.size.u.HighPart = 0;
5814 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5816 StorageImpl_WriteDirEntry(
5817 This->parentStorage,
5818 This->parentStorage->base.storageDirEntry,
5819 &rootEntry);
5821 else
5822 StorageImpl_SaveFileHeader(This->parentStorage);
5826 smallBlocksPerBigBlock =
5827 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5830 * Verify if we have to allocate big blocks to contain small blocks
5832 if (blockIndex % smallBlocksPerBigBlock == 0)
5834 DirEntry rootEntry;
5835 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5837 StorageImpl_ReadDirEntry(
5838 This->parentStorage,
5839 This->parentStorage->base.storageDirEntry,
5840 &rootEntry);
5842 if (rootEntry.size.u.LowPart <
5843 (blocksRequired * This->parentStorage->bigBlockSize))
5845 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5847 BlockChainStream_SetSize(
5848 This->parentStorage->smallBlockRootChain,
5849 rootEntry.size);
5851 StorageImpl_WriteDirEntry(
5852 This->parentStorage,
5853 This->parentStorage->base.storageDirEntry,
5854 &rootEntry);
5858 return blockIndex;
5861 /******************************************************************************
5862 * SmallBlockChainStream_ReadAt
5864 * Reads a specified number of bytes from this chain at the specified offset.
5865 * bytesRead may be NULL.
5866 * Failure will be returned if the specified number of bytes has not been read.
5868 HRESULT SmallBlockChainStream_ReadAt(
5869 SmallBlockChainStream* This,
5870 ULARGE_INTEGER offset,
5871 ULONG size,
5872 void* buffer,
5873 ULONG* bytesRead)
5875 HRESULT rc = S_OK;
5876 ULARGE_INTEGER offsetInBigBlockFile;
5877 ULONG blockNoInSequence =
5878 offset.u.LowPart / This->parentStorage->smallBlockSize;
5880 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5881 ULONG bytesToReadInBuffer;
5882 ULONG blockIndex;
5883 ULONG bytesReadFromBigBlockFile;
5884 BYTE* bufferWalker;
5887 * This should never happen on a small block file.
5889 assert(offset.u.HighPart==0);
5892 * Find the first block in the stream that contains part of the buffer.
5894 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5896 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5898 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5899 if(FAILED(rc))
5900 return rc;
5901 blockNoInSequence--;
5905 * Start reading the buffer.
5907 *bytesRead = 0;
5908 bufferWalker = buffer;
5910 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5913 * Calculate how many bytes we can copy from this small block.
5915 bytesToReadInBuffer =
5916 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5919 * Calculate the offset of the small block in the small block file.
5921 offsetInBigBlockFile.u.HighPart = 0;
5922 offsetInBigBlockFile.u.LowPart =
5923 blockIndex * This->parentStorage->smallBlockSize;
5925 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5928 * Read those bytes in the buffer from the small block file.
5929 * The small block has already been identified so it shouldn't fail
5930 * unless the file is corrupt.
5932 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5933 offsetInBigBlockFile,
5934 bytesToReadInBuffer,
5935 bufferWalker,
5936 &bytesReadFromBigBlockFile);
5938 if (FAILED(rc))
5939 return rc;
5942 * Step to the next big block.
5944 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5945 if(FAILED(rc))
5946 return STG_E_DOCFILECORRUPT;
5948 bufferWalker += bytesReadFromBigBlockFile;
5949 size -= bytesReadFromBigBlockFile;
5950 *bytesRead += bytesReadFromBigBlockFile;
5951 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5954 return (size == 0) ? S_OK : STG_E_READFAULT;
5957 /******************************************************************************
5958 * SmallBlockChainStream_WriteAt
5960 * Writes the specified number of bytes to this chain at the specified offset.
5961 * Will fail if not all specified number of bytes have been written.
5963 HRESULT SmallBlockChainStream_WriteAt(
5964 SmallBlockChainStream* This,
5965 ULARGE_INTEGER offset,
5966 ULONG size,
5967 const void* buffer,
5968 ULONG* bytesWritten)
5970 ULARGE_INTEGER offsetInBigBlockFile;
5971 ULONG blockNoInSequence =
5972 offset.u.LowPart / This->parentStorage->smallBlockSize;
5974 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5975 ULONG bytesToWriteInBuffer;
5976 ULONG blockIndex;
5977 ULONG bytesWrittenToBigBlockFile;
5978 const BYTE* bufferWalker;
5979 HRESULT res;
5982 * This should never happen on a small block file.
5984 assert(offset.u.HighPart==0);
5987 * Find the first block in the stream that contains part of the buffer.
5989 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5991 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5993 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5994 return STG_E_DOCFILECORRUPT;
5995 blockNoInSequence--;
5999 * Start writing the buffer.
6001 *bytesWritten = 0;
6002 bufferWalker = buffer;
6003 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6006 * Calculate how many bytes we can copy to this small block.
6008 bytesToWriteInBuffer =
6009 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6012 * Calculate the offset of the small block in the small block file.
6014 offsetInBigBlockFile.u.HighPart = 0;
6015 offsetInBigBlockFile.u.LowPart =
6016 blockIndex * This->parentStorage->smallBlockSize;
6018 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6021 * Write those bytes in the buffer to the small block file.
6023 res = BlockChainStream_WriteAt(
6024 This->parentStorage->smallBlockRootChain,
6025 offsetInBigBlockFile,
6026 bytesToWriteInBuffer,
6027 bufferWalker,
6028 &bytesWrittenToBigBlockFile);
6029 if (FAILED(res))
6030 return res;
6033 * Step to the next big block.
6035 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6036 &blockIndex)))
6037 return FALSE;
6038 bufferWalker += bytesWrittenToBigBlockFile;
6039 size -= bytesWrittenToBigBlockFile;
6040 *bytesWritten += bytesWrittenToBigBlockFile;
6041 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6044 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6047 /******************************************************************************
6048 * SmallBlockChainStream_Shrink
6050 * Shrinks this chain in the small block depot.
6052 static BOOL SmallBlockChainStream_Shrink(
6053 SmallBlockChainStream* This,
6054 ULARGE_INTEGER newSize)
6056 ULONG blockIndex, extraBlock;
6057 ULONG numBlocks;
6058 ULONG count = 0;
6060 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6062 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6063 numBlocks++;
6065 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6068 * Go to the new end of chain
6070 while (count < numBlocks)
6072 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6073 &blockIndex)))
6074 return FALSE;
6075 count++;
6079 * If the count is 0, we have a special case, the head of the chain was
6080 * just freed.
6082 if (count == 0)
6084 DirEntry chainEntry;
6086 StorageImpl_ReadDirEntry(This->parentStorage,
6087 This->ownerDirEntry,
6088 &chainEntry);
6090 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6092 StorageImpl_WriteDirEntry(This->parentStorage,
6093 This->ownerDirEntry,
6094 &chainEntry);
6097 * We start freeing the chain at the head block.
6099 extraBlock = blockIndex;
6101 else
6103 /* Get the next block before marking the new end */
6104 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6105 &extraBlock)))
6106 return FALSE;
6108 /* Mark the new end of chain */
6109 SmallBlockChainStream_SetNextBlockInChain(
6110 This,
6111 blockIndex,
6112 BLOCK_END_OF_CHAIN);
6116 * Mark the extra blocks as free
6118 while (extraBlock != BLOCK_END_OF_CHAIN)
6120 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6121 &blockIndex)))
6122 return FALSE;
6123 SmallBlockChainStream_FreeBlock(This, extraBlock);
6124 extraBlock = blockIndex;
6127 return TRUE;
6130 /******************************************************************************
6131 * SmallBlockChainStream_Enlarge
6133 * Grows this chain in the small block depot.
6135 static BOOL SmallBlockChainStream_Enlarge(
6136 SmallBlockChainStream* This,
6137 ULARGE_INTEGER newSize)
6139 ULONG blockIndex, currentBlock;
6140 ULONG newNumBlocks;
6141 ULONG oldNumBlocks = 0;
6143 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6146 * Empty chain. Create the head.
6148 if (blockIndex == BLOCK_END_OF_CHAIN)
6150 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6151 SmallBlockChainStream_SetNextBlockInChain(
6152 This,
6153 blockIndex,
6154 BLOCK_END_OF_CHAIN);
6156 if (This->headOfStreamPlaceHolder != NULL)
6158 *(This->headOfStreamPlaceHolder) = blockIndex;
6160 else
6162 DirEntry chainEntry;
6164 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6165 &chainEntry);
6167 chainEntry.startingBlock = blockIndex;
6169 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6170 &chainEntry);
6174 currentBlock = blockIndex;
6177 * Figure out how many blocks are needed to contain this stream
6179 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6181 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6182 newNumBlocks++;
6185 * Go to the current end of chain
6187 while (blockIndex != BLOCK_END_OF_CHAIN)
6189 oldNumBlocks++;
6190 currentBlock = blockIndex;
6191 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6192 return FALSE;
6196 * Add new blocks to the chain
6198 while (oldNumBlocks < newNumBlocks)
6200 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6201 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6203 SmallBlockChainStream_SetNextBlockInChain(
6204 This,
6205 blockIndex,
6206 BLOCK_END_OF_CHAIN);
6208 currentBlock = blockIndex;
6209 oldNumBlocks++;
6212 return TRUE;
6215 /******************************************************************************
6216 * SmallBlockChainStream_SetSize
6218 * Sets the size of this stream.
6219 * The file will grow if we grow the chain.
6221 * TODO: Free the actual blocks in the file when we shrink the chain.
6222 * Currently, the blocks are still in the file. So the file size
6223 * doesn't shrink even if we shrink streams.
6225 BOOL SmallBlockChainStream_SetSize(
6226 SmallBlockChainStream* This,
6227 ULARGE_INTEGER newSize)
6229 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6231 if (newSize.u.LowPart == size.u.LowPart)
6232 return TRUE;
6234 if (newSize.u.LowPart < size.u.LowPart)
6236 SmallBlockChainStream_Shrink(This, newSize);
6238 else
6240 SmallBlockChainStream_Enlarge(This, newSize);
6243 return TRUE;
6246 /******************************************************************************
6247 * SmallBlockChainStream_GetCount
6249 * Returns the number of small blocks that comprises this chain.
6250 * This is not the size of the stream as the last block may not be full!
6253 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6255 ULONG blockIndex;
6256 ULONG count = 0;
6258 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6260 while(blockIndex != BLOCK_END_OF_CHAIN)
6262 count++;
6264 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6265 blockIndex, &blockIndex)))
6266 return 0;
6269 return count;
6272 /******************************************************************************
6273 * SmallBlockChainStream_GetSize
6275 * Returns the size of this chain.
6277 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6279 DirEntry chainEntry;
6281 if(This->headOfStreamPlaceHolder != NULL)
6283 ULARGE_INTEGER result;
6284 result.u.HighPart = 0;
6286 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6287 This->parentStorage->smallBlockSize;
6289 return result;
6292 StorageImpl_ReadDirEntry(
6293 This->parentStorage,
6294 This->ownerDirEntry,
6295 &chainEntry);
6297 return chainEntry.size;
6300 /******************************************************************************
6301 * StgCreateDocfile [OLE32.@]
6302 * Creates a new compound file storage object
6304 * PARAMS
6305 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6306 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6307 * reserved [ ?] unused?, usually 0
6308 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6310 * RETURNS
6311 * S_OK if the file was successfully created
6312 * some STG_E_ value if error
6313 * NOTES
6314 * if pwcsName is NULL, create file with new unique name
6315 * the function can returns
6316 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6317 * (unrealized now)
6319 HRESULT WINAPI StgCreateDocfile(
6320 LPCOLESTR pwcsName,
6321 DWORD grfMode,
6322 DWORD reserved,
6323 IStorage **ppstgOpen)
6325 StorageBaseImpl* newStorage = 0;
6326 HANDLE hFile = INVALID_HANDLE_VALUE;
6327 HRESULT hr = STG_E_INVALIDFLAG;
6328 DWORD shareMode;
6329 DWORD accessMode;
6330 DWORD creationMode;
6331 DWORD fileAttributes;
6332 WCHAR tempFileName[MAX_PATH];
6334 TRACE("(%s, %x, %d, %p)\n",
6335 debugstr_w(pwcsName), grfMode,
6336 reserved, ppstgOpen);
6338 if (ppstgOpen == 0)
6339 return STG_E_INVALIDPOINTER;
6340 if (reserved != 0)
6341 return STG_E_INVALIDPARAMETER;
6343 /* if no share mode given then DENY_NONE is the default */
6344 if (STGM_SHARE_MODE(grfMode) == 0)
6345 grfMode |= STGM_SHARE_DENY_NONE;
6347 if ( FAILED( validateSTGM(grfMode) ))
6348 goto end;
6350 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6351 switch(STGM_ACCESS_MODE(grfMode))
6353 case STGM_WRITE:
6354 case STGM_READWRITE:
6355 break;
6356 default:
6357 goto end;
6360 /* in direct mode, can only use SHARE_EXCLUSIVE */
6361 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6362 goto end;
6364 /* but in transacted mode, any share mode is valid */
6367 * Generate a unique name.
6369 if (pwcsName == 0)
6371 WCHAR tempPath[MAX_PATH];
6372 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6374 memset(tempPath, 0, sizeof(tempPath));
6375 memset(tempFileName, 0, sizeof(tempFileName));
6377 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6378 tempPath[0] = '.';
6380 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6381 pwcsName = tempFileName;
6382 else
6384 hr = STG_E_INSUFFICIENTMEMORY;
6385 goto end;
6388 creationMode = TRUNCATE_EXISTING;
6390 else
6392 creationMode = GetCreationModeFromSTGM(grfMode);
6396 * Interpret the STGM value grfMode
6398 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6399 accessMode = GetAccessModeFromSTGM(grfMode);
6401 if (grfMode & STGM_DELETEONRELEASE)
6402 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6403 else
6404 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6406 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6407 FIXME("Storage share mode not implemented.\n");
6409 if (grfMode & STGM_TRANSACTED)
6410 FIXME("Transacted mode not implemented.\n");
6412 *ppstgOpen = 0;
6414 hFile = CreateFileW(pwcsName,
6415 accessMode,
6416 shareMode,
6417 NULL,
6418 creationMode,
6419 fileAttributes,
6422 if (hFile == INVALID_HANDLE_VALUE)
6424 if(GetLastError() == ERROR_FILE_EXISTS)
6425 hr = STG_E_FILEALREADYEXISTS;
6426 else
6427 hr = E_FAIL;
6428 goto end;
6432 * Allocate and initialize the new IStorage32object.
6434 hr = Storage_Construct(
6435 hFile,
6436 pwcsName,
6437 NULL,
6438 grfMode,
6439 TRUE,
6440 TRUE,
6441 &newStorage);
6443 if (FAILED(hr))
6445 goto end;
6449 * Get an "out" pointer for the caller.
6451 *ppstgOpen = (IStorage*)newStorage;
6453 end:
6454 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6456 return hr;
6459 /******************************************************************************
6460 * StgCreateStorageEx [OLE32.@]
6462 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6464 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6465 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6467 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6469 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6470 return STG_E_INVALIDPARAMETER;
6473 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6475 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6476 return STG_E_INVALIDPARAMETER;
6479 if (stgfmt == STGFMT_FILE)
6481 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6482 return STG_E_INVALIDPARAMETER;
6485 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6487 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
6488 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
6491 ERR("Invalid stgfmt argument\n");
6492 return STG_E_INVALIDPARAMETER;
6495 /******************************************************************************
6496 * StgCreatePropSetStg [OLE32.@]
6498 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6499 IPropertySetStorage **ppPropSetStg)
6501 HRESULT hr;
6503 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6504 if (reserved)
6505 hr = STG_E_INVALIDPARAMETER;
6506 else
6507 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6508 (void**)ppPropSetStg);
6509 return hr;
6512 /******************************************************************************
6513 * StgOpenStorageEx [OLE32.@]
6515 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6517 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6518 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6520 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6522 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6523 return STG_E_INVALIDPARAMETER;
6526 switch (stgfmt)
6528 case STGFMT_FILE:
6529 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6530 return STG_E_INVALIDPARAMETER;
6532 case STGFMT_STORAGE:
6533 break;
6535 case STGFMT_DOCFILE:
6536 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6538 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6539 return STG_E_INVALIDPARAMETER;
6541 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6542 break;
6544 case STGFMT_ANY:
6545 WARN("STGFMT_ANY assuming storage\n");
6546 break;
6548 default:
6549 return STG_E_INVALIDPARAMETER;
6552 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6556 /******************************************************************************
6557 * StgOpenStorage [OLE32.@]
6559 HRESULT WINAPI StgOpenStorage(
6560 const OLECHAR *pwcsName,
6561 IStorage *pstgPriority,
6562 DWORD grfMode,
6563 SNB snbExclude,
6564 DWORD reserved,
6565 IStorage **ppstgOpen)
6567 StorageBaseImpl* newStorage = 0;
6568 HRESULT hr = S_OK;
6569 HANDLE hFile = 0;
6570 DWORD shareMode;
6571 DWORD accessMode;
6573 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6574 debugstr_w(pwcsName), pstgPriority, grfMode,
6575 snbExclude, reserved, ppstgOpen);
6577 if (pwcsName == 0)
6579 hr = STG_E_INVALIDNAME;
6580 goto end;
6583 if (ppstgOpen == 0)
6585 hr = STG_E_INVALIDPOINTER;
6586 goto end;
6589 if (reserved)
6591 hr = STG_E_INVALIDPARAMETER;
6592 goto end;
6595 if (grfMode & STGM_PRIORITY)
6597 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6598 return STG_E_INVALIDFLAG;
6599 if (grfMode & STGM_DELETEONRELEASE)
6600 return STG_E_INVALIDFUNCTION;
6601 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6602 return STG_E_INVALIDFLAG;
6603 grfMode &= ~0xf0; /* remove the existing sharing mode */
6604 grfMode |= STGM_SHARE_DENY_NONE;
6606 /* STGM_PRIORITY stops other IStorage objects on the same file from
6607 * committing until the STGM_PRIORITY IStorage is closed. it also
6608 * stops non-transacted mode StgOpenStorage calls with write access from
6609 * succeeding. obviously, both of these cannot be achieved through just
6610 * file share flags */
6611 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6615 * Validate the sharing mode
6617 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6618 switch(STGM_SHARE_MODE(grfMode))
6620 case STGM_SHARE_EXCLUSIVE:
6621 case STGM_SHARE_DENY_WRITE:
6622 break;
6623 default:
6624 hr = STG_E_INVALIDFLAG;
6625 goto end;
6628 if ( FAILED( validateSTGM(grfMode) ) ||
6629 (grfMode&STGM_CREATE))
6631 hr = STG_E_INVALIDFLAG;
6632 goto end;
6635 /* shared reading requires transacted mode */
6636 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6637 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6638 !(grfMode&STGM_TRANSACTED) )
6640 hr = STG_E_INVALIDFLAG;
6641 goto end;
6645 * Interpret the STGM value grfMode
6647 shareMode = GetShareModeFromSTGM(grfMode);
6648 accessMode = GetAccessModeFromSTGM(grfMode);
6650 *ppstgOpen = 0;
6652 hFile = CreateFileW( pwcsName,
6653 accessMode,
6654 shareMode,
6655 NULL,
6656 OPEN_EXISTING,
6657 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6660 if (hFile==INVALID_HANDLE_VALUE)
6662 DWORD last_error = GetLastError();
6664 hr = E_FAIL;
6666 switch (last_error)
6668 case ERROR_FILE_NOT_FOUND:
6669 hr = STG_E_FILENOTFOUND;
6670 break;
6672 case ERROR_PATH_NOT_FOUND:
6673 hr = STG_E_PATHNOTFOUND;
6674 break;
6676 case ERROR_ACCESS_DENIED:
6677 case ERROR_WRITE_PROTECT:
6678 hr = STG_E_ACCESSDENIED;
6679 break;
6681 case ERROR_SHARING_VIOLATION:
6682 hr = STG_E_SHAREVIOLATION;
6683 break;
6685 default:
6686 hr = E_FAIL;
6689 goto end;
6693 * Refuse to open the file if it's too small to be a structured storage file
6694 * FIXME: verify the file when reading instead of here
6696 if (GetFileSize(hFile, NULL) < 0x100)
6698 CloseHandle(hFile);
6699 hr = STG_E_FILEALREADYEXISTS;
6700 goto end;
6704 * Allocate and initialize the new IStorage32object.
6706 hr = Storage_Construct(
6707 hFile,
6708 pwcsName,
6709 NULL,
6710 grfMode,
6711 TRUE,
6712 FALSE,
6713 &newStorage);
6715 if (FAILED(hr))
6718 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6720 if(hr == STG_E_INVALIDHEADER)
6721 hr = STG_E_FILEALREADYEXISTS;
6722 goto end;
6726 * Get an "out" pointer for the caller.
6728 *ppstgOpen = (IStorage*)newStorage;
6730 end:
6731 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6732 return hr;
6735 /******************************************************************************
6736 * StgCreateDocfileOnILockBytes [OLE32.@]
6738 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6739 ILockBytes *plkbyt,
6740 DWORD grfMode,
6741 DWORD reserved,
6742 IStorage** ppstgOpen)
6744 StorageBaseImpl* newStorage = 0;
6745 HRESULT hr = S_OK;
6747 if ((ppstgOpen == 0) || (plkbyt == 0))
6748 return STG_E_INVALIDPOINTER;
6751 * Allocate and initialize the new IStorage object.
6753 hr = Storage_Construct(
6756 plkbyt,
6757 grfMode,
6758 FALSE,
6759 TRUE,
6760 &newStorage);
6762 if (FAILED(hr))
6764 return hr;
6768 * Get an "out" pointer for the caller.
6770 *ppstgOpen = (IStorage*)newStorage;
6772 return hr;
6775 /******************************************************************************
6776 * StgOpenStorageOnILockBytes [OLE32.@]
6778 HRESULT WINAPI StgOpenStorageOnILockBytes(
6779 ILockBytes *plkbyt,
6780 IStorage *pstgPriority,
6781 DWORD grfMode,
6782 SNB snbExclude,
6783 DWORD reserved,
6784 IStorage **ppstgOpen)
6786 StorageBaseImpl* newStorage = 0;
6787 HRESULT hr = S_OK;
6789 if ((plkbyt == 0) || (ppstgOpen == 0))
6790 return STG_E_INVALIDPOINTER;
6792 if ( FAILED( validateSTGM(grfMode) ))
6793 return STG_E_INVALIDFLAG;
6795 *ppstgOpen = 0;
6798 * Allocate and initialize the new IStorage object.
6800 hr = Storage_Construct(
6803 plkbyt,
6804 grfMode,
6805 FALSE,
6806 FALSE,
6807 &newStorage);
6809 if (FAILED(hr))
6811 return hr;
6815 * Get an "out" pointer for the caller.
6817 *ppstgOpen = (IStorage*)newStorage;
6819 return hr;
6822 /******************************************************************************
6823 * StgSetTimes [ole32.@]
6824 * StgSetTimes [OLE32.@]
6828 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6829 FILETIME const *patime, FILETIME const *pmtime)
6831 IStorage *stg = NULL;
6832 HRESULT r;
6834 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6836 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6837 0, 0, &stg);
6838 if( SUCCEEDED(r) )
6840 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6841 IStorage_Release(stg);
6844 return r;
6847 /******************************************************************************
6848 * StgIsStorageILockBytes [OLE32.@]
6850 * Determines if the ILockBytes contains a storage object.
6852 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6854 BYTE sig[8];
6855 ULARGE_INTEGER offset;
6857 offset.u.HighPart = 0;
6858 offset.u.LowPart = 0;
6860 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6862 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6863 return S_OK;
6865 return S_FALSE;
6868 /******************************************************************************
6869 * WriteClassStg [OLE32.@]
6871 * This method will store the specified CLSID in the specified storage object
6873 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6875 HRESULT hRes;
6877 if(!pStg)
6878 return E_INVALIDARG;
6880 if(!rclsid)
6881 return STG_E_INVALIDPOINTER;
6883 hRes = IStorage_SetClass(pStg, rclsid);
6885 return hRes;
6888 /***********************************************************************
6889 * ReadClassStg (OLE32.@)
6891 * This method reads the CLSID previously written to a storage object with
6892 * the WriteClassStg.
6894 * PARAMS
6895 * pstg [I] IStorage pointer
6896 * pclsid [O] Pointer to where the CLSID is written
6898 * RETURNS
6899 * Success: S_OK.
6900 * Failure: HRESULT code.
6902 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6904 STATSTG pstatstg;
6905 HRESULT hRes;
6907 TRACE("(%p, %p)\n", pstg, pclsid);
6909 if(!pstg || !pclsid)
6910 return E_INVALIDARG;
6913 * read a STATSTG structure (contains the clsid) from the storage
6915 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6917 if(SUCCEEDED(hRes))
6918 *pclsid=pstatstg.clsid;
6920 return hRes;
6923 /***********************************************************************
6924 * OleLoadFromStream (OLE32.@)
6926 * This function loads an object from stream
6928 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6930 CLSID clsid;
6931 HRESULT res;
6932 LPPERSISTSTREAM xstm;
6934 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6936 res=ReadClassStm(pStm,&clsid);
6937 if (FAILED(res))
6938 return res;
6939 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6940 if (FAILED(res))
6941 return res;
6942 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6943 if (FAILED(res)) {
6944 IUnknown_Release((IUnknown*)*ppvObj);
6945 return res;
6947 res=IPersistStream_Load(xstm,pStm);
6948 IPersistStream_Release(xstm);
6949 /* FIXME: all refcounts ok at this point? I think they should be:
6950 * pStm : unchanged
6951 * ppvObj : 1
6952 * xstm : 0 (released)
6954 return res;
6957 /***********************************************************************
6958 * OleSaveToStream (OLE32.@)
6960 * This function saves an object with the IPersistStream interface on it
6961 * to the specified stream.
6963 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6966 CLSID clsid;
6967 HRESULT res;
6969 TRACE("(%p,%p)\n",pPStm,pStm);
6971 res=IPersistStream_GetClassID(pPStm,&clsid);
6973 if (SUCCEEDED(res)){
6975 res=WriteClassStm(pStm,&clsid);
6977 if (SUCCEEDED(res))
6979 res=IPersistStream_Save(pPStm,pStm,TRUE);
6982 TRACE("Finished Save\n");
6983 return res;
6986 /****************************************************************************
6987 * This method validate a STGM parameter that can contain the values below
6989 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6990 * The stgm values contained in 0xffff0000 are bitmasks.
6992 * STGM_DIRECT 0x00000000
6993 * STGM_TRANSACTED 0x00010000
6994 * STGM_SIMPLE 0x08000000
6996 * STGM_READ 0x00000000
6997 * STGM_WRITE 0x00000001
6998 * STGM_READWRITE 0x00000002
7000 * STGM_SHARE_DENY_NONE 0x00000040
7001 * STGM_SHARE_DENY_READ 0x00000030
7002 * STGM_SHARE_DENY_WRITE 0x00000020
7003 * STGM_SHARE_EXCLUSIVE 0x00000010
7005 * STGM_PRIORITY 0x00040000
7006 * STGM_DELETEONRELEASE 0x04000000
7008 * STGM_CREATE 0x00001000
7009 * STGM_CONVERT 0x00020000
7010 * STGM_FAILIFTHERE 0x00000000
7012 * STGM_NOSCRATCH 0x00100000
7013 * STGM_NOSNAPSHOT 0x00200000
7015 static HRESULT validateSTGM(DWORD stgm)
7017 DWORD access = STGM_ACCESS_MODE(stgm);
7018 DWORD share = STGM_SHARE_MODE(stgm);
7019 DWORD create = STGM_CREATE_MODE(stgm);
7021 if (stgm&~STGM_KNOWN_FLAGS)
7023 ERR("unknown flags %08x\n", stgm);
7024 return E_FAIL;
7027 switch (access)
7029 case STGM_READ:
7030 case STGM_WRITE:
7031 case STGM_READWRITE:
7032 break;
7033 default:
7034 return E_FAIL;
7037 switch (share)
7039 case STGM_SHARE_DENY_NONE:
7040 case STGM_SHARE_DENY_READ:
7041 case STGM_SHARE_DENY_WRITE:
7042 case STGM_SHARE_EXCLUSIVE:
7043 break;
7044 default:
7045 return E_FAIL;
7048 switch (create)
7050 case STGM_CREATE:
7051 case STGM_FAILIFTHERE:
7052 break;
7053 default:
7054 return E_FAIL;
7058 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7060 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7061 return E_FAIL;
7064 * STGM_CREATE | STGM_CONVERT
7065 * if both are false, STGM_FAILIFTHERE is set to TRUE
7067 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7068 return E_FAIL;
7071 * STGM_NOSCRATCH requires STGM_TRANSACTED
7073 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7074 return E_FAIL;
7077 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7078 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7080 if ( (stgm & STGM_NOSNAPSHOT) &&
7081 (!(stgm & STGM_TRANSACTED) ||
7082 share == STGM_SHARE_EXCLUSIVE ||
7083 share == STGM_SHARE_DENY_WRITE) )
7084 return E_FAIL;
7086 return S_OK;
7089 /****************************************************************************
7090 * GetShareModeFromSTGM
7092 * This method will return a share mode flag from a STGM value.
7093 * The STGM value is assumed valid.
7095 static DWORD GetShareModeFromSTGM(DWORD stgm)
7097 switch (STGM_SHARE_MODE(stgm))
7099 case STGM_SHARE_DENY_NONE:
7100 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7101 case STGM_SHARE_DENY_READ:
7102 return FILE_SHARE_WRITE;
7103 case STGM_SHARE_DENY_WRITE:
7104 return FILE_SHARE_READ;
7105 case STGM_SHARE_EXCLUSIVE:
7106 return 0;
7108 ERR("Invalid share mode!\n");
7109 assert(0);
7110 return 0;
7113 /****************************************************************************
7114 * GetAccessModeFromSTGM
7116 * This method will return an access mode flag from a STGM value.
7117 * The STGM value is assumed valid.
7119 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7121 switch (STGM_ACCESS_MODE(stgm))
7123 case STGM_READ:
7124 return GENERIC_READ;
7125 case STGM_WRITE:
7126 case STGM_READWRITE:
7127 return GENERIC_READ | GENERIC_WRITE;
7129 ERR("Invalid access mode!\n");
7130 assert(0);
7131 return 0;
7134 /****************************************************************************
7135 * GetCreationModeFromSTGM
7137 * This method will return a creation mode flag from a STGM value.
7138 * The STGM value is assumed valid.
7140 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7142 switch(STGM_CREATE_MODE(stgm))
7144 case STGM_CREATE:
7145 return CREATE_ALWAYS;
7146 case STGM_CONVERT:
7147 FIXME("STGM_CONVERT not implemented!\n");
7148 return CREATE_NEW;
7149 case STGM_FAILIFTHERE:
7150 return CREATE_NEW;
7152 ERR("Invalid create mode!\n");
7153 assert(0);
7154 return 0;
7158 /*************************************************************************
7159 * OLECONVERT_LoadOLE10 [Internal]
7161 * Loads the OLE10 STREAM to memory
7163 * PARAMS
7164 * pOleStream [I] The OLESTREAM
7165 * pData [I] Data Structure for the OLESTREAM Data
7167 * RETURNS
7168 * Success: S_OK
7169 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7170 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7172 * NOTES
7173 * This function is used by OleConvertOLESTREAMToIStorage only.
7175 * Memory allocated for pData must be freed by the caller
7177 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7179 DWORD dwSize;
7180 HRESULT hRes = S_OK;
7181 int nTryCnt=0;
7182 int max_try = 6;
7184 pData->pData = NULL;
7185 pData->pstrOleObjFileName = NULL;
7187 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7189 /* Get the OleID */
7190 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7191 if(dwSize != sizeof(pData->dwOleID))
7193 hRes = CONVERT10_E_OLESTREAM_GET;
7195 else if(pData->dwOleID != OLESTREAM_ID)
7197 hRes = CONVERT10_E_OLESTREAM_FMT;
7199 else
7201 hRes = S_OK;
7202 break;
7206 if(hRes == S_OK)
7208 /* Get the TypeID... more info needed for this field */
7209 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7210 if(dwSize != sizeof(pData->dwTypeID))
7212 hRes = CONVERT10_E_OLESTREAM_GET;
7215 if(hRes == S_OK)
7217 if(pData->dwTypeID != 0)
7219 /* Get the length of the OleTypeName */
7220 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7221 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7223 hRes = CONVERT10_E_OLESTREAM_GET;
7226 if(hRes == S_OK)
7228 if(pData->dwOleTypeNameLength > 0)
7230 /* Get the OleTypeName */
7231 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7232 if(dwSize != pData->dwOleTypeNameLength)
7234 hRes = CONVERT10_E_OLESTREAM_GET;
7238 if(bStrem1)
7240 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7241 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7243 hRes = CONVERT10_E_OLESTREAM_GET;
7245 if(hRes == S_OK)
7247 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7248 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7249 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7250 if(pData->pstrOleObjFileName)
7252 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7253 if(dwSize != pData->dwOleObjFileNameLength)
7255 hRes = CONVERT10_E_OLESTREAM_GET;
7258 else
7259 hRes = CONVERT10_E_OLESTREAM_GET;
7262 else
7264 /* Get the Width of the Metafile */
7265 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7266 if(dwSize != sizeof(pData->dwMetaFileWidth))
7268 hRes = CONVERT10_E_OLESTREAM_GET;
7270 if(hRes == S_OK)
7272 /* Get the Height of the Metafile */
7273 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7274 if(dwSize != sizeof(pData->dwMetaFileHeight))
7276 hRes = CONVERT10_E_OLESTREAM_GET;
7280 if(hRes == S_OK)
7282 /* Get the Length of the Data */
7283 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7284 if(dwSize != sizeof(pData->dwDataLength))
7286 hRes = CONVERT10_E_OLESTREAM_GET;
7290 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7292 if(!bStrem1) /* if it is a second OLE stream data */
7294 pData->dwDataLength -= 8;
7295 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7296 if(dwSize != sizeof(pData->strUnknown))
7298 hRes = CONVERT10_E_OLESTREAM_GET;
7302 if(hRes == S_OK)
7304 if(pData->dwDataLength > 0)
7306 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7308 /* Get Data (ex. IStorage, Metafile, or BMP) */
7309 if(pData->pData)
7311 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7312 if(dwSize != pData->dwDataLength)
7314 hRes = CONVERT10_E_OLESTREAM_GET;
7317 else
7319 hRes = CONVERT10_E_OLESTREAM_GET;
7325 return hRes;
7328 /*************************************************************************
7329 * OLECONVERT_SaveOLE10 [Internal]
7331 * Saves the OLE10 STREAM From memory
7333 * PARAMS
7334 * pData [I] Data Structure for the OLESTREAM Data
7335 * pOleStream [I] The OLESTREAM to save
7337 * RETURNS
7338 * Success: S_OK
7339 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7341 * NOTES
7342 * This function is used by OleConvertIStorageToOLESTREAM only.
7345 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7347 DWORD dwSize;
7348 HRESULT hRes = S_OK;
7351 /* Set the OleID */
7352 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7353 if(dwSize != sizeof(pData->dwOleID))
7355 hRes = CONVERT10_E_OLESTREAM_PUT;
7358 if(hRes == S_OK)
7360 /* Set the TypeID */
7361 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7362 if(dwSize != sizeof(pData->dwTypeID))
7364 hRes = CONVERT10_E_OLESTREAM_PUT;
7368 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7370 /* Set the Length of the OleTypeName */
7371 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7372 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7374 hRes = CONVERT10_E_OLESTREAM_PUT;
7377 if(hRes == S_OK)
7379 if(pData->dwOleTypeNameLength > 0)
7381 /* Set the OleTypeName */
7382 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7383 if(dwSize != pData->dwOleTypeNameLength)
7385 hRes = CONVERT10_E_OLESTREAM_PUT;
7390 if(hRes == S_OK)
7392 /* Set the width of the Metafile */
7393 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7394 if(dwSize != sizeof(pData->dwMetaFileWidth))
7396 hRes = CONVERT10_E_OLESTREAM_PUT;
7400 if(hRes == S_OK)
7402 /* Set the height of the Metafile */
7403 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7404 if(dwSize != sizeof(pData->dwMetaFileHeight))
7406 hRes = CONVERT10_E_OLESTREAM_PUT;
7410 if(hRes == S_OK)
7412 /* Set the length of the Data */
7413 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7414 if(dwSize != sizeof(pData->dwDataLength))
7416 hRes = CONVERT10_E_OLESTREAM_PUT;
7420 if(hRes == S_OK)
7422 if(pData->dwDataLength > 0)
7424 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7425 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7426 if(dwSize != pData->dwDataLength)
7428 hRes = CONVERT10_E_OLESTREAM_PUT;
7433 return hRes;
7436 /*************************************************************************
7437 * OLECONVERT_GetOLE20FromOLE10[Internal]
7439 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7440 * opens it, and copies the content to the dest IStorage for
7441 * OleConvertOLESTREAMToIStorage
7444 * PARAMS
7445 * pDestStorage [I] The IStorage to copy the data to
7446 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7447 * nBufferLength [I] The size of the buffer
7449 * RETURNS
7450 * Nothing
7452 * NOTES
7456 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7458 HRESULT hRes;
7459 HANDLE hFile;
7460 IStorage *pTempStorage;
7461 DWORD dwNumOfBytesWritten;
7462 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7463 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7465 /* Create a temp File */
7466 GetTempPathW(MAX_PATH, wstrTempDir);
7467 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7468 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7470 if(hFile != INVALID_HANDLE_VALUE)
7472 /* Write IStorage Data to File */
7473 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7474 CloseHandle(hFile);
7476 /* Open and copy temp storage to the Dest Storage */
7477 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7478 if(hRes == S_OK)
7480 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7481 IStorage_Release(pTempStorage);
7483 DeleteFileW(wstrTempFile);
7488 /*************************************************************************
7489 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7491 * Saves the OLE10 STREAM From memory
7493 * PARAMS
7494 * pStorage [I] The Src IStorage to copy
7495 * pData [I] The Dest Memory to write to.
7497 * RETURNS
7498 * The size in bytes allocated for pData
7500 * NOTES
7501 * Memory allocated for pData must be freed by the caller
7503 * Used by OleConvertIStorageToOLESTREAM only.
7506 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7508 HANDLE hFile;
7509 HRESULT hRes;
7510 DWORD nDataLength = 0;
7511 IStorage *pTempStorage;
7512 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7513 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7515 *pData = NULL;
7517 /* Create temp Storage */
7518 GetTempPathW(MAX_PATH, wstrTempDir);
7519 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7520 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7522 if(hRes == S_OK)
7524 /* Copy Src Storage to the Temp Storage */
7525 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7526 IStorage_Release(pTempStorage);
7528 /* Open Temp Storage as a file and copy to memory */
7529 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7530 if(hFile != INVALID_HANDLE_VALUE)
7532 nDataLength = GetFileSize(hFile, NULL);
7533 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7534 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7535 CloseHandle(hFile);
7537 DeleteFileW(wstrTempFile);
7539 return nDataLength;
7542 /*************************************************************************
7543 * OLECONVERT_CreateOleStream [Internal]
7545 * Creates the "\001OLE" stream in the IStorage if necessary.
7547 * PARAMS
7548 * pStorage [I] Dest storage to create the stream in
7550 * RETURNS
7551 * Nothing
7553 * NOTES
7554 * This function is used by OleConvertOLESTREAMToIStorage only.
7556 * This stream is still unknown, MS Word seems to have extra data
7557 * but since the data is stored in the OLESTREAM there should be
7558 * no need to recreate the stream. If the stream is manually
7559 * deleted it will create it with this default data.
7562 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7564 HRESULT hRes;
7565 IStream *pStream;
7566 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7567 BYTE pOleStreamHeader [] =
7569 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7570 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7571 0x00, 0x00, 0x00, 0x00
7574 /* Create stream if not present */
7575 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7576 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7578 if(hRes == S_OK)
7580 /* Write default Data */
7581 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7582 IStream_Release(pStream);
7586 /* write a string to a stream, preceded by its length */
7587 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7589 HRESULT r;
7590 LPSTR str;
7591 DWORD len = 0;
7593 if( string )
7594 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7595 r = IStream_Write( stm, &len, sizeof(len), NULL);
7596 if( FAILED( r ) )
7597 return r;
7598 if(len == 0)
7599 return r;
7600 str = CoTaskMemAlloc( len );
7601 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7602 r = IStream_Write( stm, str, len, NULL);
7603 CoTaskMemFree( str );
7604 return r;
7607 /* read a string preceded by its length from a stream */
7608 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7610 HRESULT r;
7611 DWORD len, count = 0;
7612 LPSTR str;
7613 LPWSTR wstr;
7615 r = IStream_Read( stm, &len, sizeof(len), &count );
7616 if( FAILED( r ) )
7617 return r;
7618 if( count != sizeof(len) )
7619 return E_OUTOFMEMORY;
7621 TRACE("%d bytes\n",len);
7623 str = CoTaskMemAlloc( len );
7624 if( !str )
7625 return E_OUTOFMEMORY;
7626 count = 0;
7627 r = IStream_Read( stm, str, len, &count );
7628 if( FAILED( r ) )
7629 return r;
7630 if( count != len )
7632 CoTaskMemFree( str );
7633 return E_OUTOFMEMORY;
7636 TRACE("Read string %s\n",debugstr_an(str,len));
7638 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7639 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7640 if( wstr )
7641 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7642 CoTaskMemFree( str );
7644 *string = wstr;
7646 return r;
7650 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7651 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7653 IStream *pstm;
7654 HRESULT r = S_OK;
7655 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7657 static const BYTE unknown1[12] =
7658 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7659 0xFF, 0xFF, 0xFF, 0xFF};
7660 static const BYTE unknown2[16] =
7661 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7662 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7664 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7665 debugstr_w(lpszUserType), debugstr_w(szClipName),
7666 debugstr_w(szProgIDName));
7668 /* Create a CompObj stream */
7669 r = IStorage_CreateStream(pstg, szwStreamName,
7670 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7671 if( FAILED (r) )
7672 return r;
7674 /* Write CompObj Structure to stream */
7675 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7677 if( SUCCEEDED( r ) )
7678 r = WriteClassStm( pstm, clsid );
7680 if( SUCCEEDED( r ) )
7681 r = STREAM_WriteString( pstm, lpszUserType );
7682 if( SUCCEEDED( r ) )
7683 r = STREAM_WriteString( pstm, szClipName );
7684 if( SUCCEEDED( r ) )
7685 r = STREAM_WriteString( pstm, szProgIDName );
7686 if( SUCCEEDED( r ) )
7687 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7689 IStream_Release( pstm );
7691 return r;
7694 /***********************************************************************
7695 * WriteFmtUserTypeStg (OLE32.@)
7697 HRESULT WINAPI WriteFmtUserTypeStg(
7698 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7700 HRESULT r;
7701 WCHAR szwClipName[0x40];
7702 CLSID clsid = CLSID_NULL;
7703 LPWSTR wstrProgID = NULL;
7704 DWORD n;
7706 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7708 /* get the clipboard format name */
7709 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7710 szwClipName[n]=0;
7712 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7714 /* FIXME: There's room to save a CLSID and its ProgID, but
7715 the CLSID is not looked up in the registry and in all the
7716 tests I wrote it was CLSID_NULL. Where does it come from?
7719 /* get the real program ID. This may fail, but that's fine */
7720 ProgIDFromCLSID(&clsid, &wstrProgID);
7722 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7724 r = STORAGE_WriteCompObj( pstg, &clsid,
7725 lpszUserType, szwClipName, wstrProgID );
7727 CoTaskMemFree(wstrProgID);
7729 return r;
7733 /******************************************************************************
7734 * ReadFmtUserTypeStg [OLE32.@]
7736 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7738 HRESULT r;
7739 IStream *stm = 0;
7740 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7741 unsigned char unknown1[12];
7742 unsigned char unknown2[16];
7743 DWORD count;
7744 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7745 CLSID clsid;
7747 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7749 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7750 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7751 if( FAILED ( r ) )
7753 WARN("Failed to open stream r = %08x\n", r);
7754 return r;
7757 /* read the various parts of the structure */
7758 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7759 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7760 goto end;
7761 r = ReadClassStm( stm, &clsid );
7762 if( FAILED( r ) )
7763 goto end;
7765 r = STREAM_ReadString( stm, &szCLSIDName );
7766 if( FAILED( r ) )
7767 goto end;
7769 r = STREAM_ReadString( stm, &szOleTypeName );
7770 if( FAILED( r ) )
7771 goto end;
7773 r = STREAM_ReadString( stm, &szProgIDName );
7774 if( FAILED( r ) )
7775 goto end;
7777 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7778 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7779 goto end;
7781 /* ok, success... now we just need to store what we found */
7782 if( pcf )
7783 *pcf = RegisterClipboardFormatW( szOleTypeName );
7784 CoTaskMemFree( szOleTypeName );
7786 if( lplpszUserType )
7787 *lplpszUserType = szCLSIDName;
7788 CoTaskMemFree( szProgIDName );
7790 end:
7791 IStream_Release( stm );
7793 return r;
7797 /*************************************************************************
7798 * OLECONVERT_CreateCompObjStream [Internal]
7800 * Creates a "\001CompObj" is the destination IStorage if necessary.
7802 * PARAMS
7803 * pStorage [I] The dest IStorage to create the CompObj Stream
7804 * if necessary.
7805 * strOleTypeName [I] The ProgID
7807 * RETURNS
7808 * Success: S_OK
7809 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7811 * NOTES
7812 * This function is used by OleConvertOLESTREAMToIStorage only.
7814 * The stream data is stored in the OLESTREAM and there should be
7815 * no need to recreate the stream. If the stream is manually
7816 * deleted it will attempt to create it by querying the registry.
7820 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7822 IStream *pStream;
7823 HRESULT hStorageRes, hRes = S_OK;
7824 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7825 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7826 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7828 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7829 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7831 /* Initialize the CompObj structure */
7832 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7833 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7834 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7837 /* Create a CompObj stream if it doesn't exist */
7838 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7839 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7840 if(hStorageRes == S_OK)
7842 /* copy the OleTypeName to the compobj struct */
7843 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7844 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7846 /* copy the OleTypeName to the compobj struct */
7847 /* Note: in the test made, these were Identical */
7848 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7849 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7851 /* Get the CLSID */
7852 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7853 bufferW, OLESTREAM_MAX_STR_LEN );
7854 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7856 if(hRes == S_OK)
7858 HKEY hKey;
7859 LONG hErr;
7860 /* Get the CLSID Default Name from the Registry */
7861 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7862 if(hErr == ERROR_SUCCESS)
7864 char strTemp[OLESTREAM_MAX_STR_LEN];
7865 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7866 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7867 if(hErr == ERROR_SUCCESS)
7869 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7871 RegCloseKey(hKey);
7875 /* Write CompObj Structure to stream */
7876 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7878 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7880 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7881 if(IStorageCompObj.dwCLSIDNameLength > 0)
7883 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7885 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7886 if(IStorageCompObj.dwOleTypeNameLength > 0)
7888 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7890 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7891 if(IStorageCompObj.dwProgIDNameLength > 0)
7893 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7895 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7896 IStream_Release(pStream);
7898 return hRes;
7902 /*************************************************************************
7903 * OLECONVERT_CreateOlePresStream[Internal]
7905 * Creates the "\002OlePres000" Stream with the Metafile data
7907 * PARAMS
7908 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7909 * dwExtentX [I] Width of the Metafile
7910 * dwExtentY [I] Height of the Metafile
7911 * pData [I] Metafile data
7912 * dwDataLength [I] Size of the Metafile data
7914 * RETURNS
7915 * Success: S_OK
7916 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7918 * NOTES
7919 * This function is used by OleConvertOLESTREAMToIStorage only.
7922 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7924 HRESULT hRes;
7925 IStream *pStream;
7926 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7927 BYTE pOlePresStreamHeader [] =
7929 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7930 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7931 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7932 0x00, 0x00, 0x00, 0x00
7935 BYTE pOlePresStreamHeaderEmpty [] =
7937 0x00, 0x00, 0x00, 0x00,
7938 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7939 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7940 0x00, 0x00, 0x00, 0x00
7943 /* Create the OlePres000 Stream */
7944 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7945 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7947 if(hRes == S_OK)
7949 DWORD nHeaderSize;
7950 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7952 memset(&OlePres, 0, sizeof(OlePres));
7953 /* Do we have any metafile data to save */
7954 if(dwDataLength > 0)
7956 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7957 nHeaderSize = sizeof(pOlePresStreamHeader);
7959 else
7961 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7962 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7964 /* Set width and height of the metafile */
7965 OlePres.dwExtentX = dwExtentX;
7966 OlePres.dwExtentY = -dwExtentY;
7968 /* Set Data and Length */
7969 if(dwDataLength > sizeof(METAFILEPICT16))
7971 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7972 OlePres.pData = &(pData[8]);
7974 /* Save OlePres000 Data to Stream */
7975 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7976 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7977 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7978 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7979 if(OlePres.dwSize > 0)
7981 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7983 IStream_Release(pStream);
7987 /*************************************************************************
7988 * OLECONVERT_CreateOle10NativeStream [Internal]
7990 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7992 * PARAMS
7993 * pStorage [I] Dest storage to create the stream in
7994 * pData [I] Ole10 Native Data (ex. bmp)
7995 * dwDataLength [I] Size of the Ole10 Native Data
7997 * RETURNS
7998 * Nothing
8000 * NOTES
8001 * This function is used by OleConvertOLESTREAMToIStorage only.
8003 * Might need to verify the data and return appropriate error message
8006 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8008 HRESULT hRes;
8009 IStream *pStream;
8010 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8012 /* Create the Ole10Native Stream */
8013 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8014 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8016 if(hRes == S_OK)
8018 /* Write info to stream */
8019 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8020 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8021 IStream_Release(pStream);
8026 /*************************************************************************
8027 * OLECONVERT_GetOLE10ProgID [Internal]
8029 * Finds the ProgID (or OleTypeID) from the IStorage
8031 * PARAMS
8032 * pStorage [I] The Src IStorage to get the ProgID
8033 * strProgID [I] the ProgID string to get
8034 * dwSize [I] the size of the string
8036 * RETURNS
8037 * Success: S_OK
8038 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8040 * NOTES
8041 * This function is used by OleConvertIStorageToOLESTREAM only.
8045 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8047 HRESULT hRes;
8048 IStream *pStream;
8049 LARGE_INTEGER iSeekPos;
8050 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8051 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8053 /* Open the CompObj Stream */
8054 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8055 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8056 if(hRes == S_OK)
8059 /*Get the OleType from the CompObj Stream */
8060 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8061 iSeekPos.u.HighPart = 0;
8063 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8064 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8065 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8066 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8067 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8068 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8069 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8071 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8072 if(*dwSize > 0)
8074 IStream_Read(pStream, strProgID, *dwSize, NULL);
8076 IStream_Release(pStream);
8078 else
8080 STATSTG stat;
8081 LPOLESTR wstrProgID;
8083 /* Get the OleType from the registry */
8084 REFCLSID clsid = &(stat.clsid);
8085 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8086 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8087 if(hRes == S_OK)
8089 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8093 return hRes;
8096 /*************************************************************************
8097 * OLECONVERT_GetOle10PresData [Internal]
8099 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8101 * PARAMS
8102 * pStorage [I] Src IStroage
8103 * pOleStream [I] Dest OleStream Mem Struct
8105 * RETURNS
8106 * Nothing
8108 * NOTES
8109 * This function is used by OleConvertIStorageToOLESTREAM only.
8111 * Memory allocated for pData must be freed by the caller
8115 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8118 HRESULT hRes;
8119 IStream *pStream;
8120 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8122 /* Initialize Default data for OLESTREAM */
8123 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8124 pOleStreamData[0].dwTypeID = 2;
8125 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8126 pOleStreamData[1].dwTypeID = 0;
8127 pOleStreamData[0].dwMetaFileWidth = 0;
8128 pOleStreamData[0].dwMetaFileHeight = 0;
8129 pOleStreamData[0].pData = NULL;
8130 pOleStreamData[1].pData = NULL;
8132 /* Open Ole10Native Stream */
8133 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8134 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8135 if(hRes == S_OK)
8138 /* Read Size and Data */
8139 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8140 if(pOleStreamData->dwDataLength > 0)
8142 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8143 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8145 IStream_Release(pStream);
8151 /*************************************************************************
8152 * OLECONVERT_GetOle20PresData[Internal]
8154 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8156 * PARAMS
8157 * pStorage [I] Src IStroage
8158 * pOleStreamData [I] Dest OleStream Mem Struct
8160 * RETURNS
8161 * Nothing
8163 * NOTES
8164 * This function is used by OleConvertIStorageToOLESTREAM only.
8166 * Memory allocated for pData must be freed by the caller
8168 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8170 HRESULT hRes;
8171 IStream *pStream;
8172 OLECONVERT_ISTORAGE_OLEPRES olePress;
8173 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8175 /* Initialize Default data for OLESTREAM */
8176 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8177 pOleStreamData[0].dwTypeID = 2;
8178 pOleStreamData[0].dwMetaFileWidth = 0;
8179 pOleStreamData[0].dwMetaFileHeight = 0;
8180 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8181 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8182 pOleStreamData[1].dwTypeID = 0;
8183 pOleStreamData[1].dwOleTypeNameLength = 0;
8184 pOleStreamData[1].strOleTypeName[0] = 0;
8185 pOleStreamData[1].dwMetaFileWidth = 0;
8186 pOleStreamData[1].dwMetaFileHeight = 0;
8187 pOleStreamData[1].pData = NULL;
8188 pOleStreamData[1].dwDataLength = 0;
8191 /* Open OlePress000 stream */
8192 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8193 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8194 if(hRes == S_OK)
8196 LARGE_INTEGER iSeekPos;
8197 METAFILEPICT16 MetaFilePict;
8198 static const char strMetafilePictName[] = "METAFILEPICT";
8200 /* Set the TypeID for a Metafile */
8201 pOleStreamData[1].dwTypeID = 5;
8203 /* Set the OleTypeName to Metafile */
8204 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8205 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8207 iSeekPos.u.HighPart = 0;
8208 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8210 /* Get Presentation Data */
8211 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8212 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8213 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8214 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8216 /*Set width and Height */
8217 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8218 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8219 if(olePress.dwSize > 0)
8221 /* Set Length */
8222 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8224 /* Set MetaFilePict struct */
8225 MetaFilePict.mm = 8;
8226 MetaFilePict.xExt = olePress.dwExtentX;
8227 MetaFilePict.yExt = olePress.dwExtentY;
8228 MetaFilePict.hMF = 0;
8230 /* Get Metafile Data */
8231 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8232 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8233 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8235 IStream_Release(pStream);
8239 /*************************************************************************
8240 * OleConvertOLESTREAMToIStorage [OLE32.@]
8242 * Read info on MSDN
8244 * TODO
8245 * DVTARGETDEVICE parameter is not handled
8246 * Still unsure of some mem fields for OLE 10 Stream
8247 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8248 * and "\001OLE" streams
8251 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8252 LPOLESTREAM pOleStream,
8253 LPSTORAGE pstg,
8254 const DVTARGETDEVICE* ptd)
8256 int i;
8257 HRESULT hRes=S_OK;
8258 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8260 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8262 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8264 if(ptd != NULL)
8266 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8269 if(pstg == NULL || pOleStream == NULL)
8271 hRes = E_INVALIDARG;
8274 if(hRes == S_OK)
8276 /* Load the OLESTREAM to Memory */
8277 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8280 if(hRes == S_OK)
8282 /* Load the OLESTREAM to Memory (part 2)*/
8283 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8286 if(hRes == S_OK)
8289 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8291 /* Do we have the IStorage Data in the OLESTREAM */
8292 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8294 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8295 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8297 else
8299 /* It must be an original OLE 1.0 source */
8300 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8303 else
8305 /* It must be an original OLE 1.0 source */
8306 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8309 /* Create CompObj Stream if necessary */
8310 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8311 if(hRes == S_OK)
8313 /*Create the Ole Stream if necessary */
8314 OLECONVERT_CreateOleStream(pstg);
8319 /* Free allocated memory */
8320 for(i=0; i < 2; i++)
8322 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8323 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8324 pOleStreamData[i].pstrOleObjFileName = NULL;
8326 return hRes;
8329 /*************************************************************************
8330 * OleConvertIStorageToOLESTREAM [OLE32.@]
8332 * Read info on MSDN
8334 * Read info on MSDN
8336 * TODO
8337 * Still unsure of some mem fields for OLE 10 Stream
8338 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8339 * and "\001OLE" streams.
8342 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8343 LPSTORAGE pstg,
8344 LPOLESTREAM pOleStream)
8346 int i;
8347 HRESULT hRes = S_OK;
8348 IStream *pStream;
8349 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8350 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8352 TRACE("%p %p\n", pstg, pOleStream);
8354 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8356 if(pstg == NULL || pOleStream == NULL)
8358 hRes = E_INVALIDARG;
8360 if(hRes == S_OK)
8362 /* Get the ProgID */
8363 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8364 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8366 if(hRes == S_OK)
8368 /* Was it originally Ole10 */
8369 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8370 if(hRes == S_OK)
8372 IStream_Release(pStream);
8373 /* Get Presentation Data for Ole10Native */
8374 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8376 else
8378 /* Get Presentation Data (OLE20) */
8379 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8382 /* Save OLESTREAM */
8383 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8384 if(hRes == S_OK)
8386 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8391 /* Free allocated memory */
8392 for(i=0; i < 2; i++)
8394 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8397 return hRes;
8400 /***********************************************************************
8401 * GetConvertStg (OLE32.@)
8403 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8404 FIXME("unimplemented stub!\n");
8405 return E_FAIL;
8408 /******************************************************************************
8409 * StgIsStorageFile [OLE32.@]
8410 * Verify if the file contains a storage object
8412 * PARAMS
8413 * fn [ I] Filename
8415 * RETURNS
8416 * S_OK if file has magic bytes as a storage object
8417 * S_FALSE if file is not storage
8419 HRESULT WINAPI
8420 StgIsStorageFile(LPCOLESTR fn)
8422 HANDLE hf;
8423 BYTE magic[8];
8424 DWORD bytes_read;
8426 TRACE("%s\n", debugstr_w(fn));
8427 hf = CreateFileW(fn, GENERIC_READ,
8428 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8429 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8431 if (hf == INVALID_HANDLE_VALUE)
8432 return STG_E_FILENOTFOUND;
8434 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8436 WARN(" unable to read file\n");
8437 CloseHandle(hf);
8438 return S_FALSE;
8441 CloseHandle(hf);
8443 if (bytes_read != 8) {
8444 WARN(" too short\n");
8445 return S_FALSE;
8448 if (!memcmp(magic,STORAGE_magic,8)) {
8449 WARN(" -> YES\n");
8450 return S_OK;
8453 WARN(" -> Invalid header.\n");
8454 return S_FALSE;
8457 /***********************************************************************
8458 * WriteClassStm (OLE32.@)
8460 * Writes a CLSID to a stream.
8462 * PARAMS
8463 * pStm [I] Stream to write to.
8464 * rclsid [I] CLSID to write.
8466 * RETURNS
8467 * Success: S_OK.
8468 * Failure: HRESULT code.
8470 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8472 TRACE("(%p,%p)\n",pStm,rclsid);
8474 if (!pStm || !rclsid)
8475 return E_INVALIDARG;
8477 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8480 /***********************************************************************
8481 * ReadClassStm (OLE32.@)
8483 * Reads a CLSID from a stream.
8485 * PARAMS
8486 * pStm [I] Stream to read from.
8487 * rclsid [O] CLSID to read.
8489 * RETURNS
8490 * Success: S_OK.
8491 * Failure: HRESULT code.
8493 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8495 ULONG nbByte;
8496 HRESULT res;
8498 TRACE("(%p,%p)\n",pStm,pclsid);
8500 if (!pStm || !pclsid)
8501 return E_INVALIDARG;
8503 /* clear the output args */
8504 *pclsid = CLSID_NULL;
8506 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8508 if (FAILED(res))
8509 return res;
8511 if (nbByte != sizeof(CLSID))
8512 return STG_E_READFAULT;
8513 else
8514 return S_OK;