ole32: Use ILockBytes_Stat to get the filename of a storage.
[wine/testsucceed.git] / dlls / ole32 / storage32.c
blob0eefb099e62241484acc01cbccd66eb6cdc72a79
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 typedef struct TransactedDirEntry
123 /* If applicable, a reference to the original DirEntry in the transacted
124 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
125 DirRef transactedParentEntry;
127 /* True if this entry is being used. */
128 int inuse;
130 /* True if data is up to date. */
131 int read;
133 /* True if this entry has been modified. */
134 int dirty;
136 /* True if this entry's stream has been modified. */
137 int stream_dirty;
139 /* True if this entry has been deleted in the transacted storage, but the
140 * delete has not yet been committed. */
141 int deleted;
143 /* If this entry's stream has been modified, a reference to where the stream
144 * is stored in the snapshot file. */
145 DirRef stream_entry;
147 /* This directory entry's data, including any changes that have been made. */
148 DirEntry data;
150 /* A reference to the parent of this node. This is only valid while we are
151 * committing changes. */
152 DirRef parent;
154 /* A reference to a newly-created entry in the transacted parent. This is
155 * always equal to transactedParentEntry except when committing changes. */
156 DirRef newTransactedParentEntry;
157 } TransactedDirEntry;
159 /****************************************************************************
160 * Transacted storage object.
162 typedef struct TransactedSnapshotImpl
164 struct StorageBaseImpl base;
167 * Modified streams are temporarily saved to the scratch file.
169 StorageBaseImpl *scratch;
171 /* The directory structure is kept here, so that we can track how these
172 * entries relate to those in the parent storage. */
173 TransactedDirEntry *entries;
174 ULONG entries_size;
175 ULONG firstFreeEntry;
178 * Changes are committed to the transacted parent.
180 StorageBaseImpl *transactedParent;
181 } TransactedSnapshotImpl;
183 /* Generic function to create a transacted wrapper for a direct storage object. */
184 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
186 /* OLESTREAM memory structure to use for Get and Put Routines */
187 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
188 typedef struct
190 DWORD dwOleID;
191 DWORD dwTypeID;
192 DWORD dwOleTypeNameLength;
193 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
194 CHAR *pstrOleObjFileName;
195 DWORD dwOleObjFileNameLength;
196 DWORD dwMetaFileWidth;
197 DWORD dwMetaFileHeight;
198 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
199 DWORD dwDataLength;
200 BYTE *pData;
201 }OLECONVERT_OLESTREAM_DATA;
203 /* CompObj Stream structure */
204 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
205 typedef struct
207 BYTE byUnknown1[12];
208 CLSID clsid;
209 DWORD dwCLSIDNameLength;
210 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
211 DWORD dwOleTypeNameLength;
212 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
213 DWORD dwProgIDNameLength;
214 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
215 BYTE byUnknown2[16];
216 }OLECONVERT_ISTORAGE_COMPOBJ;
219 /* Ole Presentation Stream structure */
220 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
221 typedef struct
223 BYTE byUnknown1[28];
224 DWORD dwExtentX;
225 DWORD dwExtentY;
226 DWORD dwSize;
227 BYTE *pData;
228 }OLECONVERT_ISTORAGE_OLEPRES;
232 /***********************************************************************
233 * Forward declaration of internal functions used by the method DestroyElement
235 static HRESULT deleteStorageContents(
236 StorageBaseImpl *parentStorage,
237 DirRef indexToDelete,
238 DirEntry entryDataToDelete);
240 static HRESULT deleteStreamContents(
241 StorageBaseImpl *parentStorage,
242 DirRef indexToDelete,
243 DirEntry entryDataToDelete);
245 static HRESULT removeFromTree(
246 StorageBaseImpl *This,
247 DirRef parentStorageIndex,
248 DirRef deletedIndex);
250 /***********************************************************************
251 * Declaration of the functions used to manipulate DirEntry
254 static HRESULT insertIntoTree(
255 StorageBaseImpl *This,
256 DirRef parentStorageIndex,
257 DirRef newEntryIndex);
259 static LONG entryNameCmp(
260 const OLECHAR *name1,
261 const OLECHAR *name2);
263 static DirRef findElement(
264 StorageBaseImpl *storage,
265 DirRef storageEntry,
266 const OLECHAR *name,
267 DirEntry *data);
269 static HRESULT findTreeParent(
270 StorageBaseImpl *storage,
271 DirRef storageEntry,
272 const OLECHAR *childName,
273 DirEntry *parentData,
274 DirRef *parentEntry,
275 ULONG *relation);
277 /***********************************************************************
278 * Declaration of miscellaneous functions...
280 static HRESULT validateSTGM(DWORD stgmValue);
282 static DWORD GetShareModeFromSTGM(DWORD stgm);
283 static DWORD GetAccessModeFromSTGM(DWORD stgm);
284 static DWORD GetCreationModeFromSTGM(DWORD stgm);
286 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
289 /****************************************************************************
290 * IEnumSTATSTGImpl definitions.
292 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
293 * This class allows iterating through the content of a storage and to find
294 * specific items inside it.
296 struct IEnumSTATSTGImpl
298 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
299 * since we want to cast this in an IEnumSTATSTG pointer */
301 LONG ref; /* Reference count */
302 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
303 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
305 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
309 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
310 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
312 /************************************************************************
313 ** Block Functions
316 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
318 return (index+1) * This->bigBlockSize;
321 /************************************************************************
322 ** Storage32BaseImpl implementation
324 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
325 ULARGE_INTEGER offset,
326 void* buffer,
327 ULONG size,
328 ULONG* bytesRead)
330 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
333 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
334 ULARGE_INTEGER offset,
335 const void* buffer,
336 const ULONG size,
337 ULONG* bytesWritten)
339 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
342 /************************************************************************
343 * Storage32BaseImpl_QueryInterface (IUnknown)
345 * This method implements the common QueryInterface for all IStorage32
346 * implementations contained in this file.
348 * See Windows documentation for more details on IUnknown methods.
350 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
351 IStorage* iface,
352 REFIID riid,
353 void** ppvObject)
355 StorageBaseImpl *This = (StorageBaseImpl *)iface;
357 if ( (This==0) || (ppvObject==0) )
358 return E_INVALIDARG;
360 *ppvObject = 0;
362 if (IsEqualGUID(&IID_IUnknown, riid) ||
363 IsEqualGUID(&IID_IStorage, riid))
365 *ppvObject = This;
367 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
369 *ppvObject = &This->pssVtbl;
372 if ((*ppvObject)==0)
373 return E_NOINTERFACE;
375 IStorage_AddRef(iface);
377 return S_OK;
380 /************************************************************************
381 * Storage32BaseImpl_AddRef (IUnknown)
383 * This method implements the common AddRef for all IStorage32
384 * implementations contained in this file.
386 * See Windows documentation for more details on IUnknown methods.
388 static ULONG WINAPI StorageBaseImpl_AddRef(
389 IStorage* iface)
391 StorageBaseImpl *This = (StorageBaseImpl *)iface;
392 ULONG ref = InterlockedIncrement(&This->ref);
394 TRACE("(%p) AddRef to %d\n", This, ref);
396 return ref;
399 /************************************************************************
400 * Storage32BaseImpl_Release (IUnknown)
402 * This method implements the common Release for all IStorage32
403 * implementations contained in this file.
405 * See Windows documentation for more details on IUnknown methods.
407 static ULONG WINAPI StorageBaseImpl_Release(
408 IStorage* iface)
410 StorageBaseImpl *This = (StorageBaseImpl *)iface;
412 ULONG ref = InterlockedDecrement(&This->ref);
414 TRACE("(%p) ReleaseRef to %d\n", This, ref);
416 if (ref == 0)
419 * Since we are using a system of base-classes, we want to call the
420 * destructor of the appropriate derived class. To do this, we are
421 * using virtual functions to implement the destructor.
423 StorageBaseImpl_Destroy(This);
426 return ref;
429 /************************************************************************
430 * Storage32BaseImpl_OpenStream (IStorage)
432 * This method will open the specified stream object from the current storage.
434 * See Windows documentation for more details on IStorage methods.
436 static HRESULT WINAPI StorageBaseImpl_OpenStream(
437 IStorage* iface,
438 const OLECHAR* pwcsName, /* [string][in] */
439 void* reserved1, /* [unique][in] */
440 DWORD grfMode, /* [in] */
441 DWORD reserved2, /* [in] */
442 IStream** ppstm) /* [out] */
444 StorageBaseImpl *This = (StorageBaseImpl *)iface;
445 StgStreamImpl* newStream;
446 DirEntry currentEntry;
447 DirRef streamEntryRef;
448 HRESULT res = STG_E_UNKNOWN;
450 TRACE("(%p, %s, %p, %x, %d, %p)\n",
451 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
453 if ( (pwcsName==NULL) || (ppstm==0) )
455 res = E_INVALIDARG;
456 goto end;
459 *ppstm = NULL;
461 if ( FAILED( validateSTGM(grfMode) ) ||
462 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
464 res = STG_E_INVALIDFLAG;
465 goto end;
469 * As documented.
471 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
473 res = STG_E_INVALIDFUNCTION;
474 goto end;
477 if (This->reverted)
479 res = STG_E_REVERTED;
480 goto end;
484 * Check that we're compatible with the parent's storage mode, but
485 * only if we are not in transacted mode
487 if(!(This->openFlags & STGM_TRANSACTED)) {
488 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
490 res = STG_E_INVALIDFLAG;
491 goto end;
496 * Search for the element with the given name
498 streamEntryRef = findElement(
499 This,
500 This->storageDirEntry,
501 pwcsName,
502 &currentEntry);
505 * If it was found, construct the stream object and return a pointer to it.
507 if ( (streamEntryRef!=DIRENTRY_NULL) &&
508 (currentEntry.stgType==STGTY_STREAM) )
510 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
512 /* A single stream cannot be opened a second time. */
513 res = STG_E_ACCESSDENIED;
514 goto end;
517 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
519 if (newStream!=0)
521 newStream->grfMode = grfMode;
522 *ppstm = (IStream*)newStream;
524 IStream_AddRef(*ppstm);
526 res = S_OK;
527 goto end;
530 res = E_OUTOFMEMORY;
531 goto end;
534 res = STG_E_FILENOTFOUND;
536 end:
537 if (res == S_OK)
538 TRACE("<-- IStream %p\n", *ppstm);
539 TRACE("<-- %08x\n", res);
540 return res;
543 /************************************************************************
544 * Storage32BaseImpl_OpenStorage (IStorage)
546 * This method will open a new storage object from the current storage.
548 * See Windows documentation for more details on IStorage methods.
550 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
551 IStorage* iface,
552 const OLECHAR* pwcsName, /* [string][unique][in] */
553 IStorage* pstgPriority, /* [unique][in] */
554 DWORD grfMode, /* [in] */
555 SNB snbExclude, /* [unique][in] */
556 DWORD reserved, /* [in] */
557 IStorage** ppstg) /* [out] */
559 StorageBaseImpl *This = (StorageBaseImpl *)iface;
560 StorageInternalImpl* newStorage;
561 StorageBaseImpl* newTransactedStorage;
562 DirEntry currentEntry;
563 DirRef storageEntryRef;
564 HRESULT res = STG_E_UNKNOWN;
566 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
567 iface, debugstr_w(pwcsName), pstgPriority,
568 grfMode, snbExclude, reserved, ppstg);
570 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
572 res = E_INVALIDARG;
573 goto end;
576 if (This->openFlags & STGM_SIMPLE)
578 res = STG_E_INVALIDFUNCTION;
579 goto end;
582 /* as documented */
583 if (snbExclude != NULL)
585 res = STG_E_INVALIDPARAMETER;
586 goto end;
589 if ( FAILED( validateSTGM(grfMode) ))
591 res = STG_E_INVALIDFLAG;
592 goto end;
596 * As documented.
598 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
599 (grfMode & STGM_DELETEONRELEASE) ||
600 (grfMode & STGM_PRIORITY) )
602 res = STG_E_INVALIDFUNCTION;
603 goto end;
606 if (This->reverted)
607 return STG_E_REVERTED;
610 * Check that we're compatible with the parent's storage mode,
611 * but only if we are not transacted
613 if(!(This->openFlags & STGM_TRANSACTED)) {
614 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
616 res = STG_E_ACCESSDENIED;
617 goto end;
621 *ppstg = NULL;
623 storageEntryRef = findElement(
624 This,
625 This->storageDirEntry,
626 pwcsName,
627 &currentEntry);
629 if ( (storageEntryRef!=DIRENTRY_NULL) &&
630 (currentEntry.stgType==STGTY_STORAGE) )
632 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
634 /* A single storage cannot be opened a second time. */
635 res = STG_E_ACCESSDENIED;
636 goto end;
639 newStorage = StorageInternalImpl_Construct(
640 This,
641 grfMode,
642 storageEntryRef);
644 if (newStorage != 0)
646 if (grfMode & STGM_TRANSACTED)
648 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
650 if (FAILED(res))
652 HeapFree(GetProcessHeap(), 0, newStorage);
653 goto end;
656 *ppstg = (IStorage*)newTransactedStorage;
658 else
660 *ppstg = (IStorage*)newStorage;
663 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
665 res = S_OK;
666 goto end;
669 res = STG_E_INSUFFICIENTMEMORY;
670 goto end;
673 res = STG_E_FILENOTFOUND;
675 end:
676 TRACE("<-- %08x\n", res);
677 return res;
680 /************************************************************************
681 * Storage32BaseImpl_EnumElements (IStorage)
683 * This method will create an enumerator object that can be used to
684 * retrieve information about all the elements in the storage object.
686 * See Windows documentation for more details on IStorage methods.
688 static HRESULT WINAPI StorageBaseImpl_EnumElements(
689 IStorage* iface,
690 DWORD reserved1, /* [in] */
691 void* reserved2, /* [size_is][unique][in] */
692 DWORD reserved3, /* [in] */
693 IEnumSTATSTG** ppenum) /* [out] */
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 IEnumSTATSTGImpl* newEnum;
698 TRACE("(%p, %d, %p, %d, %p)\n",
699 iface, reserved1, reserved2, reserved3, ppenum);
701 if ( (This==0) || (ppenum==0))
702 return E_INVALIDARG;
704 if (This->reverted)
705 return STG_E_REVERTED;
707 newEnum = IEnumSTATSTGImpl_Construct(
708 This,
709 This->storageDirEntry);
711 if (newEnum!=0)
713 *ppenum = (IEnumSTATSTG*)newEnum;
715 IEnumSTATSTG_AddRef(*ppenum);
717 return S_OK;
720 return E_OUTOFMEMORY;
723 /************************************************************************
724 * Storage32BaseImpl_Stat (IStorage)
726 * This method will retrieve information about this storage object.
728 * See Windows documentation for more details on IStorage methods.
730 static HRESULT WINAPI StorageBaseImpl_Stat(
731 IStorage* iface,
732 STATSTG* pstatstg, /* [out] */
733 DWORD grfStatFlag) /* [in] */
735 StorageBaseImpl *This = (StorageBaseImpl *)iface;
736 DirEntry currentEntry;
737 HRESULT res = STG_E_UNKNOWN;
739 TRACE("(%p, %p, %x)\n",
740 iface, pstatstg, grfStatFlag);
742 if ( (This==0) || (pstatstg==0))
744 res = E_INVALIDARG;
745 goto end;
748 if (This->reverted)
750 res = STG_E_REVERTED;
751 goto end;
754 res = StorageBaseImpl_ReadDirEntry(
755 This,
756 This->storageDirEntry,
757 &currentEntry);
759 if (SUCCEEDED(res))
761 StorageUtl_CopyDirEntryToSTATSTG(
762 This,
763 pstatstg,
764 &currentEntry,
765 grfStatFlag);
767 pstatstg->grfMode = This->openFlags;
768 pstatstg->grfStateBits = This->stateBits;
771 end:
772 if (res == S_OK)
774 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);
776 TRACE("<-- %08x\n", res);
777 return res;
780 /************************************************************************
781 * Storage32BaseImpl_RenameElement (IStorage)
783 * This method will rename the specified element.
785 * See Windows documentation for more details on IStorage methods.
787 static HRESULT WINAPI StorageBaseImpl_RenameElement(
788 IStorage* iface,
789 const OLECHAR* pwcsOldName, /* [in] */
790 const OLECHAR* pwcsNewName) /* [in] */
792 StorageBaseImpl *This = (StorageBaseImpl *)iface;
793 DirEntry currentEntry;
794 DirRef currentEntryRef;
796 TRACE("(%p, %s, %s)\n",
797 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
799 if (This->reverted)
800 return STG_E_REVERTED;
802 currentEntryRef = findElement(This,
803 This->storageDirEntry,
804 pwcsNewName,
805 &currentEntry);
807 if (currentEntryRef != DIRENTRY_NULL)
810 * There is already an element with the new name
812 return STG_E_FILEALREADYEXISTS;
816 * Search for the old element name
818 currentEntryRef = findElement(This,
819 This->storageDirEntry,
820 pwcsOldName,
821 &currentEntry);
823 if (currentEntryRef != DIRENTRY_NULL)
825 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
826 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
828 WARN("Element is already open; cannot rename.\n");
829 return STG_E_ACCESSDENIED;
832 /* Remove the element from its current position in the tree */
833 removeFromTree(This, This->storageDirEntry,
834 currentEntryRef);
836 /* Change the name of the element */
837 strcpyW(currentEntry.name, pwcsNewName);
839 /* Delete any sibling links */
840 currentEntry.leftChild = DIRENTRY_NULL;
841 currentEntry.rightChild = DIRENTRY_NULL;
843 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
844 &currentEntry);
846 /* Insert the element in a new position in the tree */
847 insertIntoTree(This, This->storageDirEntry,
848 currentEntryRef);
850 else
853 * There is no element with the old name
855 return STG_E_FILENOTFOUND;
858 return S_OK;
861 /************************************************************************
862 * Storage32BaseImpl_CreateStream (IStorage)
864 * This method will create a stream object within this storage
866 * See Windows documentation for more details on IStorage methods.
868 static HRESULT WINAPI StorageBaseImpl_CreateStream(
869 IStorage* iface,
870 const OLECHAR* pwcsName, /* [string][in] */
871 DWORD grfMode, /* [in] */
872 DWORD reserved1, /* [in] */
873 DWORD reserved2, /* [in] */
874 IStream** ppstm) /* [out] */
876 StorageBaseImpl *This = (StorageBaseImpl *)iface;
877 StgStreamImpl* newStream;
878 DirEntry currentEntry, newStreamEntry;
879 DirRef currentEntryRef, newStreamEntryRef;
880 HRESULT hr;
882 TRACE("(%p, %s, %x, %d, %d, %p)\n",
883 iface, debugstr_w(pwcsName), grfMode,
884 reserved1, reserved2, ppstm);
886 if (ppstm == 0)
887 return STG_E_INVALIDPOINTER;
889 if (pwcsName == 0)
890 return STG_E_INVALIDNAME;
892 if (reserved1 || reserved2)
893 return STG_E_INVALIDPARAMETER;
895 if ( FAILED( validateSTGM(grfMode) ))
896 return STG_E_INVALIDFLAG;
898 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
899 return STG_E_INVALIDFLAG;
901 if (This->reverted)
902 return STG_E_REVERTED;
905 * As documented.
907 if ((grfMode & STGM_DELETEONRELEASE) ||
908 (grfMode & STGM_TRANSACTED))
909 return STG_E_INVALIDFUNCTION;
912 * Don't worry about permissions in transacted mode, as we can always write
913 * changes; we just can't always commit them.
915 if(!(This->openFlags & STGM_TRANSACTED)) {
916 /* Can't create a stream on read-only storage */
917 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
918 return STG_E_ACCESSDENIED;
920 /* Can't create a stream with greater access than the parent. */
921 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
922 return STG_E_ACCESSDENIED;
925 if(This->openFlags & STGM_SIMPLE)
926 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
928 *ppstm = 0;
930 currentEntryRef = findElement(This,
931 This->storageDirEntry,
932 pwcsName,
933 &currentEntry);
935 if (currentEntryRef != DIRENTRY_NULL)
938 * An element with this name already exists
940 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
942 IStorage_DestroyElement(iface, pwcsName);
944 else
945 return STG_E_FILEALREADYEXISTS;
949 * memset the empty entry
951 memset(&newStreamEntry, 0, sizeof(DirEntry));
953 newStreamEntry.sizeOfNameString =
954 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
956 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
957 return STG_E_INVALIDNAME;
959 strcpyW(newStreamEntry.name, pwcsName);
961 newStreamEntry.stgType = STGTY_STREAM;
962 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
963 newStreamEntry.size.u.LowPart = 0;
964 newStreamEntry.size.u.HighPart = 0;
966 newStreamEntry.leftChild = DIRENTRY_NULL;
967 newStreamEntry.rightChild = DIRENTRY_NULL;
968 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
970 /* call CoFileTime to get the current time
971 newStreamEntry.ctime
972 newStreamEntry.mtime
975 /* newStreamEntry.clsid */
978 * Create an entry with the new data
980 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
981 if (FAILED(hr))
982 return hr;
985 * Insert the new entry in the parent storage's tree.
987 hr = insertIntoTree(
988 This,
989 This->storageDirEntry,
990 newStreamEntryRef);
991 if (FAILED(hr))
993 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
994 return hr;
998 * Open the stream to return it.
1000 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1002 if (newStream != 0)
1004 *ppstm = (IStream*)newStream;
1006 IStream_AddRef(*ppstm);
1008 else
1010 return STG_E_INSUFFICIENTMEMORY;
1013 return S_OK;
1016 /************************************************************************
1017 * Storage32BaseImpl_SetClass (IStorage)
1019 * This method will write the specified CLSID in the directory entry of this
1020 * storage.
1022 * See Windows documentation for more details on IStorage methods.
1024 static HRESULT WINAPI StorageBaseImpl_SetClass(
1025 IStorage* iface,
1026 REFCLSID clsid) /* [in] */
1028 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1029 HRESULT hRes;
1030 DirEntry currentEntry;
1032 TRACE("(%p, %p)\n", iface, clsid);
1034 if (This->reverted)
1035 return STG_E_REVERTED;
1037 hRes = StorageBaseImpl_ReadDirEntry(This,
1038 This->storageDirEntry,
1039 &currentEntry);
1040 if (SUCCEEDED(hRes))
1042 currentEntry.clsid = *clsid;
1044 hRes = StorageBaseImpl_WriteDirEntry(This,
1045 This->storageDirEntry,
1046 &currentEntry);
1049 return hRes;
1052 /************************************************************************
1053 ** Storage32Impl implementation
1056 /************************************************************************
1057 * Storage32BaseImpl_CreateStorage (IStorage)
1059 * This method will create the storage object within the provided storage.
1061 * See Windows documentation for more details on IStorage methods.
1063 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1064 IStorage* iface,
1065 const OLECHAR *pwcsName, /* [string][in] */
1066 DWORD grfMode, /* [in] */
1067 DWORD reserved1, /* [in] */
1068 DWORD reserved2, /* [in] */
1069 IStorage **ppstg) /* [out] */
1071 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1073 DirEntry currentEntry;
1074 DirEntry newEntry;
1075 DirRef currentEntryRef;
1076 DirRef newEntryRef;
1077 HRESULT hr;
1079 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1080 iface, debugstr_w(pwcsName), grfMode,
1081 reserved1, reserved2, ppstg);
1083 if (ppstg == 0)
1084 return STG_E_INVALIDPOINTER;
1086 if (This->openFlags & STGM_SIMPLE)
1088 return STG_E_INVALIDFUNCTION;
1091 if (pwcsName == 0)
1092 return STG_E_INVALIDNAME;
1094 *ppstg = NULL;
1096 if ( FAILED( validateSTGM(grfMode) ) ||
1097 (grfMode & STGM_DELETEONRELEASE) )
1099 WARN("bad grfMode: 0x%x\n", grfMode);
1100 return STG_E_INVALIDFLAG;
1103 if (This->reverted)
1104 return STG_E_REVERTED;
1107 * Check that we're compatible with the parent's storage mode
1109 if ( !(This->openFlags & STGM_TRANSACTED) &&
1110 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1112 WARN("access denied\n");
1113 return STG_E_ACCESSDENIED;
1116 currentEntryRef = findElement(This,
1117 This->storageDirEntry,
1118 pwcsName,
1119 &currentEntry);
1121 if (currentEntryRef != DIRENTRY_NULL)
1124 * An element with this name already exists
1126 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1127 ((This->openFlags & STGM_TRANSACTED) ||
1128 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1130 hr = IStorage_DestroyElement(iface, pwcsName);
1131 if (FAILED(hr))
1132 return hr;
1134 else
1136 WARN("file already exists\n");
1137 return STG_E_FILEALREADYEXISTS;
1140 else if (!(This->openFlags & STGM_TRANSACTED) &&
1141 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1143 WARN("read-only storage\n");
1144 return STG_E_ACCESSDENIED;
1147 memset(&newEntry, 0, sizeof(DirEntry));
1149 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1151 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1153 FIXME("name too long\n");
1154 return STG_E_INVALIDNAME;
1157 strcpyW(newEntry.name, pwcsName);
1159 newEntry.stgType = STGTY_STORAGE;
1160 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1161 newEntry.size.u.LowPart = 0;
1162 newEntry.size.u.HighPart = 0;
1164 newEntry.leftChild = DIRENTRY_NULL;
1165 newEntry.rightChild = DIRENTRY_NULL;
1166 newEntry.dirRootEntry = DIRENTRY_NULL;
1168 /* call CoFileTime to get the current time
1169 newEntry.ctime
1170 newEntry.mtime
1173 /* newEntry.clsid */
1176 * Create a new directory entry for the storage
1178 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1179 if (FAILED(hr))
1180 return hr;
1183 * Insert the new directory entry into the parent storage's tree
1185 hr = insertIntoTree(
1186 This,
1187 This->storageDirEntry,
1188 newEntryRef);
1189 if (FAILED(hr))
1191 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1192 return hr;
1196 * Open it to get a pointer to return.
1198 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1200 if( (hr != S_OK) || (*ppstg == NULL))
1202 return hr;
1206 return S_OK;
1210 /***************************************************************************
1212 * Internal Method
1214 * Reserve a directory entry in the file and initialize it.
1216 static HRESULT StorageImpl_CreateDirEntry(
1217 StorageBaseImpl *base,
1218 const DirEntry *newData,
1219 DirRef *index)
1221 StorageImpl *storage = (StorageImpl*)base;
1222 ULONG currentEntryIndex = 0;
1223 ULONG newEntryIndex = DIRENTRY_NULL;
1224 HRESULT hr = S_OK;
1225 BYTE currentData[RAW_DIRENTRY_SIZE];
1226 WORD sizeOfNameString;
1230 hr = StorageImpl_ReadRawDirEntry(storage,
1231 currentEntryIndex,
1232 currentData);
1234 if (SUCCEEDED(hr))
1236 StorageUtl_ReadWord(
1237 currentData,
1238 OFFSET_PS_NAMELENGTH,
1239 &sizeOfNameString);
1241 if (sizeOfNameString == 0)
1244 * The entry exists and is available, we found it.
1246 newEntryIndex = currentEntryIndex;
1249 else
1252 * We exhausted the directory entries, we will create more space below
1254 newEntryIndex = currentEntryIndex;
1256 currentEntryIndex++;
1258 } while (newEntryIndex == DIRENTRY_NULL);
1261 * grow the directory stream
1263 if (FAILED(hr))
1265 BYTE emptyData[RAW_DIRENTRY_SIZE];
1266 ULARGE_INTEGER newSize;
1267 ULONG entryIndex;
1268 ULONG lastEntry = 0;
1269 ULONG blockCount = 0;
1272 * obtain the new count of blocks in the directory stream
1274 blockCount = BlockChainStream_GetCount(
1275 storage->rootBlockChain)+1;
1278 * initialize the size used by the directory stream
1280 newSize.u.HighPart = 0;
1281 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1284 * add a block to the directory stream
1286 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1289 * memset the empty entry in order to initialize the unused newly
1290 * created entries
1292 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1295 * initialize them
1297 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1299 for(
1300 entryIndex = newEntryIndex + 1;
1301 entryIndex < lastEntry;
1302 entryIndex++)
1304 StorageImpl_WriteRawDirEntry(
1305 storage,
1306 entryIndex,
1307 emptyData);
1310 StorageImpl_SaveFileHeader(storage);
1313 UpdateRawDirEntry(currentData, newData);
1315 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1317 if (SUCCEEDED(hr))
1318 *index = newEntryIndex;
1320 return hr;
1323 /***************************************************************************
1325 * Internal Method
1327 * Mark a directory entry in the file as free.
1329 static HRESULT StorageImpl_DestroyDirEntry(
1330 StorageBaseImpl *base,
1331 DirRef index)
1333 HRESULT hr;
1334 BYTE emptyData[RAW_DIRENTRY_SIZE];
1335 StorageImpl *storage = (StorageImpl*)base;
1337 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1339 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1341 return hr;
1345 /****************************************************************************
1347 * Internal Method
1349 * Case insensitive comparison of DirEntry.name by first considering
1350 * their size.
1352 * Returns <0 when name1 < name2
1353 * >0 when name1 > name2
1354 * 0 when name1 == name2
1356 static LONG entryNameCmp(
1357 const OLECHAR *name1,
1358 const OLECHAR *name2)
1360 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1362 while (diff == 0 && *name1 != 0)
1365 * We compare the string themselves only when they are of the same length
1367 diff = toupperW(*name1++) - toupperW(*name2++);
1370 return diff;
1373 /****************************************************************************
1375 * Internal Method
1377 * Add a directory entry to a storage
1379 static HRESULT insertIntoTree(
1380 StorageBaseImpl *This,
1381 DirRef parentStorageIndex,
1382 DirRef newEntryIndex)
1384 DirEntry currentEntry;
1385 DirEntry newEntry;
1388 * Read the inserted entry
1390 StorageBaseImpl_ReadDirEntry(This,
1391 newEntryIndex,
1392 &newEntry);
1395 * Read the storage entry
1397 StorageBaseImpl_ReadDirEntry(This,
1398 parentStorageIndex,
1399 &currentEntry);
1401 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1404 * The root storage contains some element, therefore, start the research
1405 * for the appropriate location.
1407 BOOL found = 0;
1408 DirRef current, next, previous, currentEntryId;
1411 * Keep a reference to the root of the storage's element tree
1413 currentEntryId = currentEntry.dirRootEntry;
1416 * Read
1418 StorageBaseImpl_ReadDirEntry(This,
1419 currentEntry.dirRootEntry,
1420 &currentEntry);
1422 previous = currentEntry.leftChild;
1423 next = currentEntry.rightChild;
1424 current = currentEntryId;
1426 while (found == 0)
1428 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1430 if (diff < 0)
1432 if (previous != DIRENTRY_NULL)
1434 StorageBaseImpl_ReadDirEntry(This,
1435 previous,
1436 &currentEntry);
1437 current = previous;
1439 else
1441 currentEntry.leftChild = newEntryIndex;
1442 StorageBaseImpl_WriteDirEntry(This,
1443 current,
1444 &currentEntry);
1445 found = 1;
1448 else if (diff > 0)
1450 if (next != DIRENTRY_NULL)
1452 StorageBaseImpl_ReadDirEntry(This,
1453 next,
1454 &currentEntry);
1455 current = next;
1457 else
1459 currentEntry.rightChild = newEntryIndex;
1460 StorageBaseImpl_WriteDirEntry(This,
1461 current,
1462 &currentEntry);
1463 found = 1;
1466 else
1469 * Trying to insert an item with the same name in the
1470 * subtree structure.
1472 return STG_E_FILEALREADYEXISTS;
1475 previous = currentEntry.leftChild;
1476 next = currentEntry.rightChild;
1479 else
1482 * The storage is empty, make the new entry the root of its element tree
1484 currentEntry.dirRootEntry = newEntryIndex;
1485 StorageBaseImpl_WriteDirEntry(This,
1486 parentStorageIndex,
1487 &currentEntry);
1490 return S_OK;
1493 /****************************************************************************
1495 * Internal Method
1497 * Find and read the element of a storage with the given name.
1499 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1500 const OLECHAR *name, DirEntry *data)
1502 DirRef currentEntry;
1504 /* Read the storage entry to find the root of the tree. */
1505 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1507 currentEntry = data->dirRootEntry;
1509 while (currentEntry != DIRENTRY_NULL)
1511 LONG cmp;
1513 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1515 cmp = entryNameCmp(name, data->name);
1517 if (cmp == 0)
1518 /* found it */
1519 break;
1521 else if (cmp < 0)
1522 currentEntry = data->leftChild;
1524 else if (cmp > 0)
1525 currentEntry = data->rightChild;
1528 return currentEntry;
1531 /****************************************************************************
1533 * Internal Method
1535 * Find and read the binary tree parent of the element with the given name.
1537 * If there is no such element, find a place where it could be inserted and
1538 * return STG_E_FILENOTFOUND.
1540 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1541 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1542 ULONG *relation)
1544 DirRef childEntry;
1545 DirEntry childData;
1547 /* Read the storage entry to find the root of the tree. */
1548 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1550 *parentEntry = storageEntry;
1551 *relation = DIRENTRY_RELATION_DIR;
1553 childEntry = parentData->dirRootEntry;
1555 while (childEntry != DIRENTRY_NULL)
1557 LONG cmp;
1559 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1561 cmp = entryNameCmp(childName, childData.name);
1563 if (cmp == 0)
1564 /* found it */
1565 break;
1567 else if (cmp < 0)
1569 *parentData = childData;
1570 *parentEntry = childEntry;
1571 *relation = DIRENTRY_RELATION_PREVIOUS;
1573 childEntry = parentData->leftChild;
1576 else if (cmp > 0)
1578 *parentData = childData;
1579 *parentEntry = childEntry;
1580 *relation = DIRENTRY_RELATION_NEXT;
1582 childEntry = parentData->rightChild;
1586 if (childEntry == DIRENTRY_NULL)
1587 return STG_E_FILENOTFOUND;
1588 else
1589 return S_OK;
1593 /*************************************************************************
1594 * CopyTo (IStorage)
1596 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1597 IStorage* iface,
1598 DWORD ciidExclude, /* [in] */
1599 const IID* rgiidExclude, /* [size_is][unique][in] */
1600 SNB snbExclude, /* [unique][in] */
1601 IStorage* pstgDest) /* [unique][in] */
1603 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1605 IEnumSTATSTG *elements = 0;
1606 STATSTG curElement, strStat;
1607 HRESULT hr;
1608 IStorage *pstgTmp, *pstgChild;
1609 IStream *pstrTmp, *pstrChild;
1610 DirRef srcEntryRef;
1611 DirEntry srcEntry;
1612 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1613 int i;
1615 TRACE("(%p, %d, %p, %p, %p)\n",
1616 iface, ciidExclude, rgiidExclude,
1617 snbExclude, pstgDest);
1619 if ( pstgDest == 0 )
1620 return STG_E_INVALIDPOINTER;
1623 * Enumerate the elements
1625 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1627 if ( hr != S_OK )
1628 return hr;
1631 * set the class ID
1633 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1634 IStorage_SetClass( pstgDest, &curElement.clsid );
1636 for(i = 0; i < ciidExclude; ++i)
1638 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1639 skip_storage = TRUE;
1640 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1641 skip_stream = TRUE;
1642 else
1643 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1649 * Obtain the next element
1651 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1653 if ( hr == S_FALSE )
1655 hr = S_OK; /* done, every element has been copied */
1656 break;
1659 if ( snbExclude )
1661 WCHAR **snb = snbExclude;
1662 skip = FALSE;
1663 while ( *snb != NULL && !skip )
1665 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1666 skip = TRUE;
1667 ++snb;
1671 if ( skip )
1672 goto cleanup;
1674 if (curElement.type == STGTY_STORAGE)
1676 if(skip_storage)
1677 goto cleanup;
1680 * open child source storage
1682 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1683 STGM_READ|STGM_SHARE_EXCLUSIVE,
1684 NULL, 0, &pstgChild );
1686 if (hr != S_OK)
1687 goto cleanup;
1690 * create a new storage in destination storage
1692 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1693 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1694 0, 0,
1695 &pstgTmp );
1697 * if it already exist, don't create a new one use this one
1699 if (hr == STG_E_FILEALREADYEXISTS)
1701 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1702 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1703 NULL, 0, &pstgTmp );
1706 if (hr == S_OK)
1709 * do the copy recursively
1711 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1712 NULL, pstgTmp );
1714 IStorage_Release( pstgTmp );
1717 IStorage_Release( pstgChild );
1719 else if (curElement.type == STGTY_STREAM)
1721 if(skip_stream)
1722 goto cleanup;
1725 * create a new stream in destination storage. If the stream already
1726 * exist, it will be deleted and a new one will be created.
1728 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1729 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1730 0, 0, &pstrTmp );
1732 if (hr != S_OK)
1733 goto cleanup;
1736 * open child stream storage. This operation must succeed even if the
1737 * stream is already open, so we use internal functions to do it.
1739 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1740 &srcEntry);
1741 if (!srcEntryRef)
1743 ERR("source stream not found\n");
1744 hr = STG_E_DOCFILECORRUPT;
1747 if (hr == S_OK)
1749 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1750 if (pstrChild)
1751 IStream_AddRef(pstrChild);
1752 else
1753 hr = E_OUTOFMEMORY;
1756 if (hr == S_OK)
1759 * Get the size of the source stream
1761 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1764 * Set the size of the destination stream.
1766 IStream_SetSize(pstrTmp, strStat.cbSize);
1769 * do the copy
1771 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1772 NULL, NULL );
1774 IStream_Release( pstrChild );
1777 IStream_Release( pstrTmp );
1779 else
1781 WARN("unknown element type: %d\n", curElement.type);
1784 cleanup:
1785 CoTaskMemFree(curElement.pwcsName);
1786 } while (hr == S_OK);
1789 * Clean-up
1791 IEnumSTATSTG_Release(elements);
1793 return hr;
1796 /*************************************************************************
1797 * MoveElementTo (IStorage)
1799 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1800 IStorage* iface,
1801 const OLECHAR *pwcsName, /* [string][in] */
1802 IStorage *pstgDest, /* [unique][in] */
1803 const OLECHAR *pwcsNewName,/* [string][in] */
1804 DWORD grfFlags) /* [in] */
1806 FIXME("(%p %s %p %s %u): stub\n", iface,
1807 debugstr_w(pwcsName), pstgDest,
1808 debugstr_w(pwcsNewName), grfFlags);
1809 return E_NOTIMPL;
1812 /*************************************************************************
1813 * Commit (IStorage)
1815 * Ensures that any changes made to a storage object open in transacted mode
1816 * are reflected in the parent storage
1818 * NOTES
1819 * Wine doesn't implement transacted mode, which seems to be a basic
1820 * optimization, so we can ignore this stub for now.
1822 static HRESULT WINAPI StorageImpl_Commit(
1823 IStorage* iface,
1824 DWORD grfCommitFlags)/* [in] */
1826 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1827 return S_OK;
1830 /*************************************************************************
1831 * Revert (IStorage)
1833 * Discard all changes that have been made since the last commit operation
1835 static HRESULT WINAPI StorageImpl_Revert(
1836 IStorage* iface)
1838 TRACE("(%p)\n", iface);
1839 return S_OK;
1842 /*************************************************************************
1843 * DestroyElement (IStorage)
1845 * Strategy: This implementation is built this way for simplicity not for speed.
1846 * I always delete the topmost element of the enumeration and adjust
1847 * the deleted element pointer all the time. This takes longer to
1848 * do but allow to reinvoke DestroyElement whenever we encounter a
1849 * storage object. The optimisation resides in the usage of another
1850 * enumeration strategy that would give all the leaves of a storage
1851 * first. (postfix order)
1853 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1854 IStorage* iface,
1855 const OLECHAR *pwcsName)/* [string][in] */
1857 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1859 HRESULT hr = S_OK;
1860 DirEntry entryToDelete;
1861 DirRef entryToDeleteRef;
1863 TRACE("(%p, %s)\n",
1864 iface, debugstr_w(pwcsName));
1866 if (pwcsName==NULL)
1867 return STG_E_INVALIDPOINTER;
1869 if (This->reverted)
1870 return STG_E_REVERTED;
1872 if ( !(This->openFlags & STGM_TRANSACTED) &&
1873 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1874 return STG_E_ACCESSDENIED;
1876 entryToDeleteRef = findElement(
1877 This,
1878 This->storageDirEntry,
1879 pwcsName,
1880 &entryToDelete);
1882 if ( entryToDeleteRef == DIRENTRY_NULL )
1884 return STG_E_FILENOTFOUND;
1887 if ( entryToDelete.stgType == STGTY_STORAGE )
1889 hr = deleteStorageContents(
1890 This,
1891 entryToDeleteRef,
1892 entryToDelete);
1894 else if ( entryToDelete.stgType == STGTY_STREAM )
1896 hr = deleteStreamContents(
1897 This,
1898 entryToDeleteRef,
1899 entryToDelete);
1902 if (hr!=S_OK)
1903 return hr;
1906 * Remove the entry from its parent storage
1908 hr = removeFromTree(
1909 This,
1910 This->storageDirEntry,
1911 entryToDeleteRef);
1914 * Invalidate the entry
1916 if (SUCCEEDED(hr))
1917 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1919 return hr;
1923 /******************************************************************************
1924 * Internal stream list handlers
1927 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1929 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1930 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1933 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1935 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1936 list_remove(&(strm->StrmListEntry));
1939 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1941 StgStreamImpl *strm;
1943 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1945 if (strm->dirEntry == streamEntry)
1947 return TRUE;
1951 return FALSE;
1954 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1956 StorageInternalImpl *childstg;
1958 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1960 if (childstg->base.storageDirEntry == storageEntry)
1962 return TRUE;
1966 return FALSE;
1969 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1971 struct list *cur, *cur2;
1972 StgStreamImpl *strm=NULL;
1973 StorageInternalImpl *childstg=NULL;
1975 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1976 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1977 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1978 strm->parentStorage = NULL;
1979 list_remove(cur);
1982 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1983 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1984 StorageBaseImpl_Invalidate( &childstg->base );
1987 if (stg->transactedChild)
1989 StorageBaseImpl_Invalidate(stg->transactedChild);
1991 stg->transactedChild = NULL;
1996 /*********************************************************************
1998 * Internal Method
2000 * Delete the contents of a storage entry.
2003 static HRESULT deleteStorageContents(
2004 StorageBaseImpl *parentStorage,
2005 DirRef indexToDelete,
2006 DirEntry entryDataToDelete)
2008 IEnumSTATSTG *elements = 0;
2009 IStorage *childStorage = 0;
2010 STATSTG currentElement;
2011 HRESULT hr;
2012 HRESULT destroyHr = S_OK;
2013 StorageInternalImpl *stg, *stg2;
2015 /* Invalidate any open storage objects. */
2016 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2018 if (stg->base.storageDirEntry == indexToDelete)
2020 StorageBaseImpl_Invalidate(&stg->base);
2025 * Open the storage and enumerate it
2027 hr = StorageBaseImpl_OpenStorage(
2028 (IStorage*)parentStorage,
2029 entryDataToDelete.name,
2031 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2034 &childStorage);
2036 if (hr != S_OK)
2038 return hr;
2042 * Enumerate the elements
2044 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2049 * Obtain the next element
2051 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2052 if (hr==S_OK)
2054 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2056 CoTaskMemFree(currentElement.pwcsName);
2060 * We need to Reset the enumeration every time because we delete elements
2061 * and the enumeration could be invalid
2063 IEnumSTATSTG_Reset(elements);
2065 } while ((hr == S_OK) && (destroyHr == S_OK));
2067 IStorage_Release(childStorage);
2068 IEnumSTATSTG_Release(elements);
2070 return destroyHr;
2073 /*********************************************************************
2075 * Internal Method
2077 * Perform the deletion of a stream's data
2080 static HRESULT deleteStreamContents(
2081 StorageBaseImpl *parentStorage,
2082 DirRef indexToDelete,
2083 DirEntry entryDataToDelete)
2085 IStream *pis;
2086 HRESULT hr;
2087 ULARGE_INTEGER size;
2088 StgStreamImpl *strm, *strm2;
2090 /* Invalidate any open stream objects. */
2091 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2093 if (strm->dirEntry == indexToDelete)
2095 TRACE("Stream deleted %p\n", strm);
2096 strm->parentStorage = NULL;
2097 list_remove(&strm->StrmListEntry);
2101 size.u.HighPart = 0;
2102 size.u.LowPart = 0;
2104 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2105 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2107 if (hr!=S_OK)
2109 return(hr);
2113 * Zap the stream
2115 hr = IStream_SetSize(pis, size);
2117 if(hr != S_OK)
2119 return hr;
2123 * Release the stream object.
2125 IStream_Release(pis);
2127 return S_OK;
2130 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2132 switch (relation)
2134 case DIRENTRY_RELATION_PREVIOUS:
2135 entry->leftChild = new_target;
2136 break;
2137 case DIRENTRY_RELATION_NEXT:
2138 entry->rightChild = new_target;
2139 break;
2140 case DIRENTRY_RELATION_DIR:
2141 entry->dirRootEntry = new_target;
2142 break;
2143 default:
2144 assert(0);
2148 /*************************************************************************
2150 * Internal Method
2152 * This method removes a directory entry from its parent storage tree without
2153 * freeing any resources attached to it.
2155 static HRESULT removeFromTree(
2156 StorageBaseImpl *This,
2157 DirRef parentStorageIndex,
2158 DirRef deletedIndex)
2160 HRESULT hr = S_OK;
2161 DirEntry entryToDelete;
2162 DirEntry parentEntry;
2163 DirRef parentEntryRef;
2164 ULONG typeOfRelation;
2166 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2168 if (hr != S_OK)
2169 return hr;
2172 * Find the element that links to the one we want to delete.
2174 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2175 &parentEntry, &parentEntryRef, &typeOfRelation);
2177 if (hr != S_OK)
2178 return hr;
2180 if (entryToDelete.leftChild != DIRENTRY_NULL)
2183 * Replace the deleted entry with its left child
2185 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2187 hr = StorageBaseImpl_WriteDirEntry(
2188 This,
2189 parentEntryRef,
2190 &parentEntry);
2191 if(FAILED(hr))
2193 return hr;
2196 if (entryToDelete.rightChild != DIRENTRY_NULL)
2199 * We need to reinsert the right child somewhere. We already know it and
2200 * its children are greater than everything in the left tree, so we
2201 * insert it at the rightmost point in the left tree.
2203 DirRef newRightChildParent = entryToDelete.leftChild;
2204 DirEntry newRightChildParentEntry;
2208 hr = StorageBaseImpl_ReadDirEntry(
2209 This,
2210 newRightChildParent,
2211 &newRightChildParentEntry);
2212 if (FAILED(hr))
2214 return hr;
2217 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2218 newRightChildParent = newRightChildParentEntry.rightChild;
2219 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2221 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2223 hr = StorageBaseImpl_WriteDirEntry(
2224 This,
2225 newRightChildParent,
2226 &newRightChildParentEntry);
2227 if (FAILED(hr))
2229 return hr;
2233 else
2236 * Replace the deleted entry with its right child
2238 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2240 hr = StorageBaseImpl_WriteDirEntry(
2241 This,
2242 parentEntryRef,
2243 &parentEntry);
2244 if(FAILED(hr))
2246 return hr;
2250 return hr;
2254 /******************************************************************************
2255 * SetElementTimes (IStorage)
2257 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2258 IStorage* iface,
2259 const OLECHAR *pwcsName,/* [string][in] */
2260 const FILETIME *pctime, /* [in] */
2261 const FILETIME *patime, /* [in] */
2262 const FILETIME *pmtime) /* [in] */
2264 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2265 return S_OK;
2268 /******************************************************************************
2269 * SetStateBits (IStorage)
2271 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2272 IStorage* iface,
2273 DWORD grfStateBits,/* [in] */
2274 DWORD grfMask) /* [in] */
2276 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2278 if (This->reverted)
2279 return STG_E_REVERTED;
2281 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2282 return S_OK;
2285 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2286 DirRef index, const DirEntry *data)
2288 StorageImpl *This = (StorageImpl*)base;
2289 return StorageImpl_WriteDirEntry(This, index, data);
2292 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2293 DirRef index, DirEntry *data)
2295 StorageImpl *This = (StorageImpl*)base;
2296 return StorageImpl_ReadDirEntry(This, index, data);
2299 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2301 int i;
2303 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2305 if (!This->blockChainCache[i])
2307 return &This->blockChainCache[i];
2311 i = This->blockChainToEvict;
2313 BlockChainStream_Destroy(This->blockChainCache[i]);
2314 This->blockChainCache[i] = NULL;
2316 This->blockChainToEvict++;
2317 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2318 This->blockChainToEvict = 0;
2320 return &This->blockChainCache[i];
2323 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2324 DirRef index)
2326 int i, free_index=-1;
2328 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2330 if (!This->blockChainCache[i])
2332 if (free_index == -1) free_index = i;
2334 else if (This->blockChainCache[i]->ownerDirEntry == index)
2336 return &This->blockChainCache[i];
2340 if (free_index == -1)
2342 free_index = This->blockChainToEvict;
2344 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2345 This->blockChainCache[free_index] = NULL;
2347 This->blockChainToEvict++;
2348 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2349 This->blockChainToEvict = 0;
2352 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2353 return &This->blockChainCache[free_index];
2356 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2358 int i;
2360 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2362 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2364 BlockChainStream_Destroy(This->blockChainCache[i]);
2365 This->blockChainCache[i] = NULL;
2366 return;
2371 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2372 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2374 StorageImpl *This = (StorageImpl*)base;
2375 DirEntry data;
2376 HRESULT hr;
2377 ULONG bytesToRead;
2379 hr = StorageImpl_ReadDirEntry(This, index, &data);
2380 if (FAILED(hr)) return hr;
2382 if (data.size.QuadPart == 0)
2384 *bytesRead = 0;
2385 return S_OK;
2388 if (offset.QuadPart + size > data.size.QuadPart)
2390 bytesToRead = data.size.QuadPart - offset.QuadPart;
2392 else
2394 bytesToRead = size;
2397 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2399 SmallBlockChainStream *stream;
2401 stream = SmallBlockChainStream_Construct(This, NULL, index);
2402 if (!stream) return E_OUTOFMEMORY;
2404 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2406 SmallBlockChainStream_Destroy(stream);
2408 return hr;
2410 else
2412 BlockChainStream *stream = NULL;
2414 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2415 if (!stream) return E_OUTOFMEMORY;
2417 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2419 return hr;
2423 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2424 ULARGE_INTEGER newsize)
2426 StorageImpl *This = (StorageImpl*)base;
2427 DirEntry data;
2428 HRESULT hr;
2429 SmallBlockChainStream *smallblock=NULL;
2430 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2432 hr = StorageImpl_ReadDirEntry(This, index, &data);
2433 if (FAILED(hr)) return hr;
2435 /* In simple mode keep the stream size above the small block limit */
2436 if (This->base.openFlags & STGM_SIMPLE)
2437 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2439 if (data.size.QuadPart == newsize.QuadPart)
2440 return S_OK;
2442 /* Create a block chain object of the appropriate type */
2443 if (data.size.QuadPart == 0)
2445 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2447 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2448 if (!smallblock) return E_OUTOFMEMORY;
2450 else
2452 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2453 bigblock = *pbigblock;
2454 if (!bigblock) return E_OUTOFMEMORY;
2457 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2459 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2460 if (!smallblock) return E_OUTOFMEMORY;
2462 else
2464 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2465 bigblock = *pbigblock;
2466 if (!bigblock) return E_OUTOFMEMORY;
2469 /* Change the block chain type if necessary. */
2470 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2472 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2473 if (!bigblock)
2475 SmallBlockChainStream_Destroy(smallblock);
2476 return E_FAIL;
2479 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2480 *pbigblock = bigblock;
2482 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2484 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2485 if (!smallblock)
2486 return E_FAIL;
2489 /* Set the size of the block chain. */
2490 if (smallblock)
2492 SmallBlockChainStream_SetSize(smallblock, newsize);
2493 SmallBlockChainStream_Destroy(smallblock);
2495 else
2497 BlockChainStream_SetSize(bigblock, newsize);
2500 /* Set the size in the directory entry. */
2501 hr = StorageImpl_ReadDirEntry(This, index, &data);
2502 if (SUCCEEDED(hr))
2504 data.size = newsize;
2506 hr = StorageImpl_WriteDirEntry(This, index, &data);
2508 return hr;
2511 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2512 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2514 StorageImpl *This = (StorageImpl*)base;
2515 DirEntry data;
2516 HRESULT hr;
2517 ULARGE_INTEGER newSize;
2519 hr = StorageImpl_ReadDirEntry(This, index, &data);
2520 if (FAILED(hr)) return hr;
2522 /* Grow the stream if necessary */
2523 newSize.QuadPart = 0;
2524 newSize.QuadPart = offset.QuadPart + size;
2526 if (newSize.QuadPart > data.size.QuadPart)
2528 hr = StorageImpl_StreamSetSize(base, index, newSize);
2529 if (FAILED(hr))
2530 return hr;
2532 hr = StorageImpl_ReadDirEntry(This, index, &data);
2533 if (FAILED(hr)) return hr;
2536 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2538 SmallBlockChainStream *stream;
2540 stream = SmallBlockChainStream_Construct(This, NULL, index);
2541 if (!stream) return E_OUTOFMEMORY;
2543 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2545 SmallBlockChainStream_Destroy(stream);
2547 return hr;
2549 else
2551 BlockChainStream *stream;
2553 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2554 if (!stream) return E_OUTOFMEMORY;
2556 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2558 return hr;
2562 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2563 DirRef src)
2565 StorageImpl *This = (StorageImpl*)base;
2566 DirEntry dst_data, src_data;
2567 HRESULT hr;
2569 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2571 if (SUCCEEDED(hr))
2572 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2574 if (SUCCEEDED(hr))
2576 StorageImpl_DeleteCachedBlockChainStream(This, src);
2577 dst_data.startingBlock = src_data.startingBlock;
2578 dst_data.size = src_data.size;
2580 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2583 return hr;
2586 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2588 StorageImpl *This = (StorageImpl*) iface;
2589 STATSTG statstg;
2590 HRESULT hr;
2592 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2594 *result = statstg.pwcsName;
2596 return hr;
2600 * Virtual function table for the IStorage32Impl class.
2602 static const IStorageVtbl Storage32Impl_Vtbl =
2604 StorageBaseImpl_QueryInterface,
2605 StorageBaseImpl_AddRef,
2606 StorageBaseImpl_Release,
2607 StorageBaseImpl_CreateStream,
2608 StorageBaseImpl_OpenStream,
2609 StorageBaseImpl_CreateStorage,
2610 StorageBaseImpl_OpenStorage,
2611 StorageBaseImpl_CopyTo,
2612 StorageBaseImpl_MoveElementTo,
2613 StorageImpl_Commit,
2614 StorageImpl_Revert,
2615 StorageBaseImpl_EnumElements,
2616 StorageBaseImpl_DestroyElement,
2617 StorageBaseImpl_RenameElement,
2618 StorageBaseImpl_SetElementTimes,
2619 StorageBaseImpl_SetClass,
2620 StorageBaseImpl_SetStateBits,
2621 StorageBaseImpl_Stat
2624 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2626 StorageImpl_Destroy,
2627 StorageImpl_Invalidate,
2628 StorageImpl_GetFilename,
2629 StorageImpl_CreateDirEntry,
2630 StorageImpl_BaseWriteDirEntry,
2631 StorageImpl_BaseReadDirEntry,
2632 StorageImpl_DestroyDirEntry,
2633 StorageImpl_StreamReadAt,
2634 StorageImpl_StreamWriteAt,
2635 StorageImpl_StreamSetSize,
2636 StorageImpl_StreamLink
2639 static HRESULT StorageImpl_Construct(
2640 HANDLE hFile,
2641 LPCOLESTR pwcsName,
2642 ILockBytes* pLkbyt,
2643 DWORD openFlags,
2644 BOOL fileBased,
2645 BOOL create,
2646 ULONG sector_size,
2647 StorageImpl** result)
2649 StorageImpl* This;
2650 HRESULT hr = S_OK;
2651 DirEntry currentEntry;
2652 DirRef currentEntryRef;
2654 if ( FAILED( validateSTGM(openFlags) ))
2655 return STG_E_INVALIDFLAG;
2657 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2658 if (!This)
2659 return E_OUTOFMEMORY;
2661 memset(This, 0, sizeof(StorageImpl));
2663 list_init(&This->base.strmHead);
2665 list_init(&This->base.storageHead);
2667 This->base.lpVtbl = &Storage32Impl_Vtbl;
2668 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2669 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2670 This->base.openFlags = (openFlags & ~STGM_CREATE);
2671 This->base.ref = 1;
2672 This->base.create = create;
2674 This->base.reverted = 0;
2676 This->hFile = hFile;
2679 * Initialize the big block cache.
2681 This->bigBlockSize = sector_size;
2682 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2683 if (hFile)
2684 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2685 else
2687 This->lockBytes = pLkbyt;
2688 ILockBytes_AddRef(pLkbyt);
2691 if (FAILED(hr))
2692 goto end;
2694 if (create)
2696 ULARGE_INTEGER size;
2697 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2700 * Initialize all header variables:
2701 * - The big block depot consists of one block and it is at block 0
2702 * - The directory table starts at block 1
2703 * - There is no small block depot
2705 memset( This->bigBlockDepotStart,
2706 BLOCK_UNUSED,
2707 sizeof(This->bigBlockDepotStart));
2709 This->bigBlockDepotCount = 1;
2710 This->bigBlockDepotStart[0] = 0;
2711 This->rootStartBlock = 1;
2712 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2713 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2714 if (sector_size == 4096)
2715 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2716 else
2717 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2718 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2719 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2720 This->extBigBlockDepotCount = 0;
2722 StorageImpl_SaveFileHeader(This);
2725 * Add one block for the big block depot and one block for the directory table
2727 size.u.HighPart = 0;
2728 size.u.LowPart = This->bigBlockSize * 3;
2729 ILockBytes_SetSize(This->lockBytes, size);
2732 * Initialize the big block depot
2734 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2735 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2736 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2737 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2739 else
2742 * Load the header for the file.
2744 hr = StorageImpl_LoadFileHeader(This);
2746 if (FAILED(hr))
2748 goto end;
2753 * There is no block depot cached yet.
2755 This->indexBlockDepotCached = 0xFFFFFFFF;
2758 * Start searching for free blocks with block 0.
2760 This->prevFreeBlock = 0;
2762 This->firstFreeSmallBlock = 0;
2765 * Create the block chain abstractions.
2767 if(!(This->rootBlockChain =
2768 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2770 hr = STG_E_READFAULT;
2771 goto end;
2774 if(!(This->smallBlockDepotChain =
2775 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2776 DIRENTRY_NULL)))
2778 hr = STG_E_READFAULT;
2779 goto end;
2783 * Write the root storage entry (memory only)
2785 if (create)
2787 DirEntry rootEntry;
2789 * Initialize the directory table
2791 memset(&rootEntry, 0, sizeof(rootEntry));
2792 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2793 sizeof(rootEntry.name)/sizeof(WCHAR) );
2794 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2795 rootEntry.stgType = STGTY_ROOT;
2796 rootEntry.leftChild = DIRENTRY_NULL;
2797 rootEntry.rightChild = DIRENTRY_NULL;
2798 rootEntry.dirRootEntry = DIRENTRY_NULL;
2799 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2800 rootEntry.size.u.HighPart = 0;
2801 rootEntry.size.u.LowPart = 0;
2803 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2807 * Find the ID of the root storage.
2809 currentEntryRef = 0;
2813 hr = StorageImpl_ReadDirEntry(
2814 This,
2815 currentEntryRef,
2816 &currentEntry);
2818 if (SUCCEEDED(hr))
2820 if ( (currentEntry.sizeOfNameString != 0 ) &&
2821 (currentEntry.stgType == STGTY_ROOT) )
2823 This->base.storageDirEntry = currentEntryRef;
2827 currentEntryRef++;
2829 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2831 if (FAILED(hr))
2833 hr = STG_E_READFAULT;
2834 goto end;
2838 * Create the block chain abstraction for the small block root chain.
2840 if(!(This->smallBlockRootChain =
2841 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2843 hr = STG_E_READFAULT;
2846 end:
2847 if (FAILED(hr))
2849 IStorage_Release((IStorage*)This);
2850 *result = NULL;
2852 else
2853 *result = This;
2855 return hr;
2858 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2860 StorageImpl *This = (StorageImpl*) iface;
2862 StorageBaseImpl_DeleteAll(&This->base);
2864 This->base.reverted = 1;
2867 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2869 StorageImpl *This = (StorageImpl*) iface;
2870 int i;
2871 TRACE("(%p)\n", This);
2873 StorageImpl_Invalidate(iface);
2875 BlockChainStream_Destroy(This->smallBlockRootChain);
2876 BlockChainStream_Destroy(This->rootBlockChain);
2877 BlockChainStream_Destroy(This->smallBlockDepotChain);
2879 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2880 BlockChainStream_Destroy(This->blockChainCache[i]);
2882 if (This->lockBytes)
2883 ILockBytes_Release(This->lockBytes);
2884 HeapFree(GetProcessHeap(), 0, This);
2887 /******************************************************************************
2888 * Storage32Impl_GetNextFreeBigBlock
2890 * Returns the index of the next free big block.
2891 * If the big block depot is filled, this method will enlarge it.
2894 static ULONG StorageImpl_GetNextFreeBigBlock(
2895 StorageImpl* This)
2897 ULONG depotBlockIndexPos;
2898 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2899 BOOL success;
2900 ULONG depotBlockOffset;
2901 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2902 ULONG nextBlockIndex = BLOCK_SPECIAL;
2903 int depotIndex = 0;
2904 ULONG freeBlock = BLOCK_UNUSED;
2905 ULARGE_INTEGER neededSize;
2906 STATSTG statstg;
2908 depotIndex = This->prevFreeBlock / blocksPerDepot;
2909 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2912 * Scan the entire big block depot until we find a block marked free
2914 while (nextBlockIndex != BLOCK_UNUSED)
2916 if (depotIndex < COUNT_BBDEPOTINHEADER)
2918 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2921 * Grow the primary depot.
2923 if (depotBlockIndexPos == BLOCK_UNUSED)
2925 depotBlockIndexPos = depotIndex*blocksPerDepot;
2928 * Add a block depot.
2930 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2931 This->bigBlockDepotCount++;
2932 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2935 * Flag it as a block depot.
2937 StorageImpl_SetNextBlockInChain(This,
2938 depotBlockIndexPos,
2939 BLOCK_SPECIAL);
2941 /* Save new header information.
2943 StorageImpl_SaveFileHeader(This);
2946 else
2948 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2950 if (depotBlockIndexPos == BLOCK_UNUSED)
2953 * Grow the extended depot.
2955 ULONG extIndex = BLOCK_UNUSED;
2956 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2957 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2959 if (extBlockOffset == 0)
2961 /* We need an extended block.
2963 extIndex = Storage32Impl_AddExtBlockDepot(This);
2964 This->extBigBlockDepotCount++;
2965 depotBlockIndexPos = extIndex + 1;
2967 else
2968 depotBlockIndexPos = depotIndex * blocksPerDepot;
2971 * Add a block depot and mark it in the extended block.
2973 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2974 This->bigBlockDepotCount++;
2975 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2977 /* Flag the block depot.
2979 StorageImpl_SetNextBlockInChain(This,
2980 depotBlockIndexPos,
2981 BLOCK_SPECIAL);
2983 /* If necessary, flag the extended depot block.
2985 if (extIndex != BLOCK_UNUSED)
2986 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2988 /* Save header information.
2990 StorageImpl_SaveFileHeader(This);
2994 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2996 if (success)
2998 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2999 ( nextBlockIndex != BLOCK_UNUSED))
3001 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3003 if (nextBlockIndex == BLOCK_UNUSED)
3005 freeBlock = (depotIndex * blocksPerDepot) +
3006 (depotBlockOffset/sizeof(ULONG));
3009 depotBlockOffset += sizeof(ULONG);
3013 depotIndex++;
3014 depotBlockOffset = 0;
3018 * make sure that the block physically exists before using it
3020 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3022 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3024 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3025 ILockBytes_SetSize(This->lockBytes, neededSize);
3027 This->prevFreeBlock = freeBlock;
3029 return freeBlock;
3032 /******************************************************************************
3033 * Storage32Impl_AddBlockDepot
3035 * This will create a depot block, essentially it is a block initialized
3036 * to BLOCK_UNUSEDs.
3038 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3040 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3043 * Initialize blocks as free
3045 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3046 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3049 /******************************************************************************
3050 * Storage32Impl_GetExtDepotBlock
3052 * Returns the index of the block that corresponds to the specified depot
3053 * index. This method is only for depot indexes equal or greater than
3054 * COUNT_BBDEPOTINHEADER.
3056 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3058 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3059 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3060 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3061 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3062 ULONG blockIndex = BLOCK_UNUSED;
3063 ULONG extBlockIndex = This->extBigBlockDepotStart;
3065 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3067 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3068 return BLOCK_UNUSED;
3070 while (extBlockCount > 0)
3072 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3073 extBlockCount--;
3076 if (extBlockIndex != BLOCK_UNUSED)
3077 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3078 extBlockOffset * sizeof(ULONG), &blockIndex);
3080 return blockIndex;
3083 /******************************************************************************
3084 * Storage32Impl_SetExtDepotBlock
3086 * Associates the specified block index to the specified depot index.
3087 * This method is only for depot indexes equal or greater than
3088 * COUNT_BBDEPOTINHEADER.
3090 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3092 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3093 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3094 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3095 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3096 ULONG extBlockIndex = This->extBigBlockDepotStart;
3098 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3100 while (extBlockCount > 0)
3102 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3103 extBlockCount--;
3106 if (extBlockIndex != BLOCK_UNUSED)
3108 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3109 extBlockOffset * sizeof(ULONG),
3110 blockIndex);
3114 /******************************************************************************
3115 * Storage32Impl_AddExtBlockDepot
3117 * Creates an extended depot block.
3119 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3121 ULONG numExtBlocks = This->extBigBlockDepotCount;
3122 ULONG nextExtBlock = This->extBigBlockDepotStart;
3123 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3124 ULONG index = BLOCK_UNUSED;
3125 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3126 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3127 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3129 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3130 blocksPerDepotBlock;
3132 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3135 * The first extended block.
3137 This->extBigBlockDepotStart = index;
3139 else
3141 unsigned int i;
3143 * Follow the chain to the last one.
3145 for (i = 0; i < (numExtBlocks - 1); i++)
3147 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3151 * Add the new extended block to the chain.
3153 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3154 index);
3158 * Initialize this block.
3160 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3161 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3163 return index;
3166 /******************************************************************************
3167 * Storage32Impl_FreeBigBlock
3169 * This method will flag the specified block as free in the big block depot.
3171 static void StorageImpl_FreeBigBlock(
3172 StorageImpl* This,
3173 ULONG blockIndex)
3175 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3177 if (blockIndex < This->prevFreeBlock)
3178 This->prevFreeBlock = blockIndex;
3181 /************************************************************************
3182 * Storage32Impl_GetNextBlockInChain
3184 * This method will retrieve the block index of the next big block in
3185 * in the chain.
3187 * Params: This - Pointer to the Storage object.
3188 * blockIndex - Index of the block to retrieve the chain
3189 * for.
3190 * nextBlockIndex - receives the return value.
3192 * Returns: This method returns the index of the next block in the chain.
3193 * It will return the constants:
3194 * BLOCK_SPECIAL - If the block given was not part of a
3195 * chain.
3196 * BLOCK_END_OF_CHAIN - If the block given was the last in
3197 * a chain.
3198 * BLOCK_UNUSED - If the block given was not past of a chain
3199 * and is available.
3200 * BLOCK_EXTBBDEPOT - This block is part of the extended
3201 * big block depot.
3203 * See Windows documentation for more details on IStorage methods.
3205 static HRESULT StorageImpl_GetNextBlockInChain(
3206 StorageImpl* This,
3207 ULONG blockIndex,
3208 ULONG* nextBlockIndex)
3210 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3211 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3212 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3213 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3214 BOOL success;
3215 ULONG depotBlockIndexPos;
3216 int index, num_blocks;
3218 *nextBlockIndex = BLOCK_SPECIAL;
3220 if(depotBlockCount >= This->bigBlockDepotCount)
3222 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3223 This->bigBlockDepotCount);
3224 return STG_E_READFAULT;
3228 * Cache the currently accessed depot block.
3230 if (depotBlockCount != This->indexBlockDepotCached)
3232 This->indexBlockDepotCached = depotBlockCount;
3234 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3236 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3238 else
3241 * We have to look in the extended depot.
3243 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3246 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3248 if (!success)
3249 return STG_E_READFAULT;
3251 num_blocks = This->bigBlockSize / 4;
3253 for (index = 0; index < num_blocks; index++)
3255 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3256 This->blockDepotCached[index] = *nextBlockIndex;
3260 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3262 return S_OK;
3265 /******************************************************************************
3266 * Storage32Impl_GetNextExtendedBlock
3268 * Given an extended block this method will return the next extended block.
3270 * NOTES:
3271 * The last ULONG of an extended block is the block index of the next
3272 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3273 * depot.
3275 * Return values:
3276 * - The index of the next extended block
3277 * - BLOCK_UNUSED: there is no next extended block.
3278 * - Any other return values denotes failure.
3280 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3282 ULONG nextBlockIndex = BLOCK_SPECIAL;
3283 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3285 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3286 &nextBlockIndex);
3288 return nextBlockIndex;
3291 /******************************************************************************
3292 * Storage32Impl_SetNextBlockInChain
3294 * This method will write the index of the specified block's next block
3295 * in the big block depot.
3297 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3298 * do the following
3300 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3301 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3302 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3305 static void StorageImpl_SetNextBlockInChain(
3306 StorageImpl* This,
3307 ULONG blockIndex,
3308 ULONG nextBlock)
3310 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3311 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3312 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3313 ULONG depotBlockIndexPos;
3315 assert(depotBlockCount < This->bigBlockDepotCount);
3316 assert(blockIndex != nextBlock);
3318 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3320 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3322 else
3325 * We have to look in the extended depot.
3327 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3330 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3331 nextBlock);
3333 * Update the cached block depot, if necessary.
3335 if (depotBlockCount == This->indexBlockDepotCached)
3337 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3341 /******************************************************************************
3342 * Storage32Impl_LoadFileHeader
3344 * This method will read in the file header
3346 static HRESULT StorageImpl_LoadFileHeader(
3347 StorageImpl* This)
3349 HRESULT hr;
3350 BYTE headerBigBlock[HEADER_SIZE];
3351 int index;
3352 ULARGE_INTEGER offset;
3353 DWORD bytes_read;
3355 TRACE("\n");
3357 * Get a pointer to the big block of data containing the header.
3359 offset.u.HighPart = 0;
3360 offset.u.LowPart = 0;
3361 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3362 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3363 hr = STG_E_FILENOTFOUND;
3366 * Extract the information from the header.
3368 if (SUCCEEDED(hr))
3371 * Check for the "magic number" signature and return an error if it is not
3372 * found.
3374 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3376 return STG_E_OLDFORMAT;
3379 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3381 return STG_E_INVALIDHEADER;
3384 StorageUtl_ReadWord(
3385 headerBigBlock,
3386 OFFSET_BIGBLOCKSIZEBITS,
3387 &This->bigBlockSizeBits);
3389 StorageUtl_ReadWord(
3390 headerBigBlock,
3391 OFFSET_SMALLBLOCKSIZEBITS,
3392 &This->smallBlockSizeBits);
3394 StorageUtl_ReadDWord(
3395 headerBigBlock,
3396 OFFSET_BBDEPOTCOUNT,
3397 &This->bigBlockDepotCount);
3399 StorageUtl_ReadDWord(
3400 headerBigBlock,
3401 OFFSET_ROOTSTARTBLOCK,
3402 &This->rootStartBlock);
3404 StorageUtl_ReadDWord(
3405 headerBigBlock,
3406 OFFSET_SMALLBLOCKLIMIT,
3407 &This->smallBlockLimit);
3409 StorageUtl_ReadDWord(
3410 headerBigBlock,
3411 OFFSET_SBDEPOTSTART,
3412 &This->smallBlockDepotStart);
3414 StorageUtl_ReadDWord(
3415 headerBigBlock,
3416 OFFSET_EXTBBDEPOTSTART,
3417 &This->extBigBlockDepotStart);
3419 StorageUtl_ReadDWord(
3420 headerBigBlock,
3421 OFFSET_EXTBBDEPOTCOUNT,
3422 &This->extBigBlockDepotCount);
3424 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3426 StorageUtl_ReadDWord(
3427 headerBigBlock,
3428 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3429 &(This->bigBlockDepotStart[index]));
3433 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3435 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3436 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3439 * Right now, the code is making some assumptions about the size of the
3440 * blocks, just make sure they are what we're expecting.
3442 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3443 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3444 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3446 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3447 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3448 hr = STG_E_INVALIDHEADER;
3450 else
3451 hr = S_OK;
3454 return hr;
3457 /******************************************************************************
3458 * Storage32Impl_SaveFileHeader
3460 * This method will save to the file the header
3462 static void StorageImpl_SaveFileHeader(
3463 StorageImpl* This)
3465 BYTE headerBigBlock[HEADER_SIZE];
3466 int index;
3467 HRESULT hr;
3468 ULARGE_INTEGER offset;
3469 DWORD bytes_read, bytes_written;
3470 DWORD major_version, dirsectorcount;
3473 * Get a pointer to the big block of data containing the header.
3475 offset.u.HighPart = 0;
3476 offset.u.LowPart = 0;
3477 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3478 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3479 hr = STG_E_FILENOTFOUND;
3481 if (This->bigBlockSizeBits == 0x9)
3482 major_version = 3;
3483 else if (This->bigBlockSizeBits == 0xc)
3484 major_version = 4;
3485 else
3487 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3488 major_version = 4;
3492 * If the block read failed, the file is probably new.
3494 if (FAILED(hr))
3497 * Initialize for all unknown fields.
3499 memset(headerBigBlock, 0, HEADER_SIZE);
3502 * Initialize the magic number.
3504 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3508 * Write the information to the header.
3510 StorageUtl_WriteWord(
3511 headerBigBlock,
3512 OFFSET_MINORVERSION,
3513 0x3e);
3515 StorageUtl_WriteWord(
3516 headerBigBlock,
3517 OFFSET_MAJORVERSION,
3518 major_version);
3520 StorageUtl_WriteWord(
3521 headerBigBlock,
3522 OFFSET_BYTEORDERMARKER,
3523 (WORD)-2);
3525 StorageUtl_WriteWord(
3526 headerBigBlock,
3527 OFFSET_BIGBLOCKSIZEBITS,
3528 This->bigBlockSizeBits);
3530 StorageUtl_WriteWord(
3531 headerBigBlock,
3532 OFFSET_SMALLBLOCKSIZEBITS,
3533 This->smallBlockSizeBits);
3535 if (major_version >= 4)
3537 if (This->rootBlockChain)
3538 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3539 else
3540 /* This file is being created, and it will start out with one block. */
3541 dirsectorcount = 1;
3543 else
3544 /* This field must be 0 in versions older than 4 */
3545 dirsectorcount = 0;
3547 StorageUtl_WriteDWord(
3548 headerBigBlock,
3549 OFFSET_DIRSECTORCOUNT,
3550 dirsectorcount);
3552 StorageUtl_WriteDWord(
3553 headerBigBlock,
3554 OFFSET_BBDEPOTCOUNT,
3555 This->bigBlockDepotCount);
3557 StorageUtl_WriteDWord(
3558 headerBigBlock,
3559 OFFSET_ROOTSTARTBLOCK,
3560 This->rootStartBlock);
3562 StorageUtl_WriteDWord(
3563 headerBigBlock,
3564 OFFSET_SMALLBLOCKLIMIT,
3565 This->smallBlockLimit);
3567 StorageUtl_WriteDWord(
3568 headerBigBlock,
3569 OFFSET_SBDEPOTSTART,
3570 This->smallBlockDepotStart);
3572 StorageUtl_WriteDWord(
3573 headerBigBlock,
3574 OFFSET_SBDEPOTCOUNT,
3575 This->smallBlockDepotChain ?
3576 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3578 StorageUtl_WriteDWord(
3579 headerBigBlock,
3580 OFFSET_EXTBBDEPOTSTART,
3581 This->extBigBlockDepotStart);
3583 StorageUtl_WriteDWord(
3584 headerBigBlock,
3585 OFFSET_EXTBBDEPOTCOUNT,
3586 This->extBigBlockDepotCount);
3588 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3590 StorageUtl_WriteDWord(
3591 headerBigBlock,
3592 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3593 (This->bigBlockDepotStart[index]));
3597 * Write the big block back to the file.
3599 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3602 /******************************************************************************
3603 * StorageImpl_ReadRawDirEntry
3605 * This method will read the raw data from a directory entry in the file.
3607 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3609 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3611 ULARGE_INTEGER offset;
3612 HRESULT hr;
3613 ULONG bytesRead;
3615 offset.u.HighPart = 0;
3616 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3618 hr = BlockChainStream_ReadAt(
3619 This->rootBlockChain,
3620 offset,
3621 RAW_DIRENTRY_SIZE,
3622 buffer,
3623 &bytesRead);
3625 if (bytesRead != RAW_DIRENTRY_SIZE)
3626 return STG_E_READFAULT;
3628 return hr;
3631 /******************************************************************************
3632 * StorageImpl_WriteRawDirEntry
3634 * This method will write the raw data from a directory entry in the file.
3636 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3638 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3640 ULARGE_INTEGER offset;
3641 HRESULT hr;
3642 ULONG bytesRead;
3644 offset.u.HighPart = 0;
3645 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3647 hr = BlockChainStream_WriteAt(
3648 This->rootBlockChain,
3649 offset,
3650 RAW_DIRENTRY_SIZE,
3651 buffer,
3652 &bytesRead);
3654 return hr;
3657 /******************************************************************************
3658 * UpdateRawDirEntry
3660 * Update raw directory entry data from the fields in newData.
3662 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3664 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3666 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3668 memcpy(
3669 buffer + OFFSET_PS_NAME,
3670 newData->name,
3671 DIRENTRY_NAME_BUFFER_LEN );
3673 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3675 StorageUtl_WriteWord(
3676 buffer,
3677 OFFSET_PS_NAMELENGTH,
3678 newData->sizeOfNameString);
3680 StorageUtl_WriteDWord(
3681 buffer,
3682 OFFSET_PS_LEFTCHILD,
3683 newData->leftChild);
3685 StorageUtl_WriteDWord(
3686 buffer,
3687 OFFSET_PS_RIGHTCHILD,
3688 newData->rightChild);
3690 StorageUtl_WriteDWord(
3691 buffer,
3692 OFFSET_PS_DIRROOT,
3693 newData->dirRootEntry);
3695 StorageUtl_WriteGUID(
3696 buffer,
3697 OFFSET_PS_GUID,
3698 &newData->clsid);
3700 StorageUtl_WriteDWord(
3701 buffer,
3702 OFFSET_PS_CTIMELOW,
3703 newData->ctime.dwLowDateTime);
3705 StorageUtl_WriteDWord(
3706 buffer,
3707 OFFSET_PS_CTIMEHIGH,
3708 newData->ctime.dwHighDateTime);
3710 StorageUtl_WriteDWord(
3711 buffer,
3712 OFFSET_PS_MTIMELOW,
3713 newData->mtime.dwLowDateTime);
3715 StorageUtl_WriteDWord(
3716 buffer,
3717 OFFSET_PS_MTIMEHIGH,
3718 newData->ctime.dwHighDateTime);
3720 StorageUtl_WriteDWord(
3721 buffer,
3722 OFFSET_PS_STARTBLOCK,
3723 newData->startingBlock);
3725 StorageUtl_WriteDWord(
3726 buffer,
3727 OFFSET_PS_SIZE,
3728 newData->size.u.LowPart);
3731 /******************************************************************************
3732 * Storage32Impl_ReadDirEntry
3734 * This method will read the specified directory entry.
3736 HRESULT StorageImpl_ReadDirEntry(
3737 StorageImpl* This,
3738 DirRef index,
3739 DirEntry* buffer)
3741 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3742 HRESULT readRes;
3744 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3746 if (SUCCEEDED(readRes))
3748 memset(buffer->name, 0, sizeof(buffer->name));
3749 memcpy(
3750 buffer->name,
3751 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3752 DIRENTRY_NAME_BUFFER_LEN );
3753 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3755 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3757 StorageUtl_ReadWord(
3758 currentEntry,
3759 OFFSET_PS_NAMELENGTH,
3760 &buffer->sizeOfNameString);
3762 StorageUtl_ReadDWord(
3763 currentEntry,
3764 OFFSET_PS_LEFTCHILD,
3765 &buffer->leftChild);
3767 StorageUtl_ReadDWord(
3768 currentEntry,
3769 OFFSET_PS_RIGHTCHILD,
3770 &buffer->rightChild);
3772 StorageUtl_ReadDWord(
3773 currentEntry,
3774 OFFSET_PS_DIRROOT,
3775 &buffer->dirRootEntry);
3777 StorageUtl_ReadGUID(
3778 currentEntry,
3779 OFFSET_PS_GUID,
3780 &buffer->clsid);
3782 StorageUtl_ReadDWord(
3783 currentEntry,
3784 OFFSET_PS_CTIMELOW,
3785 &buffer->ctime.dwLowDateTime);
3787 StorageUtl_ReadDWord(
3788 currentEntry,
3789 OFFSET_PS_CTIMEHIGH,
3790 &buffer->ctime.dwHighDateTime);
3792 StorageUtl_ReadDWord(
3793 currentEntry,
3794 OFFSET_PS_MTIMELOW,
3795 &buffer->mtime.dwLowDateTime);
3797 StorageUtl_ReadDWord(
3798 currentEntry,
3799 OFFSET_PS_MTIMEHIGH,
3800 &buffer->mtime.dwHighDateTime);
3802 StorageUtl_ReadDWord(
3803 currentEntry,
3804 OFFSET_PS_STARTBLOCK,
3805 &buffer->startingBlock);
3807 StorageUtl_ReadDWord(
3808 currentEntry,
3809 OFFSET_PS_SIZE,
3810 &buffer->size.u.LowPart);
3812 buffer->size.u.HighPart = 0;
3815 return readRes;
3818 /*********************************************************************
3819 * Write the specified directory entry to the file
3821 HRESULT StorageImpl_WriteDirEntry(
3822 StorageImpl* This,
3823 DirRef index,
3824 const DirEntry* buffer)
3826 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3827 HRESULT writeRes;
3829 UpdateRawDirEntry(currentEntry, buffer);
3831 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3832 return writeRes;
3835 static BOOL StorageImpl_ReadBigBlock(
3836 StorageImpl* This,
3837 ULONG blockIndex,
3838 void* buffer)
3840 ULARGE_INTEGER ulOffset;
3841 DWORD read;
3843 ulOffset.u.HighPart = 0;
3844 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3846 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3847 return (read == This->bigBlockSize);
3850 static BOOL StorageImpl_ReadDWordFromBigBlock(
3851 StorageImpl* This,
3852 ULONG blockIndex,
3853 ULONG offset,
3854 DWORD* value)
3856 ULARGE_INTEGER ulOffset;
3857 DWORD read;
3858 DWORD tmp;
3860 ulOffset.u.HighPart = 0;
3861 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3862 ulOffset.u.LowPart += offset;
3864 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3865 *value = lendian32toh(tmp);
3866 return (read == sizeof(DWORD));
3869 static BOOL StorageImpl_WriteBigBlock(
3870 StorageImpl* This,
3871 ULONG blockIndex,
3872 const void* buffer)
3874 ULARGE_INTEGER ulOffset;
3875 DWORD wrote;
3877 ulOffset.u.HighPart = 0;
3878 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3880 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3881 return (wrote == This->bigBlockSize);
3884 static BOOL StorageImpl_WriteDWordToBigBlock(
3885 StorageImpl* This,
3886 ULONG blockIndex,
3887 ULONG offset,
3888 DWORD value)
3890 ULARGE_INTEGER ulOffset;
3891 DWORD wrote;
3893 ulOffset.u.HighPart = 0;
3894 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3895 ulOffset.u.LowPart += offset;
3897 value = htole32(value);
3898 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3899 return (wrote == sizeof(DWORD));
3902 /******************************************************************************
3903 * Storage32Impl_SmallBlocksToBigBlocks
3905 * This method will convert a small block chain to a big block chain.
3906 * The small block chain will be destroyed.
3908 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3909 StorageImpl* This,
3910 SmallBlockChainStream** ppsbChain)
3912 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3913 ULARGE_INTEGER size, offset;
3914 ULONG cbRead, cbWritten;
3915 ULARGE_INTEGER cbTotalRead;
3916 DirRef streamEntryRef;
3917 HRESULT resWrite = S_OK;
3918 HRESULT resRead;
3919 DirEntry streamEntry;
3920 BYTE *buffer;
3921 BlockChainStream *bbTempChain = NULL;
3922 BlockChainStream *bigBlockChain = NULL;
3925 * Create a temporary big block chain that doesn't have
3926 * an associated directory entry. This temporary chain will be
3927 * used to copy data from small blocks to big blocks.
3929 bbTempChain = BlockChainStream_Construct(This,
3930 &bbHeadOfChain,
3931 DIRENTRY_NULL);
3932 if(!bbTempChain) return NULL;
3934 * Grow the big block chain.
3936 size = SmallBlockChainStream_GetSize(*ppsbChain);
3937 BlockChainStream_SetSize(bbTempChain, size);
3940 * Copy the contents of the small block chain to the big block chain
3941 * by small block size increments.
3943 offset.u.LowPart = 0;
3944 offset.u.HighPart = 0;
3945 cbTotalRead.QuadPart = 0;
3947 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3950 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3951 offset,
3952 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3953 buffer,
3954 &cbRead);
3955 if (FAILED(resRead))
3956 break;
3958 if (cbRead > 0)
3960 cbTotalRead.QuadPart += cbRead;
3962 resWrite = BlockChainStream_WriteAt(bbTempChain,
3963 offset,
3964 cbRead,
3965 buffer,
3966 &cbWritten);
3968 if (FAILED(resWrite))
3969 break;
3971 offset.u.LowPart += cbRead;
3973 else
3975 resRead = STG_E_READFAULT;
3976 break;
3978 } while (cbTotalRead.QuadPart < size.QuadPart);
3979 HeapFree(GetProcessHeap(),0,buffer);
3981 size.u.HighPart = 0;
3982 size.u.LowPart = 0;
3984 if (FAILED(resRead) || FAILED(resWrite))
3986 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3987 BlockChainStream_SetSize(bbTempChain, size);
3988 BlockChainStream_Destroy(bbTempChain);
3989 return NULL;
3993 * Destroy the small block chain.
3995 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3996 SmallBlockChainStream_SetSize(*ppsbChain, size);
3997 SmallBlockChainStream_Destroy(*ppsbChain);
3998 *ppsbChain = 0;
4001 * Change the directory entry. This chain is now a big block chain
4002 * and it doesn't reside in the small blocks chain anymore.
4004 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4006 streamEntry.startingBlock = bbHeadOfChain;
4008 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4011 * Destroy the temporary entryless big block chain.
4012 * Create a new big block chain associated with this entry.
4014 BlockChainStream_Destroy(bbTempChain);
4015 bigBlockChain = BlockChainStream_Construct(This,
4016 NULL,
4017 streamEntryRef);
4019 return bigBlockChain;
4022 /******************************************************************************
4023 * Storage32Impl_BigBlocksToSmallBlocks
4025 * This method will convert a big block chain to a small block chain.
4026 * The big block chain will be destroyed on success.
4028 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4029 StorageImpl* This,
4030 BlockChainStream** ppbbChain)
4032 ULARGE_INTEGER size, offset, cbTotalRead;
4033 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4034 DirRef streamEntryRef;
4035 HRESULT resWrite = S_OK, resRead;
4036 DirEntry streamEntry;
4037 BYTE* buffer;
4038 SmallBlockChainStream* sbTempChain;
4040 TRACE("%p %p\n", This, ppbbChain);
4042 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4043 DIRENTRY_NULL);
4045 if(!sbTempChain)
4046 return NULL;
4048 size = BlockChainStream_GetSize(*ppbbChain);
4049 SmallBlockChainStream_SetSize(sbTempChain, size);
4051 offset.u.HighPart = 0;
4052 offset.u.LowPart = 0;
4053 cbTotalRead.QuadPart = 0;
4054 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4057 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4058 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4059 buffer, &cbRead);
4061 if(FAILED(resRead))
4062 break;
4064 if(cbRead > 0)
4066 cbTotalRead.QuadPart += cbRead;
4068 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4069 cbRead, buffer, &cbWritten);
4071 if(FAILED(resWrite))
4072 break;
4074 offset.u.LowPart += cbRead;
4076 else
4078 resRead = STG_E_READFAULT;
4079 break;
4081 }while(cbTotalRead.QuadPart < size.QuadPart);
4082 HeapFree(GetProcessHeap(), 0, buffer);
4084 size.u.HighPart = 0;
4085 size.u.LowPart = 0;
4087 if(FAILED(resRead) || FAILED(resWrite))
4089 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4090 SmallBlockChainStream_SetSize(sbTempChain, size);
4091 SmallBlockChainStream_Destroy(sbTempChain);
4092 return NULL;
4095 /* destroy the original big block chain */
4096 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4097 BlockChainStream_SetSize(*ppbbChain, size);
4098 BlockChainStream_Destroy(*ppbbChain);
4099 *ppbbChain = NULL;
4101 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4102 streamEntry.startingBlock = sbHeadOfChain;
4103 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4105 SmallBlockChainStream_Destroy(sbTempChain);
4106 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4109 static HRESULT StorageBaseImpl_CopyStream(
4110 StorageBaseImpl *dst, DirRef dst_entry,
4111 StorageBaseImpl *src, DirRef src_entry)
4113 HRESULT hr;
4114 BYTE data[4096];
4115 DirEntry srcdata;
4116 ULARGE_INTEGER bytes_copied;
4117 ULONG bytestocopy, bytesread, byteswritten;
4119 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4121 if (SUCCEEDED(hr))
4123 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4125 bytes_copied.QuadPart = 0;
4126 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4128 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4130 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4131 data, &bytesread);
4132 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4134 if (SUCCEEDED(hr))
4135 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4136 data, &byteswritten);
4137 if (SUCCEEDED(hr))
4139 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4140 bytes_copied.QuadPart += byteswritten;
4145 return hr;
4148 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4150 DirRef result=This->firstFreeEntry;
4152 while (result < This->entries_size && This->entries[result].inuse)
4153 result++;
4155 if (result == This->entries_size)
4157 ULONG new_size = This->entries_size * 2;
4158 TransactedDirEntry *new_entries;
4160 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4161 if (!new_entries) return DIRENTRY_NULL;
4163 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4164 HeapFree(GetProcessHeap(), 0, This->entries);
4166 This->entries = new_entries;
4167 This->entries_size = new_size;
4170 This->entries[result].inuse = 1;
4172 This->firstFreeEntry = result+1;
4174 return result;
4177 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4178 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4180 DirRef stubEntryRef;
4181 TransactedDirEntry *entry;
4183 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4185 if (stubEntryRef != DIRENTRY_NULL)
4187 entry = &This->entries[stubEntryRef];
4189 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4191 entry->read = 0;
4194 return stubEntryRef;
4197 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4198 TransactedSnapshotImpl *This, DirRef entry)
4200 HRESULT hr=S_OK;
4201 DirEntry data;
4203 if (!This->entries[entry].read)
4205 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4206 This->entries[entry].transactedParentEntry,
4207 &data);
4209 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4211 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4213 if (data.leftChild == DIRENTRY_NULL)
4214 hr = E_OUTOFMEMORY;
4217 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4219 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4221 if (data.rightChild == DIRENTRY_NULL)
4222 hr = E_OUTOFMEMORY;
4225 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4227 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4229 if (data.dirRootEntry == DIRENTRY_NULL)
4230 hr = E_OUTOFMEMORY;
4233 if (SUCCEEDED(hr))
4235 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4236 This->entries[entry].read = 1;
4240 return hr;
4243 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4244 TransactedSnapshotImpl *This, DirRef entry)
4246 HRESULT hr = S_OK;
4248 if (!This->entries[entry].stream_dirty)
4250 DirEntry new_entrydata;
4252 memset(&new_entrydata, 0, sizeof(DirEntry));
4253 new_entrydata.name[0] = 'S';
4254 new_entrydata.sizeOfNameString = 1;
4255 new_entrydata.stgType = STGTY_STREAM;
4256 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4257 new_entrydata.leftChild = DIRENTRY_NULL;
4258 new_entrydata.rightChild = DIRENTRY_NULL;
4259 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4261 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4262 &This->entries[entry].stream_entry);
4264 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4266 hr = StorageBaseImpl_CopyStream(
4267 This->scratch, This->entries[entry].stream_entry,
4268 This->transactedParent, This->entries[entry].transactedParentEntry);
4270 if (FAILED(hr))
4271 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4274 if (SUCCEEDED(hr))
4275 This->entries[entry].stream_dirty = 1;
4277 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4279 /* Since this entry is modified, and we aren't using its stream data, we
4280 * no longer care about the original entry. */
4281 DirRef delete_ref;
4282 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4284 if (delete_ref != DIRENTRY_NULL)
4285 This->entries[delete_ref].deleted = 1;
4287 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4291 return hr;
4294 /* Find the first entry in a depth-first traversal. */
4295 static DirRef TransactedSnapshotImpl_FindFirstChild(
4296 TransactedSnapshotImpl* This, DirRef parent)
4298 DirRef cursor, prev;
4299 TransactedDirEntry *entry;
4301 cursor = parent;
4302 entry = &This->entries[cursor];
4303 while (entry->read)
4305 if (entry->data.leftChild != DIRENTRY_NULL)
4307 prev = cursor;
4308 cursor = entry->data.leftChild;
4309 entry = &This->entries[cursor];
4310 entry->parent = prev;
4312 else if (entry->data.rightChild != DIRENTRY_NULL)
4314 prev = cursor;
4315 cursor = entry->data.rightChild;
4316 entry = &This->entries[cursor];
4317 entry->parent = prev;
4319 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4321 prev = cursor;
4322 cursor = entry->data.dirRootEntry;
4323 entry = &This->entries[cursor];
4324 entry->parent = prev;
4326 else
4327 break;
4330 return cursor;
4333 /* Find the next entry in a depth-first traversal. */
4334 static DirRef TransactedSnapshotImpl_FindNextChild(
4335 TransactedSnapshotImpl* This, DirRef current)
4337 DirRef parent;
4338 TransactedDirEntry *parent_entry;
4340 parent = This->entries[current].parent;
4341 parent_entry = &This->entries[parent];
4343 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4345 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4347 This->entries[parent_entry->data.rightChild].parent = parent;
4348 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4351 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4353 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4354 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4358 return parent;
4361 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4362 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4363 TransactedSnapshotImpl* This, DirRef entry)
4365 return entry != DIRENTRY_NULL &&
4366 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4369 /* Destroy the entries created by CopyTree. */
4370 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4371 TransactedSnapshotImpl* This, DirRef stop)
4373 DirRef cursor;
4374 TransactedDirEntry *entry;
4375 ULARGE_INTEGER zero;
4377 zero.QuadPart = 0;
4379 if (!This->entries[This->base.storageDirEntry].read)
4380 return;
4382 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4384 if (cursor == DIRENTRY_NULL)
4385 return;
4387 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4389 while (cursor != DIRENTRY_NULL && cursor != stop)
4391 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4393 entry = &This->entries[cursor];
4395 if (entry->stream_dirty)
4396 StorageBaseImpl_StreamSetSize(This->transactedParent,
4397 entry->newTransactedParentEntry, zero);
4399 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4400 entry->newTransactedParentEntry);
4402 entry->newTransactedParentEntry = entry->transactedParentEntry;
4405 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4409 /* Make a copy of our edited tree that we can use in the parent. */
4410 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4412 DirRef cursor;
4413 TransactedDirEntry *entry;
4414 HRESULT hr = S_OK;
4416 cursor = This->base.storageDirEntry;
4417 entry = &This->entries[cursor];
4418 entry->parent = DIRENTRY_NULL;
4419 entry->newTransactedParentEntry = entry->transactedParentEntry;
4421 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4422 return S_OK;
4424 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4426 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4427 entry = &This->entries[cursor];
4429 while (cursor != DIRENTRY_NULL)
4431 /* Make a copy of this entry in the transacted parent. */
4432 if (!entry->read ||
4433 (!entry->dirty && !entry->stream_dirty &&
4434 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4435 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4436 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4437 entry->newTransactedParentEntry = entry->transactedParentEntry;
4438 else
4440 DirEntry newData;
4442 memcpy(&newData, &entry->data, sizeof(DirEntry));
4444 newData.size.QuadPart = 0;
4445 newData.startingBlock = BLOCK_END_OF_CHAIN;
4447 if (newData.leftChild != DIRENTRY_NULL)
4448 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4450 if (newData.rightChild != DIRENTRY_NULL)
4451 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4453 if (newData.dirRootEntry != DIRENTRY_NULL)
4454 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4456 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4457 &entry->newTransactedParentEntry);
4458 if (FAILED(hr))
4460 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4461 return hr;
4464 if (entry->stream_dirty)
4466 hr = StorageBaseImpl_CopyStream(
4467 This->transactedParent, entry->newTransactedParentEntry,
4468 This->scratch, entry->stream_entry);
4470 else if (entry->data.size.QuadPart)
4472 hr = StorageBaseImpl_StreamLink(
4473 This->transactedParent, entry->newTransactedParentEntry,
4474 entry->transactedParentEntry);
4477 if (FAILED(hr))
4479 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4480 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4481 return hr;
4485 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4486 entry = &This->entries[cursor];
4489 return hr;
4492 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4493 IStorage* iface,
4494 DWORD grfCommitFlags) /* [in] */
4496 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4497 TransactedDirEntry *root_entry;
4498 DirRef i, dir_root_ref;
4499 DirEntry data;
4500 ULARGE_INTEGER zero;
4501 HRESULT hr;
4503 zero.QuadPart = 0;
4505 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4507 /* Cannot commit a read-only transacted storage */
4508 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4509 return STG_E_ACCESSDENIED;
4511 /* To prevent data loss, we create the new structure in the file before we
4512 * delete the old one, so that in case of errors the old data is intact. We
4513 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4514 * needed in the rare situation where we have just enough free disk space to
4515 * overwrite the existing data. */
4517 root_entry = &This->entries[This->base.storageDirEntry];
4519 if (!root_entry->read)
4520 return S_OK;
4522 hr = TransactedSnapshotImpl_CopyTree(This);
4523 if (FAILED(hr)) return hr;
4525 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4526 dir_root_ref = DIRENTRY_NULL;
4527 else
4528 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4530 /* Update the storage to use the new data in one step. */
4531 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4532 root_entry->transactedParentEntry, &data);
4534 if (SUCCEEDED(hr))
4536 data.dirRootEntry = dir_root_ref;
4537 data.clsid = root_entry->data.clsid;
4538 data.ctime = root_entry->data.ctime;
4539 data.mtime = root_entry->data.mtime;
4541 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4542 root_entry->transactedParentEntry, &data);
4545 if (SUCCEEDED(hr))
4547 /* Destroy the old now-orphaned data. */
4548 for (i=0; i<This->entries_size; i++)
4550 TransactedDirEntry *entry = &This->entries[i];
4551 if (entry->inuse)
4553 if (entry->deleted)
4555 StorageBaseImpl_StreamSetSize(This->transactedParent,
4556 entry->transactedParentEntry, zero);
4557 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4558 entry->transactedParentEntry);
4559 memset(entry, 0, sizeof(TransactedDirEntry));
4560 This->firstFreeEntry = min(i, This->firstFreeEntry);
4562 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4564 if (entry->transactedParentEntry != DIRENTRY_NULL)
4565 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4566 entry->transactedParentEntry);
4567 if (entry->stream_dirty)
4569 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4570 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4571 entry->stream_dirty = 0;
4573 entry->dirty = 0;
4574 entry->transactedParentEntry = entry->newTransactedParentEntry;
4579 else
4581 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4584 return hr;
4587 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4588 IStorage* iface)
4590 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4591 ULARGE_INTEGER zero;
4592 ULONG i;
4594 TRACE("(%p)\n", iface);
4596 /* Destroy the open objects. */
4597 StorageBaseImpl_DeleteAll(&This->base);
4599 /* Clear out the scratch file. */
4600 zero.QuadPart = 0;
4601 for (i=0; i<This->entries_size; i++)
4603 if (This->entries[i].stream_dirty)
4605 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4606 zero);
4608 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4612 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4614 This->firstFreeEntry = 0;
4615 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4617 return S_OK;
4620 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4622 if (!This->reverted)
4624 TRACE("Storage invalidated (stg=%p)\n", This);
4626 This->reverted = 1;
4628 StorageBaseImpl_DeleteAll(This);
4632 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4634 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4636 TransactedSnapshotImpl_Revert((IStorage*)iface);
4638 IStorage_Release((IStorage*)This->transactedParent);
4640 IStorage_Release((IStorage*)This->scratch);
4642 HeapFree(GetProcessHeap(), 0, This->entries);
4644 HeapFree(GetProcessHeap(), 0, This);
4647 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4649 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4651 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4654 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4655 const DirEntry *newData, DirRef *index)
4657 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4658 DirRef new_ref;
4659 TransactedDirEntry *new_entry;
4661 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4662 if (new_ref == DIRENTRY_NULL)
4663 return E_OUTOFMEMORY;
4665 new_entry = &This->entries[new_ref];
4667 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4668 new_entry->read = 1;
4669 new_entry->dirty = 1;
4670 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4672 *index = new_ref;
4674 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4676 return S_OK;
4679 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4680 DirRef index, const DirEntry *data)
4682 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4683 HRESULT hr;
4685 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4687 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4688 if (FAILED(hr)) return hr;
4690 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4692 if (index != This->base.storageDirEntry)
4694 This->entries[index].dirty = 1;
4696 if (data->size.QuadPart == 0 &&
4697 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4699 /* Since this entry is modified, and we aren't using its stream data, we
4700 * no longer care about the original entry. */
4701 DirRef delete_ref;
4702 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4704 if (delete_ref != DIRENTRY_NULL)
4705 This->entries[delete_ref].deleted = 1;
4707 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4711 return S_OK;
4714 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4715 DirRef index, DirEntry *data)
4717 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4718 HRESULT hr;
4720 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4721 if (FAILED(hr)) return hr;
4723 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4725 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4727 return S_OK;
4730 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4731 DirRef index)
4733 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4735 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4736 This->entries[index].data.size.QuadPart != 0)
4738 /* If we deleted this entry while it has stream data. We must have left the
4739 * data because some other entry is using it, and we need to leave the
4740 * original entry alone. */
4741 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4742 This->firstFreeEntry = min(index, This->firstFreeEntry);
4744 else
4746 This->entries[index].deleted = 1;
4749 return S_OK;
4752 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4753 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4755 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4757 if (This->entries[index].stream_dirty)
4759 return StorageBaseImpl_StreamReadAt(This->scratch,
4760 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4762 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4764 /* This stream doesn't live in the parent, and we haven't allocated storage
4765 * for it yet */
4766 *bytesRead = 0;
4767 return S_OK;
4769 else
4771 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4772 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4776 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4777 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4779 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4780 HRESULT hr;
4782 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4783 if (FAILED(hr)) return hr;
4785 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4786 if (FAILED(hr)) return hr;
4788 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4789 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4791 if (SUCCEEDED(hr) && size != 0)
4792 This->entries[index].data.size.QuadPart = max(
4793 This->entries[index].data.size.QuadPart,
4794 offset.QuadPart + size);
4796 return hr;
4799 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4800 DirRef index, ULARGE_INTEGER newsize)
4802 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4803 HRESULT hr;
4805 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4806 if (FAILED(hr)) return hr;
4808 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4809 return S_OK;
4811 if (newsize.QuadPart == 0)
4813 /* Destroy any parent references or entries in the scratch file. */
4814 if (This->entries[index].stream_dirty)
4816 ULARGE_INTEGER zero;
4817 zero.QuadPart = 0;
4818 StorageBaseImpl_StreamSetSize(This->scratch,
4819 This->entries[index].stream_entry, zero);
4820 StorageBaseImpl_DestroyDirEntry(This->scratch,
4821 This->entries[index].stream_entry);
4822 This->entries[index].stream_dirty = 0;
4824 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4826 DirRef delete_ref;
4827 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4829 if (delete_ref != DIRENTRY_NULL)
4830 This->entries[delete_ref].deleted = 1;
4832 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4835 else
4837 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4838 if (FAILED(hr)) return hr;
4840 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4841 This->entries[index].stream_entry, newsize);
4844 if (SUCCEEDED(hr))
4845 This->entries[index].data.size = newsize;
4847 return hr;
4850 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
4851 DirRef dst, DirRef src)
4853 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4854 HRESULT hr;
4855 TransactedDirEntry *dst_entry, *src_entry;
4857 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
4858 if (FAILED(hr)) return hr;
4860 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
4861 if (FAILED(hr)) return hr;
4863 dst_entry = &This->entries[dst];
4864 src_entry = &This->entries[src];
4866 dst_entry->stream_dirty = src_entry->stream_dirty;
4867 dst_entry->stream_entry = src_entry->stream_entry;
4868 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
4869 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
4870 dst_entry->data.size = src_entry->data.size;
4872 return S_OK;
4875 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4877 StorageBaseImpl_QueryInterface,
4878 StorageBaseImpl_AddRef,
4879 StorageBaseImpl_Release,
4880 StorageBaseImpl_CreateStream,
4881 StorageBaseImpl_OpenStream,
4882 StorageBaseImpl_CreateStorage,
4883 StorageBaseImpl_OpenStorage,
4884 StorageBaseImpl_CopyTo,
4885 StorageBaseImpl_MoveElementTo,
4886 TransactedSnapshotImpl_Commit,
4887 TransactedSnapshotImpl_Revert,
4888 StorageBaseImpl_EnumElements,
4889 StorageBaseImpl_DestroyElement,
4890 StorageBaseImpl_RenameElement,
4891 StorageBaseImpl_SetElementTimes,
4892 StorageBaseImpl_SetClass,
4893 StorageBaseImpl_SetStateBits,
4894 StorageBaseImpl_Stat
4897 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4899 TransactedSnapshotImpl_Destroy,
4900 TransactedSnapshotImpl_Invalidate,
4901 TransactedSnapshotImpl_GetFilename,
4902 TransactedSnapshotImpl_CreateDirEntry,
4903 TransactedSnapshotImpl_WriteDirEntry,
4904 TransactedSnapshotImpl_ReadDirEntry,
4905 TransactedSnapshotImpl_DestroyDirEntry,
4906 TransactedSnapshotImpl_StreamReadAt,
4907 TransactedSnapshotImpl_StreamWriteAt,
4908 TransactedSnapshotImpl_StreamSetSize,
4909 TransactedSnapshotImpl_StreamLink
4912 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4913 TransactedSnapshotImpl** result)
4915 HRESULT hr;
4917 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4918 if (*result)
4920 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4922 /* This is OK because the property set storage functions use the IStorage functions. */
4923 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4925 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4927 list_init(&(*result)->base.strmHead);
4929 list_init(&(*result)->base.storageHead);
4931 (*result)->base.ref = 1;
4933 (*result)->base.openFlags = parentStorage->openFlags;
4935 /* Create a new temporary storage to act as the scratch file. */
4936 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
4937 0, (IStorage**)&(*result)->scratch);
4939 if (SUCCEEDED(hr))
4941 ULONG num_entries = 20;
4943 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
4945 (*result)->entries_size = num_entries;
4947 (*result)->firstFreeEntry = 0;
4949 if ((*result)->entries)
4951 /* parentStorage already has 1 reference, which we take over here. */
4952 (*result)->transactedParent = parentStorage;
4954 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4956 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
4958 else
4960 IStorage_Release((IStorage*)(*result)->scratch);
4962 hr = E_OUTOFMEMORY;
4966 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4968 return hr;
4970 else
4971 return E_OUTOFMEMORY;
4974 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4975 StorageBaseImpl** result)
4977 static int fixme=0;
4979 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4981 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4984 return TransactedSnapshotImpl_Construct(parentStorage,
4985 (TransactedSnapshotImpl**)result);
4988 static HRESULT Storage_Construct(
4989 HANDLE hFile,
4990 LPCOLESTR pwcsName,
4991 ILockBytes* pLkbyt,
4992 DWORD openFlags,
4993 BOOL fileBased,
4994 BOOL create,
4995 ULONG sector_size,
4996 StorageBaseImpl** result)
4998 StorageImpl *newStorage;
4999 StorageBaseImpl *newTransactedStorage;
5000 HRESULT hr;
5002 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5003 if (FAILED(hr)) goto end;
5005 if (openFlags & STGM_TRANSACTED)
5007 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5008 if (FAILED(hr))
5009 IStorage_Release((IStorage*)newStorage);
5010 else
5011 *result = newTransactedStorage;
5013 else
5014 *result = &newStorage->base;
5016 end:
5017 return hr;
5020 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5022 StorageInternalImpl* This = (StorageInternalImpl*) base;
5024 if (!This->base.reverted)
5026 TRACE("Storage invalidated (stg=%p)\n", This);
5028 This->base.reverted = 1;
5030 This->parentStorage = NULL;
5032 StorageBaseImpl_DeleteAll(&This->base);
5034 list_remove(&This->ParentListEntry);
5038 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5040 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5042 StorageInternalImpl_Invalidate(&This->base);
5044 HeapFree(GetProcessHeap(), 0, This);
5047 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5049 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5051 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5054 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5055 const DirEntry *newData, DirRef *index)
5057 StorageInternalImpl* This = (StorageInternalImpl*) base;
5059 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5060 newData, index);
5063 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5064 DirRef index, const DirEntry *data)
5066 StorageInternalImpl* This = (StorageInternalImpl*) base;
5068 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5069 index, data);
5072 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5073 DirRef index, DirEntry *data)
5075 StorageInternalImpl* This = (StorageInternalImpl*) base;
5077 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5078 index, data);
5081 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5082 DirRef index)
5084 StorageInternalImpl* This = (StorageInternalImpl*) base;
5086 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5087 index);
5090 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5091 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5093 StorageInternalImpl* This = (StorageInternalImpl*) base;
5095 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5096 index, offset, size, buffer, bytesRead);
5099 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5100 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5102 StorageInternalImpl* This = (StorageInternalImpl*) base;
5104 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5105 index, offset, size, buffer, bytesWritten);
5108 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5109 DirRef index, ULARGE_INTEGER newsize)
5111 StorageInternalImpl* This = (StorageInternalImpl*) base;
5113 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5114 index, newsize);
5117 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5118 DirRef dst, DirRef src)
5120 StorageInternalImpl* This = (StorageInternalImpl*) base;
5122 return StorageBaseImpl_StreamLink(This->parentStorage,
5123 dst, src);
5126 /******************************************************************************
5128 ** Storage32InternalImpl_Commit
5131 static HRESULT WINAPI StorageInternalImpl_Commit(
5132 IStorage* iface,
5133 DWORD grfCommitFlags) /* [in] */
5135 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
5136 return S_OK;
5139 /******************************************************************************
5141 ** Storage32InternalImpl_Revert
5144 static HRESULT WINAPI StorageInternalImpl_Revert(
5145 IStorage* iface)
5147 FIXME("(%p): stub\n", iface);
5148 return S_OK;
5151 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5153 IStorage_Release((IStorage*)This->parentStorage);
5154 HeapFree(GetProcessHeap(), 0, This);
5157 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5158 IEnumSTATSTG* iface,
5159 REFIID riid,
5160 void** ppvObject)
5162 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5164 if (ppvObject==0)
5165 return E_INVALIDARG;
5167 *ppvObject = 0;
5169 if (IsEqualGUID(&IID_IUnknown, riid) ||
5170 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5172 *ppvObject = This;
5173 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
5174 return S_OK;
5177 return E_NOINTERFACE;
5180 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5181 IEnumSTATSTG* iface)
5183 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5184 return InterlockedIncrement(&This->ref);
5187 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5188 IEnumSTATSTG* iface)
5190 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5192 ULONG newRef;
5194 newRef = InterlockedDecrement(&This->ref);
5196 if (newRef==0)
5198 IEnumSTATSTGImpl_Destroy(This);
5201 return newRef;
5204 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5205 IEnumSTATSTGImpl* This,
5206 DirRef *ref)
5208 DirRef result = DIRENTRY_NULL;
5209 DirRef searchNode;
5210 DirEntry entry;
5211 HRESULT hr;
5212 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5214 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5215 This->parentStorage->storageDirEntry, &entry);
5216 searchNode = entry.dirRootEntry;
5218 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5220 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5222 if (SUCCEEDED(hr))
5224 LONG diff = entryNameCmp( entry.name, This->name);
5226 if (diff <= 0)
5228 searchNode = entry.rightChild;
5230 else
5232 result = searchNode;
5233 memcpy(result_name, entry.name, sizeof(result_name));
5234 searchNode = entry.leftChild;
5239 if (SUCCEEDED(hr))
5241 *ref = result;
5242 if (result != DIRENTRY_NULL)
5243 memcpy(This->name, result_name, sizeof(result_name));
5246 return hr;
5249 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5250 IEnumSTATSTG* iface,
5251 ULONG celt,
5252 STATSTG* rgelt,
5253 ULONG* pceltFetched)
5255 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5257 DirEntry currentEntry;
5258 STATSTG* currentReturnStruct = rgelt;
5259 ULONG objectFetched = 0;
5260 DirRef currentSearchNode;
5261 HRESULT hr=S_OK;
5263 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5264 return E_INVALIDARG;
5266 if (This->parentStorage->reverted)
5267 return STG_E_REVERTED;
5270 * To avoid the special case, get another pointer to a ULONG value if
5271 * the caller didn't supply one.
5273 if (pceltFetched==0)
5274 pceltFetched = &objectFetched;
5277 * Start the iteration, we will iterate until we hit the end of the
5278 * linked list or until we hit the number of items to iterate through
5280 *pceltFetched = 0;
5282 while ( *pceltFetched < celt )
5284 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5286 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5287 break;
5290 * Read the entry from the storage.
5292 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5293 currentSearchNode,
5294 &currentEntry);
5297 * Copy the information to the return buffer.
5299 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5300 currentReturnStruct,
5301 &currentEntry,
5302 STATFLAG_DEFAULT);
5305 * Step to the next item in the iteration
5307 (*pceltFetched)++;
5308 currentReturnStruct++;
5311 if (SUCCEEDED(hr) && *pceltFetched != celt)
5312 hr = S_FALSE;
5314 return hr;
5318 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5319 IEnumSTATSTG* iface,
5320 ULONG celt)
5322 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5324 ULONG objectFetched = 0;
5325 DirRef currentSearchNode;
5326 HRESULT hr=S_OK;
5328 if (This->parentStorage->reverted)
5329 return STG_E_REVERTED;
5331 while ( (objectFetched < celt) )
5333 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5335 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5336 break;
5338 objectFetched++;
5341 if (SUCCEEDED(hr) && objectFetched != celt)
5342 return S_FALSE;
5344 return hr;
5347 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5348 IEnumSTATSTG* iface)
5350 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5352 if (This->parentStorage->reverted)
5353 return STG_E_REVERTED;
5355 This->name[0] = 0;
5357 return S_OK;
5360 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5361 IEnumSTATSTG* iface,
5362 IEnumSTATSTG** ppenum)
5364 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5366 IEnumSTATSTGImpl* newClone;
5368 if (This->parentStorage->reverted)
5369 return STG_E_REVERTED;
5372 * Perform a sanity check on the parameters.
5374 if (ppenum==0)
5375 return E_INVALIDARG;
5377 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5378 This->storageDirEntry);
5382 * The new clone enumeration must point to the same current node as
5383 * the ole one.
5385 memcpy(newClone->name, This->name, sizeof(newClone->name));
5387 *ppenum = (IEnumSTATSTG*)newClone;
5390 * Don't forget to nail down a reference to the clone before
5391 * returning it.
5393 IEnumSTATSTGImpl_AddRef(*ppenum);
5395 return S_OK;
5399 * Virtual function table for the IEnumSTATSTGImpl class.
5401 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5403 IEnumSTATSTGImpl_QueryInterface,
5404 IEnumSTATSTGImpl_AddRef,
5405 IEnumSTATSTGImpl_Release,
5406 IEnumSTATSTGImpl_Next,
5407 IEnumSTATSTGImpl_Skip,
5408 IEnumSTATSTGImpl_Reset,
5409 IEnumSTATSTGImpl_Clone
5412 /******************************************************************************
5413 ** IEnumSTATSTGImpl implementation
5416 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5417 StorageBaseImpl* parentStorage,
5418 DirRef storageDirEntry)
5420 IEnumSTATSTGImpl* newEnumeration;
5422 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5424 if (newEnumeration!=0)
5427 * Set-up the virtual function table and reference count.
5429 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5430 newEnumeration->ref = 0;
5433 * We want to nail-down the reference to the storage in case the
5434 * enumeration out-lives the storage in the client application.
5436 newEnumeration->parentStorage = parentStorage;
5437 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5439 newEnumeration->storageDirEntry = storageDirEntry;
5442 * Make sure the current node of the iterator is the first one.
5444 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
5447 return newEnumeration;
5451 * Virtual function table for the Storage32InternalImpl class.
5453 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5455 StorageBaseImpl_QueryInterface,
5456 StorageBaseImpl_AddRef,
5457 StorageBaseImpl_Release,
5458 StorageBaseImpl_CreateStream,
5459 StorageBaseImpl_OpenStream,
5460 StorageBaseImpl_CreateStorage,
5461 StorageBaseImpl_OpenStorage,
5462 StorageBaseImpl_CopyTo,
5463 StorageBaseImpl_MoveElementTo,
5464 StorageInternalImpl_Commit,
5465 StorageInternalImpl_Revert,
5466 StorageBaseImpl_EnumElements,
5467 StorageBaseImpl_DestroyElement,
5468 StorageBaseImpl_RenameElement,
5469 StorageBaseImpl_SetElementTimes,
5470 StorageBaseImpl_SetClass,
5471 StorageBaseImpl_SetStateBits,
5472 StorageBaseImpl_Stat
5475 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5477 StorageInternalImpl_Destroy,
5478 StorageInternalImpl_Invalidate,
5479 StorageInternalImpl_GetFilename,
5480 StorageInternalImpl_CreateDirEntry,
5481 StorageInternalImpl_WriteDirEntry,
5482 StorageInternalImpl_ReadDirEntry,
5483 StorageInternalImpl_DestroyDirEntry,
5484 StorageInternalImpl_StreamReadAt,
5485 StorageInternalImpl_StreamWriteAt,
5486 StorageInternalImpl_StreamSetSize,
5487 StorageInternalImpl_StreamLink
5490 /******************************************************************************
5491 ** Storage32InternalImpl implementation
5494 static StorageInternalImpl* StorageInternalImpl_Construct(
5495 StorageBaseImpl* parentStorage,
5496 DWORD openFlags,
5497 DirRef storageDirEntry)
5499 StorageInternalImpl* newStorage;
5501 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5503 if (newStorage!=0)
5505 list_init(&newStorage->base.strmHead);
5507 list_init(&newStorage->base.storageHead);
5510 * Initialize the virtual function table.
5512 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5513 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5514 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5515 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5517 newStorage->base.reverted = 0;
5519 newStorage->base.ref = 1;
5521 newStorage->parentStorage = parentStorage;
5524 * Keep a reference to the directory entry of this storage
5526 newStorage->base.storageDirEntry = storageDirEntry;
5528 newStorage->base.create = 0;
5530 return newStorage;
5533 return 0;
5536 /******************************************************************************
5537 ** StorageUtl implementation
5540 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5542 WORD tmp;
5544 memcpy(&tmp, buffer+offset, sizeof(WORD));
5545 *value = lendian16toh(tmp);
5548 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5550 value = htole16(value);
5551 memcpy(buffer+offset, &value, sizeof(WORD));
5554 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5556 DWORD tmp;
5558 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5559 *value = lendian32toh(tmp);
5562 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5564 value = htole32(value);
5565 memcpy(buffer+offset, &value, sizeof(DWORD));
5568 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5569 ULARGE_INTEGER* value)
5571 #ifdef WORDS_BIGENDIAN
5572 ULARGE_INTEGER tmp;
5574 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5575 value->u.LowPart = htole32(tmp.u.HighPart);
5576 value->u.HighPart = htole32(tmp.u.LowPart);
5577 #else
5578 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5579 #endif
5582 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5583 const ULARGE_INTEGER *value)
5585 #ifdef WORDS_BIGENDIAN
5586 ULARGE_INTEGER tmp;
5588 tmp.u.LowPart = htole32(value->u.HighPart);
5589 tmp.u.HighPart = htole32(value->u.LowPart);
5590 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5591 #else
5592 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5593 #endif
5596 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5598 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5599 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5600 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5602 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5605 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5607 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5608 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5609 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5611 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5614 void StorageUtl_CopyDirEntryToSTATSTG(
5615 StorageBaseImpl* storage,
5616 STATSTG* destination,
5617 const DirEntry* source,
5618 int statFlags)
5621 * The copy of the string occurs only when the flag is not set
5623 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5625 /* Use the filename for the root storage. */
5626 destination->pwcsName = 0;
5627 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5629 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5630 (source->name[0] == 0) )
5632 destination->pwcsName = 0;
5634 else
5636 destination->pwcsName =
5637 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5639 strcpyW(destination->pwcsName, source->name);
5642 switch (source->stgType)
5644 case STGTY_STORAGE:
5645 case STGTY_ROOT:
5646 destination->type = STGTY_STORAGE;
5647 break;
5648 case STGTY_STREAM:
5649 destination->type = STGTY_STREAM;
5650 break;
5651 default:
5652 destination->type = STGTY_STREAM;
5653 break;
5656 destination->cbSize = source->size;
5658 currentReturnStruct->mtime = {0}; TODO
5659 currentReturnStruct->ctime = {0};
5660 currentReturnStruct->atime = {0};
5662 destination->grfMode = 0;
5663 destination->grfLocksSupported = 0;
5664 destination->clsid = source->clsid;
5665 destination->grfStateBits = 0;
5666 destination->reserved = 0;
5669 /******************************************************************************
5670 ** BlockChainStream implementation
5673 /* Read and save the index of all blocks in this stream. */
5674 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5676 ULONG next_sector, next_offset;
5677 HRESULT hr;
5678 struct BlockChainRun *last_run;
5680 if (This->indexCacheLen == 0)
5682 last_run = NULL;
5683 next_offset = 0;
5684 next_sector = BlockChainStream_GetHeadOfChain(This);
5686 else
5688 last_run = &This->indexCache[This->indexCacheLen-1];
5689 next_offset = last_run->lastOffset+1;
5690 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5691 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5692 &next_sector);
5693 if (FAILED(hr)) return hr;
5696 while (next_sector != BLOCK_END_OF_CHAIN)
5698 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5700 /* Add the current block to the cache. */
5701 if (This->indexCacheSize == 0)
5703 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5704 if (!This->indexCache) return E_OUTOFMEMORY;
5705 This->indexCacheSize = 16;
5707 else if (This->indexCacheSize == This->indexCacheLen)
5709 struct BlockChainRun *new_cache;
5710 ULONG new_size;
5712 new_size = This->indexCacheSize * 2;
5713 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5714 if (!new_cache) return E_OUTOFMEMORY;
5715 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5717 HeapFree(GetProcessHeap(), 0, This->indexCache);
5718 This->indexCache = new_cache;
5719 This->indexCacheSize = new_size;
5722 This->indexCacheLen++;
5723 last_run = &This->indexCache[This->indexCacheLen-1];
5724 last_run->firstSector = next_sector;
5725 last_run->firstOffset = next_offset;
5728 last_run->lastOffset = next_offset;
5730 /* Find the next block. */
5731 next_offset++;
5732 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5733 if (FAILED(hr)) return hr;
5736 if (This->indexCacheLen)
5738 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5739 This->numBlocks = last_run->lastOffset+1;
5741 else
5743 This->tailIndex = BLOCK_END_OF_CHAIN;
5744 This->numBlocks = 0;
5747 return S_OK;
5750 /* Locate the nth block in this stream. */
5751 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5753 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5754 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5756 if (offset >= This->numBlocks)
5757 return BLOCK_END_OF_CHAIN;
5759 while (min_run < max_run)
5761 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5762 if (offset < This->indexCache[run_to_check].firstOffset)
5764 max_offset = This->indexCache[run_to_check].firstOffset-1;
5765 max_run = run_to_check-1;
5767 else if (offset > This->indexCache[run_to_check].lastOffset)
5769 min_offset = This->indexCache[run_to_check].lastOffset+1;
5770 min_run = run_to_check+1;
5772 else
5773 /* Block is in this run. */
5774 min_run = max_run = run_to_check;
5777 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5780 BlockChainStream* BlockChainStream_Construct(
5781 StorageImpl* parentStorage,
5782 ULONG* headOfStreamPlaceHolder,
5783 DirRef dirEntry)
5785 BlockChainStream* newStream;
5787 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5789 newStream->parentStorage = parentStorage;
5790 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5791 newStream->ownerDirEntry = dirEntry;
5792 newStream->indexCache = NULL;
5793 newStream->indexCacheLen = 0;
5794 newStream->indexCacheSize = 0;
5796 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5798 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5799 HeapFree(GetProcessHeap(), 0, newStream);
5800 return NULL;
5803 return newStream;
5806 void BlockChainStream_Destroy(BlockChainStream* This)
5808 if (This)
5809 HeapFree(GetProcessHeap(), 0, This->indexCache);
5810 HeapFree(GetProcessHeap(), 0, This);
5813 /******************************************************************************
5814 * BlockChainStream_GetHeadOfChain
5816 * Returns the head of this stream chain.
5817 * Some special chains don't have directory entries, their heads are kept in
5818 * This->headOfStreamPlaceHolder.
5821 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5823 DirEntry chainEntry;
5824 HRESULT hr;
5826 if (This->headOfStreamPlaceHolder != 0)
5827 return *(This->headOfStreamPlaceHolder);
5829 if (This->ownerDirEntry != DIRENTRY_NULL)
5831 hr = StorageImpl_ReadDirEntry(
5832 This->parentStorage,
5833 This->ownerDirEntry,
5834 &chainEntry);
5836 if (SUCCEEDED(hr))
5838 return chainEntry.startingBlock;
5842 return BLOCK_END_OF_CHAIN;
5845 /******************************************************************************
5846 * BlockChainStream_GetCount
5848 * Returns the number of blocks that comprises this chain.
5849 * This is not the size of the stream as the last block may not be full!
5851 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5853 return This->numBlocks;
5856 /******************************************************************************
5857 * BlockChainStream_ReadAt
5859 * Reads a specified number of bytes from this chain at the specified offset.
5860 * bytesRead may be NULL.
5861 * Failure will be returned if the specified number of bytes has not been read.
5863 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5864 ULARGE_INTEGER offset,
5865 ULONG size,
5866 void* buffer,
5867 ULONG* bytesRead)
5869 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5870 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5871 ULONG bytesToReadInBuffer;
5872 ULONG blockIndex;
5873 BYTE* bufferWalker;
5874 ULARGE_INTEGER stream_size;
5876 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5879 * Find the first block in the stream that contains part of the buffer.
5881 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5883 *bytesRead = 0;
5885 stream_size = BlockChainStream_GetSize(This);
5886 if (stream_size.QuadPart > offset.QuadPart)
5887 size = min(stream_size.QuadPart - offset.QuadPart, size);
5888 else
5889 return S_OK;
5892 * Start reading the buffer.
5894 bufferWalker = buffer;
5896 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5898 ULARGE_INTEGER ulOffset;
5899 DWORD bytesReadAt;
5901 * Calculate how many bytes we can copy from this big block.
5903 bytesToReadInBuffer =
5904 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5906 TRACE("block %i\n",blockIndex);
5907 ulOffset.u.HighPart = 0;
5908 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5909 offsetInBlock;
5911 StorageImpl_ReadAt(This->parentStorage,
5912 ulOffset,
5913 bufferWalker,
5914 bytesToReadInBuffer,
5915 &bytesReadAt);
5917 * Step to the next big block.
5919 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5920 return STG_E_DOCFILECORRUPT;
5922 bufferWalker += bytesReadAt;
5923 size -= bytesReadAt;
5924 *bytesRead += bytesReadAt;
5925 offsetInBlock = 0; /* There is no offset on the next block */
5927 if (bytesToReadInBuffer != bytesReadAt)
5928 break;
5931 return S_OK;
5934 /******************************************************************************
5935 * BlockChainStream_WriteAt
5937 * Writes the specified number of bytes to this chain at the specified offset.
5938 * Will fail if not all specified number of bytes have been written.
5940 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5941 ULARGE_INTEGER offset,
5942 ULONG size,
5943 const void* buffer,
5944 ULONG* bytesWritten)
5946 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5947 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5948 ULONG bytesToWrite;
5949 ULONG blockIndex;
5950 const BYTE* bufferWalker;
5953 * Find the first block in the stream that contains part of the buffer.
5955 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5957 /* BlockChainStream_SetSize should have already been called to ensure we have
5958 * enough blocks in the chain to write into */
5959 if (blockIndex == BLOCK_END_OF_CHAIN)
5961 ERR("not enough blocks in chain to write data\n");
5962 return STG_E_DOCFILECORRUPT;
5965 *bytesWritten = 0;
5966 bufferWalker = buffer;
5968 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5970 ULARGE_INTEGER ulOffset;
5971 DWORD bytesWrittenAt;
5973 * Calculate how many bytes we can copy from this big block.
5975 bytesToWrite =
5976 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5978 TRACE("block %i\n",blockIndex);
5979 ulOffset.u.HighPart = 0;
5980 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5981 offsetInBlock;
5983 StorageImpl_WriteAt(This->parentStorage,
5984 ulOffset,
5985 bufferWalker,
5986 bytesToWrite,
5987 &bytesWrittenAt);
5990 * Step to the next big block.
5992 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5993 &blockIndex)))
5994 return STG_E_DOCFILECORRUPT;
5996 bufferWalker += bytesWrittenAt;
5997 size -= bytesWrittenAt;
5998 *bytesWritten += bytesWrittenAt;
5999 offsetInBlock = 0; /* There is no offset on the next block */
6001 if (bytesWrittenAt != bytesToWrite)
6002 break;
6005 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6008 /******************************************************************************
6009 * BlockChainStream_Shrink
6011 * Shrinks this chain in the big block depot.
6013 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6014 ULARGE_INTEGER newSize)
6016 ULONG blockIndex;
6017 ULONG numBlocks;
6020 * Figure out how many blocks are needed to contain the new size
6022 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6024 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6025 numBlocks++;
6027 if (numBlocks)
6030 * Go to the new end of chain
6032 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6034 /* Mark the new end of chain */
6035 StorageImpl_SetNextBlockInChain(
6036 This->parentStorage,
6037 blockIndex,
6038 BLOCK_END_OF_CHAIN);
6040 This->tailIndex = blockIndex;
6042 else
6044 if (This->headOfStreamPlaceHolder != 0)
6046 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6048 else
6050 DirEntry chainEntry;
6051 assert(This->ownerDirEntry != DIRENTRY_NULL);
6053 StorageImpl_ReadDirEntry(
6054 This->parentStorage,
6055 This->ownerDirEntry,
6056 &chainEntry);
6058 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6060 StorageImpl_WriteDirEntry(
6061 This->parentStorage,
6062 This->ownerDirEntry,
6063 &chainEntry);
6066 This->tailIndex = BLOCK_END_OF_CHAIN;
6069 This->numBlocks = numBlocks;
6072 * Mark the extra blocks as free
6074 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6076 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6077 StorageImpl_FreeBigBlock(This->parentStorage,
6078 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6079 if (last_run->lastOffset == last_run->firstOffset)
6080 This->indexCacheLen--;
6081 else
6082 last_run->lastOffset--;
6085 return TRUE;
6088 /******************************************************************************
6089 * BlockChainStream_Enlarge
6091 * Grows this chain in the big block depot.
6093 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6094 ULARGE_INTEGER newSize)
6096 ULONG blockIndex, currentBlock;
6097 ULONG newNumBlocks;
6098 ULONG oldNumBlocks = 0;
6100 blockIndex = BlockChainStream_GetHeadOfChain(This);
6103 * Empty chain. Create the head.
6105 if (blockIndex == BLOCK_END_OF_CHAIN)
6107 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6108 StorageImpl_SetNextBlockInChain(This->parentStorage,
6109 blockIndex,
6110 BLOCK_END_OF_CHAIN);
6112 if (This->headOfStreamPlaceHolder != 0)
6114 *(This->headOfStreamPlaceHolder) = blockIndex;
6116 else
6118 DirEntry chainEntry;
6119 assert(This->ownerDirEntry != DIRENTRY_NULL);
6121 StorageImpl_ReadDirEntry(
6122 This->parentStorage,
6123 This->ownerDirEntry,
6124 &chainEntry);
6126 chainEntry.startingBlock = blockIndex;
6128 StorageImpl_WriteDirEntry(
6129 This->parentStorage,
6130 This->ownerDirEntry,
6131 &chainEntry);
6134 This->tailIndex = blockIndex;
6135 This->numBlocks = 1;
6139 * Figure out how many blocks are needed to contain this stream
6141 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6143 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6144 newNumBlocks++;
6147 * Go to the current end of chain
6149 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6151 currentBlock = blockIndex;
6153 while (blockIndex != BLOCK_END_OF_CHAIN)
6155 This->numBlocks++;
6156 currentBlock = blockIndex;
6158 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6159 &blockIndex)))
6160 return FALSE;
6163 This->tailIndex = currentBlock;
6166 currentBlock = This->tailIndex;
6167 oldNumBlocks = This->numBlocks;
6170 * Add new blocks to the chain
6172 if (oldNumBlocks < newNumBlocks)
6174 while (oldNumBlocks < newNumBlocks)
6176 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6178 StorageImpl_SetNextBlockInChain(
6179 This->parentStorage,
6180 currentBlock,
6181 blockIndex);
6183 StorageImpl_SetNextBlockInChain(
6184 This->parentStorage,
6185 blockIndex,
6186 BLOCK_END_OF_CHAIN);
6188 currentBlock = blockIndex;
6189 oldNumBlocks++;
6192 This->tailIndex = blockIndex;
6193 This->numBlocks = newNumBlocks;
6196 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6197 return FALSE;
6199 return TRUE;
6202 /******************************************************************************
6203 * BlockChainStream_SetSize
6205 * Sets the size of this stream. The big block depot will be updated.
6206 * The file will grow if we grow the chain.
6208 * TODO: Free the actual blocks in the file when we shrink the chain.
6209 * Currently, the blocks are still in the file. So the file size
6210 * doesn't shrink even if we shrink streams.
6212 BOOL BlockChainStream_SetSize(
6213 BlockChainStream* This,
6214 ULARGE_INTEGER newSize)
6216 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6218 if (newSize.u.LowPart == size.u.LowPart)
6219 return TRUE;
6221 if (newSize.u.LowPart < size.u.LowPart)
6223 BlockChainStream_Shrink(This, newSize);
6225 else
6227 BlockChainStream_Enlarge(This, newSize);
6230 return TRUE;
6233 /******************************************************************************
6234 * BlockChainStream_GetSize
6236 * Returns the size of this chain.
6237 * Will return the block count if this chain doesn't have a directory entry.
6239 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6241 DirEntry chainEntry;
6243 if(This->headOfStreamPlaceHolder == NULL)
6246 * This chain has a directory entry so use the size value from there.
6248 StorageImpl_ReadDirEntry(
6249 This->parentStorage,
6250 This->ownerDirEntry,
6251 &chainEntry);
6253 return chainEntry.size;
6255 else
6258 * this chain is a chain that does not have a directory entry, figure out the
6259 * size by making the product number of used blocks times the
6260 * size of them
6262 ULARGE_INTEGER result;
6263 result.u.HighPart = 0;
6265 result.u.LowPart =
6266 BlockChainStream_GetCount(This) *
6267 This->parentStorage->bigBlockSize;
6269 return result;
6273 /******************************************************************************
6274 ** SmallBlockChainStream implementation
6277 SmallBlockChainStream* SmallBlockChainStream_Construct(
6278 StorageImpl* parentStorage,
6279 ULONG* headOfStreamPlaceHolder,
6280 DirRef dirEntry)
6282 SmallBlockChainStream* newStream;
6284 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6286 newStream->parentStorage = parentStorage;
6287 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6288 newStream->ownerDirEntry = dirEntry;
6290 return newStream;
6293 void SmallBlockChainStream_Destroy(
6294 SmallBlockChainStream* This)
6296 HeapFree(GetProcessHeap(), 0, This);
6299 /******************************************************************************
6300 * SmallBlockChainStream_GetHeadOfChain
6302 * Returns the head of this chain of small blocks.
6304 static ULONG SmallBlockChainStream_GetHeadOfChain(
6305 SmallBlockChainStream* This)
6307 DirEntry chainEntry;
6308 HRESULT hr;
6310 if (This->headOfStreamPlaceHolder != NULL)
6311 return *(This->headOfStreamPlaceHolder);
6313 if (This->ownerDirEntry)
6315 hr = StorageImpl_ReadDirEntry(
6316 This->parentStorage,
6317 This->ownerDirEntry,
6318 &chainEntry);
6320 if (SUCCEEDED(hr))
6322 return chainEntry.startingBlock;
6327 return BLOCK_END_OF_CHAIN;
6330 /******************************************************************************
6331 * SmallBlockChainStream_GetNextBlockInChain
6333 * Returns the index of the next small block in this chain.
6335 * Return Values:
6336 * - BLOCK_END_OF_CHAIN: end of this chain
6337 * - BLOCK_UNUSED: small block 'blockIndex' is free
6339 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6340 SmallBlockChainStream* This,
6341 ULONG blockIndex,
6342 ULONG* nextBlockInChain)
6344 ULARGE_INTEGER offsetOfBlockInDepot;
6345 DWORD buffer;
6346 ULONG bytesRead;
6347 HRESULT res;
6349 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6351 offsetOfBlockInDepot.u.HighPart = 0;
6352 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6355 * Read those bytes in the buffer from the small block file.
6357 res = BlockChainStream_ReadAt(
6358 This->parentStorage->smallBlockDepotChain,
6359 offsetOfBlockInDepot,
6360 sizeof(DWORD),
6361 &buffer,
6362 &bytesRead);
6364 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6365 res = STG_E_READFAULT;
6367 if (SUCCEEDED(res))
6369 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6370 return S_OK;
6373 return res;
6376 /******************************************************************************
6377 * SmallBlockChainStream_SetNextBlockInChain
6379 * Writes the index of the next block of the specified block in the small
6380 * block depot.
6381 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6382 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6384 static void SmallBlockChainStream_SetNextBlockInChain(
6385 SmallBlockChainStream* This,
6386 ULONG blockIndex,
6387 ULONG nextBlock)
6389 ULARGE_INTEGER offsetOfBlockInDepot;
6390 DWORD buffer;
6391 ULONG bytesWritten;
6393 offsetOfBlockInDepot.u.HighPart = 0;
6394 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6396 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6399 * Read those bytes in the buffer from the small block file.
6401 BlockChainStream_WriteAt(
6402 This->parentStorage->smallBlockDepotChain,
6403 offsetOfBlockInDepot,
6404 sizeof(DWORD),
6405 &buffer,
6406 &bytesWritten);
6409 /******************************************************************************
6410 * SmallBlockChainStream_FreeBlock
6412 * Flag small block 'blockIndex' as free in the small block depot.
6414 static void SmallBlockChainStream_FreeBlock(
6415 SmallBlockChainStream* This,
6416 ULONG blockIndex)
6418 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6421 /******************************************************************************
6422 * SmallBlockChainStream_GetNextFreeBlock
6424 * Returns the index of a free small block. The small block depot will be
6425 * enlarged if necessary. The small block chain will also be enlarged if
6426 * necessary.
6428 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6429 SmallBlockChainStream* This)
6431 ULARGE_INTEGER offsetOfBlockInDepot;
6432 DWORD buffer;
6433 ULONG bytesRead;
6434 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6435 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6436 HRESULT res = S_OK;
6437 ULONG smallBlocksPerBigBlock;
6438 DirEntry rootEntry;
6439 ULONG blocksRequired;
6440 ULARGE_INTEGER old_size, size_required;
6442 offsetOfBlockInDepot.u.HighPart = 0;
6445 * Scan the small block depot for a free block
6447 while (nextBlockIndex != BLOCK_UNUSED)
6449 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6451 res = BlockChainStream_ReadAt(
6452 This->parentStorage->smallBlockDepotChain,
6453 offsetOfBlockInDepot,
6454 sizeof(DWORD),
6455 &buffer,
6456 &bytesRead);
6459 * If we run out of space for the small block depot, enlarge it
6461 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6463 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6465 if (nextBlockIndex != BLOCK_UNUSED)
6466 blockIndex++;
6468 else
6470 ULONG count =
6471 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6473 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6474 ULARGE_INTEGER newSize, offset;
6475 ULONG bytesWritten;
6477 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6478 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6481 * Initialize all the small blocks to free
6483 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6484 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6485 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6486 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6488 StorageImpl_SaveFileHeader(This->parentStorage);
6492 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6494 smallBlocksPerBigBlock =
6495 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6498 * Verify if we have to allocate big blocks to contain small blocks
6500 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6502 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6504 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6506 if (size_required.QuadPart > old_size.QuadPart)
6508 BlockChainStream_SetSize(
6509 This->parentStorage->smallBlockRootChain,
6510 size_required);
6512 StorageImpl_ReadDirEntry(
6513 This->parentStorage,
6514 This->parentStorage->base.storageDirEntry,
6515 &rootEntry);
6517 rootEntry.size = size_required;
6519 StorageImpl_WriteDirEntry(
6520 This->parentStorage,
6521 This->parentStorage->base.storageDirEntry,
6522 &rootEntry);
6525 return blockIndex;
6528 /******************************************************************************
6529 * SmallBlockChainStream_ReadAt
6531 * Reads a specified number of bytes from this chain at the specified offset.
6532 * bytesRead may be NULL.
6533 * Failure will be returned if the specified number of bytes has not been read.
6535 HRESULT SmallBlockChainStream_ReadAt(
6536 SmallBlockChainStream* This,
6537 ULARGE_INTEGER offset,
6538 ULONG size,
6539 void* buffer,
6540 ULONG* bytesRead)
6542 HRESULT rc = S_OK;
6543 ULARGE_INTEGER offsetInBigBlockFile;
6544 ULONG blockNoInSequence =
6545 offset.u.LowPart / This->parentStorage->smallBlockSize;
6547 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6548 ULONG bytesToReadInBuffer;
6549 ULONG blockIndex;
6550 ULONG bytesReadFromBigBlockFile;
6551 BYTE* bufferWalker;
6552 ULARGE_INTEGER stream_size;
6555 * This should never happen on a small block file.
6557 assert(offset.u.HighPart==0);
6559 *bytesRead = 0;
6561 stream_size = SmallBlockChainStream_GetSize(This);
6562 if (stream_size.QuadPart > offset.QuadPart)
6563 size = min(stream_size.QuadPart - offset.QuadPart, size);
6564 else
6565 return S_OK;
6568 * Find the first block in the stream that contains part of the buffer.
6570 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6572 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6574 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6575 if(FAILED(rc))
6576 return rc;
6577 blockNoInSequence--;
6581 * Start reading the buffer.
6583 bufferWalker = buffer;
6585 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6588 * Calculate how many bytes we can copy from this small block.
6590 bytesToReadInBuffer =
6591 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6594 * Calculate the offset of the small block in the small block file.
6596 offsetInBigBlockFile.u.HighPart = 0;
6597 offsetInBigBlockFile.u.LowPart =
6598 blockIndex * This->parentStorage->smallBlockSize;
6600 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6603 * Read those bytes in the buffer from the small block file.
6604 * The small block has already been identified so it shouldn't fail
6605 * unless the file is corrupt.
6607 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6608 offsetInBigBlockFile,
6609 bytesToReadInBuffer,
6610 bufferWalker,
6611 &bytesReadFromBigBlockFile);
6613 if (FAILED(rc))
6614 return rc;
6616 if (!bytesReadFromBigBlockFile)
6617 return STG_E_DOCFILECORRUPT;
6620 * Step to the next big block.
6622 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6623 if(FAILED(rc))
6624 return STG_E_DOCFILECORRUPT;
6626 bufferWalker += bytesReadFromBigBlockFile;
6627 size -= bytesReadFromBigBlockFile;
6628 *bytesRead += bytesReadFromBigBlockFile;
6629 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6632 return S_OK;
6635 /******************************************************************************
6636 * SmallBlockChainStream_WriteAt
6638 * Writes the specified number of bytes to this chain at the specified offset.
6639 * Will fail if not all specified number of bytes have been written.
6641 HRESULT SmallBlockChainStream_WriteAt(
6642 SmallBlockChainStream* This,
6643 ULARGE_INTEGER offset,
6644 ULONG size,
6645 const void* buffer,
6646 ULONG* bytesWritten)
6648 ULARGE_INTEGER offsetInBigBlockFile;
6649 ULONG blockNoInSequence =
6650 offset.u.LowPart / This->parentStorage->smallBlockSize;
6652 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6653 ULONG bytesToWriteInBuffer;
6654 ULONG blockIndex;
6655 ULONG bytesWrittenToBigBlockFile;
6656 const BYTE* bufferWalker;
6657 HRESULT res;
6660 * This should never happen on a small block file.
6662 assert(offset.u.HighPart==0);
6665 * Find the first block in the stream that contains part of the buffer.
6667 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6669 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6671 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6672 return STG_E_DOCFILECORRUPT;
6673 blockNoInSequence--;
6677 * Start writing the buffer.
6679 *bytesWritten = 0;
6680 bufferWalker = buffer;
6681 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6684 * Calculate how many bytes we can copy to this small block.
6686 bytesToWriteInBuffer =
6687 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6690 * Calculate the offset of the small block in the small block file.
6692 offsetInBigBlockFile.u.HighPart = 0;
6693 offsetInBigBlockFile.u.LowPart =
6694 blockIndex * This->parentStorage->smallBlockSize;
6696 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6699 * Write those bytes in the buffer to the small block file.
6701 res = BlockChainStream_WriteAt(
6702 This->parentStorage->smallBlockRootChain,
6703 offsetInBigBlockFile,
6704 bytesToWriteInBuffer,
6705 bufferWalker,
6706 &bytesWrittenToBigBlockFile);
6707 if (FAILED(res))
6708 return res;
6711 * Step to the next big block.
6713 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6714 &blockIndex)))
6715 return FALSE;
6716 bufferWalker += bytesWrittenToBigBlockFile;
6717 size -= bytesWrittenToBigBlockFile;
6718 *bytesWritten += bytesWrittenToBigBlockFile;
6719 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6722 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6725 /******************************************************************************
6726 * SmallBlockChainStream_Shrink
6728 * Shrinks this chain in the small block depot.
6730 static BOOL SmallBlockChainStream_Shrink(
6731 SmallBlockChainStream* This,
6732 ULARGE_INTEGER newSize)
6734 ULONG blockIndex, extraBlock;
6735 ULONG numBlocks;
6736 ULONG count = 0;
6738 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6740 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6741 numBlocks++;
6743 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6746 * Go to the new end of chain
6748 while (count < numBlocks)
6750 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6751 &blockIndex)))
6752 return FALSE;
6753 count++;
6757 * If the count is 0, we have a special case, the head of the chain was
6758 * just freed.
6760 if (count == 0)
6762 DirEntry chainEntry;
6764 StorageImpl_ReadDirEntry(This->parentStorage,
6765 This->ownerDirEntry,
6766 &chainEntry);
6768 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6770 StorageImpl_WriteDirEntry(This->parentStorage,
6771 This->ownerDirEntry,
6772 &chainEntry);
6775 * We start freeing the chain at the head block.
6777 extraBlock = blockIndex;
6779 else
6781 /* Get the next block before marking the new end */
6782 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6783 &extraBlock)))
6784 return FALSE;
6786 /* Mark the new end of chain */
6787 SmallBlockChainStream_SetNextBlockInChain(
6788 This,
6789 blockIndex,
6790 BLOCK_END_OF_CHAIN);
6794 * Mark the extra blocks as free
6796 while (extraBlock != BLOCK_END_OF_CHAIN)
6798 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6799 &blockIndex)))
6800 return FALSE;
6801 SmallBlockChainStream_FreeBlock(This, extraBlock);
6802 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
6803 extraBlock = blockIndex;
6806 return TRUE;
6809 /******************************************************************************
6810 * SmallBlockChainStream_Enlarge
6812 * Grows this chain in the small block depot.
6814 static BOOL SmallBlockChainStream_Enlarge(
6815 SmallBlockChainStream* This,
6816 ULARGE_INTEGER newSize)
6818 ULONG blockIndex, currentBlock;
6819 ULONG newNumBlocks;
6820 ULONG oldNumBlocks = 0;
6822 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6825 * Empty chain. Create the head.
6827 if (blockIndex == BLOCK_END_OF_CHAIN)
6829 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6830 SmallBlockChainStream_SetNextBlockInChain(
6831 This,
6832 blockIndex,
6833 BLOCK_END_OF_CHAIN);
6835 if (This->headOfStreamPlaceHolder != NULL)
6837 *(This->headOfStreamPlaceHolder) = blockIndex;
6839 else
6841 DirEntry chainEntry;
6843 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6844 &chainEntry);
6846 chainEntry.startingBlock = blockIndex;
6848 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6849 &chainEntry);
6853 currentBlock = blockIndex;
6856 * Figure out how many blocks are needed to contain this stream
6858 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6860 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6861 newNumBlocks++;
6864 * Go to the current end of chain
6866 while (blockIndex != BLOCK_END_OF_CHAIN)
6868 oldNumBlocks++;
6869 currentBlock = blockIndex;
6870 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6871 return FALSE;
6875 * Add new blocks to the chain
6877 while (oldNumBlocks < newNumBlocks)
6879 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6880 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6882 SmallBlockChainStream_SetNextBlockInChain(
6883 This,
6884 blockIndex,
6885 BLOCK_END_OF_CHAIN);
6887 currentBlock = blockIndex;
6888 oldNumBlocks++;
6891 return TRUE;
6894 /******************************************************************************
6895 * SmallBlockChainStream_SetSize
6897 * Sets the size of this stream.
6898 * The file will grow if we grow the chain.
6900 * TODO: Free the actual blocks in the file when we shrink the chain.
6901 * Currently, the blocks are still in the file. So the file size
6902 * doesn't shrink even if we shrink streams.
6904 BOOL SmallBlockChainStream_SetSize(
6905 SmallBlockChainStream* This,
6906 ULARGE_INTEGER newSize)
6908 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6910 if (newSize.u.LowPart == size.u.LowPart)
6911 return TRUE;
6913 if (newSize.u.LowPart < size.u.LowPart)
6915 SmallBlockChainStream_Shrink(This, newSize);
6917 else
6919 SmallBlockChainStream_Enlarge(This, newSize);
6922 return TRUE;
6925 /******************************************************************************
6926 * SmallBlockChainStream_GetCount
6928 * Returns the number of small blocks that comprises this chain.
6929 * This is not the size of the stream as the last block may not be full!
6932 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6934 ULONG blockIndex;
6935 ULONG count = 0;
6937 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6939 while(blockIndex != BLOCK_END_OF_CHAIN)
6941 count++;
6943 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6944 blockIndex, &blockIndex)))
6945 return 0;
6948 return count;
6951 /******************************************************************************
6952 * SmallBlockChainStream_GetSize
6954 * Returns the size of this chain.
6956 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6958 DirEntry chainEntry;
6960 if(This->headOfStreamPlaceHolder != NULL)
6962 ULARGE_INTEGER result;
6963 result.u.HighPart = 0;
6965 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6966 This->parentStorage->smallBlockSize;
6968 return result;
6971 StorageImpl_ReadDirEntry(
6972 This->parentStorage,
6973 This->ownerDirEntry,
6974 &chainEntry);
6976 return chainEntry.size;
6979 static HRESULT create_storagefile(
6980 LPCOLESTR pwcsName,
6981 DWORD grfMode,
6982 DWORD grfAttrs,
6983 STGOPTIONS* pStgOptions,
6984 REFIID riid,
6985 void** ppstgOpen)
6987 StorageBaseImpl* newStorage = 0;
6988 HANDLE hFile = INVALID_HANDLE_VALUE;
6989 HRESULT hr = STG_E_INVALIDFLAG;
6990 DWORD shareMode;
6991 DWORD accessMode;
6992 DWORD creationMode;
6993 DWORD fileAttributes;
6994 WCHAR tempFileName[MAX_PATH];
6996 if (ppstgOpen == 0)
6997 return STG_E_INVALIDPOINTER;
6999 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7000 return STG_E_INVALIDPARAMETER;
7002 /* if no share mode given then DENY_NONE is the default */
7003 if (STGM_SHARE_MODE(grfMode) == 0)
7004 grfMode |= STGM_SHARE_DENY_NONE;
7006 if ( FAILED( validateSTGM(grfMode) ))
7007 goto end;
7009 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7010 switch(STGM_ACCESS_MODE(grfMode))
7012 case STGM_WRITE:
7013 case STGM_READWRITE:
7014 break;
7015 default:
7016 goto end;
7019 /* in direct mode, can only use SHARE_EXCLUSIVE */
7020 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7021 goto end;
7023 /* but in transacted mode, any share mode is valid */
7026 * Generate a unique name.
7028 if (pwcsName == 0)
7030 WCHAR tempPath[MAX_PATH];
7031 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7033 memset(tempPath, 0, sizeof(tempPath));
7034 memset(tempFileName, 0, sizeof(tempFileName));
7036 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7037 tempPath[0] = '.';
7039 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7040 pwcsName = tempFileName;
7041 else
7043 hr = STG_E_INSUFFICIENTMEMORY;
7044 goto end;
7047 creationMode = TRUNCATE_EXISTING;
7049 else
7051 creationMode = GetCreationModeFromSTGM(grfMode);
7055 * Interpret the STGM value grfMode
7057 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7058 accessMode = GetAccessModeFromSTGM(grfMode);
7060 if (grfMode & STGM_DELETEONRELEASE)
7061 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7062 else
7063 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7065 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7067 static int fixme;
7068 if (!fixme++)
7069 FIXME("Storage share mode not implemented.\n");
7072 *ppstgOpen = 0;
7074 hFile = CreateFileW(pwcsName,
7075 accessMode,
7076 shareMode,
7077 NULL,
7078 creationMode,
7079 fileAttributes,
7082 if (hFile == INVALID_HANDLE_VALUE)
7084 if(GetLastError() == ERROR_FILE_EXISTS)
7085 hr = STG_E_FILEALREADYEXISTS;
7086 else
7087 hr = E_FAIL;
7088 goto end;
7092 * Allocate and initialize the new IStorage32object.
7094 hr = Storage_Construct(
7095 hFile,
7096 pwcsName,
7097 NULL,
7098 grfMode,
7099 TRUE,
7100 TRUE,
7101 pStgOptions->ulSectorSize,
7102 &newStorage);
7104 if (FAILED(hr))
7106 goto end;
7109 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7111 IStorage_Release((IStorage*)newStorage);
7113 end:
7114 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7116 return hr;
7119 /******************************************************************************
7120 * StgCreateDocfile [OLE32.@]
7121 * Creates a new compound file storage object
7123 * PARAMS
7124 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7125 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7126 * reserved [ ?] unused?, usually 0
7127 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7129 * RETURNS
7130 * S_OK if the file was successfully created
7131 * some STG_E_ value if error
7132 * NOTES
7133 * if pwcsName is NULL, create file with new unique name
7134 * the function can returns
7135 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7136 * (unrealized now)
7138 HRESULT WINAPI StgCreateDocfile(
7139 LPCOLESTR pwcsName,
7140 DWORD grfMode,
7141 DWORD reserved,
7142 IStorage **ppstgOpen)
7144 STGOPTIONS stgoptions = {1, 0, 512};
7146 TRACE("(%s, %x, %d, %p)\n",
7147 debugstr_w(pwcsName), grfMode,
7148 reserved, ppstgOpen);
7150 if (ppstgOpen == 0)
7151 return STG_E_INVALIDPOINTER;
7152 if (reserved != 0)
7153 return STG_E_INVALIDPARAMETER;
7155 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7158 /******************************************************************************
7159 * StgCreateStorageEx [OLE32.@]
7161 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7163 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7164 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7166 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7168 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7169 return STG_E_INVALIDPARAMETER;
7172 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7174 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7175 return STG_E_INVALIDPARAMETER;
7178 if (stgfmt == STGFMT_FILE)
7180 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7181 return STG_E_INVALIDPARAMETER;
7184 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7186 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7190 ERR("Invalid stgfmt argument\n");
7191 return STG_E_INVALIDPARAMETER;
7194 /******************************************************************************
7195 * StgCreatePropSetStg [OLE32.@]
7197 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7198 IPropertySetStorage **ppPropSetStg)
7200 HRESULT hr;
7202 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7203 if (reserved)
7204 hr = STG_E_INVALIDPARAMETER;
7205 else
7206 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7207 (void**)ppPropSetStg);
7208 return hr;
7211 /******************************************************************************
7212 * StgOpenStorageEx [OLE32.@]
7214 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7216 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7217 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7219 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7221 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7222 return STG_E_INVALIDPARAMETER;
7225 switch (stgfmt)
7227 case STGFMT_FILE:
7228 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7229 return STG_E_INVALIDPARAMETER;
7231 case STGFMT_STORAGE:
7232 break;
7234 case STGFMT_DOCFILE:
7235 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7237 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7238 return STG_E_INVALIDPARAMETER;
7240 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7241 break;
7243 case STGFMT_ANY:
7244 WARN("STGFMT_ANY assuming storage\n");
7245 break;
7247 default:
7248 return STG_E_INVALIDPARAMETER;
7251 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7255 /******************************************************************************
7256 * StgOpenStorage [OLE32.@]
7258 HRESULT WINAPI StgOpenStorage(
7259 const OLECHAR *pwcsName,
7260 IStorage *pstgPriority,
7261 DWORD grfMode,
7262 SNB snbExclude,
7263 DWORD reserved,
7264 IStorage **ppstgOpen)
7266 StorageBaseImpl* newStorage = 0;
7267 HRESULT hr = S_OK;
7268 HANDLE hFile = 0;
7269 DWORD shareMode;
7270 DWORD accessMode;
7272 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7273 debugstr_w(pwcsName), pstgPriority, grfMode,
7274 snbExclude, reserved, ppstgOpen);
7276 if (pwcsName == 0)
7278 hr = STG_E_INVALIDNAME;
7279 goto end;
7282 if (ppstgOpen == 0)
7284 hr = STG_E_INVALIDPOINTER;
7285 goto end;
7288 if (reserved)
7290 hr = STG_E_INVALIDPARAMETER;
7291 goto end;
7294 if (grfMode & STGM_PRIORITY)
7296 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7297 return STG_E_INVALIDFLAG;
7298 if (grfMode & STGM_DELETEONRELEASE)
7299 return STG_E_INVALIDFUNCTION;
7300 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7301 return STG_E_INVALIDFLAG;
7302 grfMode &= ~0xf0; /* remove the existing sharing mode */
7303 grfMode |= STGM_SHARE_DENY_NONE;
7305 /* STGM_PRIORITY stops other IStorage objects on the same file from
7306 * committing until the STGM_PRIORITY IStorage is closed. it also
7307 * stops non-transacted mode StgOpenStorage calls with write access from
7308 * succeeding. obviously, both of these cannot be achieved through just
7309 * file share flags */
7310 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7314 * Validate the sharing mode
7316 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7317 switch(STGM_SHARE_MODE(grfMode))
7319 case STGM_SHARE_EXCLUSIVE:
7320 case STGM_SHARE_DENY_WRITE:
7321 break;
7322 default:
7323 hr = STG_E_INVALIDFLAG;
7324 goto end;
7327 if ( FAILED( validateSTGM(grfMode) ) ||
7328 (grfMode&STGM_CREATE))
7330 hr = STG_E_INVALIDFLAG;
7331 goto end;
7334 /* shared reading requires transacted mode */
7335 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7336 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7337 !(grfMode&STGM_TRANSACTED) )
7339 hr = STG_E_INVALIDFLAG;
7340 goto end;
7344 * Interpret the STGM value grfMode
7346 shareMode = GetShareModeFromSTGM(grfMode);
7347 accessMode = GetAccessModeFromSTGM(grfMode);
7349 *ppstgOpen = 0;
7351 hFile = CreateFileW( pwcsName,
7352 accessMode,
7353 shareMode,
7354 NULL,
7355 OPEN_EXISTING,
7356 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7359 if (hFile==INVALID_HANDLE_VALUE)
7361 DWORD last_error = GetLastError();
7363 hr = E_FAIL;
7365 switch (last_error)
7367 case ERROR_FILE_NOT_FOUND:
7368 hr = STG_E_FILENOTFOUND;
7369 break;
7371 case ERROR_PATH_NOT_FOUND:
7372 hr = STG_E_PATHNOTFOUND;
7373 break;
7375 case ERROR_ACCESS_DENIED:
7376 case ERROR_WRITE_PROTECT:
7377 hr = STG_E_ACCESSDENIED;
7378 break;
7380 case ERROR_SHARING_VIOLATION:
7381 hr = STG_E_SHAREVIOLATION;
7382 break;
7384 default:
7385 hr = E_FAIL;
7388 goto end;
7392 * Refuse to open the file if it's too small to be a structured storage file
7393 * FIXME: verify the file when reading instead of here
7395 if (GetFileSize(hFile, NULL) < 0x100)
7397 CloseHandle(hFile);
7398 hr = STG_E_FILEALREADYEXISTS;
7399 goto end;
7403 * Allocate and initialize the new IStorage32object.
7405 hr = Storage_Construct(
7406 hFile,
7407 pwcsName,
7408 NULL,
7409 grfMode,
7410 TRUE,
7411 FALSE,
7412 512,
7413 &newStorage);
7415 if (FAILED(hr))
7418 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7420 if(hr == STG_E_INVALIDHEADER)
7421 hr = STG_E_FILEALREADYEXISTS;
7422 goto end;
7426 * Get an "out" pointer for the caller.
7428 *ppstgOpen = (IStorage*)newStorage;
7430 end:
7431 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7432 return hr;
7435 /******************************************************************************
7436 * StgCreateDocfileOnILockBytes [OLE32.@]
7438 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7439 ILockBytes *plkbyt,
7440 DWORD grfMode,
7441 DWORD reserved,
7442 IStorage** ppstgOpen)
7444 StorageBaseImpl* newStorage = 0;
7445 HRESULT hr = S_OK;
7447 if ((ppstgOpen == 0) || (plkbyt == 0))
7448 return STG_E_INVALIDPOINTER;
7451 * Allocate and initialize the new IStorage object.
7453 hr = Storage_Construct(
7456 plkbyt,
7457 grfMode,
7458 FALSE,
7459 TRUE,
7460 512,
7461 &newStorage);
7463 if (FAILED(hr))
7465 return hr;
7469 * Get an "out" pointer for the caller.
7471 *ppstgOpen = (IStorage*)newStorage;
7473 return hr;
7476 /******************************************************************************
7477 * StgOpenStorageOnILockBytes [OLE32.@]
7479 HRESULT WINAPI StgOpenStorageOnILockBytes(
7480 ILockBytes *plkbyt,
7481 IStorage *pstgPriority,
7482 DWORD grfMode,
7483 SNB snbExclude,
7484 DWORD reserved,
7485 IStorage **ppstgOpen)
7487 StorageBaseImpl* newStorage = 0;
7488 HRESULT hr = S_OK;
7490 if ((plkbyt == 0) || (ppstgOpen == 0))
7491 return STG_E_INVALIDPOINTER;
7493 if ( FAILED( validateSTGM(grfMode) ))
7494 return STG_E_INVALIDFLAG;
7496 *ppstgOpen = 0;
7499 * Allocate and initialize the new IStorage object.
7501 hr = Storage_Construct(
7504 plkbyt,
7505 grfMode,
7506 FALSE,
7507 FALSE,
7508 512,
7509 &newStorage);
7511 if (FAILED(hr))
7513 return hr;
7517 * Get an "out" pointer for the caller.
7519 *ppstgOpen = (IStorage*)newStorage;
7521 return hr;
7524 /******************************************************************************
7525 * StgSetTimes [ole32.@]
7526 * StgSetTimes [OLE32.@]
7530 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7531 FILETIME const *patime, FILETIME const *pmtime)
7533 IStorage *stg = NULL;
7534 HRESULT r;
7536 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7538 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7539 0, 0, &stg);
7540 if( SUCCEEDED(r) )
7542 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7543 IStorage_Release(stg);
7546 return r;
7549 /******************************************************************************
7550 * StgIsStorageILockBytes [OLE32.@]
7552 * Determines if the ILockBytes contains a storage object.
7554 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7556 BYTE sig[8];
7557 ULARGE_INTEGER offset;
7559 offset.u.HighPart = 0;
7560 offset.u.LowPart = 0;
7562 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7564 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7565 return S_OK;
7567 return S_FALSE;
7570 /******************************************************************************
7571 * WriteClassStg [OLE32.@]
7573 * This method will store the specified CLSID in the specified storage object
7575 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7577 HRESULT hRes;
7579 if(!pStg)
7580 return E_INVALIDARG;
7582 if(!rclsid)
7583 return STG_E_INVALIDPOINTER;
7585 hRes = IStorage_SetClass(pStg, rclsid);
7587 return hRes;
7590 /***********************************************************************
7591 * ReadClassStg (OLE32.@)
7593 * This method reads the CLSID previously written to a storage object with
7594 * the WriteClassStg.
7596 * PARAMS
7597 * pstg [I] IStorage pointer
7598 * pclsid [O] Pointer to where the CLSID is written
7600 * RETURNS
7601 * Success: S_OK.
7602 * Failure: HRESULT code.
7604 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7606 STATSTG pstatstg;
7607 HRESULT hRes;
7609 TRACE("(%p, %p)\n", pstg, pclsid);
7611 if(!pstg || !pclsid)
7612 return E_INVALIDARG;
7615 * read a STATSTG structure (contains the clsid) from the storage
7617 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7619 if(SUCCEEDED(hRes))
7620 *pclsid=pstatstg.clsid;
7622 return hRes;
7625 /***********************************************************************
7626 * OleLoadFromStream (OLE32.@)
7628 * This function loads an object from stream
7630 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7632 CLSID clsid;
7633 HRESULT res;
7634 LPPERSISTSTREAM xstm;
7636 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7638 res=ReadClassStm(pStm,&clsid);
7639 if (FAILED(res))
7640 return res;
7641 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7642 if (FAILED(res))
7643 return res;
7644 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7645 if (FAILED(res)) {
7646 IUnknown_Release((IUnknown*)*ppvObj);
7647 return res;
7649 res=IPersistStream_Load(xstm,pStm);
7650 IPersistStream_Release(xstm);
7651 /* FIXME: all refcounts ok at this point? I think they should be:
7652 * pStm : unchanged
7653 * ppvObj : 1
7654 * xstm : 0 (released)
7656 return res;
7659 /***********************************************************************
7660 * OleSaveToStream (OLE32.@)
7662 * This function saves an object with the IPersistStream interface on it
7663 * to the specified stream.
7665 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7668 CLSID clsid;
7669 HRESULT res;
7671 TRACE("(%p,%p)\n",pPStm,pStm);
7673 res=IPersistStream_GetClassID(pPStm,&clsid);
7675 if (SUCCEEDED(res)){
7677 res=WriteClassStm(pStm,&clsid);
7679 if (SUCCEEDED(res))
7681 res=IPersistStream_Save(pPStm,pStm,TRUE);
7684 TRACE("Finished Save\n");
7685 return res;
7688 /****************************************************************************
7689 * This method validate a STGM parameter that can contain the values below
7691 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7692 * The stgm values contained in 0xffff0000 are bitmasks.
7694 * STGM_DIRECT 0x00000000
7695 * STGM_TRANSACTED 0x00010000
7696 * STGM_SIMPLE 0x08000000
7698 * STGM_READ 0x00000000
7699 * STGM_WRITE 0x00000001
7700 * STGM_READWRITE 0x00000002
7702 * STGM_SHARE_DENY_NONE 0x00000040
7703 * STGM_SHARE_DENY_READ 0x00000030
7704 * STGM_SHARE_DENY_WRITE 0x00000020
7705 * STGM_SHARE_EXCLUSIVE 0x00000010
7707 * STGM_PRIORITY 0x00040000
7708 * STGM_DELETEONRELEASE 0x04000000
7710 * STGM_CREATE 0x00001000
7711 * STGM_CONVERT 0x00020000
7712 * STGM_FAILIFTHERE 0x00000000
7714 * STGM_NOSCRATCH 0x00100000
7715 * STGM_NOSNAPSHOT 0x00200000
7717 static HRESULT validateSTGM(DWORD stgm)
7719 DWORD access = STGM_ACCESS_MODE(stgm);
7720 DWORD share = STGM_SHARE_MODE(stgm);
7721 DWORD create = STGM_CREATE_MODE(stgm);
7723 if (stgm&~STGM_KNOWN_FLAGS)
7725 ERR("unknown flags %08x\n", stgm);
7726 return E_FAIL;
7729 switch (access)
7731 case STGM_READ:
7732 case STGM_WRITE:
7733 case STGM_READWRITE:
7734 break;
7735 default:
7736 return E_FAIL;
7739 switch (share)
7741 case STGM_SHARE_DENY_NONE:
7742 case STGM_SHARE_DENY_READ:
7743 case STGM_SHARE_DENY_WRITE:
7744 case STGM_SHARE_EXCLUSIVE:
7745 break;
7746 default:
7747 return E_FAIL;
7750 switch (create)
7752 case STGM_CREATE:
7753 case STGM_FAILIFTHERE:
7754 break;
7755 default:
7756 return E_FAIL;
7760 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7762 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7763 return E_FAIL;
7766 * STGM_CREATE | STGM_CONVERT
7767 * if both are false, STGM_FAILIFTHERE is set to TRUE
7769 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7770 return E_FAIL;
7773 * STGM_NOSCRATCH requires STGM_TRANSACTED
7775 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7776 return E_FAIL;
7779 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7780 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7782 if ( (stgm & STGM_NOSNAPSHOT) &&
7783 (!(stgm & STGM_TRANSACTED) ||
7784 share == STGM_SHARE_EXCLUSIVE ||
7785 share == STGM_SHARE_DENY_WRITE) )
7786 return E_FAIL;
7788 return S_OK;
7791 /****************************************************************************
7792 * GetShareModeFromSTGM
7794 * This method will return a share mode flag from a STGM value.
7795 * The STGM value is assumed valid.
7797 static DWORD GetShareModeFromSTGM(DWORD stgm)
7799 switch (STGM_SHARE_MODE(stgm))
7801 case STGM_SHARE_DENY_NONE:
7802 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7803 case STGM_SHARE_DENY_READ:
7804 return FILE_SHARE_WRITE;
7805 case STGM_SHARE_DENY_WRITE:
7806 return FILE_SHARE_READ;
7807 case STGM_SHARE_EXCLUSIVE:
7808 return 0;
7810 ERR("Invalid share mode!\n");
7811 assert(0);
7812 return 0;
7815 /****************************************************************************
7816 * GetAccessModeFromSTGM
7818 * This method will return an access mode flag from a STGM value.
7819 * The STGM value is assumed valid.
7821 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7823 switch (STGM_ACCESS_MODE(stgm))
7825 case STGM_READ:
7826 return GENERIC_READ;
7827 case STGM_WRITE:
7828 case STGM_READWRITE:
7829 return GENERIC_READ | GENERIC_WRITE;
7831 ERR("Invalid access mode!\n");
7832 assert(0);
7833 return 0;
7836 /****************************************************************************
7837 * GetCreationModeFromSTGM
7839 * This method will return a creation mode flag from a STGM value.
7840 * The STGM value is assumed valid.
7842 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7844 switch(STGM_CREATE_MODE(stgm))
7846 case STGM_CREATE:
7847 return CREATE_ALWAYS;
7848 case STGM_CONVERT:
7849 FIXME("STGM_CONVERT not implemented!\n");
7850 return CREATE_NEW;
7851 case STGM_FAILIFTHERE:
7852 return CREATE_NEW;
7854 ERR("Invalid create mode!\n");
7855 assert(0);
7856 return 0;
7860 /*************************************************************************
7861 * OLECONVERT_LoadOLE10 [Internal]
7863 * Loads the OLE10 STREAM to memory
7865 * PARAMS
7866 * pOleStream [I] The OLESTREAM
7867 * pData [I] Data Structure for the OLESTREAM Data
7869 * RETURNS
7870 * Success: S_OK
7871 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7872 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7874 * NOTES
7875 * This function is used by OleConvertOLESTREAMToIStorage only.
7877 * Memory allocated for pData must be freed by the caller
7879 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7881 DWORD dwSize;
7882 HRESULT hRes = S_OK;
7883 int nTryCnt=0;
7884 int max_try = 6;
7886 pData->pData = NULL;
7887 pData->pstrOleObjFileName = NULL;
7889 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7891 /* Get the OleID */
7892 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7893 if(dwSize != sizeof(pData->dwOleID))
7895 hRes = CONVERT10_E_OLESTREAM_GET;
7897 else if(pData->dwOleID != OLESTREAM_ID)
7899 hRes = CONVERT10_E_OLESTREAM_FMT;
7901 else
7903 hRes = S_OK;
7904 break;
7908 if(hRes == S_OK)
7910 /* Get the TypeID... more info needed for this field */
7911 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7912 if(dwSize != sizeof(pData->dwTypeID))
7914 hRes = CONVERT10_E_OLESTREAM_GET;
7917 if(hRes == S_OK)
7919 if(pData->dwTypeID != 0)
7921 /* Get the length of the OleTypeName */
7922 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7923 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7925 hRes = CONVERT10_E_OLESTREAM_GET;
7928 if(hRes == S_OK)
7930 if(pData->dwOleTypeNameLength > 0)
7932 /* Get the OleTypeName */
7933 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7934 if(dwSize != pData->dwOleTypeNameLength)
7936 hRes = CONVERT10_E_OLESTREAM_GET;
7940 if(bStrem1)
7942 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7943 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7945 hRes = CONVERT10_E_OLESTREAM_GET;
7947 if(hRes == S_OK)
7949 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7950 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7951 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7952 if(pData->pstrOleObjFileName)
7954 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7955 if(dwSize != pData->dwOleObjFileNameLength)
7957 hRes = CONVERT10_E_OLESTREAM_GET;
7960 else
7961 hRes = CONVERT10_E_OLESTREAM_GET;
7964 else
7966 /* Get the Width of the Metafile */
7967 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7968 if(dwSize != sizeof(pData->dwMetaFileWidth))
7970 hRes = CONVERT10_E_OLESTREAM_GET;
7972 if(hRes == S_OK)
7974 /* Get the Height of the Metafile */
7975 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7976 if(dwSize != sizeof(pData->dwMetaFileHeight))
7978 hRes = CONVERT10_E_OLESTREAM_GET;
7982 if(hRes == S_OK)
7984 /* Get the Length of the Data */
7985 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7986 if(dwSize != sizeof(pData->dwDataLength))
7988 hRes = CONVERT10_E_OLESTREAM_GET;
7992 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7994 if(!bStrem1) /* if it is a second OLE stream data */
7996 pData->dwDataLength -= 8;
7997 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7998 if(dwSize != sizeof(pData->strUnknown))
8000 hRes = CONVERT10_E_OLESTREAM_GET;
8004 if(hRes == S_OK)
8006 if(pData->dwDataLength > 0)
8008 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8010 /* Get Data (ex. IStorage, Metafile, or BMP) */
8011 if(pData->pData)
8013 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8014 if(dwSize != pData->dwDataLength)
8016 hRes = CONVERT10_E_OLESTREAM_GET;
8019 else
8021 hRes = CONVERT10_E_OLESTREAM_GET;
8027 return hRes;
8030 /*************************************************************************
8031 * OLECONVERT_SaveOLE10 [Internal]
8033 * Saves the OLE10 STREAM From memory
8035 * PARAMS
8036 * pData [I] Data Structure for the OLESTREAM Data
8037 * pOleStream [I] The OLESTREAM to save
8039 * RETURNS
8040 * Success: S_OK
8041 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8043 * NOTES
8044 * This function is used by OleConvertIStorageToOLESTREAM only.
8047 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8049 DWORD dwSize;
8050 HRESULT hRes = S_OK;
8053 /* Set the OleID */
8054 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8055 if(dwSize != sizeof(pData->dwOleID))
8057 hRes = CONVERT10_E_OLESTREAM_PUT;
8060 if(hRes == S_OK)
8062 /* Set the TypeID */
8063 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8064 if(dwSize != sizeof(pData->dwTypeID))
8066 hRes = CONVERT10_E_OLESTREAM_PUT;
8070 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8072 /* Set the Length of the OleTypeName */
8073 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8074 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8076 hRes = CONVERT10_E_OLESTREAM_PUT;
8079 if(hRes == S_OK)
8081 if(pData->dwOleTypeNameLength > 0)
8083 /* Set the OleTypeName */
8084 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8085 if(dwSize != pData->dwOleTypeNameLength)
8087 hRes = CONVERT10_E_OLESTREAM_PUT;
8092 if(hRes == S_OK)
8094 /* Set the width of the Metafile */
8095 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8096 if(dwSize != sizeof(pData->dwMetaFileWidth))
8098 hRes = CONVERT10_E_OLESTREAM_PUT;
8102 if(hRes == S_OK)
8104 /* Set the height of the Metafile */
8105 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8106 if(dwSize != sizeof(pData->dwMetaFileHeight))
8108 hRes = CONVERT10_E_OLESTREAM_PUT;
8112 if(hRes == S_OK)
8114 /* Set the length of the Data */
8115 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8116 if(dwSize != sizeof(pData->dwDataLength))
8118 hRes = CONVERT10_E_OLESTREAM_PUT;
8122 if(hRes == S_OK)
8124 if(pData->dwDataLength > 0)
8126 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8127 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8128 if(dwSize != pData->dwDataLength)
8130 hRes = CONVERT10_E_OLESTREAM_PUT;
8135 return hRes;
8138 /*************************************************************************
8139 * OLECONVERT_GetOLE20FromOLE10[Internal]
8141 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8142 * opens it, and copies the content to the dest IStorage for
8143 * OleConvertOLESTREAMToIStorage
8146 * PARAMS
8147 * pDestStorage [I] The IStorage to copy the data to
8148 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8149 * nBufferLength [I] The size of the buffer
8151 * RETURNS
8152 * Nothing
8154 * NOTES
8158 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8160 HRESULT hRes;
8161 HANDLE hFile;
8162 IStorage *pTempStorage;
8163 DWORD dwNumOfBytesWritten;
8164 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8165 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8167 /* Create a temp File */
8168 GetTempPathW(MAX_PATH, wstrTempDir);
8169 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8170 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8172 if(hFile != INVALID_HANDLE_VALUE)
8174 /* Write IStorage Data to File */
8175 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8176 CloseHandle(hFile);
8178 /* Open and copy temp storage to the Dest Storage */
8179 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8180 if(hRes == S_OK)
8182 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8183 IStorage_Release(pTempStorage);
8185 DeleteFileW(wstrTempFile);
8190 /*************************************************************************
8191 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8193 * Saves the OLE10 STREAM From memory
8195 * PARAMS
8196 * pStorage [I] The Src IStorage to copy
8197 * pData [I] The Dest Memory to write to.
8199 * RETURNS
8200 * The size in bytes allocated for pData
8202 * NOTES
8203 * Memory allocated for pData must be freed by the caller
8205 * Used by OleConvertIStorageToOLESTREAM only.
8208 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8210 HANDLE hFile;
8211 HRESULT hRes;
8212 DWORD nDataLength = 0;
8213 IStorage *pTempStorage;
8214 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8215 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8217 *pData = NULL;
8219 /* Create temp Storage */
8220 GetTempPathW(MAX_PATH, wstrTempDir);
8221 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8222 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8224 if(hRes == S_OK)
8226 /* Copy Src Storage to the Temp Storage */
8227 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8228 IStorage_Release(pTempStorage);
8230 /* Open Temp Storage as a file and copy to memory */
8231 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8232 if(hFile != INVALID_HANDLE_VALUE)
8234 nDataLength = GetFileSize(hFile, NULL);
8235 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8236 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8237 CloseHandle(hFile);
8239 DeleteFileW(wstrTempFile);
8241 return nDataLength;
8244 /*************************************************************************
8245 * OLECONVERT_CreateOleStream [Internal]
8247 * Creates the "\001OLE" stream in the IStorage if necessary.
8249 * PARAMS
8250 * pStorage [I] Dest storage to create the stream in
8252 * RETURNS
8253 * Nothing
8255 * NOTES
8256 * This function is used by OleConvertOLESTREAMToIStorage only.
8258 * This stream is still unknown, MS Word seems to have extra data
8259 * but since the data is stored in the OLESTREAM there should be
8260 * no need to recreate the stream. If the stream is manually
8261 * deleted it will create it with this default data.
8264 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8266 HRESULT hRes;
8267 IStream *pStream;
8268 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8269 BYTE pOleStreamHeader [] =
8271 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8272 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8273 0x00, 0x00, 0x00, 0x00
8276 /* Create stream if not present */
8277 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8278 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8280 if(hRes == S_OK)
8282 /* Write default Data */
8283 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8284 IStream_Release(pStream);
8288 /* write a string to a stream, preceded by its length */
8289 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8291 HRESULT r;
8292 LPSTR str;
8293 DWORD len = 0;
8295 if( string )
8296 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8297 r = IStream_Write( stm, &len, sizeof(len), NULL);
8298 if( FAILED( r ) )
8299 return r;
8300 if(len == 0)
8301 return r;
8302 str = CoTaskMemAlloc( len );
8303 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8304 r = IStream_Write( stm, str, len, NULL);
8305 CoTaskMemFree( str );
8306 return r;
8309 /* read a string preceded by its length from a stream */
8310 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8312 HRESULT r;
8313 DWORD len, count = 0;
8314 LPSTR str;
8315 LPWSTR wstr;
8317 r = IStream_Read( stm, &len, sizeof(len), &count );
8318 if( FAILED( r ) )
8319 return r;
8320 if( count != sizeof(len) )
8321 return E_OUTOFMEMORY;
8323 TRACE("%d bytes\n",len);
8325 str = CoTaskMemAlloc( len );
8326 if( !str )
8327 return E_OUTOFMEMORY;
8328 count = 0;
8329 r = IStream_Read( stm, str, len, &count );
8330 if( FAILED( r ) )
8331 return r;
8332 if( count != len )
8334 CoTaskMemFree( str );
8335 return E_OUTOFMEMORY;
8338 TRACE("Read string %s\n",debugstr_an(str,len));
8340 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8341 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8342 if( wstr )
8343 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8344 CoTaskMemFree( str );
8346 *string = wstr;
8348 return r;
8352 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8353 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8355 IStream *pstm;
8356 HRESULT r = S_OK;
8357 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8359 static const BYTE unknown1[12] =
8360 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8361 0xFF, 0xFF, 0xFF, 0xFF};
8362 static const BYTE unknown2[16] =
8363 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8364 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8366 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8367 debugstr_w(lpszUserType), debugstr_w(szClipName),
8368 debugstr_w(szProgIDName));
8370 /* Create a CompObj stream */
8371 r = IStorage_CreateStream(pstg, szwStreamName,
8372 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8373 if( FAILED (r) )
8374 return r;
8376 /* Write CompObj Structure to stream */
8377 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8379 if( SUCCEEDED( r ) )
8380 r = WriteClassStm( pstm, clsid );
8382 if( SUCCEEDED( r ) )
8383 r = STREAM_WriteString( pstm, lpszUserType );
8384 if( SUCCEEDED( r ) )
8385 r = STREAM_WriteString( pstm, szClipName );
8386 if( SUCCEEDED( r ) )
8387 r = STREAM_WriteString( pstm, szProgIDName );
8388 if( SUCCEEDED( r ) )
8389 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8391 IStream_Release( pstm );
8393 return r;
8396 /***********************************************************************
8397 * WriteFmtUserTypeStg (OLE32.@)
8399 HRESULT WINAPI WriteFmtUserTypeStg(
8400 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8402 HRESULT r;
8403 WCHAR szwClipName[0x40];
8404 CLSID clsid = CLSID_NULL;
8405 LPWSTR wstrProgID = NULL;
8406 DWORD n;
8408 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8410 /* get the clipboard format name */
8411 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8412 szwClipName[n]=0;
8414 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8416 /* FIXME: There's room to save a CLSID and its ProgID, but
8417 the CLSID is not looked up in the registry and in all the
8418 tests I wrote it was CLSID_NULL. Where does it come from?
8421 /* get the real program ID. This may fail, but that's fine */
8422 ProgIDFromCLSID(&clsid, &wstrProgID);
8424 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8426 r = STORAGE_WriteCompObj( pstg, &clsid,
8427 lpszUserType, szwClipName, wstrProgID );
8429 CoTaskMemFree(wstrProgID);
8431 return r;
8435 /******************************************************************************
8436 * ReadFmtUserTypeStg [OLE32.@]
8438 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8440 HRESULT r;
8441 IStream *stm = 0;
8442 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8443 unsigned char unknown1[12];
8444 unsigned char unknown2[16];
8445 DWORD count;
8446 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8447 CLSID clsid;
8449 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8451 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8452 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8453 if( FAILED ( r ) )
8455 WARN("Failed to open stream r = %08x\n", r);
8456 return r;
8459 /* read the various parts of the structure */
8460 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8461 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8462 goto end;
8463 r = ReadClassStm( stm, &clsid );
8464 if( FAILED( r ) )
8465 goto end;
8467 r = STREAM_ReadString( stm, &szCLSIDName );
8468 if( FAILED( r ) )
8469 goto end;
8471 r = STREAM_ReadString( stm, &szOleTypeName );
8472 if( FAILED( r ) )
8473 goto end;
8475 r = STREAM_ReadString( stm, &szProgIDName );
8476 if( FAILED( r ) )
8477 goto end;
8479 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8480 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8481 goto end;
8483 /* ok, success... now we just need to store what we found */
8484 if( pcf )
8485 *pcf = RegisterClipboardFormatW( szOleTypeName );
8486 CoTaskMemFree( szOleTypeName );
8488 if( lplpszUserType )
8489 *lplpszUserType = szCLSIDName;
8490 CoTaskMemFree( szProgIDName );
8492 end:
8493 IStream_Release( stm );
8495 return r;
8499 /*************************************************************************
8500 * OLECONVERT_CreateCompObjStream [Internal]
8502 * Creates a "\001CompObj" is the destination IStorage if necessary.
8504 * PARAMS
8505 * pStorage [I] The dest IStorage to create the CompObj Stream
8506 * if necessary.
8507 * strOleTypeName [I] The ProgID
8509 * RETURNS
8510 * Success: S_OK
8511 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8513 * NOTES
8514 * This function is used by OleConvertOLESTREAMToIStorage only.
8516 * The stream data is stored in the OLESTREAM and there should be
8517 * no need to recreate the stream. If the stream is manually
8518 * deleted it will attempt to create it by querying the registry.
8522 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8524 IStream *pStream;
8525 HRESULT hStorageRes, hRes = S_OK;
8526 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8527 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8528 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8530 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8531 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8533 /* Initialize the CompObj structure */
8534 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8535 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8536 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8539 /* Create a CompObj stream if it doesn't exist */
8540 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8541 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8542 if(hStorageRes == S_OK)
8544 /* copy the OleTypeName to the compobj struct */
8545 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8546 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8548 /* copy the OleTypeName to the compobj struct */
8549 /* Note: in the test made, these were Identical */
8550 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8551 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8553 /* Get the CLSID */
8554 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8555 bufferW, OLESTREAM_MAX_STR_LEN );
8556 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8558 if(hRes == S_OK)
8560 HKEY hKey;
8561 LONG hErr;
8562 /* Get the CLSID Default Name from the Registry */
8563 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8564 if(hErr == ERROR_SUCCESS)
8566 char strTemp[OLESTREAM_MAX_STR_LEN];
8567 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8568 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8569 if(hErr == ERROR_SUCCESS)
8571 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8573 RegCloseKey(hKey);
8577 /* Write CompObj Structure to stream */
8578 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8580 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8582 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8583 if(IStorageCompObj.dwCLSIDNameLength > 0)
8585 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8587 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8588 if(IStorageCompObj.dwOleTypeNameLength > 0)
8590 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8592 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8593 if(IStorageCompObj.dwProgIDNameLength > 0)
8595 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8597 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8598 IStream_Release(pStream);
8600 return hRes;
8604 /*************************************************************************
8605 * OLECONVERT_CreateOlePresStream[Internal]
8607 * Creates the "\002OlePres000" Stream with the Metafile data
8609 * PARAMS
8610 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8611 * dwExtentX [I] Width of the Metafile
8612 * dwExtentY [I] Height of the Metafile
8613 * pData [I] Metafile data
8614 * dwDataLength [I] Size of the Metafile data
8616 * RETURNS
8617 * Success: S_OK
8618 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8620 * NOTES
8621 * This function is used by OleConvertOLESTREAMToIStorage only.
8624 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8626 HRESULT hRes;
8627 IStream *pStream;
8628 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8629 BYTE pOlePresStreamHeader [] =
8631 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8632 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8633 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8634 0x00, 0x00, 0x00, 0x00
8637 BYTE pOlePresStreamHeaderEmpty [] =
8639 0x00, 0x00, 0x00, 0x00,
8640 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8641 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8642 0x00, 0x00, 0x00, 0x00
8645 /* Create the OlePres000 Stream */
8646 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8647 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8649 if(hRes == S_OK)
8651 DWORD nHeaderSize;
8652 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8654 memset(&OlePres, 0, sizeof(OlePres));
8655 /* Do we have any metafile data to save */
8656 if(dwDataLength > 0)
8658 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8659 nHeaderSize = sizeof(pOlePresStreamHeader);
8661 else
8663 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8664 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8666 /* Set width and height of the metafile */
8667 OlePres.dwExtentX = dwExtentX;
8668 OlePres.dwExtentY = -dwExtentY;
8670 /* Set Data and Length */
8671 if(dwDataLength > sizeof(METAFILEPICT16))
8673 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8674 OlePres.pData = &(pData[8]);
8676 /* Save OlePres000 Data to Stream */
8677 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8678 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8679 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8680 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8681 if(OlePres.dwSize > 0)
8683 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8685 IStream_Release(pStream);
8689 /*************************************************************************
8690 * OLECONVERT_CreateOle10NativeStream [Internal]
8692 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8694 * PARAMS
8695 * pStorage [I] Dest storage to create the stream in
8696 * pData [I] Ole10 Native Data (ex. bmp)
8697 * dwDataLength [I] Size of the Ole10 Native Data
8699 * RETURNS
8700 * Nothing
8702 * NOTES
8703 * This function is used by OleConvertOLESTREAMToIStorage only.
8705 * Might need to verify the data and return appropriate error message
8708 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8710 HRESULT hRes;
8711 IStream *pStream;
8712 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8714 /* Create the Ole10Native Stream */
8715 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8716 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8718 if(hRes == S_OK)
8720 /* Write info to stream */
8721 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8722 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8723 IStream_Release(pStream);
8728 /*************************************************************************
8729 * OLECONVERT_GetOLE10ProgID [Internal]
8731 * Finds the ProgID (or OleTypeID) from the IStorage
8733 * PARAMS
8734 * pStorage [I] The Src IStorage to get the ProgID
8735 * strProgID [I] the ProgID string to get
8736 * dwSize [I] the size of the string
8738 * RETURNS
8739 * Success: S_OK
8740 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8742 * NOTES
8743 * This function is used by OleConvertIStorageToOLESTREAM only.
8747 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8749 HRESULT hRes;
8750 IStream *pStream;
8751 LARGE_INTEGER iSeekPos;
8752 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8753 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8755 /* Open the CompObj Stream */
8756 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8757 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8758 if(hRes == S_OK)
8761 /*Get the OleType from the CompObj Stream */
8762 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8763 iSeekPos.u.HighPart = 0;
8765 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8766 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8767 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8768 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8769 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8770 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8771 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8773 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8774 if(*dwSize > 0)
8776 IStream_Read(pStream, strProgID, *dwSize, NULL);
8778 IStream_Release(pStream);
8780 else
8782 STATSTG stat;
8783 LPOLESTR wstrProgID;
8785 /* Get the OleType from the registry */
8786 REFCLSID clsid = &(stat.clsid);
8787 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8788 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8789 if(hRes == S_OK)
8791 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8795 return hRes;
8798 /*************************************************************************
8799 * OLECONVERT_GetOle10PresData [Internal]
8801 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8803 * PARAMS
8804 * pStorage [I] Src IStroage
8805 * pOleStream [I] Dest OleStream Mem Struct
8807 * RETURNS
8808 * Nothing
8810 * NOTES
8811 * This function is used by OleConvertIStorageToOLESTREAM only.
8813 * Memory allocated for pData must be freed by the caller
8817 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8820 HRESULT hRes;
8821 IStream *pStream;
8822 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8824 /* Initialize Default data for OLESTREAM */
8825 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8826 pOleStreamData[0].dwTypeID = 2;
8827 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8828 pOleStreamData[1].dwTypeID = 0;
8829 pOleStreamData[0].dwMetaFileWidth = 0;
8830 pOleStreamData[0].dwMetaFileHeight = 0;
8831 pOleStreamData[0].pData = NULL;
8832 pOleStreamData[1].pData = NULL;
8834 /* Open Ole10Native Stream */
8835 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8836 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8837 if(hRes == S_OK)
8840 /* Read Size and Data */
8841 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8842 if(pOleStreamData->dwDataLength > 0)
8844 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8845 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8847 IStream_Release(pStream);
8853 /*************************************************************************
8854 * OLECONVERT_GetOle20PresData[Internal]
8856 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8858 * PARAMS
8859 * pStorage [I] Src IStroage
8860 * pOleStreamData [I] Dest OleStream Mem Struct
8862 * RETURNS
8863 * Nothing
8865 * NOTES
8866 * This function is used by OleConvertIStorageToOLESTREAM only.
8868 * Memory allocated for pData must be freed by the caller
8870 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8872 HRESULT hRes;
8873 IStream *pStream;
8874 OLECONVERT_ISTORAGE_OLEPRES olePress;
8875 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8877 /* Initialize Default data for OLESTREAM */
8878 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8879 pOleStreamData[0].dwTypeID = 2;
8880 pOleStreamData[0].dwMetaFileWidth = 0;
8881 pOleStreamData[0].dwMetaFileHeight = 0;
8882 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8883 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8884 pOleStreamData[1].dwTypeID = 0;
8885 pOleStreamData[1].dwOleTypeNameLength = 0;
8886 pOleStreamData[1].strOleTypeName[0] = 0;
8887 pOleStreamData[1].dwMetaFileWidth = 0;
8888 pOleStreamData[1].dwMetaFileHeight = 0;
8889 pOleStreamData[1].pData = NULL;
8890 pOleStreamData[1].dwDataLength = 0;
8893 /* Open OlePress000 stream */
8894 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8895 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8896 if(hRes == S_OK)
8898 LARGE_INTEGER iSeekPos;
8899 METAFILEPICT16 MetaFilePict;
8900 static const char strMetafilePictName[] = "METAFILEPICT";
8902 /* Set the TypeID for a Metafile */
8903 pOleStreamData[1].dwTypeID = 5;
8905 /* Set the OleTypeName to Metafile */
8906 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8907 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8909 iSeekPos.u.HighPart = 0;
8910 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8912 /* Get Presentation Data */
8913 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8914 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8915 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8916 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8918 /*Set width and Height */
8919 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8920 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8921 if(olePress.dwSize > 0)
8923 /* Set Length */
8924 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8926 /* Set MetaFilePict struct */
8927 MetaFilePict.mm = 8;
8928 MetaFilePict.xExt = olePress.dwExtentX;
8929 MetaFilePict.yExt = olePress.dwExtentY;
8930 MetaFilePict.hMF = 0;
8932 /* Get Metafile Data */
8933 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8934 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8935 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8937 IStream_Release(pStream);
8941 /*************************************************************************
8942 * OleConvertOLESTREAMToIStorage [OLE32.@]
8944 * Read info on MSDN
8946 * TODO
8947 * DVTARGETDEVICE parameter is not handled
8948 * Still unsure of some mem fields for OLE 10 Stream
8949 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8950 * and "\001OLE" streams
8953 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8954 LPOLESTREAM pOleStream,
8955 LPSTORAGE pstg,
8956 const DVTARGETDEVICE* ptd)
8958 int i;
8959 HRESULT hRes=S_OK;
8960 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8962 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8964 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8966 if(ptd != NULL)
8968 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8971 if(pstg == NULL || pOleStream == NULL)
8973 hRes = E_INVALIDARG;
8976 if(hRes == S_OK)
8978 /* Load the OLESTREAM to Memory */
8979 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8982 if(hRes == S_OK)
8984 /* Load the OLESTREAM to Memory (part 2)*/
8985 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8988 if(hRes == S_OK)
8991 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8993 /* Do we have the IStorage Data in the OLESTREAM */
8994 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8996 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8997 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8999 else
9001 /* It must be an original OLE 1.0 source */
9002 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9005 else
9007 /* It must be an original OLE 1.0 source */
9008 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9011 /* Create CompObj Stream if necessary */
9012 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9013 if(hRes == S_OK)
9015 /*Create the Ole Stream if necessary */
9016 OLECONVERT_CreateOleStream(pstg);
9021 /* Free allocated memory */
9022 for(i=0; i < 2; i++)
9024 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9025 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9026 pOleStreamData[i].pstrOleObjFileName = NULL;
9028 return hRes;
9031 /*************************************************************************
9032 * OleConvertIStorageToOLESTREAM [OLE32.@]
9034 * Read info on MSDN
9036 * Read info on MSDN
9038 * TODO
9039 * Still unsure of some mem fields for OLE 10 Stream
9040 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9041 * and "\001OLE" streams.
9044 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9045 LPSTORAGE pstg,
9046 LPOLESTREAM pOleStream)
9048 int i;
9049 HRESULT hRes = S_OK;
9050 IStream *pStream;
9051 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9052 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9054 TRACE("%p %p\n", pstg, pOleStream);
9056 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9058 if(pstg == NULL || pOleStream == NULL)
9060 hRes = E_INVALIDARG;
9062 if(hRes == S_OK)
9064 /* Get the ProgID */
9065 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9066 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9068 if(hRes == S_OK)
9070 /* Was it originally Ole10 */
9071 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9072 if(hRes == S_OK)
9074 IStream_Release(pStream);
9075 /* Get Presentation Data for Ole10Native */
9076 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9078 else
9080 /* Get Presentation Data (OLE20) */
9081 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9084 /* Save OLESTREAM */
9085 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9086 if(hRes == S_OK)
9088 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9093 /* Free allocated memory */
9094 for(i=0; i < 2; i++)
9096 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9099 return hRes;
9102 /***********************************************************************
9103 * GetConvertStg (OLE32.@)
9105 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9106 FIXME("unimplemented stub!\n");
9107 return E_FAIL;
9110 /******************************************************************************
9111 * StgIsStorageFile [OLE32.@]
9112 * Verify if the file contains a storage object
9114 * PARAMS
9115 * fn [ I] Filename
9117 * RETURNS
9118 * S_OK if file has magic bytes as a storage object
9119 * S_FALSE if file is not storage
9121 HRESULT WINAPI
9122 StgIsStorageFile(LPCOLESTR fn)
9124 HANDLE hf;
9125 BYTE magic[8];
9126 DWORD bytes_read;
9128 TRACE("%s\n", debugstr_w(fn));
9129 hf = CreateFileW(fn, GENERIC_READ,
9130 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9131 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9133 if (hf == INVALID_HANDLE_VALUE)
9134 return STG_E_FILENOTFOUND;
9136 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9138 WARN(" unable to read file\n");
9139 CloseHandle(hf);
9140 return S_FALSE;
9143 CloseHandle(hf);
9145 if (bytes_read != 8) {
9146 TRACE(" too short\n");
9147 return S_FALSE;
9150 if (!memcmp(magic,STORAGE_magic,8)) {
9151 TRACE(" -> YES\n");
9152 return S_OK;
9155 TRACE(" -> Invalid header.\n");
9156 return S_FALSE;
9159 /***********************************************************************
9160 * WriteClassStm (OLE32.@)
9162 * Writes a CLSID to a stream.
9164 * PARAMS
9165 * pStm [I] Stream to write to.
9166 * rclsid [I] CLSID to write.
9168 * RETURNS
9169 * Success: S_OK.
9170 * Failure: HRESULT code.
9172 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9174 TRACE("(%p,%p)\n",pStm,rclsid);
9176 if (!pStm || !rclsid)
9177 return E_INVALIDARG;
9179 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9182 /***********************************************************************
9183 * ReadClassStm (OLE32.@)
9185 * Reads a CLSID from a stream.
9187 * PARAMS
9188 * pStm [I] Stream to read from.
9189 * rclsid [O] CLSID to read.
9191 * RETURNS
9192 * Success: S_OK.
9193 * Failure: HRESULT code.
9195 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9197 ULONG nbByte;
9198 HRESULT res;
9200 TRACE("(%p,%p)\n",pStm,pclsid);
9202 if (!pStm || !pclsid)
9203 return E_INVALIDARG;
9205 /* clear the output args */
9206 *pclsid = CLSID_NULL;
9208 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9210 if (FAILED(res))
9211 return res;
9213 if (nbByte != sizeof(CLSID))
9214 return STG_E_READFAULT;
9215 else
9216 return S_OK;