winex11: Don't reset OpenGL swap interval of window on SetParent().
[wine.git] / dlls / ole32 / storage32.c
blobdebdbbf3ccd62fff7f788f6a440d00a4c937151b
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"
55 #include "compobj_private.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(storage);
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
60 #define OLESTREAM_ID 0x501
61 #define OLESTREAM_MAX_STR_LEN 255
64 * These are signatures to detect the type of Document file.
66 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
67 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
69 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
71 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
74 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
76 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
79 /****************************************************************************
80 * Storage32InternalImpl definitions.
82 * Definition of the implementation structure for the IStorage32 interface.
83 * This one implements the IStorage32 interface for storage that are
84 * inside another storage.
86 struct StorageInternalImpl
88 struct StorageBaseImpl base;
91 * Entry in the parent's stream tracking list
93 struct list ParentListEntry;
95 StorageBaseImpl *parentStorage;
97 typedef struct StorageInternalImpl StorageInternalImpl;
99 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
100 static const IStorageVtbl Storage32InternalImpl_Vtbl;
102 /* Method definitions for the Storage32InternalImpl class. */
103 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
104 DWORD openFlags, DirRef storageDirEntry);
105 static void StorageImpl_Destroy(StorageBaseImpl* iface);
106 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
107 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
108 static HRESULT StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer, ULONG *read );
109 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
110 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
111 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
112 static void StorageImpl_SaveFileHeader(StorageImpl* This);
114 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
115 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
116 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
117 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
118 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
120 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
121 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
122 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
124 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
125 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
126 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
127 ULONG blockIndex, ULONG offset, DWORD value);
128 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
129 ULONG blockIndex, ULONG offset, DWORD* value);
131 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
132 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
134 typedef struct TransactedDirEntry
136 /* If applicable, a reference to the original DirEntry in the transacted
137 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
138 DirRef transactedParentEntry;
140 /* True if this entry is being used. */
141 BOOL inuse;
143 /* True if data is up to date. */
144 BOOL read;
146 /* True if this entry has been modified. */
147 BOOL dirty;
149 /* True if this entry's stream has been modified. */
150 BOOL stream_dirty;
152 /* True if this entry has been deleted in the transacted storage, but the
153 * delete has not yet been committed. */
154 BOOL deleted;
156 /* If this entry's stream has been modified, a reference to where the stream
157 * is stored in the snapshot file. */
158 DirRef stream_entry;
160 /* This directory entry's data, including any changes that have been made. */
161 DirEntry data;
163 /* A reference to the parent of this node. This is only valid while we are
164 * committing changes. */
165 DirRef parent;
167 /* A reference to a newly-created entry in the transacted parent. This is
168 * always equal to transactedParentEntry except when committing changes. */
169 DirRef newTransactedParentEntry;
170 } TransactedDirEntry;
172 /****************************************************************************
173 * Transacted storage object.
175 typedef struct TransactedSnapshotImpl
177 struct StorageBaseImpl base;
180 * Modified streams are temporarily saved to the scratch file.
182 StorageBaseImpl *scratch;
184 /* The directory structure is kept here, so that we can track how these
185 * entries relate to those in the parent storage. */
186 TransactedDirEntry *entries;
187 ULONG entries_size;
188 ULONG firstFreeEntry;
191 * Changes are committed to the transacted parent.
193 StorageBaseImpl *transactedParent;
194 } TransactedSnapshotImpl;
196 /* Generic function to create a transacted wrapper for a direct storage object. */
197 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
199 /* OLESTREAM memory structure to use for Get and Put Routines */
200 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
201 typedef struct
203 DWORD dwOleID;
204 DWORD dwTypeID;
205 DWORD dwOleTypeNameLength;
206 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
207 CHAR *pstrOleObjFileName;
208 DWORD dwOleObjFileNameLength;
209 DWORD dwMetaFileWidth;
210 DWORD dwMetaFileHeight;
211 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
212 DWORD dwDataLength;
213 BYTE *pData;
214 }OLECONVERT_OLESTREAM_DATA;
216 /* CompObj Stream structure */
217 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
218 typedef struct
220 BYTE byUnknown1[12];
221 CLSID clsid;
222 DWORD dwCLSIDNameLength;
223 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
224 DWORD dwOleTypeNameLength;
225 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
226 DWORD dwProgIDNameLength;
227 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
228 BYTE byUnknown2[16];
229 }OLECONVERT_ISTORAGE_COMPOBJ;
232 /* Ole Presentation Stream structure */
233 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
234 typedef struct
236 BYTE byUnknown1[28];
237 DWORD dwExtentX;
238 DWORD dwExtentY;
239 DWORD dwSize;
240 BYTE *pData;
241 }OLECONVERT_ISTORAGE_OLEPRES;
245 /***********************************************************************
246 * Forward declaration of internal functions used by the method DestroyElement
248 static HRESULT deleteStorageContents(
249 StorageBaseImpl *parentStorage,
250 DirRef indexToDelete,
251 DirEntry entryDataToDelete);
253 static HRESULT deleteStreamContents(
254 StorageBaseImpl *parentStorage,
255 DirRef indexToDelete,
256 DirEntry entryDataToDelete);
258 static HRESULT removeFromTree(
259 StorageBaseImpl *This,
260 DirRef parentStorageIndex,
261 DirRef deletedIndex);
263 /***********************************************************************
264 * Declaration of the functions used to manipulate DirEntry
267 static HRESULT insertIntoTree(
268 StorageBaseImpl *This,
269 DirRef parentStorageIndex,
270 DirRef newEntryIndex);
272 static LONG entryNameCmp(
273 const OLECHAR *name1,
274 const OLECHAR *name2);
276 static DirRef findElement(
277 StorageBaseImpl *storage,
278 DirRef storageEntry,
279 const OLECHAR *name,
280 DirEntry *data);
282 static HRESULT findTreeParent(
283 StorageBaseImpl *storage,
284 DirRef storageEntry,
285 const OLECHAR *childName,
286 DirEntry *parentData,
287 DirRef *parentEntry,
288 ULONG *relation);
290 /***********************************************************************
291 * Declaration of miscellaneous functions...
293 static HRESULT validateSTGM(DWORD stgmValue);
295 static DWORD GetShareModeFromSTGM(DWORD stgm);
296 static DWORD GetAccessModeFromSTGM(DWORD stgm);
297 static DWORD GetCreationModeFromSTGM(DWORD stgm);
299 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
302 /****************************************************************************
303 * IEnumSTATSTGImpl definitions.
305 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
306 * This class allows iterating through the content of a storage and to find
307 * specific items inside it.
309 struct IEnumSTATSTGImpl
311 IEnumSTATSTG IEnumSTATSTG_iface;
313 LONG ref; /* Reference count */
314 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
315 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
317 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
320 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
322 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
326 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
327 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
329 /************************************************************************
330 ** Block Functions
333 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
335 return (index+1) * This->bigBlockSize;
338 /************************************************************************
339 ** Storage32BaseImpl implementation
341 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
342 ULARGE_INTEGER offset,
343 void* buffer,
344 ULONG size,
345 ULONG* bytesRead)
347 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
350 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
351 ULARGE_INTEGER offset,
352 const void* buffer,
353 const ULONG size,
354 ULONG* bytesWritten)
356 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
359 /************************************************************************
360 * Storage32BaseImpl_QueryInterface (IUnknown)
362 * This method implements the common QueryInterface for all IStorage32
363 * implementations contained in this file.
365 * See Windows documentation for more details on IUnknown methods.
367 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
368 IStorage* iface,
369 REFIID riid,
370 void** ppvObject)
372 StorageBaseImpl *This = impl_from_IStorage(iface);
374 if (!ppvObject)
375 return E_INVALIDARG;
377 *ppvObject = 0;
379 if (IsEqualGUID(&IID_IUnknown, riid) ||
380 IsEqualGUID(&IID_IStorage, riid))
382 *ppvObject = &This->IStorage_iface;
384 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
386 *ppvObject = &This->IPropertySetStorage_iface;
388 /* locking interface is reported for writer only */
389 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
391 *ppvObject = &This->IDirectWriterLock_iface;
393 else
394 return E_NOINTERFACE;
396 IStorage_AddRef(iface);
398 return S_OK;
401 /************************************************************************
402 * Storage32BaseImpl_AddRef (IUnknown)
404 * This method implements the common AddRef for all IStorage32
405 * implementations contained in this file.
407 * See Windows documentation for more details on IUnknown methods.
409 static ULONG WINAPI StorageBaseImpl_AddRef(
410 IStorage* iface)
412 StorageBaseImpl *This = impl_from_IStorage(iface);
413 ULONG ref = InterlockedIncrement(&This->ref);
415 TRACE("(%p) AddRef to %d\n", This, ref);
417 return ref;
420 /************************************************************************
421 * Storage32BaseImpl_Release (IUnknown)
423 * This method implements the common Release for all IStorage32
424 * implementations contained in this file.
426 * See Windows documentation for more details on IUnknown methods.
428 static ULONG WINAPI StorageBaseImpl_Release(
429 IStorage* iface)
431 StorageBaseImpl *This = impl_from_IStorage(iface);
433 ULONG ref = InterlockedDecrement(&This->ref);
435 TRACE("(%p) ReleaseRef to %d\n", This, ref);
437 if (ref == 0)
440 * Since we are using a system of base-classes, we want to call the
441 * destructor of the appropriate derived class. To do this, we are
442 * using virtual functions to implement the destructor.
444 StorageBaseImpl_Destroy(This);
447 return ref;
450 /************************************************************************
451 * Storage32BaseImpl_OpenStream (IStorage)
453 * This method will open the specified stream object from the current storage.
455 * See Windows documentation for more details on IStorage methods.
457 static HRESULT WINAPI StorageBaseImpl_OpenStream(
458 IStorage* iface,
459 const OLECHAR* pwcsName, /* [string][in] */
460 void* reserved1, /* [unique][in] */
461 DWORD grfMode, /* [in] */
462 DWORD reserved2, /* [in] */
463 IStream** ppstm) /* [out] */
465 StorageBaseImpl *This = impl_from_IStorage(iface);
466 StgStreamImpl* newStream;
467 DirEntry currentEntry;
468 DirRef streamEntryRef;
469 HRESULT res = STG_E_UNKNOWN;
471 TRACE("(%p, %s, %p, %x, %d, %p)\n",
472 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
474 if ( (pwcsName==NULL) || (ppstm==0) )
476 res = E_INVALIDARG;
477 goto end;
480 *ppstm = NULL;
482 if ( FAILED( validateSTGM(grfMode) ) ||
483 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
485 res = STG_E_INVALIDFLAG;
486 goto end;
490 * As documented.
492 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
494 res = STG_E_INVALIDFUNCTION;
495 goto end;
498 if (This->reverted)
500 res = STG_E_REVERTED;
501 goto end;
505 * Check that we're compatible with the parent's storage mode, but
506 * only if we are not in transacted mode
508 if(!(This->openFlags & STGM_TRANSACTED)) {
509 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
511 res = STG_E_INVALIDFLAG;
512 goto end;
517 * Search for the element with the given name
519 streamEntryRef = findElement(
520 This,
521 This->storageDirEntry,
522 pwcsName,
523 &currentEntry);
526 * If it was found, construct the stream object and return a pointer to it.
528 if ( (streamEntryRef!=DIRENTRY_NULL) &&
529 (currentEntry.stgType==STGTY_STREAM) )
531 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
533 /* A single stream cannot be opened a second time. */
534 res = STG_E_ACCESSDENIED;
535 goto end;
538 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
540 if (newStream)
542 newStream->grfMode = grfMode;
543 *ppstm = &newStream->IStream_iface;
545 IStream_AddRef(*ppstm);
547 res = S_OK;
548 goto end;
551 res = E_OUTOFMEMORY;
552 goto end;
555 res = STG_E_FILENOTFOUND;
557 end:
558 if (res == S_OK)
559 TRACE("<-- IStream %p\n", *ppstm);
560 TRACE("<-- %08x\n", res);
561 return res;
564 /************************************************************************
565 * Storage32BaseImpl_OpenStorage (IStorage)
567 * This method will open a new storage object from the current storage.
569 * See Windows documentation for more details on IStorage methods.
571 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
572 IStorage* iface,
573 const OLECHAR* pwcsName, /* [string][unique][in] */
574 IStorage* pstgPriority, /* [unique][in] */
575 DWORD grfMode, /* [in] */
576 SNB snbExclude, /* [unique][in] */
577 DWORD reserved, /* [in] */
578 IStorage** ppstg) /* [out] */
580 StorageBaseImpl *This = impl_from_IStorage(iface);
581 StorageInternalImpl* newStorage;
582 StorageBaseImpl* newTransactedStorage;
583 DirEntry currentEntry;
584 DirRef storageEntryRef;
585 HRESULT res = STG_E_UNKNOWN;
587 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
588 iface, debugstr_w(pwcsName), pstgPriority,
589 grfMode, snbExclude, reserved, ppstg);
591 if ((pwcsName==NULL) || (ppstg==0) )
593 res = E_INVALIDARG;
594 goto end;
597 if (This->openFlags & STGM_SIMPLE)
599 res = STG_E_INVALIDFUNCTION;
600 goto end;
603 /* as documented */
604 if (snbExclude != NULL)
606 res = STG_E_INVALIDPARAMETER;
607 goto end;
610 if ( FAILED( validateSTGM(grfMode) ))
612 res = STG_E_INVALIDFLAG;
613 goto end;
617 * As documented.
619 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
620 (grfMode & STGM_DELETEONRELEASE) ||
621 (grfMode & STGM_PRIORITY) )
623 res = STG_E_INVALIDFUNCTION;
624 goto end;
627 if (This->reverted)
628 return STG_E_REVERTED;
631 * Check that we're compatible with the parent's storage mode,
632 * but only if we are not transacted
634 if(!(This->openFlags & STGM_TRANSACTED)) {
635 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
637 res = STG_E_ACCESSDENIED;
638 goto end;
642 *ppstg = NULL;
644 storageEntryRef = findElement(
645 This,
646 This->storageDirEntry,
647 pwcsName,
648 &currentEntry);
650 if ( (storageEntryRef!=DIRENTRY_NULL) &&
651 (currentEntry.stgType==STGTY_STORAGE) )
653 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
655 /* A single storage cannot be opened a second time. */
656 res = STG_E_ACCESSDENIED;
657 goto end;
660 newStorage = StorageInternalImpl_Construct(
661 This,
662 grfMode,
663 storageEntryRef);
665 if (newStorage != 0)
667 if (grfMode & STGM_TRANSACTED)
669 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
671 if (FAILED(res))
673 HeapFree(GetProcessHeap(), 0, newStorage);
674 goto end;
677 *ppstg = &newTransactedStorage->IStorage_iface;
679 else
681 *ppstg = &newStorage->base.IStorage_iface;
684 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
686 res = S_OK;
687 goto end;
690 res = STG_E_INSUFFICIENTMEMORY;
691 goto end;
694 res = STG_E_FILENOTFOUND;
696 end:
697 TRACE("<-- %08x\n", res);
698 return res;
701 /************************************************************************
702 * Storage32BaseImpl_EnumElements (IStorage)
704 * This method will create an enumerator object that can be used to
705 * retrieve information about all the elements in the storage object.
707 * See Windows documentation for more details on IStorage methods.
709 static HRESULT WINAPI StorageBaseImpl_EnumElements(
710 IStorage* iface,
711 DWORD reserved1, /* [in] */
712 void* reserved2, /* [size_is][unique][in] */
713 DWORD reserved3, /* [in] */
714 IEnumSTATSTG** ppenum) /* [out] */
716 StorageBaseImpl *This = impl_from_IStorage(iface);
717 IEnumSTATSTGImpl* newEnum;
719 TRACE("(%p, %d, %p, %d, %p)\n",
720 iface, reserved1, reserved2, reserved3, ppenum);
722 if (!ppenum)
723 return E_INVALIDARG;
725 if (This->reverted)
726 return STG_E_REVERTED;
728 newEnum = IEnumSTATSTGImpl_Construct(
729 This,
730 This->storageDirEntry);
732 if (newEnum)
734 *ppenum = &newEnum->IEnumSTATSTG_iface;
735 return S_OK;
738 return E_OUTOFMEMORY;
741 /************************************************************************
742 * Storage32BaseImpl_Stat (IStorage)
744 * This method will retrieve information about this storage object.
746 * See Windows documentation for more details on IStorage methods.
748 static HRESULT WINAPI StorageBaseImpl_Stat(
749 IStorage* iface,
750 STATSTG* pstatstg, /* [out] */
751 DWORD grfStatFlag) /* [in] */
753 StorageBaseImpl *This = impl_from_IStorage(iface);
754 DirEntry currentEntry;
755 HRESULT res = STG_E_UNKNOWN;
757 TRACE("(%p, %p, %x)\n",
758 iface, pstatstg, grfStatFlag);
760 if (!pstatstg)
762 res = E_INVALIDARG;
763 goto end;
766 if (This->reverted)
768 res = STG_E_REVERTED;
769 goto end;
772 res = StorageBaseImpl_ReadDirEntry(
773 This,
774 This->storageDirEntry,
775 &currentEntry);
777 if (SUCCEEDED(res))
779 StorageUtl_CopyDirEntryToSTATSTG(
780 This,
781 pstatstg,
782 &currentEntry,
783 grfStatFlag);
785 pstatstg->grfMode = This->openFlags;
786 pstatstg->grfStateBits = This->stateBits;
789 end:
790 if (res == S_OK)
792 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);
794 TRACE("<-- %08x\n", res);
795 return res;
798 /************************************************************************
799 * Storage32BaseImpl_RenameElement (IStorage)
801 * This method will rename the specified element.
803 * See Windows documentation for more details on IStorage methods.
805 static HRESULT WINAPI StorageBaseImpl_RenameElement(
806 IStorage* iface,
807 const OLECHAR* pwcsOldName, /* [in] */
808 const OLECHAR* pwcsNewName) /* [in] */
810 StorageBaseImpl *This = impl_from_IStorage(iface);
811 DirEntry currentEntry;
812 DirRef currentEntryRef;
814 TRACE("(%p, %s, %s)\n",
815 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
817 if (This->reverted)
818 return STG_E_REVERTED;
820 currentEntryRef = findElement(This,
821 This->storageDirEntry,
822 pwcsNewName,
823 &currentEntry);
825 if (currentEntryRef != DIRENTRY_NULL)
828 * There is already an element with the new name
830 return STG_E_FILEALREADYEXISTS;
834 * Search for the old element name
836 currentEntryRef = findElement(This,
837 This->storageDirEntry,
838 pwcsOldName,
839 &currentEntry);
841 if (currentEntryRef != DIRENTRY_NULL)
843 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
844 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
846 WARN("Element is already open; cannot rename.\n");
847 return STG_E_ACCESSDENIED;
850 /* Remove the element from its current position in the tree */
851 removeFromTree(This, This->storageDirEntry,
852 currentEntryRef);
854 /* Change the name of the element */
855 strcpyW(currentEntry.name, pwcsNewName);
857 /* Delete any sibling links */
858 currentEntry.leftChild = DIRENTRY_NULL;
859 currentEntry.rightChild = DIRENTRY_NULL;
861 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
862 &currentEntry);
864 /* Insert the element in a new position in the tree */
865 insertIntoTree(This, This->storageDirEntry,
866 currentEntryRef);
868 else
871 * There is no element with the old name
873 return STG_E_FILENOTFOUND;
876 return StorageBaseImpl_Flush(This);
879 /************************************************************************
880 * Storage32BaseImpl_CreateStream (IStorage)
882 * This method will create a stream object within this storage
884 * See Windows documentation for more details on IStorage methods.
886 static HRESULT WINAPI StorageBaseImpl_CreateStream(
887 IStorage* iface,
888 const OLECHAR* pwcsName, /* [string][in] */
889 DWORD grfMode, /* [in] */
890 DWORD reserved1, /* [in] */
891 DWORD reserved2, /* [in] */
892 IStream** ppstm) /* [out] */
894 StorageBaseImpl *This = impl_from_IStorage(iface);
895 StgStreamImpl* newStream;
896 DirEntry currentEntry, newStreamEntry;
897 DirRef currentEntryRef, newStreamEntryRef;
898 HRESULT hr;
900 TRACE("(%p, %s, %x, %d, %d, %p)\n",
901 iface, debugstr_w(pwcsName), grfMode,
902 reserved1, reserved2, ppstm);
904 if (ppstm == 0)
905 return STG_E_INVALIDPOINTER;
907 if (pwcsName == 0)
908 return STG_E_INVALIDNAME;
910 if (reserved1 || reserved2)
911 return STG_E_INVALIDPARAMETER;
913 if ( FAILED( validateSTGM(grfMode) ))
914 return STG_E_INVALIDFLAG;
916 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
917 return STG_E_INVALIDFLAG;
919 if (This->reverted)
920 return STG_E_REVERTED;
923 * As documented.
925 if ((grfMode & STGM_DELETEONRELEASE) ||
926 (grfMode & STGM_TRANSACTED))
927 return STG_E_INVALIDFUNCTION;
930 * Don't worry about permissions in transacted mode, as we can always write
931 * changes; we just can't always commit them.
933 if(!(This->openFlags & STGM_TRANSACTED)) {
934 /* Can't create a stream on read-only storage */
935 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
936 return STG_E_ACCESSDENIED;
938 /* Can't create a stream with greater access than the parent. */
939 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
940 return STG_E_ACCESSDENIED;
943 if(This->openFlags & STGM_SIMPLE)
944 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
946 *ppstm = 0;
948 currentEntryRef = findElement(This,
949 This->storageDirEntry,
950 pwcsName,
951 &currentEntry);
953 if (currentEntryRef != DIRENTRY_NULL)
956 * An element with this name already exists
958 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
960 IStorage_DestroyElement(iface, pwcsName);
962 else
963 return STG_E_FILEALREADYEXISTS;
967 * memset the empty entry
969 memset(&newStreamEntry, 0, sizeof(DirEntry));
971 newStreamEntry.sizeOfNameString =
972 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
974 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
975 return STG_E_INVALIDNAME;
977 strcpyW(newStreamEntry.name, pwcsName);
979 newStreamEntry.stgType = STGTY_STREAM;
980 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
981 newStreamEntry.size.u.LowPart = 0;
982 newStreamEntry.size.u.HighPart = 0;
984 newStreamEntry.leftChild = DIRENTRY_NULL;
985 newStreamEntry.rightChild = DIRENTRY_NULL;
986 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
988 /* call CoFileTime to get the current time
989 newStreamEntry.ctime
990 newStreamEntry.mtime
993 /* newStreamEntry.clsid */
996 * Create an entry with the new data
998 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
999 if (FAILED(hr))
1000 return hr;
1003 * Insert the new entry in the parent storage's tree.
1005 hr = insertIntoTree(
1006 This,
1007 This->storageDirEntry,
1008 newStreamEntryRef);
1009 if (FAILED(hr))
1011 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1012 return hr;
1016 * Open the stream to return it.
1018 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1020 if (newStream)
1022 *ppstm = &newStream->IStream_iface;
1023 IStream_AddRef(*ppstm);
1025 else
1027 return STG_E_INSUFFICIENTMEMORY;
1030 return StorageBaseImpl_Flush(This);
1033 /************************************************************************
1034 * Storage32BaseImpl_SetClass (IStorage)
1036 * This method will write the specified CLSID in the directory entry of this
1037 * storage.
1039 * See Windows documentation for more details on IStorage methods.
1041 static HRESULT WINAPI StorageBaseImpl_SetClass(
1042 IStorage* iface,
1043 REFCLSID clsid) /* [in] */
1045 StorageBaseImpl *This = impl_from_IStorage(iface);
1046 HRESULT hRes;
1047 DirEntry currentEntry;
1049 TRACE("(%p, %p)\n", iface, clsid);
1051 if (This->reverted)
1052 return STG_E_REVERTED;
1054 hRes = StorageBaseImpl_ReadDirEntry(This,
1055 This->storageDirEntry,
1056 &currentEntry);
1057 if (SUCCEEDED(hRes))
1059 currentEntry.clsid = *clsid;
1061 hRes = StorageBaseImpl_WriteDirEntry(This,
1062 This->storageDirEntry,
1063 &currentEntry);
1066 if (SUCCEEDED(hRes))
1067 hRes = StorageBaseImpl_Flush(This);
1069 return hRes;
1072 /************************************************************************
1073 ** Storage32Impl implementation
1076 /************************************************************************
1077 * Storage32BaseImpl_CreateStorage (IStorage)
1079 * This method will create the storage object within the provided storage.
1081 * See Windows documentation for more details on IStorage methods.
1083 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1084 IStorage* iface,
1085 const OLECHAR *pwcsName, /* [string][in] */
1086 DWORD grfMode, /* [in] */
1087 DWORD reserved1, /* [in] */
1088 DWORD reserved2, /* [in] */
1089 IStorage **ppstg) /* [out] */
1091 StorageBaseImpl* This = impl_from_IStorage(iface);
1093 DirEntry currentEntry;
1094 DirEntry newEntry;
1095 DirRef currentEntryRef;
1096 DirRef newEntryRef;
1097 HRESULT hr;
1099 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1100 iface, debugstr_w(pwcsName), grfMode,
1101 reserved1, reserved2, ppstg);
1103 if (ppstg == 0)
1104 return STG_E_INVALIDPOINTER;
1106 if (This->openFlags & STGM_SIMPLE)
1108 return STG_E_INVALIDFUNCTION;
1111 if (pwcsName == 0)
1112 return STG_E_INVALIDNAME;
1114 *ppstg = NULL;
1116 if ( FAILED( validateSTGM(grfMode) ) ||
1117 (grfMode & STGM_DELETEONRELEASE) )
1119 WARN("bad grfMode: 0x%x\n", grfMode);
1120 return STG_E_INVALIDFLAG;
1123 if (This->reverted)
1124 return STG_E_REVERTED;
1127 * Check that we're compatible with the parent's storage mode
1129 if ( !(This->openFlags & STGM_TRANSACTED) &&
1130 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1132 WARN("access denied\n");
1133 return STG_E_ACCESSDENIED;
1136 currentEntryRef = findElement(This,
1137 This->storageDirEntry,
1138 pwcsName,
1139 &currentEntry);
1141 if (currentEntryRef != DIRENTRY_NULL)
1144 * An element with this name already exists
1146 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1147 ((This->openFlags & STGM_TRANSACTED) ||
1148 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1150 hr = IStorage_DestroyElement(iface, pwcsName);
1151 if (FAILED(hr))
1152 return hr;
1154 else
1156 WARN("file already exists\n");
1157 return STG_E_FILEALREADYEXISTS;
1160 else if (!(This->openFlags & STGM_TRANSACTED) &&
1161 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1163 WARN("read-only storage\n");
1164 return STG_E_ACCESSDENIED;
1167 memset(&newEntry, 0, sizeof(DirEntry));
1169 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1171 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1173 FIXME("name too long\n");
1174 return STG_E_INVALIDNAME;
1177 strcpyW(newEntry.name, pwcsName);
1179 newEntry.stgType = STGTY_STORAGE;
1180 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1181 newEntry.size.u.LowPart = 0;
1182 newEntry.size.u.HighPart = 0;
1184 newEntry.leftChild = DIRENTRY_NULL;
1185 newEntry.rightChild = DIRENTRY_NULL;
1186 newEntry.dirRootEntry = DIRENTRY_NULL;
1188 /* call CoFileTime to get the current time
1189 newEntry.ctime
1190 newEntry.mtime
1193 /* newEntry.clsid */
1196 * Create a new directory entry for the storage
1198 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1199 if (FAILED(hr))
1200 return hr;
1203 * Insert the new directory entry into the parent storage's tree
1205 hr = insertIntoTree(
1206 This,
1207 This->storageDirEntry,
1208 newEntryRef);
1209 if (FAILED(hr))
1211 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1212 return hr;
1216 * Open it to get a pointer to return.
1218 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1220 if( (hr != S_OK) || (*ppstg == NULL))
1222 return hr;
1225 if (SUCCEEDED(hr))
1226 hr = StorageBaseImpl_Flush(This);
1228 return S_OK;
1232 /***************************************************************************
1234 * Internal Method
1236 * Reserve a directory entry in the file and initialize it.
1238 static HRESULT StorageImpl_CreateDirEntry(
1239 StorageBaseImpl *base,
1240 const DirEntry *newData,
1241 DirRef *index)
1243 StorageImpl *storage = (StorageImpl*)base;
1244 ULONG currentEntryIndex = 0;
1245 ULONG newEntryIndex = DIRENTRY_NULL;
1246 HRESULT hr = S_OK;
1247 BYTE currentData[RAW_DIRENTRY_SIZE];
1248 WORD sizeOfNameString;
1252 hr = StorageImpl_ReadRawDirEntry(storage,
1253 currentEntryIndex,
1254 currentData);
1256 if (SUCCEEDED(hr))
1258 StorageUtl_ReadWord(
1259 currentData,
1260 OFFSET_PS_NAMELENGTH,
1261 &sizeOfNameString);
1263 if (sizeOfNameString == 0)
1266 * The entry exists and is available, we found it.
1268 newEntryIndex = currentEntryIndex;
1271 else
1274 * We exhausted the directory entries, we will create more space below
1276 newEntryIndex = currentEntryIndex;
1278 currentEntryIndex++;
1280 } while (newEntryIndex == DIRENTRY_NULL);
1283 * grow the directory stream
1285 if (FAILED(hr))
1287 BYTE emptyData[RAW_DIRENTRY_SIZE];
1288 ULARGE_INTEGER newSize;
1289 ULONG entryIndex;
1290 ULONG lastEntry = 0;
1291 ULONG blockCount = 0;
1294 * obtain the new count of blocks in the directory stream
1296 blockCount = BlockChainStream_GetCount(
1297 storage->rootBlockChain)+1;
1300 * initialize the size used by the directory stream
1302 newSize.u.HighPart = 0;
1303 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1306 * add a block to the directory stream
1308 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1311 * memset the empty entry in order to initialize the unused newly
1312 * created entries
1314 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1317 * initialize them
1319 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1321 for(
1322 entryIndex = newEntryIndex + 1;
1323 entryIndex < lastEntry;
1324 entryIndex++)
1326 StorageImpl_WriteRawDirEntry(
1327 storage,
1328 entryIndex,
1329 emptyData);
1332 StorageImpl_SaveFileHeader(storage);
1335 UpdateRawDirEntry(currentData, newData);
1337 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1339 if (SUCCEEDED(hr))
1340 *index = newEntryIndex;
1342 return hr;
1345 /***************************************************************************
1347 * Internal Method
1349 * Mark a directory entry in the file as free.
1351 static HRESULT StorageImpl_DestroyDirEntry(
1352 StorageBaseImpl *base,
1353 DirRef index)
1355 BYTE emptyData[RAW_DIRENTRY_SIZE];
1356 StorageImpl *storage = (StorageImpl*)base;
1358 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1360 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1364 /****************************************************************************
1366 * Internal Method
1368 * Case insensitive comparison of DirEntry.name by first considering
1369 * their size.
1371 * Returns <0 when name1 < name2
1372 * >0 when name1 > name2
1373 * 0 when name1 == name2
1375 static LONG entryNameCmp(
1376 const OLECHAR *name1,
1377 const OLECHAR *name2)
1379 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1381 while (diff == 0 && *name1 != 0)
1384 * We compare the string themselves only when they are of the same length
1386 diff = toupperW(*name1++) - toupperW(*name2++);
1389 return diff;
1392 /****************************************************************************
1394 * Internal Method
1396 * Add a directory entry to a storage
1398 static HRESULT insertIntoTree(
1399 StorageBaseImpl *This,
1400 DirRef parentStorageIndex,
1401 DirRef newEntryIndex)
1403 DirEntry currentEntry;
1404 DirEntry newEntry;
1407 * Read the inserted entry
1409 StorageBaseImpl_ReadDirEntry(This,
1410 newEntryIndex,
1411 &newEntry);
1414 * Read the storage entry
1416 StorageBaseImpl_ReadDirEntry(This,
1417 parentStorageIndex,
1418 &currentEntry);
1420 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1423 * The root storage contains some element, therefore, start the research
1424 * for the appropriate location.
1426 BOOL found = FALSE;
1427 DirRef current, next, previous, currentEntryId;
1430 * Keep a reference to the root of the storage's element tree
1432 currentEntryId = currentEntry.dirRootEntry;
1435 * Read
1437 StorageBaseImpl_ReadDirEntry(This,
1438 currentEntry.dirRootEntry,
1439 &currentEntry);
1441 previous = currentEntry.leftChild;
1442 next = currentEntry.rightChild;
1443 current = currentEntryId;
1445 while (!found)
1447 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1449 if (diff < 0)
1451 if (previous != DIRENTRY_NULL)
1453 StorageBaseImpl_ReadDirEntry(This,
1454 previous,
1455 &currentEntry);
1456 current = previous;
1458 else
1460 currentEntry.leftChild = newEntryIndex;
1461 StorageBaseImpl_WriteDirEntry(This,
1462 current,
1463 &currentEntry);
1464 found = TRUE;
1467 else if (diff > 0)
1469 if (next != DIRENTRY_NULL)
1471 StorageBaseImpl_ReadDirEntry(This,
1472 next,
1473 &currentEntry);
1474 current = next;
1476 else
1478 currentEntry.rightChild = newEntryIndex;
1479 StorageBaseImpl_WriteDirEntry(This,
1480 current,
1481 &currentEntry);
1482 found = TRUE;
1485 else
1488 * Trying to insert an item with the same name in the
1489 * subtree structure.
1491 return STG_E_FILEALREADYEXISTS;
1494 previous = currentEntry.leftChild;
1495 next = currentEntry.rightChild;
1498 else
1501 * The storage is empty, make the new entry the root of its element tree
1503 currentEntry.dirRootEntry = newEntryIndex;
1504 StorageBaseImpl_WriteDirEntry(This,
1505 parentStorageIndex,
1506 &currentEntry);
1509 return S_OK;
1512 /****************************************************************************
1514 * Internal Method
1516 * Find and read the element of a storage with the given name.
1518 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1519 const OLECHAR *name, DirEntry *data)
1521 DirRef currentEntry;
1523 /* Read the storage entry to find the root of the tree. */
1524 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1526 currentEntry = data->dirRootEntry;
1528 while (currentEntry != DIRENTRY_NULL)
1530 LONG cmp;
1532 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1534 cmp = entryNameCmp(name, data->name);
1536 if (cmp == 0)
1537 /* found it */
1538 break;
1540 else if (cmp < 0)
1541 currentEntry = data->leftChild;
1543 else if (cmp > 0)
1544 currentEntry = data->rightChild;
1547 return currentEntry;
1550 /****************************************************************************
1552 * Internal Method
1554 * Find and read the binary tree parent of the element with the given name.
1556 * If there is no such element, find a place where it could be inserted and
1557 * return STG_E_FILENOTFOUND.
1559 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1560 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1561 ULONG *relation)
1563 DirRef childEntry;
1564 DirEntry childData;
1566 /* Read the storage entry to find the root of the tree. */
1567 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1569 *parentEntry = storageEntry;
1570 *relation = DIRENTRY_RELATION_DIR;
1572 childEntry = parentData->dirRootEntry;
1574 while (childEntry != DIRENTRY_NULL)
1576 LONG cmp;
1578 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1580 cmp = entryNameCmp(childName, childData.name);
1582 if (cmp == 0)
1583 /* found it */
1584 break;
1586 else if (cmp < 0)
1588 *parentData = childData;
1589 *parentEntry = childEntry;
1590 *relation = DIRENTRY_RELATION_PREVIOUS;
1592 childEntry = parentData->leftChild;
1595 else if (cmp > 0)
1597 *parentData = childData;
1598 *parentEntry = childEntry;
1599 *relation = DIRENTRY_RELATION_NEXT;
1601 childEntry = parentData->rightChild;
1605 if (childEntry == DIRENTRY_NULL)
1606 return STG_E_FILENOTFOUND;
1607 else
1608 return S_OK;
1612 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1613 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1614 SNB snbExclude, IStorage *pstgDest);
1616 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1617 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1618 SNB snbExclude, IStorage *pstgDest)
1620 DirEntry data;
1621 HRESULT hr;
1622 BOOL skip = FALSE;
1623 IStorage *pstgTmp;
1624 IStream *pstrChild, *pstrTmp;
1625 STATSTG strStat;
1627 if (srcEntry == DIRENTRY_NULL)
1628 return S_OK;
1630 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1632 if (FAILED(hr))
1633 return hr;
1635 if ( snbExclude )
1637 WCHAR **snb = snbExclude;
1639 while ( *snb != NULL && !skip )
1641 if ( lstrcmpW(data.name, *snb) == 0 )
1642 skip = TRUE;
1643 ++snb;
1647 if (!skip)
1649 if (data.stgType == STGTY_STORAGE && !skip_storage)
1652 * create a new storage in destination storage
1654 hr = IStorage_CreateStorage( pstgDest, data.name,
1655 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1656 0, 0,
1657 &pstgTmp );
1660 * if it already exist, don't create a new one use this one
1662 if (hr == STG_E_FILEALREADYEXISTS)
1664 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1665 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1666 NULL, 0, &pstgTmp );
1669 if (SUCCEEDED(hr))
1671 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1672 skip_stream, NULL, pstgTmp );
1674 IStorage_Release(pstgTmp);
1677 else if (data.stgType == STGTY_STREAM && !skip_stream)
1680 * create a new stream in destination storage. If the stream already
1681 * exist, it will be deleted and a new one will be created.
1683 hr = IStorage_CreateStream( pstgDest, data.name,
1684 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1685 0, 0, &pstrTmp );
1688 * open child stream storage. This operation must succeed even if the
1689 * stream is already open, so we use internal functions to do it.
1691 if (hr == S_OK)
1693 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1695 if (streamimpl)
1697 pstrChild = &streamimpl->IStream_iface;
1698 if (pstrChild)
1699 IStream_AddRef(pstrChild);
1701 else
1703 pstrChild = NULL;
1704 hr = E_OUTOFMEMORY;
1708 if (hr == S_OK)
1711 * Get the size of the source stream
1713 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1716 * Set the size of the destination stream.
1718 IStream_SetSize(pstrTmp, strStat.cbSize);
1721 * do the copy
1723 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1724 NULL, NULL );
1726 IStream_Release( pstrChild );
1729 IStream_Release( pstrTmp );
1733 /* copy siblings */
1734 if (SUCCEEDED(hr))
1735 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1736 skip_stream, snbExclude, pstgDest );
1738 if (SUCCEEDED(hr))
1739 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1740 skip_stream, snbExclude, pstgDest );
1742 return hr;
1745 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1746 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1747 SNB snbExclude, IStorage *pstgDest)
1749 DirEntry data;
1750 HRESULT hr;
1752 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1754 if (SUCCEEDED(hr))
1755 hr = IStorage_SetClass( pstgDest, &data.clsid );
1757 if (SUCCEEDED(hr))
1758 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1759 skip_stream, snbExclude, pstgDest );
1761 return hr;
1764 /*************************************************************************
1765 * CopyTo (IStorage)
1767 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1768 IStorage* iface,
1769 DWORD ciidExclude, /* [in] */
1770 const IID* rgiidExclude, /* [size_is][unique][in] */
1771 SNB snbExclude, /* [unique][in] */
1772 IStorage* pstgDest) /* [unique][in] */
1774 StorageBaseImpl *This = impl_from_IStorage(iface);
1776 BOOL skip_storage = FALSE, skip_stream = FALSE;
1777 DWORD i;
1779 TRACE("(%p, %d, %p, %p, %p)\n",
1780 iface, ciidExclude, rgiidExclude,
1781 snbExclude, pstgDest);
1783 if ( pstgDest == 0 )
1784 return STG_E_INVALIDPOINTER;
1786 for(i = 0; i < ciidExclude; ++i)
1788 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1789 skip_storage = TRUE;
1790 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1791 skip_stream = TRUE;
1792 else
1793 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1796 if (!skip_storage)
1798 /* Give up early if it looks like this would be infinitely recursive.
1799 * Oddly enough, this includes some cases that aren't really recursive, like
1800 * copying to a transacted child. */
1801 IStorage *pstgDestAncestor = pstgDest;
1802 IStorage *pstgDestAncestorChild = NULL;
1804 /* Go up the chain from the destination until we find the source storage. */
1805 while (pstgDestAncestor != iface) {
1806 pstgDestAncestorChild = pstgDest;
1808 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1810 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
1812 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
1814 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1816 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
1818 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
1820 else
1821 break;
1824 if (pstgDestAncestor == iface)
1826 BOOL fail = TRUE;
1828 if (pstgDestAncestorChild && snbExclude)
1830 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1831 DirEntry data;
1832 WCHAR **snb = snbExclude;
1834 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1836 while ( *snb != NULL && fail )
1838 if ( lstrcmpW(data.name, *snb) == 0 )
1839 fail = FALSE;
1840 ++snb;
1844 if (fail)
1845 return STG_E_ACCESSDENIED;
1849 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1850 skip_storage, skip_stream, snbExclude, pstgDest );
1853 /*************************************************************************
1854 * MoveElementTo (IStorage)
1856 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1857 IStorage* iface,
1858 const OLECHAR *pwcsName, /* [string][in] */
1859 IStorage *pstgDest, /* [unique][in] */
1860 const OLECHAR *pwcsNewName,/* [string][in] */
1861 DWORD grfFlags) /* [in] */
1863 FIXME("(%p %s %p %s %u): stub\n", iface,
1864 debugstr_w(pwcsName), pstgDest,
1865 debugstr_w(pwcsNewName), grfFlags);
1866 return E_NOTIMPL;
1869 /*************************************************************************
1870 * Commit (IStorage)
1872 * Ensures that any changes made to a storage object open in transacted mode
1873 * are reflected in the parent storage
1875 * In a non-transacted mode, this ensures all cached writes are completed.
1877 static HRESULT WINAPI StorageImpl_Commit(
1878 IStorage* iface,
1879 DWORD grfCommitFlags)/* [in] */
1881 StorageBaseImpl* This = impl_from_IStorage(iface);
1882 TRACE("(%p %d)\n", iface, grfCommitFlags);
1883 return StorageBaseImpl_Flush(This);
1886 /*************************************************************************
1887 * Revert (IStorage)
1889 * Discard all changes that have been made since the last commit operation
1891 static HRESULT WINAPI StorageImpl_Revert(
1892 IStorage* iface)
1894 TRACE("(%p)\n", iface);
1895 return S_OK;
1898 /*************************************************************************
1899 * DestroyElement (IStorage)
1901 * Strategy: This implementation is built this way for simplicity not for speed.
1902 * I always delete the topmost element of the enumeration and adjust
1903 * the deleted element pointer all the time. This takes longer to
1904 * do but allow to reinvoke DestroyElement whenever we encounter a
1905 * storage object. The optimisation resides in the usage of another
1906 * enumeration strategy that would give all the leaves of a storage
1907 * first. (postfix order)
1909 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1910 IStorage* iface,
1911 const OLECHAR *pwcsName)/* [string][in] */
1913 StorageBaseImpl *This = impl_from_IStorage(iface);
1915 HRESULT hr = S_OK;
1916 DirEntry entryToDelete;
1917 DirRef entryToDeleteRef;
1919 TRACE("(%p, %s)\n",
1920 iface, debugstr_w(pwcsName));
1922 if (pwcsName==NULL)
1923 return STG_E_INVALIDPOINTER;
1925 if (This->reverted)
1926 return STG_E_REVERTED;
1928 if ( !(This->openFlags & STGM_TRANSACTED) &&
1929 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1930 return STG_E_ACCESSDENIED;
1932 entryToDeleteRef = findElement(
1933 This,
1934 This->storageDirEntry,
1935 pwcsName,
1936 &entryToDelete);
1938 if ( entryToDeleteRef == DIRENTRY_NULL )
1940 return STG_E_FILENOTFOUND;
1943 if ( entryToDelete.stgType == STGTY_STORAGE )
1945 hr = deleteStorageContents(
1946 This,
1947 entryToDeleteRef,
1948 entryToDelete);
1950 else if ( entryToDelete.stgType == STGTY_STREAM )
1952 hr = deleteStreamContents(
1953 This,
1954 entryToDeleteRef,
1955 entryToDelete);
1958 if (hr!=S_OK)
1959 return hr;
1962 * Remove the entry from its parent storage
1964 hr = removeFromTree(
1965 This,
1966 This->storageDirEntry,
1967 entryToDeleteRef);
1970 * Invalidate the entry
1972 if (SUCCEEDED(hr))
1973 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1975 if (SUCCEEDED(hr))
1976 hr = StorageBaseImpl_Flush(This);
1978 return hr;
1982 /******************************************************************************
1983 * Internal stream list handlers
1986 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1988 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1989 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1992 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1994 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1995 list_remove(&(strm->StrmListEntry));
1998 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
2000 StgStreamImpl *strm;
2002 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
2004 if (strm->dirEntry == streamEntry)
2006 return TRUE;
2010 return FALSE;
2013 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2015 StorageInternalImpl *childstg;
2017 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2019 if (childstg->base.storageDirEntry == storageEntry)
2021 return TRUE;
2025 return FALSE;
2028 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2030 struct list *cur, *cur2;
2031 StgStreamImpl *strm=NULL;
2032 StorageInternalImpl *childstg=NULL;
2034 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2035 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2036 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2037 strm->parentStorage = NULL;
2038 list_remove(cur);
2041 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2042 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2043 StorageBaseImpl_Invalidate( &childstg->base );
2046 if (stg->transactedChild)
2048 StorageBaseImpl_Invalidate(stg->transactedChild);
2050 stg->transactedChild = NULL;
2055 /*********************************************************************
2057 * Internal Method
2059 * Delete the contents of a storage entry.
2062 static HRESULT deleteStorageContents(
2063 StorageBaseImpl *parentStorage,
2064 DirRef indexToDelete,
2065 DirEntry entryDataToDelete)
2067 IEnumSTATSTG *elements = 0;
2068 IStorage *childStorage = 0;
2069 STATSTG currentElement;
2070 HRESULT hr;
2071 HRESULT destroyHr = S_OK;
2072 StorageInternalImpl *stg, *stg2;
2074 /* Invalidate any open storage objects. */
2075 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2077 if (stg->base.storageDirEntry == indexToDelete)
2079 StorageBaseImpl_Invalidate(&stg->base);
2084 * Open the storage and enumerate it
2086 hr = IStorage_OpenStorage(
2087 &parentStorage->IStorage_iface,
2088 entryDataToDelete.name,
2090 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2093 &childStorage);
2095 if (hr != S_OK)
2097 return hr;
2101 * Enumerate the elements
2103 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2108 * Obtain the next element
2110 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2111 if (hr==S_OK)
2113 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2115 CoTaskMemFree(currentElement.pwcsName);
2119 * We need to Reset the enumeration every time because we delete elements
2120 * and the enumeration could be invalid
2122 IEnumSTATSTG_Reset(elements);
2124 } while ((hr == S_OK) && (destroyHr == S_OK));
2126 IStorage_Release(childStorage);
2127 IEnumSTATSTG_Release(elements);
2129 return destroyHr;
2132 /*********************************************************************
2134 * Internal Method
2136 * Perform the deletion of a stream's data
2139 static HRESULT deleteStreamContents(
2140 StorageBaseImpl *parentStorage,
2141 DirRef indexToDelete,
2142 DirEntry entryDataToDelete)
2144 IStream *pis;
2145 HRESULT hr;
2146 ULARGE_INTEGER size;
2147 StgStreamImpl *strm, *strm2;
2149 /* Invalidate any open stream objects. */
2150 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2152 if (strm->dirEntry == indexToDelete)
2154 TRACE("Stream deleted %p\n", strm);
2155 strm->parentStorage = NULL;
2156 list_remove(&strm->StrmListEntry);
2160 size.u.HighPart = 0;
2161 size.u.LowPart = 0;
2163 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2164 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2166 if (hr!=S_OK)
2168 return(hr);
2172 * Zap the stream
2174 hr = IStream_SetSize(pis, size);
2176 if(hr != S_OK)
2178 return hr;
2182 * Release the stream object.
2184 IStream_Release(pis);
2186 return S_OK;
2189 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2191 switch (relation)
2193 case DIRENTRY_RELATION_PREVIOUS:
2194 entry->leftChild = new_target;
2195 break;
2196 case DIRENTRY_RELATION_NEXT:
2197 entry->rightChild = new_target;
2198 break;
2199 case DIRENTRY_RELATION_DIR:
2200 entry->dirRootEntry = new_target;
2201 break;
2202 default:
2203 assert(0);
2207 /*************************************************************************
2209 * Internal Method
2211 * This method removes a directory entry from its parent storage tree without
2212 * freeing any resources attached to it.
2214 static HRESULT removeFromTree(
2215 StorageBaseImpl *This,
2216 DirRef parentStorageIndex,
2217 DirRef deletedIndex)
2219 DirEntry entryToDelete;
2220 DirEntry parentEntry;
2221 DirRef parentEntryRef;
2222 ULONG typeOfRelation;
2223 HRESULT hr;
2225 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2227 if (hr != S_OK)
2228 return hr;
2231 * Find the element that links to the one we want to delete.
2233 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2234 &parentEntry, &parentEntryRef, &typeOfRelation);
2236 if (hr != S_OK)
2237 return hr;
2239 if (entryToDelete.leftChild != DIRENTRY_NULL)
2242 * Replace the deleted entry with its left child
2244 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2246 hr = StorageBaseImpl_WriteDirEntry(
2247 This,
2248 parentEntryRef,
2249 &parentEntry);
2250 if(FAILED(hr))
2252 return hr;
2255 if (entryToDelete.rightChild != DIRENTRY_NULL)
2258 * We need to reinsert the right child somewhere. We already know it and
2259 * its children are greater than everything in the left tree, so we
2260 * insert it at the rightmost point in the left tree.
2262 DirRef newRightChildParent = entryToDelete.leftChild;
2263 DirEntry newRightChildParentEntry;
2267 hr = StorageBaseImpl_ReadDirEntry(
2268 This,
2269 newRightChildParent,
2270 &newRightChildParentEntry);
2271 if (FAILED(hr))
2273 return hr;
2276 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2277 newRightChildParent = newRightChildParentEntry.rightChild;
2278 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2280 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2282 hr = StorageBaseImpl_WriteDirEntry(
2283 This,
2284 newRightChildParent,
2285 &newRightChildParentEntry);
2286 if (FAILED(hr))
2288 return hr;
2292 else
2295 * Replace the deleted entry with its right child
2297 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2299 hr = StorageBaseImpl_WriteDirEntry(
2300 This,
2301 parentEntryRef,
2302 &parentEntry);
2303 if(FAILED(hr))
2305 return hr;
2309 return hr;
2313 /******************************************************************************
2314 * SetElementTimes (IStorage)
2316 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2317 IStorage* iface,
2318 const OLECHAR *pwcsName,/* [string][in] */
2319 const FILETIME *pctime, /* [in] */
2320 const FILETIME *patime, /* [in] */
2321 const FILETIME *pmtime) /* [in] */
2323 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2324 return S_OK;
2327 /******************************************************************************
2328 * SetStateBits (IStorage)
2330 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2331 IStorage* iface,
2332 DWORD grfStateBits,/* [in] */
2333 DWORD grfMask) /* [in] */
2335 StorageBaseImpl *This = impl_from_IStorage(iface);
2337 if (This->reverted)
2338 return STG_E_REVERTED;
2340 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2341 return S_OK;
2344 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2345 DirRef index, const DirEntry *data)
2347 StorageImpl *This = (StorageImpl*)base;
2348 return StorageImpl_WriteDirEntry(This, index, data);
2351 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2352 DirRef index, DirEntry *data)
2354 StorageImpl *This = (StorageImpl*)base;
2355 return StorageImpl_ReadDirEntry(This, index, data);
2358 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2360 int i;
2362 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2364 if (!This->blockChainCache[i])
2366 return &This->blockChainCache[i];
2370 i = This->blockChainToEvict;
2372 BlockChainStream_Destroy(This->blockChainCache[i]);
2373 This->blockChainCache[i] = NULL;
2375 This->blockChainToEvict++;
2376 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2377 This->blockChainToEvict = 0;
2379 return &This->blockChainCache[i];
2382 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2383 DirRef index)
2385 int i, free_index=-1;
2387 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2389 if (!This->blockChainCache[i])
2391 if (free_index == -1) free_index = i;
2393 else if (This->blockChainCache[i]->ownerDirEntry == index)
2395 return &This->blockChainCache[i];
2399 if (free_index == -1)
2401 free_index = This->blockChainToEvict;
2403 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2404 This->blockChainCache[free_index] = NULL;
2406 This->blockChainToEvict++;
2407 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2408 This->blockChainToEvict = 0;
2411 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2412 return &This->blockChainCache[free_index];
2415 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2417 int i;
2419 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2421 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2423 BlockChainStream_Destroy(This->blockChainCache[i]);
2424 This->blockChainCache[i] = NULL;
2425 return;
2430 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2431 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2433 StorageImpl *This = (StorageImpl*)base;
2434 DirEntry data;
2435 HRESULT hr;
2436 ULONG bytesToRead;
2438 hr = StorageImpl_ReadDirEntry(This, index, &data);
2439 if (FAILED(hr)) return hr;
2441 if (data.size.QuadPart == 0)
2443 *bytesRead = 0;
2444 return S_OK;
2447 if (offset.QuadPart + size > data.size.QuadPart)
2449 bytesToRead = data.size.QuadPart - offset.QuadPart;
2451 else
2453 bytesToRead = size;
2456 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2458 SmallBlockChainStream *stream;
2460 stream = SmallBlockChainStream_Construct(This, NULL, index);
2461 if (!stream) return E_OUTOFMEMORY;
2463 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2465 SmallBlockChainStream_Destroy(stream);
2467 return hr;
2469 else
2471 BlockChainStream *stream = NULL;
2473 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2474 if (!stream) return E_OUTOFMEMORY;
2476 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2478 return hr;
2482 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2483 ULARGE_INTEGER newsize)
2485 StorageImpl *This = (StorageImpl*)base;
2486 DirEntry data;
2487 HRESULT hr;
2488 SmallBlockChainStream *smallblock=NULL;
2489 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2491 hr = StorageImpl_ReadDirEntry(This, index, &data);
2492 if (FAILED(hr)) return hr;
2494 /* In simple mode keep the stream size above the small block limit */
2495 if (This->base.openFlags & STGM_SIMPLE)
2496 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2498 if (data.size.QuadPart == newsize.QuadPart)
2499 return S_OK;
2501 /* Create a block chain object of the appropriate type */
2502 if (data.size.QuadPart == 0)
2504 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2506 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2507 if (!smallblock) return E_OUTOFMEMORY;
2509 else
2511 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2512 bigblock = *pbigblock;
2513 if (!bigblock) return E_OUTOFMEMORY;
2516 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2518 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2519 if (!smallblock) return E_OUTOFMEMORY;
2521 else
2523 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2524 bigblock = *pbigblock;
2525 if (!bigblock) return E_OUTOFMEMORY;
2528 /* Change the block chain type if necessary. */
2529 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2531 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2532 if (!bigblock)
2534 SmallBlockChainStream_Destroy(smallblock);
2535 return E_FAIL;
2538 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2539 *pbigblock = bigblock;
2541 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2543 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2544 if (!smallblock)
2545 return E_FAIL;
2548 /* Set the size of the block chain. */
2549 if (smallblock)
2551 SmallBlockChainStream_SetSize(smallblock, newsize);
2552 SmallBlockChainStream_Destroy(smallblock);
2554 else
2556 BlockChainStream_SetSize(bigblock, newsize);
2559 /* Set the size in the directory entry. */
2560 hr = StorageImpl_ReadDirEntry(This, index, &data);
2561 if (SUCCEEDED(hr))
2563 data.size = newsize;
2565 hr = StorageImpl_WriteDirEntry(This, index, &data);
2567 return hr;
2570 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2571 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2573 StorageImpl *This = (StorageImpl*)base;
2574 DirEntry data;
2575 HRESULT hr;
2576 ULARGE_INTEGER newSize;
2578 hr = StorageImpl_ReadDirEntry(This, index, &data);
2579 if (FAILED(hr)) return hr;
2581 /* Grow the stream if necessary */
2582 newSize.QuadPart = 0;
2583 newSize.QuadPart = offset.QuadPart + size;
2585 if (newSize.QuadPart > data.size.QuadPart)
2587 hr = StorageImpl_StreamSetSize(base, index, newSize);
2588 if (FAILED(hr))
2589 return hr;
2591 hr = StorageImpl_ReadDirEntry(This, index, &data);
2592 if (FAILED(hr)) return hr;
2595 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2597 SmallBlockChainStream *stream;
2599 stream = SmallBlockChainStream_Construct(This, NULL, index);
2600 if (!stream) return E_OUTOFMEMORY;
2602 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2604 SmallBlockChainStream_Destroy(stream);
2606 return hr;
2608 else
2610 BlockChainStream *stream;
2612 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2613 if (!stream) return E_OUTOFMEMORY;
2615 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2619 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2620 DirRef src)
2622 StorageImpl *This = (StorageImpl*)base;
2623 DirEntry dst_data, src_data;
2624 HRESULT hr;
2626 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2628 if (SUCCEEDED(hr))
2629 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2631 if (SUCCEEDED(hr))
2633 StorageImpl_DeleteCachedBlockChainStream(This, src);
2634 dst_data.startingBlock = src_data.startingBlock;
2635 dst_data.size = src_data.size;
2637 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2640 return hr;
2643 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2645 StorageImpl *This = (StorageImpl*) iface;
2646 STATSTG statstg;
2647 HRESULT hr;
2649 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2651 *result = statstg.pwcsName;
2653 return hr;
2656 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
2658 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2659 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
2662 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
2664 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2665 return IStorage_AddRef(&This->IStorage_iface);
2668 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
2670 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2671 return IStorage_Release(&This->IStorage_iface);
2674 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
2676 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2677 FIXME("(%p)->(%d): stub\n", This, timeout);
2678 return E_NOTIMPL;
2681 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
2683 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2684 FIXME("(%p): stub\n", This);
2685 return E_NOTIMPL;
2688 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
2690 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2691 FIXME("(%p): stub\n", This);
2692 return E_NOTIMPL;
2695 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
2697 directwriterlock_QueryInterface,
2698 directwriterlock_AddRef,
2699 directwriterlock_Release,
2700 directwriterlock_WaitForWriteAccess,
2701 directwriterlock_ReleaseWriteAccess,
2702 directwriterlock_HaveWriteAccess
2706 * Virtual function table for the IStorage32Impl class.
2708 static const IStorageVtbl Storage32Impl_Vtbl =
2710 StorageBaseImpl_QueryInterface,
2711 StorageBaseImpl_AddRef,
2712 StorageBaseImpl_Release,
2713 StorageBaseImpl_CreateStream,
2714 StorageBaseImpl_OpenStream,
2715 StorageBaseImpl_CreateStorage,
2716 StorageBaseImpl_OpenStorage,
2717 StorageBaseImpl_CopyTo,
2718 StorageBaseImpl_MoveElementTo,
2719 StorageImpl_Commit,
2720 StorageImpl_Revert,
2721 StorageBaseImpl_EnumElements,
2722 StorageBaseImpl_DestroyElement,
2723 StorageBaseImpl_RenameElement,
2724 StorageBaseImpl_SetElementTimes,
2725 StorageBaseImpl_SetClass,
2726 StorageBaseImpl_SetStateBits,
2727 StorageBaseImpl_Stat
2730 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2732 StorageImpl_Destroy,
2733 StorageImpl_Invalidate,
2734 StorageImpl_Flush,
2735 StorageImpl_GetFilename,
2736 StorageImpl_CreateDirEntry,
2737 StorageImpl_BaseWriteDirEntry,
2738 StorageImpl_BaseReadDirEntry,
2739 StorageImpl_DestroyDirEntry,
2740 StorageImpl_StreamReadAt,
2741 StorageImpl_StreamWriteAt,
2742 StorageImpl_StreamSetSize,
2743 StorageImpl_StreamLink
2746 static HRESULT StorageImpl_Construct(
2747 HANDLE hFile,
2748 LPCOLESTR pwcsName,
2749 ILockBytes* pLkbyt,
2750 DWORD openFlags,
2751 BOOL fileBased,
2752 BOOL create,
2753 ULONG sector_size,
2754 StorageImpl** result)
2756 StorageImpl* This;
2757 HRESULT hr = S_OK;
2758 DirEntry currentEntry;
2759 DirRef currentEntryRef;
2761 if ( FAILED( validateSTGM(openFlags) ))
2762 return STG_E_INVALIDFLAG;
2764 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2765 if (!This)
2766 return E_OUTOFMEMORY;
2768 memset(This, 0, sizeof(StorageImpl));
2770 list_init(&This->base.strmHead);
2772 list_init(&This->base.storageHead);
2774 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
2775 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
2776 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
2777 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2778 This->base.openFlags = (openFlags & ~STGM_CREATE);
2779 This->base.ref = 1;
2780 This->base.create = create;
2782 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
2783 This->base.lockingrole = SWMR_Writer;
2784 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
2785 This->base.lockingrole = SWMR_Reader;
2786 else
2787 This->base.lockingrole = SWMR_None;
2789 This->base.reverted = FALSE;
2792 * Initialize the big block cache.
2794 This->bigBlockSize = sector_size;
2795 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2796 if (hFile)
2797 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2798 else
2800 This->lockBytes = pLkbyt;
2801 ILockBytes_AddRef(pLkbyt);
2804 if (FAILED(hr))
2805 goto end;
2807 if (create)
2809 ULARGE_INTEGER size;
2810 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2812 /* Discard any existing data. */
2813 size.QuadPart = 0;
2814 ILockBytes_SetSize(This->lockBytes, size);
2817 * Initialize all header variables:
2818 * - The big block depot consists of one block and it is at block 0
2819 * - The directory table starts at block 1
2820 * - There is no small block depot
2822 memset( This->bigBlockDepotStart,
2823 BLOCK_UNUSED,
2824 sizeof(This->bigBlockDepotStart));
2826 This->bigBlockDepotCount = 1;
2827 This->bigBlockDepotStart[0] = 0;
2828 This->rootStartBlock = 1;
2829 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2830 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2831 if (sector_size == 4096)
2832 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2833 else
2834 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2835 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2836 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2837 This->extBigBlockDepotCount = 0;
2839 StorageImpl_SaveFileHeader(This);
2842 * Add one block for the big block depot and one block for the directory table
2844 size.u.HighPart = 0;
2845 size.u.LowPart = This->bigBlockSize * 3;
2846 ILockBytes_SetSize(This->lockBytes, size);
2849 * Initialize the big block depot
2851 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2852 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2853 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2854 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2856 else
2859 * Load the header for the file.
2861 hr = StorageImpl_LoadFileHeader(This);
2863 if (FAILED(hr))
2865 goto end;
2870 * There is no block depot cached yet.
2872 This->indexBlockDepotCached = 0xFFFFFFFF;
2873 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2876 * Start searching for free blocks with block 0.
2878 This->prevFreeBlock = 0;
2880 This->firstFreeSmallBlock = 0;
2882 /* Read the extended big block depot locations. */
2883 if (This->extBigBlockDepotCount != 0)
2885 ULONG current_block = This->extBigBlockDepotStart;
2886 ULONG cache_size = This->extBigBlockDepotCount * 2;
2887 ULONG i;
2889 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2890 if (!This->extBigBlockDepotLocations)
2892 hr = E_OUTOFMEMORY;
2893 goto end;
2896 This->extBigBlockDepotLocationsSize = cache_size;
2898 for (i=0; i<This->extBigBlockDepotCount; i++)
2900 if (current_block == BLOCK_END_OF_CHAIN)
2902 WARN("File has too few extended big block depot blocks.\n");
2903 hr = STG_E_DOCFILECORRUPT;
2904 goto end;
2906 This->extBigBlockDepotLocations[i] = current_block;
2907 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2910 else
2912 This->extBigBlockDepotLocations = NULL;
2913 This->extBigBlockDepotLocationsSize = 0;
2917 * Create the block chain abstractions.
2919 if(!(This->rootBlockChain =
2920 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2922 hr = STG_E_READFAULT;
2923 goto end;
2926 if(!(This->smallBlockDepotChain =
2927 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2928 DIRENTRY_NULL)))
2930 hr = STG_E_READFAULT;
2931 goto end;
2935 * Write the root storage entry (memory only)
2937 if (create)
2939 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
2940 DirEntry rootEntry;
2942 * Initialize the directory table
2944 memset(&rootEntry, 0, sizeof(rootEntry));
2945 strcpyW(rootEntry.name, rootentryW);
2946 rootEntry.sizeOfNameString = sizeof(rootentryW);
2947 rootEntry.stgType = STGTY_ROOT;
2948 rootEntry.leftChild = DIRENTRY_NULL;
2949 rootEntry.rightChild = DIRENTRY_NULL;
2950 rootEntry.dirRootEntry = DIRENTRY_NULL;
2951 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2952 rootEntry.size.u.HighPart = 0;
2953 rootEntry.size.u.LowPart = 0;
2955 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2959 * Find the ID of the root storage.
2961 currentEntryRef = 0;
2965 hr = StorageImpl_ReadDirEntry(
2966 This,
2967 currentEntryRef,
2968 &currentEntry);
2970 if (SUCCEEDED(hr))
2972 if ( (currentEntry.sizeOfNameString != 0 ) &&
2973 (currentEntry.stgType == STGTY_ROOT) )
2975 This->base.storageDirEntry = currentEntryRef;
2979 currentEntryRef++;
2981 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2983 if (FAILED(hr))
2985 hr = STG_E_READFAULT;
2986 goto end;
2990 * Create the block chain abstraction for the small block root chain.
2992 if(!(This->smallBlockRootChain =
2993 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2995 hr = STG_E_READFAULT;
2998 end:
2999 if (FAILED(hr))
3001 IStorage_Release(&This->base.IStorage_iface);
3002 *result = NULL;
3004 else
3006 StorageImpl_Flush(&This->base);
3007 *result = This;
3010 return hr;
3013 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
3015 StorageImpl *This = (StorageImpl*) iface;
3017 StorageBaseImpl_DeleteAll(&This->base);
3019 This->base.reverted = TRUE;
3022 static void StorageImpl_Destroy(StorageBaseImpl* iface)
3024 StorageImpl *This = (StorageImpl*) iface;
3025 int i;
3026 TRACE("(%p)\n", This);
3028 StorageImpl_Flush(iface);
3030 StorageImpl_Invalidate(iface);
3032 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3034 BlockChainStream_Destroy(This->smallBlockRootChain);
3035 BlockChainStream_Destroy(This->rootBlockChain);
3036 BlockChainStream_Destroy(This->smallBlockDepotChain);
3038 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3039 BlockChainStream_Destroy(This->blockChainCache[i]);
3041 if (This->lockBytes)
3042 ILockBytes_Release(This->lockBytes);
3043 HeapFree(GetProcessHeap(), 0, This);
3046 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
3048 StorageImpl *This = (StorageImpl*)storage;
3049 int i;
3050 HRESULT hr;
3051 TRACE("(%p)\n", This);
3053 hr = BlockChainStream_Flush(This->smallBlockRootChain);
3055 if (SUCCEEDED(hr))
3056 hr = BlockChainStream_Flush(This->rootBlockChain);
3058 if (SUCCEEDED(hr))
3059 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
3061 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
3062 if (This->blockChainCache[i])
3063 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3065 if (SUCCEEDED(hr))
3066 hr = ILockBytes_Flush(This->lockBytes);
3068 return hr;
3071 /******************************************************************************
3072 * Storage32Impl_GetNextFreeBigBlock
3074 * Returns the index of the next free big block.
3075 * If the big block depot is filled, this method will enlarge it.
3078 static ULONG StorageImpl_GetNextFreeBigBlock(
3079 StorageImpl* This)
3081 ULONG depotBlockIndexPos;
3082 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3083 ULONG depotBlockOffset;
3084 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3085 ULONG nextBlockIndex = BLOCK_SPECIAL;
3086 int depotIndex = 0;
3087 ULONG freeBlock = BLOCK_UNUSED;
3088 ULONG read;
3089 ULARGE_INTEGER neededSize;
3090 STATSTG statstg;
3092 depotIndex = This->prevFreeBlock / blocksPerDepot;
3093 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3096 * Scan the entire big block depot until we find a block marked free
3098 while (nextBlockIndex != BLOCK_UNUSED)
3100 if (depotIndex < COUNT_BBDEPOTINHEADER)
3102 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3105 * Grow the primary depot.
3107 if (depotBlockIndexPos == BLOCK_UNUSED)
3109 depotBlockIndexPos = depotIndex*blocksPerDepot;
3112 * Add a block depot.
3114 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3115 This->bigBlockDepotCount++;
3116 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3119 * Flag it as a block depot.
3121 StorageImpl_SetNextBlockInChain(This,
3122 depotBlockIndexPos,
3123 BLOCK_SPECIAL);
3125 /* Save new header information.
3127 StorageImpl_SaveFileHeader(This);
3130 else
3132 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3134 if (depotBlockIndexPos == BLOCK_UNUSED)
3137 * Grow the extended depot.
3139 ULONG extIndex = BLOCK_UNUSED;
3140 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3141 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3143 if (extBlockOffset == 0)
3145 /* We need an extended block.
3147 extIndex = Storage32Impl_AddExtBlockDepot(This);
3148 This->extBigBlockDepotCount++;
3149 depotBlockIndexPos = extIndex + 1;
3151 else
3152 depotBlockIndexPos = depotIndex * blocksPerDepot;
3155 * Add a block depot and mark it in the extended block.
3157 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3158 This->bigBlockDepotCount++;
3159 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3161 /* Flag the block depot.
3163 StorageImpl_SetNextBlockInChain(This,
3164 depotBlockIndexPos,
3165 BLOCK_SPECIAL);
3167 /* If necessary, flag the extended depot block.
3169 if (extIndex != BLOCK_UNUSED)
3170 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3172 /* Save header information.
3174 StorageImpl_SaveFileHeader(This);
3178 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3180 if (read)
3182 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3183 ( nextBlockIndex != BLOCK_UNUSED))
3185 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3187 if (nextBlockIndex == BLOCK_UNUSED)
3189 freeBlock = (depotIndex * blocksPerDepot) +
3190 (depotBlockOffset/sizeof(ULONG));
3193 depotBlockOffset += sizeof(ULONG);
3197 depotIndex++;
3198 depotBlockOffset = 0;
3202 * make sure that the block physically exists before using it
3204 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3206 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3208 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3209 ILockBytes_SetSize(This->lockBytes, neededSize);
3211 This->prevFreeBlock = freeBlock;
3213 return freeBlock;
3216 /******************************************************************************
3217 * Storage32Impl_AddBlockDepot
3219 * This will create a depot block, essentially it is a block initialized
3220 * to BLOCK_UNUSEDs.
3222 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3224 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3227 * Initialize blocks as free
3229 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3230 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3233 /******************************************************************************
3234 * Storage32Impl_GetExtDepotBlock
3236 * Returns the index of the block that corresponds to the specified depot
3237 * index. This method is only for depot indexes equal or greater than
3238 * COUNT_BBDEPOTINHEADER.
3240 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3242 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3243 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3244 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3245 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3246 ULONG blockIndex = BLOCK_UNUSED;
3247 ULONG extBlockIndex;
3248 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3249 int index, num_blocks;
3251 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3253 if (extBlockCount >= This->extBigBlockDepotCount)
3254 return BLOCK_UNUSED;
3256 if (This->indexExtBlockDepotCached != extBlockCount)
3258 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3260 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3262 num_blocks = This->bigBlockSize / 4;
3264 for (index = 0; index < num_blocks; index++)
3266 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3267 This->extBlockDepotCached[index] = blockIndex;
3270 This->indexExtBlockDepotCached = extBlockCount;
3273 blockIndex = This->extBlockDepotCached[extBlockOffset];
3275 return blockIndex;
3278 /******************************************************************************
3279 * Storage32Impl_SetExtDepotBlock
3281 * Associates the specified block index to the specified depot index.
3282 * This method is only for depot indexes equal or greater than
3283 * COUNT_BBDEPOTINHEADER.
3285 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3287 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3288 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3289 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3290 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3291 ULONG extBlockIndex;
3293 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3295 assert(extBlockCount < This->extBigBlockDepotCount);
3297 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3299 if (extBlockIndex != BLOCK_UNUSED)
3301 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3302 extBlockOffset * sizeof(ULONG),
3303 blockIndex);
3306 if (This->indexExtBlockDepotCached == extBlockCount)
3308 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3312 /******************************************************************************
3313 * Storage32Impl_AddExtBlockDepot
3315 * Creates an extended depot block.
3317 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3319 ULONG numExtBlocks = This->extBigBlockDepotCount;
3320 ULONG nextExtBlock = This->extBigBlockDepotStart;
3321 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3322 ULONG index = BLOCK_UNUSED;
3323 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3324 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3325 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3327 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3328 blocksPerDepotBlock;
3330 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3333 * The first extended block.
3335 This->extBigBlockDepotStart = index;
3337 else
3340 * Find the last existing extended block.
3342 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3345 * Add the new extended block to the chain.
3347 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3348 index);
3352 * Initialize this block.
3354 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3355 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3357 /* Add the block to our cache. */
3358 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3360 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3361 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3363 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3364 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3366 This->extBigBlockDepotLocations = new_cache;
3367 This->extBigBlockDepotLocationsSize = new_cache_size;
3369 This->extBigBlockDepotLocations[numExtBlocks] = index;
3371 return index;
3374 /******************************************************************************
3375 * Storage32Impl_FreeBigBlock
3377 * This method will flag the specified block as free in the big block depot.
3379 static void StorageImpl_FreeBigBlock(
3380 StorageImpl* This,
3381 ULONG blockIndex)
3383 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3385 if (blockIndex < This->prevFreeBlock)
3386 This->prevFreeBlock = blockIndex;
3389 /************************************************************************
3390 * Storage32Impl_GetNextBlockInChain
3392 * This method will retrieve the block index of the next big block in
3393 * in the chain.
3395 * Params: This - Pointer to the Storage object.
3396 * blockIndex - Index of the block to retrieve the chain
3397 * for.
3398 * nextBlockIndex - receives the return value.
3400 * Returns: This method returns the index of the next block in the chain.
3401 * It will return the constants:
3402 * BLOCK_SPECIAL - If the block given was not part of a
3403 * chain.
3404 * BLOCK_END_OF_CHAIN - If the block given was the last in
3405 * a chain.
3406 * BLOCK_UNUSED - If the block given was not past of a chain
3407 * and is available.
3408 * BLOCK_EXTBBDEPOT - This block is part of the extended
3409 * big block depot.
3411 * See Windows documentation for more details on IStorage methods.
3413 static HRESULT StorageImpl_GetNextBlockInChain(
3414 StorageImpl* This,
3415 ULONG blockIndex,
3416 ULONG* nextBlockIndex)
3418 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3419 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3420 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3421 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3422 ULONG read;
3423 ULONG depotBlockIndexPos;
3424 int index, num_blocks;
3426 *nextBlockIndex = BLOCK_SPECIAL;
3428 if(depotBlockCount >= This->bigBlockDepotCount)
3430 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3431 This->bigBlockDepotCount);
3432 return STG_E_READFAULT;
3436 * Cache the currently accessed depot block.
3438 if (depotBlockCount != This->indexBlockDepotCached)
3440 This->indexBlockDepotCached = depotBlockCount;
3442 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3444 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3446 else
3449 * We have to look in the extended depot.
3451 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3454 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3456 if (!read)
3457 return STG_E_READFAULT;
3459 num_blocks = This->bigBlockSize / 4;
3461 for (index = 0; index < num_blocks; index++)
3463 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3464 This->blockDepotCached[index] = *nextBlockIndex;
3468 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3470 return S_OK;
3473 /******************************************************************************
3474 * Storage32Impl_GetNextExtendedBlock
3476 * Given an extended block this method will return the next extended block.
3478 * NOTES:
3479 * The last ULONG of an extended block is the block index of the next
3480 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3481 * depot.
3483 * Return values:
3484 * - The index of the next extended block
3485 * - BLOCK_UNUSED: there is no next extended block.
3486 * - Any other return values denotes failure.
3488 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3490 ULONG nextBlockIndex = BLOCK_SPECIAL;
3491 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3493 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3494 &nextBlockIndex);
3496 return nextBlockIndex;
3499 /******************************************************************************
3500 * Storage32Impl_SetNextBlockInChain
3502 * This method will write the index of the specified block's next block
3503 * in the big block depot.
3505 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3506 * do the following
3508 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3509 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3510 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3513 static void StorageImpl_SetNextBlockInChain(
3514 StorageImpl* This,
3515 ULONG blockIndex,
3516 ULONG nextBlock)
3518 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3519 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3520 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3521 ULONG depotBlockIndexPos;
3523 assert(depotBlockCount < This->bigBlockDepotCount);
3524 assert(blockIndex != nextBlock);
3526 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3528 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3530 else
3533 * We have to look in the extended depot.
3535 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3538 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3539 nextBlock);
3541 * Update the cached block depot, if necessary.
3543 if (depotBlockCount == This->indexBlockDepotCached)
3545 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3549 /******************************************************************************
3550 * Storage32Impl_LoadFileHeader
3552 * This method will read in the file header
3554 static HRESULT StorageImpl_LoadFileHeader(
3555 StorageImpl* This)
3557 HRESULT hr;
3558 BYTE headerBigBlock[HEADER_SIZE];
3559 int index;
3560 ULARGE_INTEGER offset;
3561 DWORD bytes_read;
3563 TRACE("\n");
3565 * Get a pointer to the big block of data containing the header.
3567 offset.u.HighPart = 0;
3568 offset.u.LowPart = 0;
3569 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3570 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3571 hr = STG_E_FILENOTFOUND;
3574 * Extract the information from the header.
3576 if (SUCCEEDED(hr))
3579 * Check for the "magic number" signature and return an error if it is not
3580 * found.
3582 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3584 return STG_E_OLDFORMAT;
3587 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3589 return STG_E_INVALIDHEADER;
3592 StorageUtl_ReadWord(
3593 headerBigBlock,
3594 OFFSET_BIGBLOCKSIZEBITS,
3595 &This->bigBlockSizeBits);
3597 StorageUtl_ReadWord(
3598 headerBigBlock,
3599 OFFSET_SMALLBLOCKSIZEBITS,
3600 &This->smallBlockSizeBits);
3602 StorageUtl_ReadDWord(
3603 headerBigBlock,
3604 OFFSET_BBDEPOTCOUNT,
3605 &This->bigBlockDepotCount);
3607 StorageUtl_ReadDWord(
3608 headerBigBlock,
3609 OFFSET_ROOTSTARTBLOCK,
3610 &This->rootStartBlock);
3612 StorageUtl_ReadDWord(
3613 headerBigBlock,
3614 OFFSET_SMALLBLOCKLIMIT,
3615 &This->smallBlockLimit);
3617 StorageUtl_ReadDWord(
3618 headerBigBlock,
3619 OFFSET_SBDEPOTSTART,
3620 &This->smallBlockDepotStart);
3622 StorageUtl_ReadDWord(
3623 headerBigBlock,
3624 OFFSET_EXTBBDEPOTSTART,
3625 &This->extBigBlockDepotStart);
3627 StorageUtl_ReadDWord(
3628 headerBigBlock,
3629 OFFSET_EXTBBDEPOTCOUNT,
3630 &This->extBigBlockDepotCount);
3632 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3634 StorageUtl_ReadDWord(
3635 headerBigBlock,
3636 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3637 &(This->bigBlockDepotStart[index]));
3641 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3643 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3644 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3647 * Right now, the code is making some assumptions about the size of the
3648 * blocks, just make sure they are what we're expecting.
3650 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3651 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3652 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3654 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3655 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3656 hr = STG_E_INVALIDHEADER;
3658 else
3659 hr = S_OK;
3662 return hr;
3665 /******************************************************************************
3666 * Storage32Impl_SaveFileHeader
3668 * This method will save to the file the header
3670 static void StorageImpl_SaveFileHeader(
3671 StorageImpl* This)
3673 BYTE headerBigBlock[HEADER_SIZE];
3674 int index;
3675 HRESULT hr;
3676 ULARGE_INTEGER offset;
3677 DWORD bytes_read, bytes_written;
3678 DWORD major_version, dirsectorcount;
3681 * Get a pointer to the big block of data containing the header.
3683 offset.u.HighPart = 0;
3684 offset.u.LowPart = 0;
3685 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3686 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3687 hr = STG_E_FILENOTFOUND;
3689 if (This->bigBlockSizeBits == 0x9)
3690 major_version = 3;
3691 else if (This->bigBlockSizeBits == 0xc)
3692 major_version = 4;
3693 else
3695 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3696 major_version = 4;
3700 * If the block read failed, the file is probably new.
3702 if (FAILED(hr))
3705 * Initialize for all unknown fields.
3707 memset(headerBigBlock, 0, HEADER_SIZE);
3710 * Initialize the magic number.
3712 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3716 * Write the information to the header.
3718 StorageUtl_WriteWord(
3719 headerBigBlock,
3720 OFFSET_MINORVERSION,
3721 0x3e);
3723 StorageUtl_WriteWord(
3724 headerBigBlock,
3725 OFFSET_MAJORVERSION,
3726 major_version);
3728 StorageUtl_WriteWord(
3729 headerBigBlock,
3730 OFFSET_BYTEORDERMARKER,
3731 (WORD)-2);
3733 StorageUtl_WriteWord(
3734 headerBigBlock,
3735 OFFSET_BIGBLOCKSIZEBITS,
3736 This->bigBlockSizeBits);
3738 StorageUtl_WriteWord(
3739 headerBigBlock,
3740 OFFSET_SMALLBLOCKSIZEBITS,
3741 This->smallBlockSizeBits);
3743 if (major_version >= 4)
3745 if (This->rootBlockChain)
3746 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3747 else
3748 /* This file is being created, and it will start out with one block. */
3749 dirsectorcount = 1;
3751 else
3752 /* This field must be 0 in versions older than 4 */
3753 dirsectorcount = 0;
3755 StorageUtl_WriteDWord(
3756 headerBigBlock,
3757 OFFSET_DIRSECTORCOUNT,
3758 dirsectorcount);
3760 StorageUtl_WriteDWord(
3761 headerBigBlock,
3762 OFFSET_BBDEPOTCOUNT,
3763 This->bigBlockDepotCount);
3765 StorageUtl_WriteDWord(
3766 headerBigBlock,
3767 OFFSET_ROOTSTARTBLOCK,
3768 This->rootStartBlock);
3770 StorageUtl_WriteDWord(
3771 headerBigBlock,
3772 OFFSET_SMALLBLOCKLIMIT,
3773 This->smallBlockLimit);
3775 StorageUtl_WriteDWord(
3776 headerBigBlock,
3777 OFFSET_SBDEPOTSTART,
3778 This->smallBlockDepotStart);
3780 StorageUtl_WriteDWord(
3781 headerBigBlock,
3782 OFFSET_SBDEPOTCOUNT,
3783 This->smallBlockDepotChain ?
3784 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3786 StorageUtl_WriteDWord(
3787 headerBigBlock,
3788 OFFSET_EXTBBDEPOTSTART,
3789 This->extBigBlockDepotStart);
3791 StorageUtl_WriteDWord(
3792 headerBigBlock,
3793 OFFSET_EXTBBDEPOTCOUNT,
3794 This->extBigBlockDepotCount);
3796 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3798 StorageUtl_WriteDWord(
3799 headerBigBlock,
3800 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3801 (This->bigBlockDepotStart[index]));
3805 * Write the big block back to the file.
3807 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3810 /******************************************************************************
3811 * StorageImpl_ReadRawDirEntry
3813 * This method will read the raw data from a directory entry in the file.
3815 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3817 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3819 ULARGE_INTEGER offset;
3820 HRESULT hr;
3821 ULONG bytesRead;
3823 offset.u.HighPart = 0;
3824 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3826 hr = BlockChainStream_ReadAt(
3827 This->rootBlockChain,
3828 offset,
3829 RAW_DIRENTRY_SIZE,
3830 buffer,
3831 &bytesRead);
3833 if (bytesRead != RAW_DIRENTRY_SIZE)
3834 return STG_E_READFAULT;
3836 return hr;
3839 /******************************************************************************
3840 * StorageImpl_WriteRawDirEntry
3842 * This method will write the raw data from a directory entry in the file.
3844 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3846 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3848 ULARGE_INTEGER offset;
3849 ULONG bytesRead;
3851 offset.u.HighPart = 0;
3852 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3854 return BlockChainStream_WriteAt(
3855 This->rootBlockChain,
3856 offset,
3857 RAW_DIRENTRY_SIZE,
3858 buffer,
3859 &bytesRead);
3862 /******************************************************************************
3863 * UpdateRawDirEntry
3865 * Update raw directory entry data from the fields in newData.
3867 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3869 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3871 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3873 memcpy(
3874 buffer + OFFSET_PS_NAME,
3875 newData->name,
3876 DIRENTRY_NAME_BUFFER_LEN );
3878 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3880 StorageUtl_WriteWord(
3881 buffer,
3882 OFFSET_PS_NAMELENGTH,
3883 newData->sizeOfNameString);
3885 StorageUtl_WriteDWord(
3886 buffer,
3887 OFFSET_PS_LEFTCHILD,
3888 newData->leftChild);
3890 StorageUtl_WriteDWord(
3891 buffer,
3892 OFFSET_PS_RIGHTCHILD,
3893 newData->rightChild);
3895 StorageUtl_WriteDWord(
3896 buffer,
3897 OFFSET_PS_DIRROOT,
3898 newData->dirRootEntry);
3900 StorageUtl_WriteGUID(
3901 buffer,
3902 OFFSET_PS_GUID,
3903 &newData->clsid);
3905 StorageUtl_WriteDWord(
3906 buffer,
3907 OFFSET_PS_CTIMELOW,
3908 newData->ctime.dwLowDateTime);
3910 StorageUtl_WriteDWord(
3911 buffer,
3912 OFFSET_PS_CTIMEHIGH,
3913 newData->ctime.dwHighDateTime);
3915 StorageUtl_WriteDWord(
3916 buffer,
3917 OFFSET_PS_MTIMELOW,
3918 newData->mtime.dwLowDateTime);
3920 StorageUtl_WriteDWord(
3921 buffer,
3922 OFFSET_PS_MTIMEHIGH,
3923 newData->ctime.dwHighDateTime);
3925 StorageUtl_WriteDWord(
3926 buffer,
3927 OFFSET_PS_STARTBLOCK,
3928 newData->startingBlock);
3930 StorageUtl_WriteDWord(
3931 buffer,
3932 OFFSET_PS_SIZE,
3933 newData->size.u.LowPart);
3936 /******************************************************************************
3937 * Storage32Impl_ReadDirEntry
3939 * This method will read the specified directory entry.
3941 HRESULT StorageImpl_ReadDirEntry(
3942 StorageImpl* This,
3943 DirRef index,
3944 DirEntry* buffer)
3946 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3947 HRESULT readRes;
3949 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3951 if (SUCCEEDED(readRes))
3953 memset(buffer->name, 0, sizeof(buffer->name));
3954 memcpy(
3955 buffer->name,
3956 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3957 DIRENTRY_NAME_BUFFER_LEN );
3958 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3960 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3962 StorageUtl_ReadWord(
3963 currentEntry,
3964 OFFSET_PS_NAMELENGTH,
3965 &buffer->sizeOfNameString);
3967 StorageUtl_ReadDWord(
3968 currentEntry,
3969 OFFSET_PS_LEFTCHILD,
3970 &buffer->leftChild);
3972 StorageUtl_ReadDWord(
3973 currentEntry,
3974 OFFSET_PS_RIGHTCHILD,
3975 &buffer->rightChild);
3977 StorageUtl_ReadDWord(
3978 currentEntry,
3979 OFFSET_PS_DIRROOT,
3980 &buffer->dirRootEntry);
3982 StorageUtl_ReadGUID(
3983 currentEntry,
3984 OFFSET_PS_GUID,
3985 &buffer->clsid);
3987 StorageUtl_ReadDWord(
3988 currentEntry,
3989 OFFSET_PS_CTIMELOW,
3990 &buffer->ctime.dwLowDateTime);
3992 StorageUtl_ReadDWord(
3993 currentEntry,
3994 OFFSET_PS_CTIMEHIGH,
3995 &buffer->ctime.dwHighDateTime);
3997 StorageUtl_ReadDWord(
3998 currentEntry,
3999 OFFSET_PS_MTIMELOW,
4000 &buffer->mtime.dwLowDateTime);
4002 StorageUtl_ReadDWord(
4003 currentEntry,
4004 OFFSET_PS_MTIMEHIGH,
4005 &buffer->mtime.dwHighDateTime);
4007 StorageUtl_ReadDWord(
4008 currentEntry,
4009 OFFSET_PS_STARTBLOCK,
4010 &buffer->startingBlock);
4012 StorageUtl_ReadDWord(
4013 currentEntry,
4014 OFFSET_PS_SIZE,
4015 &buffer->size.u.LowPart);
4017 buffer->size.u.HighPart = 0;
4020 return readRes;
4023 /*********************************************************************
4024 * Write the specified directory entry to the file
4026 HRESULT StorageImpl_WriteDirEntry(
4027 StorageImpl* This,
4028 DirRef index,
4029 const DirEntry* buffer)
4031 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4033 UpdateRawDirEntry(currentEntry, buffer);
4035 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
4038 static HRESULT StorageImpl_ReadBigBlock(
4039 StorageImpl* This,
4040 ULONG blockIndex,
4041 void* buffer,
4042 ULONG* out_read)
4044 ULARGE_INTEGER ulOffset;
4045 DWORD read=0;
4046 HRESULT hr;
4048 ulOffset.u.HighPart = 0;
4049 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4051 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
4053 if (SUCCEEDED(hr) && read < This->bigBlockSize)
4055 /* File ends during this block; fill the rest with 0's. */
4056 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
4059 if (out_read) *out_read = read;
4061 return hr;
4064 static BOOL StorageImpl_ReadDWordFromBigBlock(
4065 StorageImpl* This,
4066 ULONG blockIndex,
4067 ULONG offset,
4068 DWORD* value)
4070 ULARGE_INTEGER ulOffset;
4071 DWORD read;
4072 DWORD tmp;
4074 ulOffset.u.HighPart = 0;
4075 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4076 ulOffset.u.LowPart += offset;
4078 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4079 *value = lendian32toh(tmp);
4080 return (read == sizeof(DWORD));
4083 static BOOL StorageImpl_WriteBigBlock(
4084 StorageImpl* This,
4085 ULONG blockIndex,
4086 const void* buffer)
4088 ULARGE_INTEGER ulOffset;
4089 DWORD wrote;
4091 ulOffset.u.HighPart = 0;
4092 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4094 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4095 return (wrote == This->bigBlockSize);
4098 static BOOL StorageImpl_WriteDWordToBigBlock(
4099 StorageImpl* This,
4100 ULONG blockIndex,
4101 ULONG offset,
4102 DWORD value)
4104 ULARGE_INTEGER ulOffset;
4105 DWORD wrote;
4107 ulOffset.u.HighPart = 0;
4108 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4109 ulOffset.u.LowPart += offset;
4111 value = htole32(value);
4112 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4113 return (wrote == sizeof(DWORD));
4116 /******************************************************************************
4117 * Storage32Impl_SmallBlocksToBigBlocks
4119 * This method will convert a small block chain to a big block chain.
4120 * The small block chain will be destroyed.
4122 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4123 StorageImpl* This,
4124 SmallBlockChainStream** ppsbChain)
4126 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4127 ULARGE_INTEGER size, offset;
4128 ULONG cbRead, cbWritten;
4129 ULARGE_INTEGER cbTotalRead;
4130 DirRef streamEntryRef;
4131 HRESULT resWrite = S_OK;
4132 HRESULT resRead;
4133 DirEntry streamEntry;
4134 BYTE *buffer;
4135 BlockChainStream *bbTempChain = NULL;
4136 BlockChainStream *bigBlockChain = NULL;
4139 * Create a temporary big block chain that doesn't have
4140 * an associated directory entry. This temporary chain will be
4141 * used to copy data from small blocks to big blocks.
4143 bbTempChain = BlockChainStream_Construct(This,
4144 &bbHeadOfChain,
4145 DIRENTRY_NULL);
4146 if(!bbTempChain) return NULL;
4148 * Grow the big block chain.
4150 size = SmallBlockChainStream_GetSize(*ppsbChain);
4151 BlockChainStream_SetSize(bbTempChain, size);
4154 * Copy the contents of the small block chain to the big block chain
4155 * by small block size increments.
4157 offset.u.LowPart = 0;
4158 offset.u.HighPart = 0;
4159 cbTotalRead.QuadPart = 0;
4161 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4164 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4165 offset,
4166 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4167 buffer,
4168 &cbRead);
4169 if (FAILED(resRead))
4170 break;
4172 if (cbRead > 0)
4174 cbTotalRead.QuadPart += cbRead;
4176 resWrite = BlockChainStream_WriteAt(bbTempChain,
4177 offset,
4178 cbRead,
4179 buffer,
4180 &cbWritten);
4182 if (FAILED(resWrite))
4183 break;
4185 offset.u.LowPart += cbRead;
4187 else
4189 resRead = STG_E_READFAULT;
4190 break;
4192 } while (cbTotalRead.QuadPart < size.QuadPart);
4193 HeapFree(GetProcessHeap(),0,buffer);
4195 size.u.HighPart = 0;
4196 size.u.LowPart = 0;
4198 if (FAILED(resRead) || FAILED(resWrite))
4200 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4201 BlockChainStream_SetSize(bbTempChain, size);
4202 BlockChainStream_Destroy(bbTempChain);
4203 return NULL;
4207 * Destroy the small block chain.
4209 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4210 SmallBlockChainStream_SetSize(*ppsbChain, size);
4211 SmallBlockChainStream_Destroy(*ppsbChain);
4212 *ppsbChain = 0;
4215 * Change the directory entry. This chain is now a big block chain
4216 * and it doesn't reside in the small blocks chain anymore.
4218 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4220 streamEntry.startingBlock = bbHeadOfChain;
4222 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4225 * Destroy the temporary entryless big block chain.
4226 * Create a new big block chain associated with this entry.
4228 BlockChainStream_Destroy(bbTempChain);
4229 bigBlockChain = BlockChainStream_Construct(This,
4230 NULL,
4231 streamEntryRef);
4233 return bigBlockChain;
4236 /******************************************************************************
4237 * Storage32Impl_BigBlocksToSmallBlocks
4239 * This method will convert a big block chain to a small block chain.
4240 * The big block chain will be destroyed on success.
4242 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4243 StorageImpl* This,
4244 BlockChainStream** ppbbChain,
4245 ULARGE_INTEGER newSize)
4247 ULARGE_INTEGER size, offset, cbTotalRead;
4248 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4249 DirRef streamEntryRef;
4250 HRESULT resWrite = S_OK, resRead = S_OK;
4251 DirEntry streamEntry;
4252 BYTE* buffer;
4253 SmallBlockChainStream* sbTempChain;
4255 TRACE("%p %p\n", This, ppbbChain);
4257 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4258 DIRENTRY_NULL);
4260 if(!sbTempChain)
4261 return NULL;
4263 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4264 size = BlockChainStream_GetSize(*ppbbChain);
4265 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4267 offset.u.HighPart = 0;
4268 offset.u.LowPart = 0;
4269 cbTotalRead.QuadPart = 0;
4270 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4271 while(cbTotalRead.QuadPart < size.QuadPart)
4273 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4274 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4275 buffer, &cbRead);
4277 if(FAILED(resRead))
4278 break;
4280 if(cbRead > 0)
4282 cbTotalRead.QuadPart += cbRead;
4284 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4285 cbRead, buffer, &cbWritten);
4287 if(FAILED(resWrite))
4288 break;
4290 offset.u.LowPart += cbRead;
4292 else
4294 resRead = STG_E_READFAULT;
4295 break;
4298 HeapFree(GetProcessHeap(), 0, buffer);
4300 size.u.HighPart = 0;
4301 size.u.LowPart = 0;
4303 if(FAILED(resRead) || FAILED(resWrite))
4305 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4306 SmallBlockChainStream_SetSize(sbTempChain, size);
4307 SmallBlockChainStream_Destroy(sbTempChain);
4308 return NULL;
4311 /* destroy the original big block chain */
4312 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4313 BlockChainStream_SetSize(*ppbbChain, size);
4314 BlockChainStream_Destroy(*ppbbChain);
4315 *ppbbChain = NULL;
4317 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4318 streamEntry.startingBlock = sbHeadOfChain;
4319 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4321 SmallBlockChainStream_Destroy(sbTempChain);
4322 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4325 static HRESULT StorageBaseImpl_CopyStream(
4326 StorageBaseImpl *dst, DirRef dst_entry,
4327 StorageBaseImpl *src, DirRef src_entry)
4329 HRESULT hr;
4330 BYTE data[4096];
4331 DirEntry srcdata;
4332 ULARGE_INTEGER bytes_copied;
4333 ULONG bytestocopy, bytesread, byteswritten;
4335 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4337 if (SUCCEEDED(hr))
4339 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4341 bytes_copied.QuadPart = 0;
4342 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4344 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4346 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4347 data, &bytesread);
4348 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4350 if (SUCCEEDED(hr))
4351 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4352 data, &byteswritten);
4353 if (SUCCEEDED(hr))
4355 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4356 bytes_copied.QuadPart += byteswritten;
4361 return hr;
4364 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4366 DirRef result=This->firstFreeEntry;
4368 while (result < This->entries_size && This->entries[result].inuse)
4369 result++;
4371 if (result == This->entries_size)
4373 ULONG new_size = This->entries_size * 2;
4374 TransactedDirEntry *new_entries;
4376 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4377 if (!new_entries) return DIRENTRY_NULL;
4379 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4380 HeapFree(GetProcessHeap(), 0, This->entries);
4382 This->entries = new_entries;
4383 This->entries_size = new_size;
4386 This->entries[result].inuse = TRUE;
4388 This->firstFreeEntry = result+1;
4390 return result;
4393 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4394 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4396 DirRef stubEntryRef;
4397 TransactedDirEntry *entry;
4399 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4401 if (stubEntryRef != DIRENTRY_NULL)
4403 entry = &This->entries[stubEntryRef];
4405 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4407 entry->read = FALSE;
4410 return stubEntryRef;
4413 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4414 TransactedSnapshotImpl *This, DirRef entry)
4416 HRESULT hr=S_OK;
4417 DirEntry data;
4419 if (!This->entries[entry].read)
4421 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4422 This->entries[entry].transactedParentEntry,
4423 &data);
4425 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4427 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4429 if (data.leftChild == DIRENTRY_NULL)
4430 hr = E_OUTOFMEMORY;
4433 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4435 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4437 if (data.rightChild == DIRENTRY_NULL)
4438 hr = E_OUTOFMEMORY;
4441 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4443 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4445 if (data.dirRootEntry == DIRENTRY_NULL)
4446 hr = E_OUTOFMEMORY;
4449 if (SUCCEEDED(hr))
4451 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4452 This->entries[entry].read = TRUE;
4456 return hr;
4459 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4460 TransactedSnapshotImpl *This, DirRef entry)
4462 HRESULT hr = S_OK;
4464 if (!This->entries[entry].stream_dirty)
4466 DirEntry new_entrydata;
4468 memset(&new_entrydata, 0, sizeof(DirEntry));
4469 new_entrydata.name[0] = 'S';
4470 new_entrydata.sizeOfNameString = 1;
4471 new_entrydata.stgType = STGTY_STREAM;
4472 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4473 new_entrydata.leftChild = DIRENTRY_NULL;
4474 new_entrydata.rightChild = DIRENTRY_NULL;
4475 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4477 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4478 &This->entries[entry].stream_entry);
4480 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4482 hr = StorageBaseImpl_CopyStream(
4483 This->scratch, This->entries[entry].stream_entry,
4484 This->transactedParent, This->entries[entry].transactedParentEntry);
4486 if (FAILED(hr))
4487 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4490 if (SUCCEEDED(hr))
4491 This->entries[entry].stream_dirty = TRUE;
4493 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4495 /* Since this entry is modified, and we aren't using its stream data, we
4496 * no longer care about the original entry. */
4497 DirRef delete_ref;
4498 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4500 if (delete_ref != DIRENTRY_NULL)
4501 This->entries[delete_ref].deleted = TRUE;
4503 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4507 return hr;
4510 /* Find the first entry in a depth-first traversal. */
4511 static DirRef TransactedSnapshotImpl_FindFirstChild(
4512 TransactedSnapshotImpl* This, DirRef parent)
4514 DirRef cursor, prev;
4515 TransactedDirEntry *entry;
4517 cursor = parent;
4518 entry = &This->entries[cursor];
4519 while (entry->read)
4521 if (entry->data.leftChild != DIRENTRY_NULL)
4523 prev = cursor;
4524 cursor = entry->data.leftChild;
4525 entry = &This->entries[cursor];
4526 entry->parent = prev;
4528 else if (entry->data.rightChild != DIRENTRY_NULL)
4530 prev = cursor;
4531 cursor = entry->data.rightChild;
4532 entry = &This->entries[cursor];
4533 entry->parent = prev;
4535 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4537 prev = cursor;
4538 cursor = entry->data.dirRootEntry;
4539 entry = &This->entries[cursor];
4540 entry->parent = prev;
4542 else
4543 break;
4546 return cursor;
4549 /* Find the next entry in a depth-first traversal. */
4550 static DirRef TransactedSnapshotImpl_FindNextChild(
4551 TransactedSnapshotImpl* This, DirRef current)
4553 DirRef parent;
4554 TransactedDirEntry *parent_entry;
4556 parent = This->entries[current].parent;
4557 parent_entry = &This->entries[parent];
4559 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4561 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4563 This->entries[parent_entry->data.rightChild].parent = parent;
4564 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4567 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4569 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4570 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4574 return parent;
4577 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4578 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4579 TransactedSnapshotImpl* This, DirRef entry)
4581 return entry != DIRENTRY_NULL &&
4582 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4585 /* Destroy the entries created by CopyTree. */
4586 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4587 TransactedSnapshotImpl* This, DirRef stop)
4589 DirRef cursor;
4590 TransactedDirEntry *entry;
4591 ULARGE_INTEGER zero;
4593 zero.QuadPart = 0;
4595 if (!This->entries[This->base.storageDirEntry].read)
4596 return;
4598 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4600 if (cursor == DIRENTRY_NULL)
4601 return;
4603 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4605 while (cursor != DIRENTRY_NULL && cursor != stop)
4607 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4609 entry = &This->entries[cursor];
4611 if (entry->stream_dirty)
4612 StorageBaseImpl_StreamSetSize(This->transactedParent,
4613 entry->newTransactedParentEntry, zero);
4615 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4616 entry->newTransactedParentEntry);
4618 entry->newTransactedParentEntry = entry->transactedParentEntry;
4621 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4625 /* Make a copy of our edited tree that we can use in the parent. */
4626 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4628 DirRef cursor;
4629 TransactedDirEntry *entry;
4630 HRESULT hr = S_OK;
4632 cursor = This->base.storageDirEntry;
4633 entry = &This->entries[cursor];
4634 entry->parent = DIRENTRY_NULL;
4635 entry->newTransactedParentEntry = entry->transactedParentEntry;
4637 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4638 return S_OK;
4640 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4642 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4643 entry = &This->entries[cursor];
4645 while (cursor != DIRENTRY_NULL)
4647 /* Make a copy of this entry in the transacted parent. */
4648 if (!entry->read ||
4649 (!entry->dirty && !entry->stream_dirty &&
4650 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4651 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4652 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4653 entry->newTransactedParentEntry = entry->transactedParentEntry;
4654 else
4656 DirEntry newData;
4658 memcpy(&newData, &entry->data, sizeof(DirEntry));
4660 newData.size.QuadPart = 0;
4661 newData.startingBlock = BLOCK_END_OF_CHAIN;
4663 if (newData.leftChild != DIRENTRY_NULL)
4664 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4666 if (newData.rightChild != DIRENTRY_NULL)
4667 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4669 if (newData.dirRootEntry != DIRENTRY_NULL)
4670 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4672 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4673 &entry->newTransactedParentEntry);
4674 if (FAILED(hr))
4676 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4677 return hr;
4680 if (entry->stream_dirty)
4682 hr = StorageBaseImpl_CopyStream(
4683 This->transactedParent, entry->newTransactedParentEntry,
4684 This->scratch, entry->stream_entry);
4686 else if (entry->data.size.QuadPart)
4688 hr = StorageBaseImpl_StreamLink(
4689 This->transactedParent, entry->newTransactedParentEntry,
4690 entry->transactedParentEntry);
4693 if (FAILED(hr))
4695 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4696 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4697 return hr;
4701 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4702 entry = &This->entries[cursor];
4705 return hr;
4708 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4709 IStorage* iface,
4710 DWORD grfCommitFlags) /* [in] */
4712 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4713 TransactedDirEntry *root_entry;
4714 DirRef i, dir_root_ref;
4715 DirEntry data;
4716 ULARGE_INTEGER zero;
4717 HRESULT hr;
4719 zero.QuadPart = 0;
4721 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4723 /* Cannot commit a read-only transacted storage */
4724 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4725 return STG_E_ACCESSDENIED;
4727 /* To prevent data loss, we create the new structure in the file before we
4728 * delete the old one, so that in case of errors the old data is intact. We
4729 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4730 * needed in the rare situation where we have just enough free disk space to
4731 * overwrite the existing data. */
4733 root_entry = &This->entries[This->base.storageDirEntry];
4735 if (!root_entry->read)
4736 return S_OK;
4738 hr = TransactedSnapshotImpl_CopyTree(This);
4739 if (FAILED(hr)) return hr;
4741 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4742 dir_root_ref = DIRENTRY_NULL;
4743 else
4744 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4746 hr = StorageBaseImpl_Flush(This->transactedParent);
4748 /* Update the storage to use the new data in one step. */
4749 if (SUCCEEDED(hr))
4750 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4751 root_entry->transactedParentEntry, &data);
4753 if (SUCCEEDED(hr))
4755 data.dirRootEntry = dir_root_ref;
4756 data.clsid = root_entry->data.clsid;
4757 data.ctime = root_entry->data.ctime;
4758 data.mtime = root_entry->data.mtime;
4760 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4761 root_entry->transactedParentEntry, &data);
4764 /* Try to flush after updating the root storage, but if the flush fails, keep
4765 * going, on the theory that it'll either succeed later or the subsequent
4766 * writes will fail. */
4767 StorageBaseImpl_Flush(This->transactedParent);
4769 if (SUCCEEDED(hr))
4771 /* Destroy the old now-orphaned data. */
4772 for (i=0; i<This->entries_size; i++)
4774 TransactedDirEntry *entry = &This->entries[i];
4775 if (entry->inuse)
4777 if (entry->deleted)
4779 StorageBaseImpl_StreamSetSize(This->transactedParent,
4780 entry->transactedParentEntry, zero);
4781 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4782 entry->transactedParentEntry);
4783 memset(entry, 0, sizeof(TransactedDirEntry));
4784 This->firstFreeEntry = min(i, This->firstFreeEntry);
4786 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4788 if (entry->transactedParentEntry != DIRENTRY_NULL)
4789 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4790 entry->transactedParentEntry);
4791 if (entry->stream_dirty)
4793 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4794 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4795 entry->stream_dirty = FALSE;
4797 entry->dirty = FALSE;
4798 entry->transactedParentEntry = entry->newTransactedParentEntry;
4803 else
4805 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4808 if (SUCCEEDED(hr))
4809 hr = StorageBaseImpl_Flush(This->transactedParent);
4811 return hr;
4814 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4815 IStorage* iface)
4817 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4818 ULARGE_INTEGER zero;
4819 ULONG i;
4821 TRACE("(%p)\n", iface);
4823 /* Destroy the open objects. */
4824 StorageBaseImpl_DeleteAll(&This->base);
4826 /* Clear out the scratch file. */
4827 zero.QuadPart = 0;
4828 for (i=0; i<This->entries_size; i++)
4830 if (This->entries[i].stream_dirty)
4832 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4833 zero);
4835 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4839 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4841 This->firstFreeEntry = 0;
4842 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4844 return S_OK;
4847 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4849 if (!This->reverted)
4851 TRACE("Storage invalidated (stg=%p)\n", This);
4853 This->reverted = TRUE;
4855 StorageBaseImpl_DeleteAll(This);
4859 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4861 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4863 IStorage_Revert(&This->base.IStorage_iface);
4864 IStorage_Release(&This->transactedParent->IStorage_iface);
4865 IStorage_Release(&This->scratch->IStorage_iface);
4866 HeapFree(GetProcessHeap(), 0, This->entries);
4867 HeapFree(GetProcessHeap(), 0, This);
4870 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4872 /* We only need to flush when committing. */
4873 return S_OK;
4876 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4878 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4880 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4883 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4884 const DirEntry *newData, DirRef *index)
4886 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4887 DirRef new_ref;
4888 TransactedDirEntry *new_entry;
4890 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4891 if (new_ref == DIRENTRY_NULL)
4892 return E_OUTOFMEMORY;
4894 new_entry = &This->entries[new_ref];
4896 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4897 new_entry->read = TRUE;
4898 new_entry->dirty = TRUE;
4899 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4901 *index = new_ref;
4903 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4905 return S_OK;
4908 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4909 DirRef index, const DirEntry *data)
4911 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4912 HRESULT hr;
4914 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4916 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4917 if (FAILED(hr)) return hr;
4919 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4921 if (index != This->base.storageDirEntry)
4923 This->entries[index].dirty = TRUE;
4925 if (data->size.QuadPart == 0 &&
4926 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4928 /* Since this entry is modified, and we aren't using its stream data, we
4929 * no longer care about the original entry. */
4930 DirRef delete_ref;
4931 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4933 if (delete_ref != DIRENTRY_NULL)
4934 This->entries[delete_ref].deleted = TRUE;
4936 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4940 return S_OK;
4943 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4944 DirRef index, DirEntry *data)
4946 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4947 HRESULT hr;
4949 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4950 if (FAILED(hr)) return hr;
4952 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4954 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4956 return S_OK;
4959 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4960 DirRef index)
4962 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4964 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4965 This->entries[index].data.size.QuadPart != 0)
4967 /* If we deleted this entry while it has stream data. We must have left the
4968 * data because some other entry is using it, and we need to leave the
4969 * original entry alone. */
4970 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4971 This->firstFreeEntry = min(index, This->firstFreeEntry);
4973 else
4975 This->entries[index].deleted = TRUE;
4978 return S_OK;
4981 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4982 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4984 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4986 if (This->entries[index].stream_dirty)
4988 return StorageBaseImpl_StreamReadAt(This->scratch,
4989 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4991 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4993 /* This stream doesn't live in the parent, and we haven't allocated storage
4994 * for it yet */
4995 *bytesRead = 0;
4996 return S_OK;
4998 else
5000 return StorageBaseImpl_StreamReadAt(This->transactedParent,
5001 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
5005 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
5006 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5008 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5009 HRESULT hr;
5011 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5012 if (FAILED(hr)) return hr;
5014 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5015 if (FAILED(hr)) return hr;
5017 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
5018 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
5020 if (SUCCEEDED(hr) && size != 0)
5021 This->entries[index].data.size.QuadPart = max(
5022 This->entries[index].data.size.QuadPart,
5023 offset.QuadPart + size);
5025 return hr;
5028 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
5029 DirRef index, ULARGE_INTEGER newsize)
5031 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5032 HRESULT hr;
5034 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5035 if (FAILED(hr)) return hr;
5037 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
5038 return S_OK;
5040 if (newsize.QuadPart == 0)
5042 /* Destroy any parent references or entries in the scratch file. */
5043 if (This->entries[index].stream_dirty)
5045 ULARGE_INTEGER zero;
5046 zero.QuadPart = 0;
5047 StorageBaseImpl_StreamSetSize(This->scratch,
5048 This->entries[index].stream_entry, zero);
5049 StorageBaseImpl_DestroyDirEntry(This->scratch,
5050 This->entries[index].stream_entry);
5051 This->entries[index].stream_dirty = FALSE;
5053 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5055 DirRef delete_ref;
5056 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5058 if (delete_ref != DIRENTRY_NULL)
5059 This->entries[delete_ref].deleted = TRUE;
5061 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5064 else
5066 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5067 if (FAILED(hr)) return hr;
5069 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5070 This->entries[index].stream_entry, newsize);
5073 if (SUCCEEDED(hr))
5074 This->entries[index].data.size = newsize;
5076 return hr;
5079 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5080 DirRef dst, DirRef src)
5082 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5083 HRESULT hr;
5084 TransactedDirEntry *dst_entry, *src_entry;
5086 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5087 if (FAILED(hr)) return hr;
5089 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5090 if (FAILED(hr)) return hr;
5092 dst_entry = &This->entries[dst];
5093 src_entry = &This->entries[src];
5095 dst_entry->stream_dirty = src_entry->stream_dirty;
5096 dst_entry->stream_entry = src_entry->stream_entry;
5097 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5098 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5099 dst_entry->data.size = src_entry->data.size;
5101 return S_OK;
5104 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5106 StorageBaseImpl_QueryInterface,
5107 StorageBaseImpl_AddRef,
5108 StorageBaseImpl_Release,
5109 StorageBaseImpl_CreateStream,
5110 StorageBaseImpl_OpenStream,
5111 StorageBaseImpl_CreateStorage,
5112 StorageBaseImpl_OpenStorage,
5113 StorageBaseImpl_CopyTo,
5114 StorageBaseImpl_MoveElementTo,
5115 TransactedSnapshotImpl_Commit,
5116 TransactedSnapshotImpl_Revert,
5117 StorageBaseImpl_EnumElements,
5118 StorageBaseImpl_DestroyElement,
5119 StorageBaseImpl_RenameElement,
5120 StorageBaseImpl_SetElementTimes,
5121 StorageBaseImpl_SetClass,
5122 StorageBaseImpl_SetStateBits,
5123 StorageBaseImpl_Stat
5126 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5128 TransactedSnapshotImpl_Destroy,
5129 TransactedSnapshotImpl_Invalidate,
5130 TransactedSnapshotImpl_Flush,
5131 TransactedSnapshotImpl_GetFilename,
5132 TransactedSnapshotImpl_CreateDirEntry,
5133 TransactedSnapshotImpl_WriteDirEntry,
5134 TransactedSnapshotImpl_ReadDirEntry,
5135 TransactedSnapshotImpl_DestroyDirEntry,
5136 TransactedSnapshotImpl_StreamReadAt,
5137 TransactedSnapshotImpl_StreamWriteAt,
5138 TransactedSnapshotImpl_StreamSetSize,
5139 TransactedSnapshotImpl_StreamLink
5142 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5143 TransactedSnapshotImpl** result)
5145 HRESULT hr;
5147 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5148 if (*result)
5150 IStorage *scratch;
5152 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5154 /* This is OK because the property set storage functions use the IStorage functions. */
5155 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5156 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5158 list_init(&(*result)->base.strmHead);
5160 list_init(&(*result)->base.storageHead);
5162 (*result)->base.ref = 1;
5164 (*result)->base.openFlags = parentStorage->openFlags;
5166 /* Create a new temporary storage to act as the scratch file. */
5167 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5168 0, &scratch);
5169 (*result)->scratch = impl_from_IStorage(scratch);
5171 if (SUCCEEDED(hr))
5173 ULONG num_entries = 20;
5175 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5176 (*result)->entries_size = num_entries;
5177 (*result)->firstFreeEntry = 0;
5179 if ((*result)->entries)
5181 /* parentStorage already has 1 reference, which we take over here. */
5182 (*result)->transactedParent = parentStorage;
5184 parentStorage->transactedChild = &(*result)->base;
5186 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5188 else
5190 IStorage_Release(scratch);
5192 hr = E_OUTOFMEMORY;
5196 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
5198 return hr;
5200 else
5201 return E_OUTOFMEMORY;
5204 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5205 StorageBaseImpl** result)
5207 static int fixme=0;
5209 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5211 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5214 return TransactedSnapshotImpl_Construct(parentStorage,
5215 (TransactedSnapshotImpl**)result);
5218 static HRESULT Storage_Construct(
5219 HANDLE hFile,
5220 LPCOLESTR pwcsName,
5221 ILockBytes* pLkbyt,
5222 DWORD openFlags,
5223 BOOL fileBased,
5224 BOOL create,
5225 ULONG sector_size,
5226 StorageBaseImpl** result)
5228 StorageImpl *newStorage;
5229 StorageBaseImpl *newTransactedStorage;
5230 HRESULT hr;
5232 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5233 if (FAILED(hr)) goto end;
5235 if (openFlags & STGM_TRANSACTED)
5237 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5238 if (FAILED(hr))
5239 IStorage_Release(&newStorage->base.IStorage_iface);
5240 else
5241 *result = newTransactedStorage;
5243 else
5244 *result = &newStorage->base;
5246 end:
5247 return hr;
5250 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5252 StorageInternalImpl* This = (StorageInternalImpl*) base;
5254 if (!This->base.reverted)
5256 TRACE("Storage invalidated (stg=%p)\n", This);
5258 This->base.reverted = TRUE;
5260 This->parentStorage = NULL;
5262 StorageBaseImpl_DeleteAll(&This->base);
5264 list_remove(&This->ParentListEntry);
5268 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5270 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5272 StorageInternalImpl_Invalidate(&This->base);
5274 HeapFree(GetProcessHeap(), 0, This);
5277 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5279 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5281 return StorageBaseImpl_Flush(This->parentStorage);
5284 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5286 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5288 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5291 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5292 const DirEntry *newData, DirRef *index)
5294 StorageInternalImpl* This = (StorageInternalImpl*) base;
5296 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5297 newData, index);
5300 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5301 DirRef index, const DirEntry *data)
5303 StorageInternalImpl* This = (StorageInternalImpl*) base;
5305 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5306 index, data);
5309 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5310 DirRef index, DirEntry *data)
5312 StorageInternalImpl* This = (StorageInternalImpl*) base;
5314 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5315 index, data);
5318 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5319 DirRef index)
5321 StorageInternalImpl* This = (StorageInternalImpl*) base;
5323 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5324 index);
5327 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5328 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5330 StorageInternalImpl* This = (StorageInternalImpl*) base;
5332 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5333 index, offset, size, buffer, bytesRead);
5336 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5337 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5339 StorageInternalImpl* This = (StorageInternalImpl*) base;
5341 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5342 index, offset, size, buffer, bytesWritten);
5345 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5346 DirRef index, ULARGE_INTEGER newsize)
5348 StorageInternalImpl* This = (StorageInternalImpl*) base;
5350 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5351 index, newsize);
5354 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5355 DirRef dst, DirRef src)
5357 StorageInternalImpl* This = (StorageInternalImpl*) base;
5359 return StorageBaseImpl_StreamLink(This->parentStorage,
5360 dst, src);
5363 /******************************************************************************
5365 ** Storage32InternalImpl_Commit
5368 static HRESULT WINAPI StorageInternalImpl_Commit(
5369 IStorage* iface,
5370 DWORD grfCommitFlags) /* [in] */
5372 StorageBaseImpl* This = impl_from_IStorage(iface);
5373 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5374 return StorageBaseImpl_Flush(This);
5377 /******************************************************************************
5379 ** Storage32InternalImpl_Revert
5382 static HRESULT WINAPI StorageInternalImpl_Revert(
5383 IStorage* iface)
5385 FIXME("(%p): stub\n", iface);
5386 return S_OK;
5389 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5391 IStorage_Release(&This->parentStorage->IStorage_iface);
5392 HeapFree(GetProcessHeap(), 0, This);
5395 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5396 IEnumSTATSTG* iface,
5397 REFIID riid,
5398 void** ppvObject)
5400 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5402 if (ppvObject==0)
5403 return E_INVALIDARG;
5405 *ppvObject = 0;
5407 if (IsEqualGUID(&IID_IUnknown, riid) ||
5408 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5410 *ppvObject = This;
5411 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5412 return S_OK;
5415 return E_NOINTERFACE;
5418 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5419 IEnumSTATSTG* iface)
5421 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5422 return InterlockedIncrement(&This->ref);
5425 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5426 IEnumSTATSTG* iface)
5428 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5430 ULONG newRef;
5432 newRef = InterlockedDecrement(&This->ref);
5434 if (newRef==0)
5436 IEnumSTATSTGImpl_Destroy(This);
5439 return newRef;
5442 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5443 IEnumSTATSTGImpl* This,
5444 DirRef *ref)
5446 DirRef result = DIRENTRY_NULL;
5447 DirRef searchNode;
5448 DirEntry entry;
5449 HRESULT hr;
5450 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5452 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5453 This->parentStorage->storageDirEntry, &entry);
5454 searchNode = entry.dirRootEntry;
5456 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5458 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5460 if (SUCCEEDED(hr))
5462 LONG diff = entryNameCmp( entry.name, This->name);
5464 if (diff <= 0)
5466 searchNode = entry.rightChild;
5468 else
5470 result = searchNode;
5471 memcpy(result_name, entry.name, sizeof(result_name));
5472 searchNode = entry.leftChild;
5477 if (SUCCEEDED(hr))
5479 *ref = result;
5480 if (result != DIRENTRY_NULL)
5481 memcpy(This->name, result_name, sizeof(result_name));
5484 return hr;
5487 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5488 IEnumSTATSTG* iface,
5489 ULONG celt,
5490 STATSTG* rgelt,
5491 ULONG* pceltFetched)
5493 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5495 DirEntry currentEntry;
5496 STATSTG* currentReturnStruct = rgelt;
5497 ULONG objectFetched = 0;
5498 DirRef currentSearchNode;
5499 HRESULT hr=S_OK;
5501 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5502 return E_INVALIDARG;
5504 if (This->parentStorage->reverted)
5505 return STG_E_REVERTED;
5508 * To avoid the special case, get another pointer to a ULONG value if
5509 * the caller didn't supply one.
5511 if (pceltFetched==0)
5512 pceltFetched = &objectFetched;
5515 * Start the iteration, we will iterate until we hit the end of the
5516 * linked list or until we hit the number of items to iterate through
5518 *pceltFetched = 0;
5520 while ( *pceltFetched < celt )
5522 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5524 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5525 break;
5528 * Read the entry from the storage.
5530 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5531 currentSearchNode,
5532 &currentEntry);
5535 * Copy the information to the return buffer.
5537 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5538 currentReturnStruct,
5539 &currentEntry,
5540 STATFLAG_DEFAULT);
5543 * Step to the next item in the iteration
5545 (*pceltFetched)++;
5546 currentReturnStruct++;
5549 if (SUCCEEDED(hr) && *pceltFetched != celt)
5550 hr = S_FALSE;
5552 return hr;
5556 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5557 IEnumSTATSTG* iface,
5558 ULONG celt)
5560 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5562 ULONG objectFetched = 0;
5563 DirRef currentSearchNode;
5564 HRESULT hr=S_OK;
5566 if (This->parentStorage->reverted)
5567 return STG_E_REVERTED;
5569 while ( (objectFetched < celt) )
5571 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5573 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5574 break;
5576 objectFetched++;
5579 if (SUCCEEDED(hr) && objectFetched != celt)
5580 return S_FALSE;
5582 return hr;
5585 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5586 IEnumSTATSTG* iface)
5588 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5590 if (This->parentStorage->reverted)
5591 return STG_E_REVERTED;
5593 This->name[0] = 0;
5595 return S_OK;
5598 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5599 IEnumSTATSTG* iface,
5600 IEnumSTATSTG** ppenum)
5602 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5603 IEnumSTATSTGImpl* newClone;
5605 if (This->parentStorage->reverted)
5606 return STG_E_REVERTED;
5608 if (ppenum==0)
5609 return E_INVALIDARG;
5611 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5612 This->storageDirEntry);
5613 if (!newClone)
5615 *ppenum = NULL;
5616 return E_OUTOFMEMORY;
5620 * The new clone enumeration must point to the same current node as
5621 * the old one.
5623 memcpy(newClone->name, This->name, sizeof(newClone->name));
5625 *ppenum = &newClone->IEnumSTATSTG_iface;
5627 return S_OK;
5631 * Virtual function table for the IEnumSTATSTGImpl class.
5633 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5635 IEnumSTATSTGImpl_QueryInterface,
5636 IEnumSTATSTGImpl_AddRef,
5637 IEnumSTATSTGImpl_Release,
5638 IEnumSTATSTGImpl_Next,
5639 IEnumSTATSTGImpl_Skip,
5640 IEnumSTATSTGImpl_Reset,
5641 IEnumSTATSTGImpl_Clone
5644 /******************************************************************************
5645 ** IEnumSTATSTGImpl implementation
5648 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5649 StorageBaseImpl* parentStorage,
5650 DirRef storageDirEntry)
5652 IEnumSTATSTGImpl* newEnumeration;
5654 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5656 if (newEnumeration)
5658 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5659 newEnumeration->ref = 1;
5660 newEnumeration->name[0] = 0;
5663 * We want to nail-down the reference to the storage in case the
5664 * enumeration out-lives the storage in the client application.
5666 newEnumeration->parentStorage = parentStorage;
5667 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
5669 newEnumeration->storageDirEntry = storageDirEntry;
5672 return newEnumeration;
5676 * Virtual function table for the Storage32InternalImpl class.
5678 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5680 StorageBaseImpl_QueryInterface,
5681 StorageBaseImpl_AddRef,
5682 StorageBaseImpl_Release,
5683 StorageBaseImpl_CreateStream,
5684 StorageBaseImpl_OpenStream,
5685 StorageBaseImpl_CreateStorage,
5686 StorageBaseImpl_OpenStorage,
5687 StorageBaseImpl_CopyTo,
5688 StorageBaseImpl_MoveElementTo,
5689 StorageInternalImpl_Commit,
5690 StorageInternalImpl_Revert,
5691 StorageBaseImpl_EnumElements,
5692 StorageBaseImpl_DestroyElement,
5693 StorageBaseImpl_RenameElement,
5694 StorageBaseImpl_SetElementTimes,
5695 StorageBaseImpl_SetClass,
5696 StorageBaseImpl_SetStateBits,
5697 StorageBaseImpl_Stat
5700 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5702 StorageInternalImpl_Destroy,
5703 StorageInternalImpl_Invalidate,
5704 StorageInternalImpl_Flush,
5705 StorageInternalImpl_GetFilename,
5706 StorageInternalImpl_CreateDirEntry,
5707 StorageInternalImpl_WriteDirEntry,
5708 StorageInternalImpl_ReadDirEntry,
5709 StorageInternalImpl_DestroyDirEntry,
5710 StorageInternalImpl_StreamReadAt,
5711 StorageInternalImpl_StreamWriteAt,
5712 StorageInternalImpl_StreamSetSize,
5713 StorageInternalImpl_StreamLink
5716 /******************************************************************************
5717 ** Storage32InternalImpl implementation
5720 static StorageInternalImpl* StorageInternalImpl_Construct(
5721 StorageBaseImpl* parentStorage,
5722 DWORD openFlags,
5723 DirRef storageDirEntry)
5725 StorageInternalImpl* newStorage;
5727 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5729 if (newStorage!=0)
5731 list_init(&newStorage->base.strmHead);
5733 list_init(&newStorage->base.storageHead);
5736 * Initialize the virtual function table.
5738 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
5739 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5740 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5741 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5743 newStorage->base.reverted = FALSE;
5745 newStorage->base.ref = 1;
5747 newStorage->parentStorage = parentStorage;
5750 * Keep a reference to the directory entry of this storage
5752 newStorage->base.storageDirEntry = storageDirEntry;
5754 newStorage->base.create = FALSE;
5756 return newStorage;
5759 return 0;
5762 /******************************************************************************
5763 ** StorageUtl implementation
5766 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5768 WORD tmp;
5770 memcpy(&tmp, buffer+offset, sizeof(WORD));
5771 *value = lendian16toh(tmp);
5774 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5776 value = htole16(value);
5777 memcpy(buffer+offset, &value, sizeof(WORD));
5780 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5782 DWORD tmp;
5784 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5785 *value = lendian32toh(tmp);
5788 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5790 value = htole32(value);
5791 memcpy(buffer+offset, &value, sizeof(DWORD));
5794 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5795 ULARGE_INTEGER* value)
5797 #ifdef WORDS_BIGENDIAN
5798 ULARGE_INTEGER tmp;
5800 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5801 value->u.LowPart = htole32(tmp.u.HighPart);
5802 value->u.HighPart = htole32(tmp.u.LowPart);
5803 #else
5804 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5805 #endif
5808 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5809 const ULARGE_INTEGER *value)
5811 #ifdef WORDS_BIGENDIAN
5812 ULARGE_INTEGER tmp;
5814 tmp.u.LowPart = htole32(value->u.HighPart);
5815 tmp.u.HighPart = htole32(value->u.LowPart);
5816 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5817 #else
5818 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5819 #endif
5822 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5824 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5825 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5826 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5828 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5831 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5833 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5834 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5835 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5837 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5840 void StorageUtl_CopyDirEntryToSTATSTG(
5841 StorageBaseImpl* storage,
5842 STATSTG* destination,
5843 const DirEntry* source,
5844 int statFlags)
5847 * The copy of the string occurs only when the flag is not set
5849 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5851 /* Use the filename for the root storage. */
5852 destination->pwcsName = 0;
5853 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5855 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5856 (source->name[0] == 0) )
5858 destination->pwcsName = 0;
5860 else
5862 destination->pwcsName =
5863 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5865 strcpyW(destination->pwcsName, source->name);
5868 switch (source->stgType)
5870 case STGTY_STORAGE:
5871 case STGTY_ROOT:
5872 destination->type = STGTY_STORAGE;
5873 break;
5874 case STGTY_STREAM:
5875 destination->type = STGTY_STREAM;
5876 break;
5877 default:
5878 destination->type = STGTY_STREAM;
5879 break;
5882 destination->cbSize = source->size;
5884 currentReturnStruct->mtime = {0}; TODO
5885 currentReturnStruct->ctime = {0};
5886 currentReturnStruct->atime = {0};
5888 destination->grfMode = 0;
5889 destination->grfLocksSupported = 0;
5890 destination->clsid = source->clsid;
5891 destination->grfStateBits = 0;
5892 destination->reserved = 0;
5895 /******************************************************************************
5896 ** BlockChainStream implementation
5899 /* Read and save the index of all blocks in this stream. */
5900 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5902 ULONG next_sector, next_offset;
5903 HRESULT hr;
5904 struct BlockChainRun *last_run;
5906 if (This->indexCacheLen == 0)
5908 last_run = NULL;
5909 next_offset = 0;
5910 next_sector = BlockChainStream_GetHeadOfChain(This);
5912 else
5914 last_run = &This->indexCache[This->indexCacheLen-1];
5915 next_offset = last_run->lastOffset+1;
5916 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5917 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5918 &next_sector);
5919 if (FAILED(hr)) return hr;
5922 while (next_sector != BLOCK_END_OF_CHAIN)
5924 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5926 /* Add the current block to the cache. */
5927 if (This->indexCacheSize == 0)
5929 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5930 if (!This->indexCache) return E_OUTOFMEMORY;
5931 This->indexCacheSize = 16;
5933 else if (This->indexCacheSize == This->indexCacheLen)
5935 struct BlockChainRun *new_cache;
5936 ULONG new_size;
5938 new_size = This->indexCacheSize * 2;
5939 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5940 if (!new_cache) return E_OUTOFMEMORY;
5941 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5943 HeapFree(GetProcessHeap(), 0, This->indexCache);
5944 This->indexCache = new_cache;
5945 This->indexCacheSize = new_size;
5948 This->indexCacheLen++;
5949 last_run = &This->indexCache[This->indexCacheLen-1];
5950 last_run->firstSector = next_sector;
5951 last_run->firstOffset = next_offset;
5954 last_run->lastOffset = next_offset;
5956 /* Find the next block. */
5957 next_offset++;
5958 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5959 if (FAILED(hr)) return hr;
5962 if (This->indexCacheLen)
5964 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5965 This->numBlocks = last_run->lastOffset+1;
5967 else
5969 This->tailIndex = BLOCK_END_OF_CHAIN;
5970 This->numBlocks = 0;
5973 return S_OK;
5976 /* Locate the nth block in this stream. */
5977 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5979 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5980 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5982 if (offset >= This->numBlocks)
5983 return BLOCK_END_OF_CHAIN;
5985 while (min_run < max_run)
5987 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5988 if (offset < This->indexCache[run_to_check].firstOffset)
5990 max_offset = This->indexCache[run_to_check].firstOffset-1;
5991 max_run = run_to_check-1;
5993 else if (offset > This->indexCache[run_to_check].lastOffset)
5995 min_offset = This->indexCache[run_to_check].lastOffset+1;
5996 min_run = run_to_check+1;
5998 else
5999 /* Block is in this run. */
6000 min_run = max_run = run_to_check;
6003 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
6006 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
6007 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
6009 BlockChainBlock *result=NULL;
6010 int i;
6012 for (i=0; i<2; i++)
6013 if (This->cachedBlocks[i].index == index)
6015 *sector = This->cachedBlocks[i].sector;
6016 *block = &This->cachedBlocks[i];
6017 return S_OK;
6020 *sector = BlockChainStream_GetSectorOfOffset(This, index);
6021 if (*sector == BLOCK_END_OF_CHAIN)
6022 return STG_E_DOCFILECORRUPT;
6024 if (create)
6026 if (This->cachedBlocks[0].index == 0xffffffff)
6027 result = &This->cachedBlocks[0];
6028 else if (This->cachedBlocks[1].index == 0xffffffff)
6029 result = &This->cachedBlocks[1];
6030 else
6032 result = &This->cachedBlocks[This->blockToEvict++];
6033 if (This->blockToEvict == 2)
6034 This->blockToEvict = 0;
6037 if (result->dirty)
6039 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
6040 return STG_E_WRITEFAULT;
6041 result->dirty = FALSE;
6044 result->read = FALSE;
6045 result->index = index;
6046 result->sector = *sector;
6049 *block = result;
6050 return S_OK;
6053 BlockChainStream* BlockChainStream_Construct(
6054 StorageImpl* parentStorage,
6055 ULONG* headOfStreamPlaceHolder,
6056 DirRef dirEntry)
6058 BlockChainStream* newStream;
6060 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6062 newStream->parentStorage = parentStorage;
6063 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6064 newStream->ownerDirEntry = dirEntry;
6065 newStream->indexCache = NULL;
6066 newStream->indexCacheLen = 0;
6067 newStream->indexCacheSize = 0;
6068 newStream->cachedBlocks[0].index = 0xffffffff;
6069 newStream->cachedBlocks[0].dirty = FALSE;
6070 newStream->cachedBlocks[1].index = 0xffffffff;
6071 newStream->cachedBlocks[1].dirty = FALSE;
6072 newStream->blockToEvict = 0;
6074 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6076 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6077 HeapFree(GetProcessHeap(), 0, newStream);
6078 return NULL;
6081 return newStream;
6084 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6086 int i;
6087 if (!This) return S_OK;
6088 for (i=0; i<2; i++)
6090 if (This->cachedBlocks[i].dirty)
6092 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6093 This->cachedBlocks[i].dirty = FALSE;
6094 else
6095 return STG_E_WRITEFAULT;
6098 return S_OK;
6101 void BlockChainStream_Destroy(BlockChainStream* This)
6103 if (This)
6105 BlockChainStream_Flush(This);
6106 HeapFree(GetProcessHeap(), 0, This->indexCache);
6108 HeapFree(GetProcessHeap(), 0, This);
6111 /******************************************************************************
6112 * BlockChainStream_GetHeadOfChain
6114 * Returns the head of this stream chain.
6115 * Some special chains don't have directory entries, their heads are kept in
6116 * This->headOfStreamPlaceHolder.
6119 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6121 DirEntry chainEntry;
6122 HRESULT hr;
6124 if (This->headOfStreamPlaceHolder != 0)
6125 return *(This->headOfStreamPlaceHolder);
6127 if (This->ownerDirEntry != DIRENTRY_NULL)
6129 hr = StorageImpl_ReadDirEntry(
6130 This->parentStorage,
6131 This->ownerDirEntry,
6132 &chainEntry);
6134 if (SUCCEEDED(hr))
6136 return chainEntry.startingBlock;
6140 return BLOCK_END_OF_CHAIN;
6143 /******************************************************************************
6144 * BlockChainStream_GetCount
6146 * Returns the number of blocks that comprises this chain.
6147 * This is not the size of the stream as the last block may not be full!
6149 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6151 return This->numBlocks;
6154 /******************************************************************************
6155 * BlockChainStream_ReadAt
6157 * Reads a specified number of bytes from this chain at the specified offset.
6158 * bytesRead may be NULL.
6159 * Failure will be returned if the specified number of bytes has not been read.
6161 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6162 ULARGE_INTEGER offset,
6163 ULONG size,
6164 void* buffer,
6165 ULONG* bytesRead)
6167 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6168 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6169 ULONG bytesToReadInBuffer;
6170 ULONG blockIndex;
6171 BYTE* bufferWalker;
6172 ULARGE_INTEGER stream_size;
6173 HRESULT hr;
6174 BlockChainBlock *cachedBlock;
6176 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6179 * Find the first block in the stream that contains part of the buffer.
6181 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6183 *bytesRead = 0;
6185 stream_size = BlockChainStream_GetSize(This);
6186 if (stream_size.QuadPart > offset.QuadPart)
6187 size = min(stream_size.QuadPart - offset.QuadPart, size);
6188 else
6189 return S_OK;
6192 * Start reading the buffer.
6194 bufferWalker = buffer;
6196 while (size > 0)
6198 ULARGE_INTEGER ulOffset;
6199 DWORD bytesReadAt;
6202 * Calculate how many bytes we can copy from this big block.
6204 bytesToReadInBuffer =
6205 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6207 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6209 if (FAILED(hr))
6210 return hr;
6212 if (!cachedBlock)
6214 /* Not in cache, and we're going to read past the end of the block. */
6215 ulOffset.u.HighPart = 0;
6216 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6217 offsetInBlock;
6219 StorageImpl_ReadAt(This->parentStorage,
6220 ulOffset,
6221 bufferWalker,
6222 bytesToReadInBuffer,
6223 &bytesReadAt);
6225 else
6227 if (!cachedBlock->read)
6229 ULONG read;
6230 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
6231 return STG_E_READFAULT;
6233 cachedBlock->read = TRUE;
6236 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6237 bytesReadAt = bytesToReadInBuffer;
6240 blockNoInSequence++;
6241 bufferWalker += bytesReadAt;
6242 size -= bytesReadAt;
6243 *bytesRead += bytesReadAt;
6244 offsetInBlock = 0; /* There is no offset on the next block */
6246 if (bytesToReadInBuffer != bytesReadAt)
6247 break;
6250 return S_OK;
6253 /******************************************************************************
6254 * BlockChainStream_WriteAt
6256 * Writes the specified number of bytes to this chain at the specified offset.
6257 * Will fail if not all specified number of bytes have been written.
6259 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6260 ULARGE_INTEGER offset,
6261 ULONG size,
6262 const void* buffer,
6263 ULONG* bytesWritten)
6265 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6266 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6267 ULONG bytesToWrite;
6268 ULONG blockIndex;
6269 const BYTE* bufferWalker;
6270 HRESULT hr;
6271 BlockChainBlock *cachedBlock;
6273 *bytesWritten = 0;
6274 bufferWalker = buffer;
6276 while (size > 0)
6278 ULARGE_INTEGER ulOffset;
6279 DWORD bytesWrittenAt;
6282 * Calculate how many bytes we can copy to this big block.
6284 bytesToWrite =
6285 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6287 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6289 /* BlockChainStream_SetSize should have already been called to ensure we have
6290 * enough blocks in the chain to write into */
6291 if (FAILED(hr))
6293 ERR("not enough blocks in chain to write data\n");
6294 return hr;
6297 if (!cachedBlock)
6299 /* Not in cache, and we're going to write past the end of the block. */
6300 ulOffset.u.HighPart = 0;
6301 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6302 offsetInBlock;
6304 StorageImpl_WriteAt(This->parentStorage,
6305 ulOffset,
6306 bufferWalker,
6307 bytesToWrite,
6308 &bytesWrittenAt);
6310 else
6312 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6314 ULONG read;
6315 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
6316 return STG_E_READFAULT;
6319 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6320 bytesWrittenAt = bytesToWrite;
6321 cachedBlock->read = TRUE;
6322 cachedBlock->dirty = TRUE;
6325 blockNoInSequence++;
6326 bufferWalker += bytesWrittenAt;
6327 size -= bytesWrittenAt;
6328 *bytesWritten += bytesWrittenAt;
6329 offsetInBlock = 0; /* There is no offset on the next block */
6331 if (bytesWrittenAt != bytesToWrite)
6332 break;
6335 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6338 /******************************************************************************
6339 * BlockChainStream_Shrink
6341 * Shrinks this chain in the big block depot.
6343 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6344 ULARGE_INTEGER newSize)
6346 ULONG blockIndex;
6347 ULONG numBlocks;
6348 int i;
6351 * Figure out how many blocks are needed to contain the new size
6353 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6355 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6356 numBlocks++;
6358 if (numBlocks)
6361 * Go to the new end of chain
6363 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6365 /* Mark the new end of chain */
6366 StorageImpl_SetNextBlockInChain(
6367 This->parentStorage,
6368 blockIndex,
6369 BLOCK_END_OF_CHAIN);
6371 This->tailIndex = blockIndex;
6373 else
6375 if (This->headOfStreamPlaceHolder != 0)
6377 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6379 else
6381 DirEntry chainEntry;
6382 assert(This->ownerDirEntry != DIRENTRY_NULL);
6384 StorageImpl_ReadDirEntry(
6385 This->parentStorage,
6386 This->ownerDirEntry,
6387 &chainEntry);
6389 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6391 StorageImpl_WriteDirEntry(
6392 This->parentStorage,
6393 This->ownerDirEntry,
6394 &chainEntry);
6397 This->tailIndex = BLOCK_END_OF_CHAIN;
6400 This->numBlocks = numBlocks;
6403 * Mark the extra blocks as free
6405 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6407 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6408 StorageImpl_FreeBigBlock(This->parentStorage,
6409 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6410 if (last_run->lastOffset == last_run->firstOffset)
6411 This->indexCacheLen--;
6412 else
6413 last_run->lastOffset--;
6417 * Reset the last accessed block cache.
6419 for (i=0; i<2; i++)
6421 if (This->cachedBlocks[i].index >= numBlocks)
6423 This->cachedBlocks[i].index = 0xffffffff;
6424 This->cachedBlocks[i].dirty = FALSE;
6428 return TRUE;
6431 /******************************************************************************
6432 * BlockChainStream_Enlarge
6434 * Grows this chain in the big block depot.
6436 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6437 ULARGE_INTEGER newSize)
6439 ULONG blockIndex, currentBlock;
6440 ULONG newNumBlocks;
6441 ULONG oldNumBlocks = 0;
6443 blockIndex = BlockChainStream_GetHeadOfChain(This);
6446 * Empty chain. Create the head.
6448 if (blockIndex == BLOCK_END_OF_CHAIN)
6450 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6451 StorageImpl_SetNextBlockInChain(This->parentStorage,
6452 blockIndex,
6453 BLOCK_END_OF_CHAIN);
6455 if (This->headOfStreamPlaceHolder != 0)
6457 *(This->headOfStreamPlaceHolder) = blockIndex;
6459 else
6461 DirEntry chainEntry;
6462 assert(This->ownerDirEntry != DIRENTRY_NULL);
6464 StorageImpl_ReadDirEntry(
6465 This->parentStorage,
6466 This->ownerDirEntry,
6467 &chainEntry);
6469 chainEntry.startingBlock = blockIndex;
6471 StorageImpl_WriteDirEntry(
6472 This->parentStorage,
6473 This->ownerDirEntry,
6474 &chainEntry);
6477 This->tailIndex = blockIndex;
6478 This->numBlocks = 1;
6482 * Figure out how many blocks are needed to contain this stream
6484 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6486 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6487 newNumBlocks++;
6490 * Go to the current end of chain
6492 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6494 currentBlock = blockIndex;
6496 while (blockIndex != BLOCK_END_OF_CHAIN)
6498 This->numBlocks++;
6499 currentBlock = blockIndex;
6501 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6502 &blockIndex)))
6503 return FALSE;
6506 This->tailIndex = currentBlock;
6509 currentBlock = This->tailIndex;
6510 oldNumBlocks = This->numBlocks;
6513 * Add new blocks to the chain
6515 if (oldNumBlocks < newNumBlocks)
6517 while (oldNumBlocks < newNumBlocks)
6519 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6521 StorageImpl_SetNextBlockInChain(
6522 This->parentStorage,
6523 currentBlock,
6524 blockIndex);
6526 StorageImpl_SetNextBlockInChain(
6527 This->parentStorage,
6528 blockIndex,
6529 BLOCK_END_OF_CHAIN);
6531 currentBlock = blockIndex;
6532 oldNumBlocks++;
6535 This->tailIndex = blockIndex;
6536 This->numBlocks = newNumBlocks;
6539 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6540 return FALSE;
6542 return TRUE;
6545 /******************************************************************************
6546 * BlockChainStream_SetSize
6548 * Sets the size of this stream. The big block depot will be updated.
6549 * The file will grow if we grow the chain.
6551 * TODO: Free the actual blocks in the file when we shrink the chain.
6552 * Currently, the blocks are still in the file. So the file size
6553 * doesn't shrink even if we shrink streams.
6555 BOOL BlockChainStream_SetSize(
6556 BlockChainStream* This,
6557 ULARGE_INTEGER newSize)
6559 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6561 if (newSize.u.LowPart == size.u.LowPart)
6562 return TRUE;
6564 if (newSize.u.LowPart < size.u.LowPart)
6566 BlockChainStream_Shrink(This, newSize);
6568 else
6570 BlockChainStream_Enlarge(This, newSize);
6573 return TRUE;
6576 /******************************************************************************
6577 * BlockChainStream_GetSize
6579 * Returns the size of this chain.
6580 * Will return the block count if this chain doesn't have a directory entry.
6582 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6584 DirEntry chainEntry;
6586 if(This->headOfStreamPlaceHolder == NULL)
6589 * This chain has a directory entry so use the size value from there.
6591 StorageImpl_ReadDirEntry(
6592 This->parentStorage,
6593 This->ownerDirEntry,
6594 &chainEntry);
6596 return chainEntry.size;
6598 else
6601 * this chain is a chain that does not have a directory entry, figure out the
6602 * size by making the product number of used blocks times the
6603 * size of them
6605 ULARGE_INTEGER result;
6606 result.u.HighPart = 0;
6608 result.u.LowPart =
6609 BlockChainStream_GetCount(This) *
6610 This->parentStorage->bigBlockSize;
6612 return result;
6616 /******************************************************************************
6617 ** SmallBlockChainStream implementation
6620 SmallBlockChainStream* SmallBlockChainStream_Construct(
6621 StorageImpl* parentStorage,
6622 ULONG* headOfStreamPlaceHolder,
6623 DirRef dirEntry)
6625 SmallBlockChainStream* newStream;
6627 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6629 newStream->parentStorage = parentStorage;
6630 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6631 newStream->ownerDirEntry = dirEntry;
6633 return newStream;
6636 void SmallBlockChainStream_Destroy(
6637 SmallBlockChainStream* This)
6639 HeapFree(GetProcessHeap(), 0, This);
6642 /******************************************************************************
6643 * SmallBlockChainStream_GetHeadOfChain
6645 * Returns the head of this chain of small blocks.
6647 static ULONG SmallBlockChainStream_GetHeadOfChain(
6648 SmallBlockChainStream* This)
6650 DirEntry chainEntry;
6651 HRESULT hr;
6653 if (This->headOfStreamPlaceHolder != NULL)
6654 return *(This->headOfStreamPlaceHolder);
6656 if (This->ownerDirEntry)
6658 hr = StorageImpl_ReadDirEntry(
6659 This->parentStorage,
6660 This->ownerDirEntry,
6661 &chainEntry);
6663 if (SUCCEEDED(hr))
6665 return chainEntry.startingBlock;
6670 return BLOCK_END_OF_CHAIN;
6673 /******************************************************************************
6674 * SmallBlockChainStream_GetNextBlockInChain
6676 * Returns the index of the next small block in this chain.
6678 * Return Values:
6679 * - BLOCK_END_OF_CHAIN: end of this chain
6680 * - BLOCK_UNUSED: small block 'blockIndex' is free
6682 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6683 SmallBlockChainStream* This,
6684 ULONG blockIndex,
6685 ULONG* nextBlockInChain)
6687 ULARGE_INTEGER offsetOfBlockInDepot;
6688 DWORD buffer;
6689 ULONG bytesRead;
6690 HRESULT res;
6692 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6694 offsetOfBlockInDepot.u.HighPart = 0;
6695 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6698 * Read those bytes in the buffer from the small block file.
6700 res = BlockChainStream_ReadAt(
6701 This->parentStorage->smallBlockDepotChain,
6702 offsetOfBlockInDepot,
6703 sizeof(DWORD),
6704 &buffer,
6705 &bytesRead);
6707 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6708 res = STG_E_READFAULT;
6710 if (SUCCEEDED(res))
6712 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6713 return S_OK;
6716 return res;
6719 /******************************************************************************
6720 * SmallBlockChainStream_SetNextBlockInChain
6722 * Writes the index of the next block of the specified block in the small
6723 * block depot.
6724 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6725 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6727 static void SmallBlockChainStream_SetNextBlockInChain(
6728 SmallBlockChainStream* This,
6729 ULONG blockIndex,
6730 ULONG nextBlock)
6732 ULARGE_INTEGER offsetOfBlockInDepot;
6733 DWORD buffer;
6734 ULONG bytesWritten;
6736 offsetOfBlockInDepot.u.HighPart = 0;
6737 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6739 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6742 * Read those bytes in the buffer from the small block file.
6744 BlockChainStream_WriteAt(
6745 This->parentStorage->smallBlockDepotChain,
6746 offsetOfBlockInDepot,
6747 sizeof(DWORD),
6748 &buffer,
6749 &bytesWritten);
6752 /******************************************************************************
6753 * SmallBlockChainStream_FreeBlock
6755 * Flag small block 'blockIndex' as free in the small block depot.
6757 static void SmallBlockChainStream_FreeBlock(
6758 SmallBlockChainStream* This,
6759 ULONG blockIndex)
6761 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6764 /******************************************************************************
6765 * SmallBlockChainStream_GetNextFreeBlock
6767 * Returns the index of a free small block. The small block depot will be
6768 * enlarged if necessary. The small block chain will also be enlarged if
6769 * necessary.
6771 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6772 SmallBlockChainStream* This)
6774 ULARGE_INTEGER offsetOfBlockInDepot;
6775 DWORD buffer;
6776 ULONG bytesRead;
6777 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6778 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6779 HRESULT res = S_OK;
6780 ULONG smallBlocksPerBigBlock;
6781 DirEntry rootEntry;
6782 ULONG blocksRequired;
6783 ULARGE_INTEGER old_size, size_required;
6785 offsetOfBlockInDepot.u.HighPart = 0;
6788 * Scan the small block depot for a free block
6790 while (nextBlockIndex != BLOCK_UNUSED)
6792 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6794 res = BlockChainStream_ReadAt(
6795 This->parentStorage->smallBlockDepotChain,
6796 offsetOfBlockInDepot,
6797 sizeof(DWORD),
6798 &buffer,
6799 &bytesRead);
6802 * If we run out of space for the small block depot, enlarge it
6804 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6806 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6808 if (nextBlockIndex != BLOCK_UNUSED)
6809 blockIndex++;
6811 else
6813 ULONG count =
6814 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6816 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6817 ULARGE_INTEGER newSize, offset;
6818 ULONG bytesWritten;
6820 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6821 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6824 * Initialize all the small blocks to free
6826 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6827 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6828 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6829 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6831 StorageImpl_SaveFileHeader(This->parentStorage);
6835 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6837 smallBlocksPerBigBlock =
6838 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6841 * Verify if we have to allocate big blocks to contain small blocks
6843 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6845 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6847 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6849 if (size_required.QuadPart > old_size.QuadPart)
6851 BlockChainStream_SetSize(
6852 This->parentStorage->smallBlockRootChain,
6853 size_required);
6855 StorageImpl_ReadDirEntry(
6856 This->parentStorage,
6857 This->parentStorage->base.storageDirEntry,
6858 &rootEntry);
6860 rootEntry.size = size_required;
6862 StorageImpl_WriteDirEntry(
6863 This->parentStorage,
6864 This->parentStorage->base.storageDirEntry,
6865 &rootEntry);
6868 return blockIndex;
6871 /******************************************************************************
6872 * SmallBlockChainStream_ReadAt
6874 * Reads a specified number of bytes from this chain at the specified offset.
6875 * bytesRead may be NULL.
6876 * Failure will be returned if the specified number of bytes has not been read.
6878 HRESULT SmallBlockChainStream_ReadAt(
6879 SmallBlockChainStream* This,
6880 ULARGE_INTEGER offset,
6881 ULONG size,
6882 void* buffer,
6883 ULONG* bytesRead)
6885 HRESULT rc = S_OK;
6886 ULARGE_INTEGER offsetInBigBlockFile;
6887 ULONG blockNoInSequence =
6888 offset.u.LowPart / This->parentStorage->smallBlockSize;
6890 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6891 ULONG bytesToReadInBuffer;
6892 ULONG blockIndex;
6893 ULONG bytesReadFromBigBlockFile;
6894 BYTE* bufferWalker;
6895 ULARGE_INTEGER stream_size;
6898 * This should never happen on a small block file.
6900 assert(offset.u.HighPart==0);
6902 *bytesRead = 0;
6904 stream_size = SmallBlockChainStream_GetSize(This);
6905 if (stream_size.QuadPart > offset.QuadPart)
6906 size = min(stream_size.QuadPart - offset.QuadPart, size);
6907 else
6908 return S_OK;
6911 * Find the first block in the stream that contains part of the buffer.
6913 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6915 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6917 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6918 if(FAILED(rc))
6919 return rc;
6920 blockNoInSequence--;
6924 * Start reading the buffer.
6926 bufferWalker = buffer;
6928 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6931 * Calculate how many bytes we can copy from this small block.
6933 bytesToReadInBuffer =
6934 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6937 * Calculate the offset of the small block in the small block file.
6939 offsetInBigBlockFile.u.HighPart = 0;
6940 offsetInBigBlockFile.u.LowPart =
6941 blockIndex * This->parentStorage->smallBlockSize;
6943 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6946 * Read those bytes in the buffer from the small block file.
6947 * The small block has already been identified so it shouldn't fail
6948 * unless the file is corrupt.
6950 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6951 offsetInBigBlockFile,
6952 bytesToReadInBuffer,
6953 bufferWalker,
6954 &bytesReadFromBigBlockFile);
6956 if (FAILED(rc))
6957 return rc;
6959 if (!bytesReadFromBigBlockFile)
6960 return STG_E_DOCFILECORRUPT;
6963 * Step to the next big block.
6965 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6966 if(FAILED(rc))
6967 return STG_E_DOCFILECORRUPT;
6969 bufferWalker += bytesReadFromBigBlockFile;
6970 size -= bytesReadFromBigBlockFile;
6971 *bytesRead += bytesReadFromBigBlockFile;
6972 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6975 return S_OK;
6978 /******************************************************************************
6979 * SmallBlockChainStream_WriteAt
6981 * Writes the specified number of bytes to this chain at the specified offset.
6982 * Will fail if not all specified number of bytes have been written.
6984 HRESULT SmallBlockChainStream_WriteAt(
6985 SmallBlockChainStream* This,
6986 ULARGE_INTEGER offset,
6987 ULONG size,
6988 const void* buffer,
6989 ULONG* bytesWritten)
6991 ULARGE_INTEGER offsetInBigBlockFile;
6992 ULONG blockNoInSequence =
6993 offset.u.LowPart / This->parentStorage->smallBlockSize;
6995 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6996 ULONG bytesToWriteInBuffer;
6997 ULONG blockIndex;
6998 ULONG bytesWrittenToBigBlockFile;
6999 const BYTE* bufferWalker;
7000 HRESULT res;
7003 * This should never happen on a small block file.
7005 assert(offset.u.HighPart==0);
7008 * Find the first block in the stream that contains part of the buffer.
7010 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7012 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7014 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
7015 return STG_E_DOCFILECORRUPT;
7016 blockNoInSequence--;
7020 * Start writing the buffer.
7022 *bytesWritten = 0;
7023 bufferWalker = buffer;
7024 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7027 * Calculate how many bytes we can copy to this small block.
7029 bytesToWriteInBuffer =
7030 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7033 * Calculate the offset of the small block in the small block file.
7035 offsetInBigBlockFile.u.HighPart = 0;
7036 offsetInBigBlockFile.u.LowPart =
7037 blockIndex * This->parentStorage->smallBlockSize;
7039 offsetInBigBlockFile.u.LowPart += offsetInBlock;
7042 * Write those bytes in the buffer to the small block file.
7044 res = BlockChainStream_WriteAt(
7045 This->parentStorage->smallBlockRootChain,
7046 offsetInBigBlockFile,
7047 bytesToWriteInBuffer,
7048 bufferWalker,
7049 &bytesWrittenToBigBlockFile);
7050 if (FAILED(res))
7051 return res;
7054 * Step to the next big block.
7056 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7057 &blockIndex)))
7058 return FALSE;
7059 bufferWalker += bytesWrittenToBigBlockFile;
7060 size -= bytesWrittenToBigBlockFile;
7061 *bytesWritten += bytesWrittenToBigBlockFile;
7062 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7065 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7068 /******************************************************************************
7069 * SmallBlockChainStream_Shrink
7071 * Shrinks this chain in the small block depot.
7073 static BOOL SmallBlockChainStream_Shrink(
7074 SmallBlockChainStream* This,
7075 ULARGE_INTEGER newSize)
7077 ULONG blockIndex, extraBlock;
7078 ULONG numBlocks;
7079 ULONG count = 0;
7081 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7083 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7084 numBlocks++;
7086 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7089 * Go to the new end of chain
7091 while (count < numBlocks)
7093 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7094 &blockIndex)))
7095 return FALSE;
7096 count++;
7100 * If the count is 0, we have a special case, the head of the chain was
7101 * just freed.
7103 if (count == 0)
7105 DirEntry chainEntry;
7107 StorageImpl_ReadDirEntry(This->parentStorage,
7108 This->ownerDirEntry,
7109 &chainEntry);
7111 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7113 StorageImpl_WriteDirEntry(This->parentStorage,
7114 This->ownerDirEntry,
7115 &chainEntry);
7118 * We start freeing the chain at the head block.
7120 extraBlock = blockIndex;
7122 else
7124 /* Get the next block before marking the new end */
7125 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7126 &extraBlock)))
7127 return FALSE;
7129 /* Mark the new end of chain */
7130 SmallBlockChainStream_SetNextBlockInChain(
7131 This,
7132 blockIndex,
7133 BLOCK_END_OF_CHAIN);
7137 * Mark the extra blocks as free
7139 while (extraBlock != BLOCK_END_OF_CHAIN)
7141 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7142 &blockIndex)))
7143 return FALSE;
7144 SmallBlockChainStream_FreeBlock(This, extraBlock);
7145 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7146 extraBlock = blockIndex;
7149 return TRUE;
7152 /******************************************************************************
7153 * SmallBlockChainStream_Enlarge
7155 * Grows this chain in the small block depot.
7157 static BOOL SmallBlockChainStream_Enlarge(
7158 SmallBlockChainStream* This,
7159 ULARGE_INTEGER newSize)
7161 ULONG blockIndex, currentBlock;
7162 ULONG newNumBlocks;
7163 ULONG oldNumBlocks = 0;
7165 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7168 * Empty chain. Create the head.
7170 if (blockIndex == BLOCK_END_OF_CHAIN)
7172 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7173 SmallBlockChainStream_SetNextBlockInChain(
7174 This,
7175 blockIndex,
7176 BLOCK_END_OF_CHAIN);
7178 if (This->headOfStreamPlaceHolder != NULL)
7180 *(This->headOfStreamPlaceHolder) = blockIndex;
7182 else
7184 DirEntry chainEntry;
7186 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7187 &chainEntry);
7189 chainEntry.startingBlock = blockIndex;
7191 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7192 &chainEntry);
7196 currentBlock = blockIndex;
7199 * Figure out how many blocks are needed to contain this stream
7201 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7203 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7204 newNumBlocks++;
7207 * Go to the current end of chain
7209 while (blockIndex != BLOCK_END_OF_CHAIN)
7211 oldNumBlocks++;
7212 currentBlock = blockIndex;
7213 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7214 return FALSE;
7218 * Add new blocks to the chain
7220 while (oldNumBlocks < newNumBlocks)
7222 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7223 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7225 SmallBlockChainStream_SetNextBlockInChain(
7226 This,
7227 blockIndex,
7228 BLOCK_END_OF_CHAIN);
7230 currentBlock = blockIndex;
7231 oldNumBlocks++;
7234 return TRUE;
7237 /******************************************************************************
7238 * SmallBlockChainStream_SetSize
7240 * Sets the size of this stream.
7241 * The file will grow if we grow the chain.
7243 * TODO: Free the actual blocks in the file when we shrink the chain.
7244 * Currently, the blocks are still in the file. So the file size
7245 * doesn't shrink even if we shrink streams.
7247 BOOL SmallBlockChainStream_SetSize(
7248 SmallBlockChainStream* This,
7249 ULARGE_INTEGER newSize)
7251 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7253 if (newSize.u.LowPart == size.u.LowPart)
7254 return TRUE;
7256 if (newSize.u.LowPart < size.u.LowPart)
7258 SmallBlockChainStream_Shrink(This, newSize);
7260 else
7262 SmallBlockChainStream_Enlarge(This, newSize);
7265 return TRUE;
7268 /******************************************************************************
7269 * SmallBlockChainStream_GetCount
7271 * Returns the number of small blocks that comprises this chain.
7272 * This is not the size of the stream as the last block may not be full!
7275 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7277 ULONG blockIndex;
7278 ULONG count = 0;
7280 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7282 while(blockIndex != BLOCK_END_OF_CHAIN)
7284 count++;
7286 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7287 blockIndex, &blockIndex)))
7288 return 0;
7291 return count;
7294 /******************************************************************************
7295 * SmallBlockChainStream_GetSize
7297 * Returns the size of this chain.
7299 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7301 DirEntry chainEntry;
7303 if(This->headOfStreamPlaceHolder != NULL)
7305 ULARGE_INTEGER result;
7306 result.u.HighPart = 0;
7308 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7309 This->parentStorage->smallBlockSize;
7311 return result;
7314 StorageImpl_ReadDirEntry(
7315 This->parentStorage,
7316 This->ownerDirEntry,
7317 &chainEntry);
7319 return chainEntry.size;
7322 static HRESULT create_storagefile(
7323 LPCOLESTR pwcsName,
7324 DWORD grfMode,
7325 DWORD grfAttrs,
7326 STGOPTIONS* pStgOptions,
7327 REFIID riid,
7328 void** ppstgOpen)
7330 StorageBaseImpl* newStorage = 0;
7331 HANDLE hFile = INVALID_HANDLE_VALUE;
7332 HRESULT hr = STG_E_INVALIDFLAG;
7333 DWORD shareMode;
7334 DWORD accessMode;
7335 DWORD creationMode;
7336 DWORD fileAttributes;
7337 WCHAR tempFileName[MAX_PATH];
7339 if (ppstgOpen == 0)
7340 return STG_E_INVALIDPOINTER;
7342 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7343 return STG_E_INVALIDPARAMETER;
7345 /* if no share mode given then DENY_NONE is the default */
7346 if (STGM_SHARE_MODE(grfMode) == 0)
7347 grfMode |= STGM_SHARE_DENY_NONE;
7349 if ( FAILED( validateSTGM(grfMode) ))
7350 goto end;
7352 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7353 switch(STGM_ACCESS_MODE(grfMode))
7355 case STGM_WRITE:
7356 case STGM_READWRITE:
7357 break;
7358 default:
7359 goto end;
7362 /* in direct mode, can only use SHARE_EXCLUSIVE */
7363 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7364 goto end;
7366 /* but in transacted mode, any share mode is valid */
7369 * Generate a unique name.
7371 if (pwcsName == 0)
7373 WCHAR tempPath[MAX_PATH];
7374 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7376 memset(tempPath, 0, sizeof(tempPath));
7377 memset(tempFileName, 0, sizeof(tempFileName));
7379 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7380 tempPath[0] = '.';
7382 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7383 pwcsName = tempFileName;
7384 else
7386 hr = STG_E_INSUFFICIENTMEMORY;
7387 goto end;
7390 creationMode = TRUNCATE_EXISTING;
7392 else
7394 creationMode = GetCreationModeFromSTGM(grfMode);
7398 * Interpret the STGM value grfMode
7400 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7401 accessMode = GetAccessModeFromSTGM(grfMode);
7403 if (grfMode & STGM_DELETEONRELEASE)
7404 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7405 else
7406 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7408 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7410 static int fixme;
7411 if (!fixme++)
7412 FIXME("Storage share mode not implemented.\n");
7415 *ppstgOpen = 0;
7417 hFile = CreateFileW(pwcsName,
7418 accessMode,
7419 shareMode,
7420 NULL,
7421 creationMode,
7422 fileAttributes,
7425 if (hFile == INVALID_HANDLE_VALUE)
7427 if(GetLastError() == ERROR_FILE_EXISTS)
7428 hr = STG_E_FILEALREADYEXISTS;
7429 else
7430 hr = E_FAIL;
7431 goto end;
7435 * Allocate and initialize the new IStorage32object.
7437 hr = Storage_Construct(
7438 hFile,
7439 pwcsName,
7440 NULL,
7441 grfMode,
7442 TRUE,
7443 TRUE,
7444 pStgOptions->ulSectorSize,
7445 &newStorage);
7447 if (FAILED(hr))
7449 goto end;
7452 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
7453 IStorage_Release(&newStorage->IStorage_iface);
7455 end:
7456 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7458 return hr;
7461 /******************************************************************************
7462 * StgCreateDocfile [OLE32.@]
7463 * Creates a new compound file storage object
7465 * PARAMS
7466 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7467 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7468 * reserved [ ?] unused?, usually 0
7469 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7471 * RETURNS
7472 * S_OK if the file was successfully created
7473 * some STG_E_ value if error
7474 * NOTES
7475 * if pwcsName is NULL, create file with new unique name
7476 * the function can returns
7477 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7478 * (unrealized now)
7480 HRESULT WINAPI StgCreateDocfile(
7481 LPCOLESTR pwcsName,
7482 DWORD grfMode,
7483 DWORD reserved,
7484 IStorage **ppstgOpen)
7486 STGOPTIONS stgoptions = {1, 0, 512};
7488 TRACE("(%s, %x, %d, %p)\n",
7489 debugstr_w(pwcsName), grfMode,
7490 reserved, ppstgOpen);
7492 if (ppstgOpen == 0)
7493 return STG_E_INVALIDPOINTER;
7494 if (reserved != 0)
7495 return STG_E_INVALIDPARAMETER;
7497 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7500 /******************************************************************************
7501 * StgCreateStorageEx [OLE32.@]
7503 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7505 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7506 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7508 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7510 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7511 return STG_E_INVALIDPARAMETER;
7514 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7516 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7517 return STG_E_INVALIDPARAMETER;
7520 if (stgfmt == STGFMT_FILE)
7522 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7523 return STG_E_INVALIDPARAMETER;
7526 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7528 STGOPTIONS defaultOptions = {1, 0, 512};
7530 if (!pStgOptions) pStgOptions = &defaultOptions;
7531 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7535 ERR("Invalid stgfmt argument\n");
7536 return STG_E_INVALIDPARAMETER;
7539 /******************************************************************************
7540 * StgCreatePropSetStg [OLE32.@]
7542 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7543 IPropertySetStorage **propset)
7545 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
7546 if (reserved)
7547 return STG_E_INVALIDPARAMETER;
7549 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
7552 /******************************************************************************
7553 * StgOpenStorageEx [OLE32.@]
7555 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7557 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7558 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7560 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7562 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7563 return STG_E_INVALIDPARAMETER;
7566 switch (stgfmt)
7568 case STGFMT_FILE:
7569 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7570 return STG_E_INVALIDPARAMETER;
7572 case STGFMT_STORAGE:
7573 break;
7575 case STGFMT_DOCFILE:
7576 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7578 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7579 return STG_E_INVALIDPARAMETER;
7581 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7582 break;
7584 case STGFMT_ANY:
7585 WARN("STGFMT_ANY assuming storage\n");
7586 break;
7588 default:
7589 return STG_E_INVALIDPARAMETER;
7592 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7596 /******************************************************************************
7597 * StgOpenStorage [OLE32.@]
7599 HRESULT WINAPI StgOpenStorage(
7600 const OLECHAR *pwcsName,
7601 IStorage *pstgPriority,
7602 DWORD grfMode,
7603 SNB snbExclude,
7604 DWORD reserved,
7605 IStorage **ppstgOpen)
7607 StorageBaseImpl* newStorage = 0;
7608 HRESULT hr = S_OK;
7609 HANDLE hFile = 0;
7610 DWORD shareMode;
7611 DWORD accessMode;
7612 LPWSTR temp_name = NULL;
7614 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7615 debugstr_w(pwcsName), pstgPriority, grfMode,
7616 snbExclude, reserved, ppstgOpen);
7618 if (pstgPriority)
7620 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
7621 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
7622 if (FAILED(hr)) goto end;
7623 pwcsName = temp_name;
7624 TRACE("using filename %s\n", debugstr_w(temp_name));
7627 if (pwcsName == 0)
7629 hr = STG_E_INVALIDNAME;
7630 goto end;
7633 if (ppstgOpen == 0)
7635 hr = STG_E_INVALIDPOINTER;
7636 goto end;
7639 if (reserved)
7641 hr = STG_E_INVALIDPARAMETER;
7642 goto end;
7645 if (grfMode & STGM_PRIORITY)
7647 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7648 return STG_E_INVALIDFLAG;
7649 if (grfMode & STGM_DELETEONRELEASE)
7650 return STG_E_INVALIDFUNCTION;
7651 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7652 return STG_E_INVALIDFLAG;
7653 grfMode &= ~0xf0; /* remove the existing sharing mode */
7654 grfMode |= STGM_SHARE_DENY_NONE;
7656 /* STGM_PRIORITY stops other IStorage objects on the same file from
7657 * committing until the STGM_PRIORITY IStorage is closed. it also
7658 * stops non-transacted mode StgOpenStorage calls with write access from
7659 * succeeding. obviously, both of these cannot be achieved through just
7660 * file share flags */
7661 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7665 * Validate the sharing mode
7667 if (grfMode & STGM_DIRECT_SWMR)
7669 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
7670 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
7672 hr = STG_E_INVALIDFLAG;
7673 goto end;
7676 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7677 switch(STGM_SHARE_MODE(grfMode))
7679 case STGM_SHARE_EXCLUSIVE:
7680 case STGM_SHARE_DENY_WRITE:
7681 break;
7682 default:
7683 hr = STG_E_INVALIDFLAG;
7684 goto end;
7687 if ( FAILED( validateSTGM(grfMode) ) ||
7688 (grfMode&STGM_CREATE))
7690 hr = STG_E_INVALIDFLAG;
7691 goto end;
7694 /* shared reading requires transacted or single writer mode */
7695 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7696 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7697 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
7699 hr = STG_E_INVALIDFLAG;
7700 goto end;
7704 * Interpret the STGM value grfMode
7706 shareMode = GetShareModeFromSTGM(grfMode);
7707 accessMode = GetAccessModeFromSTGM(grfMode);
7709 *ppstgOpen = 0;
7711 hFile = CreateFileW( pwcsName,
7712 accessMode,
7713 shareMode,
7714 NULL,
7715 OPEN_EXISTING,
7716 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7719 if (hFile==INVALID_HANDLE_VALUE)
7721 DWORD last_error = GetLastError();
7723 hr = E_FAIL;
7725 switch (last_error)
7727 case ERROR_FILE_NOT_FOUND:
7728 hr = STG_E_FILENOTFOUND;
7729 break;
7731 case ERROR_PATH_NOT_FOUND:
7732 hr = STG_E_PATHNOTFOUND;
7733 break;
7735 case ERROR_ACCESS_DENIED:
7736 case ERROR_WRITE_PROTECT:
7737 hr = STG_E_ACCESSDENIED;
7738 break;
7740 case ERROR_SHARING_VIOLATION:
7741 hr = STG_E_SHAREVIOLATION;
7742 break;
7744 default:
7745 hr = E_FAIL;
7748 goto end;
7752 * Refuse to open the file if it's too small to be a structured storage file
7753 * FIXME: verify the file when reading instead of here
7755 if (GetFileSize(hFile, NULL) < 0x100)
7757 CloseHandle(hFile);
7758 hr = STG_E_FILEALREADYEXISTS;
7759 goto end;
7763 * Allocate and initialize the new IStorage32object.
7765 hr = Storage_Construct(
7766 hFile,
7767 pwcsName,
7768 NULL,
7769 grfMode,
7770 TRUE,
7771 FALSE,
7772 512,
7773 &newStorage);
7775 if (FAILED(hr))
7778 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7780 if(hr == STG_E_INVALIDHEADER)
7781 hr = STG_E_FILEALREADYEXISTS;
7782 goto end;
7785 *ppstgOpen = &newStorage->IStorage_iface;
7787 end:
7788 CoTaskMemFree(temp_name);
7789 if (pstgPriority) IStorage_Release(pstgPriority);
7790 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7791 return hr;
7794 /******************************************************************************
7795 * StgCreateDocfileOnILockBytes [OLE32.@]
7797 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7798 ILockBytes *plkbyt,
7799 DWORD grfMode,
7800 DWORD reserved,
7801 IStorage** ppstgOpen)
7803 StorageBaseImpl* newStorage = 0;
7804 HRESULT hr = S_OK;
7806 if ((ppstgOpen == 0) || (plkbyt == 0))
7807 return STG_E_INVALIDPOINTER;
7810 * Allocate and initialize the new IStorage object.
7812 hr = Storage_Construct(
7815 plkbyt,
7816 grfMode,
7817 FALSE,
7818 TRUE,
7819 512,
7820 &newStorage);
7822 if (FAILED(hr))
7824 return hr;
7827 *ppstgOpen = &newStorage->IStorage_iface;
7829 return hr;
7832 /******************************************************************************
7833 * StgOpenStorageOnILockBytes [OLE32.@]
7835 HRESULT WINAPI StgOpenStorageOnILockBytes(
7836 ILockBytes *plkbyt,
7837 IStorage *pstgPriority,
7838 DWORD grfMode,
7839 SNB snbExclude,
7840 DWORD reserved,
7841 IStorage **ppstgOpen)
7843 StorageBaseImpl* newStorage = 0;
7844 HRESULT hr = S_OK;
7846 if ((plkbyt == 0) || (ppstgOpen == 0))
7847 return STG_E_INVALIDPOINTER;
7849 if ( FAILED( validateSTGM(grfMode) ))
7850 return STG_E_INVALIDFLAG;
7852 *ppstgOpen = 0;
7855 * Allocate and initialize the new IStorage object.
7857 hr = Storage_Construct(
7860 plkbyt,
7861 grfMode,
7862 FALSE,
7863 FALSE,
7864 512,
7865 &newStorage);
7867 if (FAILED(hr))
7869 return hr;
7872 *ppstgOpen = &newStorage->IStorage_iface;
7874 return hr;
7877 /******************************************************************************
7878 * StgSetTimes [ole32.@]
7879 * StgSetTimes [OLE32.@]
7883 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7884 FILETIME const *patime, FILETIME const *pmtime)
7886 IStorage *stg = NULL;
7887 HRESULT r;
7889 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7891 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7892 0, 0, &stg);
7893 if( SUCCEEDED(r) )
7895 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7896 IStorage_Release(stg);
7899 return r;
7902 /******************************************************************************
7903 * StgIsStorageILockBytes [OLE32.@]
7905 * Determines if the ILockBytes contains a storage object.
7907 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7909 BYTE sig[sizeof(STORAGE_magic)];
7910 ULARGE_INTEGER offset;
7911 ULONG read = 0;
7913 offset.u.HighPart = 0;
7914 offset.u.LowPart = 0;
7916 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
7918 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
7919 return S_OK;
7921 return S_FALSE;
7924 /******************************************************************************
7925 * WriteClassStg [OLE32.@]
7927 * This method will store the specified CLSID in the specified storage object
7929 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7931 if(!pStg)
7932 return E_INVALIDARG;
7934 if(!rclsid)
7935 return STG_E_INVALIDPOINTER;
7937 return IStorage_SetClass(pStg, rclsid);
7940 /***********************************************************************
7941 * ReadClassStg (OLE32.@)
7943 * This method reads the CLSID previously written to a storage object with
7944 * the WriteClassStg.
7946 * PARAMS
7947 * pstg [I] IStorage pointer
7948 * pclsid [O] Pointer to where the CLSID is written
7950 * RETURNS
7951 * Success: S_OK.
7952 * Failure: HRESULT code.
7954 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7956 STATSTG pstatstg;
7957 HRESULT hRes;
7959 TRACE("(%p, %p)\n", pstg, pclsid);
7961 if(!pstg || !pclsid)
7962 return E_INVALIDARG;
7965 * read a STATSTG structure (contains the clsid) from the storage
7967 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7969 if(SUCCEEDED(hRes))
7970 *pclsid=pstatstg.clsid;
7972 return hRes;
7975 /***********************************************************************
7976 * OleLoadFromStream (OLE32.@)
7978 * This function loads an object from stream
7980 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7982 CLSID clsid;
7983 HRESULT res;
7984 LPPERSISTSTREAM xstm;
7986 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7988 res=ReadClassStm(pStm,&clsid);
7989 if (FAILED(res))
7990 return res;
7991 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7992 if (FAILED(res))
7993 return res;
7994 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7995 if (FAILED(res)) {
7996 IUnknown_Release((IUnknown*)*ppvObj);
7997 return res;
7999 res=IPersistStream_Load(xstm,pStm);
8000 IPersistStream_Release(xstm);
8001 /* FIXME: all refcounts ok at this point? I think they should be:
8002 * pStm : unchanged
8003 * ppvObj : 1
8004 * xstm : 0 (released)
8006 return res;
8009 /***********************************************************************
8010 * OleSaveToStream (OLE32.@)
8012 * This function saves an object with the IPersistStream interface on it
8013 * to the specified stream.
8015 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
8018 CLSID clsid;
8019 HRESULT res;
8021 TRACE("(%p,%p)\n",pPStm,pStm);
8023 res=IPersistStream_GetClassID(pPStm,&clsid);
8025 if (SUCCEEDED(res)){
8027 res=WriteClassStm(pStm,&clsid);
8029 if (SUCCEEDED(res))
8031 res=IPersistStream_Save(pPStm,pStm,TRUE);
8034 TRACE("Finished Save\n");
8035 return res;
8038 /****************************************************************************
8039 * This method validate a STGM parameter that can contain the values below
8041 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8042 * The stgm values contained in 0xffff0000 are bitmasks.
8044 * STGM_DIRECT 0x00000000
8045 * STGM_TRANSACTED 0x00010000
8046 * STGM_SIMPLE 0x08000000
8048 * STGM_READ 0x00000000
8049 * STGM_WRITE 0x00000001
8050 * STGM_READWRITE 0x00000002
8052 * STGM_SHARE_DENY_NONE 0x00000040
8053 * STGM_SHARE_DENY_READ 0x00000030
8054 * STGM_SHARE_DENY_WRITE 0x00000020
8055 * STGM_SHARE_EXCLUSIVE 0x00000010
8057 * STGM_PRIORITY 0x00040000
8058 * STGM_DELETEONRELEASE 0x04000000
8060 * STGM_CREATE 0x00001000
8061 * STGM_CONVERT 0x00020000
8062 * STGM_FAILIFTHERE 0x00000000
8064 * STGM_NOSCRATCH 0x00100000
8065 * STGM_NOSNAPSHOT 0x00200000
8067 static HRESULT validateSTGM(DWORD stgm)
8069 DWORD access = STGM_ACCESS_MODE(stgm);
8070 DWORD share = STGM_SHARE_MODE(stgm);
8071 DWORD create = STGM_CREATE_MODE(stgm);
8073 if (stgm&~STGM_KNOWN_FLAGS)
8075 ERR("unknown flags %08x\n", stgm);
8076 return E_FAIL;
8079 switch (access)
8081 case STGM_READ:
8082 case STGM_WRITE:
8083 case STGM_READWRITE:
8084 break;
8085 default:
8086 return E_FAIL;
8089 switch (share)
8091 case STGM_SHARE_DENY_NONE:
8092 case STGM_SHARE_DENY_READ:
8093 case STGM_SHARE_DENY_WRITE:
8094 case STGM_SHARE_EXCLUSIVE:
8095 break;
8096 default:
8097 return E_FAIL;
8100 switch (create)
8102 case STGM_CREATE:
8103 case STGM_FAILIFTHERE:
8104 break;
8105 default:
8106 return E_FAIL;
8110 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8112 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8113 return E_FAIL;
8116 * STGM_CREATE | STGM_CONVERT
8117 * if both are false, STGM_FAILIFTHERE is set to TRUE
8119 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8120 return E_FAIL;
8123 * STGM_NOSCRATCH requires STGM_TRANSACTED
8125 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8126 return E_FAIL;
8129 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8130 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8132 if ( (stgm & STGM_NOSNAPSHOT) &&
8133 (!(stgm & STGM_TRANSACTED) ||
8134 share == STGM_SHARE_EXCLUSIVE ||
8135 share == STGM_SHARE_DENY_WRITE) )
8136 return E_FAIL;
8138 return S_OK;
8141 /****************************************************************************
8142 * GetShareModeFromSTGM
8144 * This method will return a share mode flag from a STGM value.
8145 * The STGM value is assumed valid.
8147 static DWORD GetShareModeFromSTGM(DWORD stgm)
8149 switch (STGM_SHARE_MODE(stgm))
8151 case STGM_SHARE_DENY_NONE:
8152 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8153 case STGM_SHARE_DENY_READ:
8154 return FILE_SHARE_WRITE;
8155 case STGM_SHARE_DENY_WRITE:
8156 return FILE_SHARE_READ;
8157 case STGM_SHARE_EXCLUSIVE:
8158 return 0;
8160 ERR("Invalid share mode!\n");
8161 assert(0);
8162 return 0;
8165 /****************************************************************************
8166 * GetAccessModeFromSTGM
8168 * This method will return an access mode flag from a STGM value.
8169 * The STGM value is assumed valid.
8171 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8173 switch (STGM_ACCESS_MODE(stgm))
8175 case STGM_READ:
8176 return GENERIC_READ;
8177 case STGM_WRITE:
8178 case STGM_READWRITE:
8179 return GENERIC_READ | GENERIC_WRITE;
8181 ERR("Invalid access mode!\n");
8182 assert(0);
8183 return 0;
8186 /****************************************************************************
8187 * GetCreationModeFromSTGM
8189 * This method will return a creation mode flag from a STGM value.
8190 * The STGM value is assumed valid.
8192 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8194 switch(STGM_CREATE_MODE(stgm))
8196 case STGM_CREATE:
8197 return CREATE_ALWAYS;
8198 case STGM_CONVERT:
8199 FIXME("STGM_CONVERT not implemented!\n");
8200 return CREATE_NEW;
8201 case STGM_FAILIFTHERE:
8202 return CREATE_NEW;
8204 ERR("Invalid create mode!\n");
8205 assert(0);
8206 return 0;
8210 /*************************************************************************
8211 * OLECONVERT_LoadOLE10 [Internal]
8213 * Loads the OLE10 STREAM to memory
8215 * PARAMS
8216 * pOleStream [I] The OLESTREAM
8217 * pData [I] Data Structure for the OLESTREAM Data
8219 * RETURNS
8220 * Success: S_OK
8221 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8222 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8224 * NOTES
8225 * This function is used by OleConvertOLESTREAMToIStorage only.
8227 * Memory allocated for pData must be freed by the caller
8229 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8231 DWORD dwSize;
8232 HRESULT hRes = S_OK;
8233 int nTryCnt=0;
8234 int max_try = 6;
8236 pData->pData = NULL;
8237 pData->pstrOleObjFileName = NULL;
8239 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8241 /* Get the OleID */
8242 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8243 if(dwSize != sizeof(pData->dwOleID))
8245 hRes = CONVERT10_E_OLESTREAM_GET;
8247 else if(pData->dwOleID != OLESTREAM_ID)
8249 hRes = CONVERT10_E_OLESTREAM_FMT;
8251 else
8253 hRes = S_OK;
8254 break;
8258 if(hRes == S_OK)
8260 /* Get the TypeID... more info needed for this field */
8261 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8262 if(dwSize != sizeof(pData->dwTypeID))
8264 hRes = CONVERT10_E_OLESTREAM_GET;
8267 if(hRes == S_OK)
8269 if(pData->dwTypeID != 0)
8271 /* Get the length of the OleTypeName */
8272 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8273 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8275 hRes = CONVERT10_E_OLESTREAM_GET;
8278 if(hRes == S_OK)
8280 if(pData->dwOleTypeNameLength > 0)
8282 /* Get the OleTypeName */
8283 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8284 if(dwSize != pData->dwOleTypeNameLength)
8286 hRes = CONVERT10_E_OLESTREAM_GET;
8290 if(bStrem1)
8292 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8293 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8295 hRes = CONVERT10_E_OLESTREAM_GET;
8297 if(hRes == S_OK)
8299 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8300 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8301 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8302 if(pData->pstrOleObjFileName)
8304 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8305 if(dwSize != pData->dwOleObjFileNameLength)
8307 hRes = CONVERT10_E_OLESTREAM_GET;
8310 else
8311 hRes = CONVERT10_E_OLESTREAM_GET;
8314 else
8316 /* Get the Width of the Metafile */
8317 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8318 if(dwSize != sizeof(pData->dwMetaFileWidth))
8320 hRes = CONVERT10_E_OLESTREAM_GET;
8322 if(hRes == S_OK)
8324 /* Get the Height of the Metafile */
8325 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8326 if(dwSize != sizeof(pData->dwMetaFileHeight))
8328 hRes = CONVERT10_E_OLESTREAM_GET;
8332 if(hRes == S_OK)
8334 /* Get the Length of the Data */
8335 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8336 if(dwSize != sizeof(pData->dwDataLength))
8338 hRes = CONVERT10_E_OLESTREAM_GET;
8342 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8344 if(!bStrem1) /* if it is a second OLE stream data */
8346 pData->dwDataLength -= 8;
8347 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8348 if(dwSize != sizeof(pData->strUnknown))
8350 hRes = CONVERT10_E_OLESTREAM_GET;
8354 if(hRes == S_OK)
8356 if(pData->dwDataLength > 0)
8358 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8360 /* Get Data (ex. IStorage, Metafile, or BMP) */
8361 if(pData->pData)
8363 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8364 if(dwSize != pData->dwDataLength)
8366 hRes = CONVERT10_E_OLESTREAM_GET;
8369 else
8371 hRes = CONVERT10_E_OLESTREAM_GET;
8377 return hRes;
8380 /*************************************************************************
8381 * OLECONVERT_SaveOLE10 [Internal]
8383 * Saves the OLE10 STREAM From memory
8385 * PARAMS
8386 * pData [I] Data Structure for the OLESTREAM Data
8387 * pOleStream [I] The OLESTREAM to save
8389 * RETURNS
8390 * Success: S_OK
8391 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8393 * NOTES
8394 * This function is used by OleConvertIStorageToOLESTREAM only.
8397 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8399 DWORD dwSize;
8400 HRESULT hRes = S_OK;
8403 /* Set the OleID */
8404 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8405 if(dwSize != sizeof(pData->dwOleID))
8407 hRes = CONVERT10_E_OLESTREAM_PUT;
8410 if(hRes == S_OK)
8412 /* Set the TypeID */
8413 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8414 if(dwSize != sizeof(pData->dwTypeID))
8416 hRes = CONVERT10_E_OLESTREAM_PUT;
8420 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8422 /* Set the Length of the OleTypeName */
8423 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8424 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8426 hRes = CONVERT10_E_OLESTREAM_PUT;
8429 if(hRes == S_OK)
8431 if(pData->dwOleTypeNameLength > 0)
8433 /* Set the OleTypeName */
8434 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8435 if(dwSize != pData->dwOleTypeNameLength)
8437 hRes = CONVERT10_E_OLESTREAM_PUT;
8442 if(hRes == S_OK)
8444 /* Set the width of the Metafile */
8445 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8446 if(dwSize != sizeof(pData->dwMetaFileWidth))
8448 hRes = CONVERT10_E_OLESTREAM_PUT;
8452 if(hRes == S_OK)
8454 /* Set the height of the Metafile */
8455 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8456 if(dwSize != sizeof(pData->dwMetaFileHeight))
8458 hRes = CONVERT10_E_OLESTREAM_PUT;
8462 if(hRes == S_OK)
8464 /* Set the length of the Data */
8465 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8466 if(dwSize != sizeof(pData->dwDataLength))
8468 hRes = CONVERT10_E_OLESTREAM_PUT;
8472 if(hRes == S_OK)
8474 if(pData->dwDataLength > 0)
8476 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8477 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8478 if(dwSize != pData->dwDataLength)
8480 hRes = CONVERT10_E_OLESTREAM_PUT;
8485 return hRes;
8488 /*************************************************************************
8489 * OLECONVERT_GetOLE20FromOLE10[Internal]
8491 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8492 * opens it, and copies the content to the dest IStorage for
8493 * OleConvertOLESTREAMToIStorage
8496 * PARAMS
8497 * pDestStorage [I] The IStorage to copy the data to
8498 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8499 * nBufferLength [I] The size of the buffer
8501 * RETURNS
8502 * Nothing
8504 * NOTES
8508 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8510 HRESULT hRes;
8511 HANDLE hFile;
8512 IStorage *pTempStorage;
8513 DWORD dwNumOfBytesWritten;
8514 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8515 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8517 /* Create a temp File */
8518 GetTempPathW(MAX_PATH, wstrTempDir);
8519 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8520 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8522 if(hFile != INVALID_HANDLE_VALUE)
8524 /* Write IStorage Data to File */
8525 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8526 CloseHandle(hFile);
8528 /* Open and copy temp storage to the Dest Storage */
8529 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8530 if(hRes == S_OK)
8532 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8533 IStorage_Release(pTempStorage);
8535 DeleteFileW(wstrTempFile);
8540 /*************************************************************************
8541 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8543 * Saves the OLE10 STREAM From memory
8545 * PARAMS
8546 * pStorage [I] The Src IStorage to copy
8547 * pData [I] The Dest Memory to write to.
8549 * RETURNS
8550 * The size in bytes allocated for pData
8552 * NOTES
8553 * Memory allocated for pData must be freed by the caller
8555 * Used by OleConvertIStorageToOLESTREAM only.
8558 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8560 HANDLE hFile;
8561 HRESULT hRes;
8562 DWORD nDataLength = 0;
8563 IStorage *pTempStorage;
8564 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8565 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8567 *pData = NULL;
8569 /* Create temp Storage */
8570 GetTempPathW(MAX_PATH, wstrTempDir);
8571 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8572 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8574 if(hRes == S_OK)
8576 /* Copy Src Storage to the Temp Storage */
8577 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8578 IStorage_Release(pTempStorage);
8580 /* Open Temp Storage as a file and copy to memory */
8581 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8582 if(hFile != INVALID_HANDLE_VALUE)
8584 nDataLength = GetFileSize(hFile, NULL);
8585 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8586 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8587 CloseHandle(hFile);
8589 DeleteFileW(wstrTempFile);
8591 return nDataLength;
8594 /*************************************************************************
8595 * STORAGE_CreateOleStream [Internal]
8597 * Creates the "\001OLE" stream in the IStorage if necessary.
8599 * PARAMS
8600 * storage [I] Dest storage to create the stream in
8601 * flags [I] flags to be set for newly created stream
8603 * RETURNS
8604 * HRESULT return value
8606 * NOTES
8608 * This stream is still unknown, MS Word seems to have extra data
8609 * but since the data is stored in the OLESTREAM there should be
8610 * no need to recreate the stream. If the stream is manually
8611 * deleted it will create it with this default data.
8614 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
8616 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
8617 static const DWORD version_magic = 0x02000001;
8618 IStream *stream;
8619 HRESULT hr;
8621 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
8622 if (hr == S_OK)
8624 struct empty_1ole_stream {
8625 DWORD version_magic;
8626 DWORD flags;
8627 DWORD update_options;
8628 DWORD reserved;
8629 DWORD mon_stream_size;
8631 struct empty_1ole_stream stream_data;
8633 stream_data.version_magic = version_magic;
8634 stream_data.flags = flags;
8635 stream_data.update_options = 0;
8636 stream_data.reserved = 0;
8637 stream_data.mon_stream_size = 0;
8639 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
8640 IStream_Release(stream);
8643 return hr;
8646 /* write a string to a stream, preceded by its length */
8647 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8649 HRESULT r;
8650 LPSTR str;
8651 DWORD len = 0;
8653 if( string )
8654 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8655 r = IStream_Write( stm, &len, sizeof(len), NULL);
8656 if( FAILED( r ) )
8657 return r;
8658 if(len == 0)
8659 return r;
8660 str = CoTaskMemAlloc( len );
8661 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8662 r = IStream_Write( stm, str, len, NULL);
8663 CoTaskMemFree( str );
8664 return r;
8667 /* read a string preceded by its length from a stream */
8668 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8670 HRESULT r;
8671 DWORD len, count = 0;
8672 LPSTR str;
8673 LPWSTR wstr;
8675 r = IStream_Read( stm, &len, sizeof(len), &count );
8676 if( FAILED( r ) )
8677 return r;
8678 if( count != sizeof(len) )
8679 return E_OUTOFMEMORY;
8681 TRACE("%d bytes\n",len);
8683 str = CoTaskMemAlloc( len );
8684 if( !str )
8685 return E_OUTOFMEMORY;
8686 count = 0;
8687 r = IStream_Read( stm, str, len, &count );
8688 if( FAILED( r ) )
8689 return r;
8690 if( count != len )
8692 CoTaskMemFree( str );
8693 return E_OUTOFMEMORY;
8696 TRACE("Read string %s\n",debugstr_an(str,len));
8698 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8699 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8700 if( wstr )
8701 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8702 CoTaskMemFree( str );
8704 *string = wstr;
8706 return r;
8710 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8711 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8713 IStream *pstm;
8714 HRESULT r = S_OK;
8715 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8717 static const BYTE unknown1[12] =
8718 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8719 0xFF, 0xFF, 0xFF, 0xFF};
8720 static const BYTE unknown2[16] =
8721 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8722 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8724 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8725 debugstr_w(lpszUserType), debugstr_w(szClipName),
8726 debugstr_w(szProgIDName));
8728 /* Create a CompObj stream */
8729 r = IStorage_CreateStream(pstg, szwStreamName,
8730 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8731 if( FAILED (r) )
8732 return r;
8734 /* Write CompObj Structure to stream */
8735 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8737 if( SUCCEEDED( r ) )
8738 r = WriteClassStm( pstm, clsid );
8740 if( SUCCEEDED( r ) )
8741 r = STREAM_WriteString( pstm, lpszUserType );
8742 if( SUCCEEDED( r ) )
8743 r = STREAM_WriteString( pstm, szClipName );
8744 if( SUCCEEDED( r ) )
8745 r = STREAM_WriteString( pstm, szProgIDName );
8746 if( SUCCEEDED( r ) )
8747 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8749 IStream_Release( pstm );
8751 return r;
8754 /***********************************************************************
8755 * WriteFmtUserTypeStg (OLE32.@)
8757 HRESULT WINAPI WriteFmtUserTypeStg(
8758 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8760 HRESULT r;
8761 WCHAR szwClipName[0x40];
8762 CLSID clsid = CLSID_NULL;
8763 LPWSTR wstrProgID = NULL;
8764 DWORD n;
8766 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8768 /* get the clipboard format name */
8769 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8770 szwClipName[n]=0;
8772 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8774 /* FIXME: There's room to save a CLSID and its ProgID, but
8775 the CLSID is not looked up in the registry and in all the
8776 tests I wrote it was CLSID_NULL. Where does it come from?
8779 /* get the real program ID. This may fail, but that's fine */
8780 ProgIDFromCLSID(&clsid, &wstrProgID);
8782 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8784 r = STORAGE_WriteCompObj( pstg, &clsid,
8785 lpszUserType, szwClipName, wstrProgID );
8787 CoTaskMemFree(wstrProgID);
8789 return r;
8793 /******************************************************************************
8794 * ReadFmtUserTypeStg [OLE32.@]
8796 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8798 HRESULT r;
8799 IStream *stm = 0;
8800 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8801 unsigned char unknown1[12];
8802 unsigned char unknown2[16];
8803 DWORD count;
8804 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8805 CLSID clsid;
8807 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8809 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8810 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8811 if( FAILED ( r ) )
8813 WARN("Failed to open stream r = %08x\n", r);
8814 return r;
8817 /* read the various parts of the structure */
8818 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8819 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8820 goto end;
8821 r = ReadClassStm( stm, &clsid );
8822 if( FAILED( r ) )
8823 goto end;
8825 r = STREAM_ReadString( stm, &szCLSIDName );
8826 if( FAILED( r ) )
8827 goto end;
8829 r = STREAM_ReadString( stm, &szOleTypeName );
8830 if( FAILED( r ) )
8831 goto end;
8833 r = STREAM_ReadString( stm, &szProgIDName );
8834 if( FAILED( r ) )
8835 goto end;
8837 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8838 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8839 goto end;
8841 /* ok, success... now we just need to store what we found */
8842 if( pcf )
8843 *pcf = RegisterClipboardFormatW( szOleTypeName );
8845 if( lplpszUserType )
8847 *lplpszUserType = szCLSIDName;
8848 szCLSIDName = NULL;
8851 end:
8852 CoTaskMemFree( szCLSIDName );
8853 CoTaskMemFree( szOleTypeName );
8854 CoTaskMemFree( szProgIDName );
8855 IStream_Release( stm );
8857 return r;
8861 /*************************************************************************
8862 * OLECONVERT_CreateCompObjStream [Internal]
8864 * Creates a "\001CompObj" is the destination IStorage if necessary.
8866 * PARAMS
8867 * pStorage [I] The dest IStorage to create the CompObj Stream
8868 * if necessary.
8869 * strOleTypeName [I] The ProgID
8871 * RETURNS
8872 * Success: S_OK
8873 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8875 * NOTES
8876 * This function is used by OleConvertOLESTREAMToIStorage only.
8878 * The stream data is stored in the OLESTREAM and there should be
8879 * no need to recreate the stream. If the stream is manually
8880 * deleted it will attempt to create it by querying the registry.
8884 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8886 IStream *pStream;
8887 HRESULT hStorageRes, hRes = S_OK;
8888 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8889 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8890 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8892 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8893 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8895 /* Initialize the CompObj structure */
8896 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8897 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8898 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8901 /* Create a CompObj stream if it doesn't exist */
8902 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8903 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8904 if(hStorageRes == S_OK)
8906 /* copy the OleTypeName to the compobj struct */
8907 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8908 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8910 /* copy the OleTypeName to the compobj struct */
8911 /* Note: in the test made, these were Identical */
8912 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8913 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8915 /* Get the CLSID */
8916 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8917 bufferW, OLESTREAM_MAX_STR_LEN );
8918 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8920 if(hRes == S_OK)
8922 HKEY hKey;
8923 LONG hErr;
8924 /* Get the CLSID Default Name from the Registry */
8925 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
8926 if(hErr == ERROR_SUCCESS)
8928 char strTemp[OLESTREAM_MAX_STR_LEN];
8929 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8930 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8931 if(hErr == ERROR_SUCCESS)
8933 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8935 RegCloseKey(hKey);
8939 /* Write CompObj Structure to stream */
8940 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8942 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8944 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8945 if(IStorageCompObj.dwCLSIDNameLength > 0)
8947 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8949 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8950 if(IStorageCompObj.dwOleTypeNameLength > 0)
8952 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8954 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8955 if(IStorageCompObj.dwProgIDNameLength > 0)
8957 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8959 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8960 IStream_Release(pStream);
8962 return hRes;
8966 /*************************************************************************
8967 * OLECONVERT_CreateOlePresStream[Internal]
8969 * Creates the "\002OlePres000" Stream with the Metafile data
8971 * PARAMS
8972 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8973 * dwExtentX [I] Width of the Metafile
8974 * dwExtentY [I] Height of the Metafile
8975 * pData [I] Metafile data
8976 * dwDataLength [I] Size of the Metafile data
8978 * RETURNS
8979 * Success: S_OK
8980 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8982 * NOTES
8983 * This function is used by OleConvertOLESTREAMToIStorage only.
8986 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8988 HRESULT hRes;
8989 IStream *pStream;
8990 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8991 BYTE pOlePresStreamHeader [] =
8993 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8994 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8995 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8996 0x00, 0x00, 0x00, 0x00
8999 BYTE pOlePresStreamHeaderEmpty [] =
9001 0x00, 0x00, 0x00, 0x00,
9002 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9003 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9004 0x00, 0x00, 0x00, 0x00
9007 /* Create the OlePres000 Stream */
9008 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9009 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9011 if(hRes == S_OK)
9013 DWORD nHeaderSize;
9014 OLECONVERT_ISTORAGE_OLEPRES OlePres;
9016 memset(&OlePres, 0, sizeof(OlePres));
9017 /* Do we have any metafile data to save */
9018 if(dwDataLength > 0)
9020 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
9021 nHeaderSize = sizeof(pOlePresStreamHeader);
9023 else
9025 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
9026 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
9028 /* Set width and height of the metafile */
9029 OlePres.dwExtentX = dwExtentX;
9030 OlePres.dwExtentY = -dwExtentY;
9032 /* Set Data and Length */
9033 if(dwDataLength > sizeof(METAFILEPICT16))
9035 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
9036 OlePres.pData = &(pData[8]);
9038 /* Save OlePres000 Data to Stream */
9039 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
9040 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
9041 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
9042 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
9043 if(OlePres.dwSize > 0)
9045 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
9047 IStream_Release(pStream);
9051 /*************************************************************************
9052 * OLECONVERT_CreateOle10NativeStream [Internal]
9054 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9056 * PARAMS
9057 * pStorage [I] Dest storage to create the stream in
9058 * pData [I] Ole10 Native Data (ex. bmp)
9059 * dwDataLength [I] Size of the Ole10 Native Data
9061 * RETURNS
9062 * Nothing
9064 * NOTES
9065 * This function is used by OleConvertOLESTREAMToIStorage only.
9067 * Might need to verify the data and return appropriate error message
9070 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9072 HRESULT hRes;
9073 IStream *pStream;
9074 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9076 /* Create the Ole10Native Stream */
9077 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9078 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9080 if(hRes == S_OK)
9082 /* Write info to stream */
9083 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9084 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9085 IStream_Release(pStream);
9090 /*************************************************************************
9091 * OLECONVERT_GetOLE10ProgID [Internal]
9093 * Finds the ProgID (or OleTypeID) from the IStorage
9095 * PARAMS
9096 * pStorage [I] The Src IStorage to get the ProgID
9097 * strProgID [I] the ProgID string to get
9098 * dwSize [I] the size of the string
9100 * RETURNS
9101 * Success: S_OK
9102 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9104 * NOTES
9105 * This function is used by OleConvertIStorageToOLESTREAM only.
9109 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9111 HRESULT hRes;
9112 IStream *pStream;
9113 LARGE_INTEGER iSeekPos;
9114 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9115 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9117 /* Open the CompObj Stream */
9118 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9119 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9120 if(hRes == S_OK)
9123 /*Get the OleType from the CompObj Stream */
9124 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9125 iSeekPos.u.HighPart = 0;
9127 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9128 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9129 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9130 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9131 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9132 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9133 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9135 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9136 if(*dwSize > 0)
9138 IStream_Read(pStream, strProgID, *dwSize, NULL);
9140 IStream_Release(pStream);
9142 else
9144 STATSTG stat;
9145 LPOLESTR wstrProgID;
9147 /* Get the OleType from the registry */
9148 REFCLSID clsid = &(stat.clsid);
9149 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9150 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9151 if(hRes == S_OK)
9153 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9154 CoTaskMemFree(wstrProgID);
9158 return hRes;
9161 /*************************************************************************
9162 * OLECONVERT_GetOle10PresData [Internal]
9164 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9166 * PARAMS
9167 * pStorage [I] Src IStroage
9168 * pOleStream [I] Dest OleStream Mem Struct
9170 * RETURNS
9171 * Nothing
9173 * NOTES
9174 * This function is used by OleConvertIStorageToOLESTREAM only.
9176 * Memory allocated for pData must be freed by the caller
9180 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9183 HRESULT hRes;
9184 IStream *pStream;
9185 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9187 /* Initialize Default data for OLESTREAM */
9188 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9189 pOleStreamData[0].dwTypeID = 2;
9190 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9191 pOleStreamData[1].dwTypeID = 0;
9192 pOleStreamData[0].dwMetaFileWidth = 0;
9193 pOleStreamData[0].dwMetaFileHeight = 0;
9194 pOleStreamData[0].pData = NULL;
9195 pOleStreamData[1].pData = NULL;
9197 /* Open Ole10Native Stream */
9198 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9199 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9200 if(hRes == S_OK)
9203 /* Read Size and Data */
9204 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9205 if(pOleStreamData->dwDataLength > 0)
9207 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9208 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9210 IStream_Release(pStream);
9216 /*************************************************************************
9217 * OLECONVERT_GetOle20PresData[Internal]
9219 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9221 * PARAMS
9222 * pStorage [I] Src IStroage
9223 * pOleStreamData [I] Dest OleStream Mem Struct
9225 * RETURNS
9226 * Nothing
9228 * NOTES
9229 * This function is used by OleConvertIStorageToOLESTREAM only.
9231 * Memory allocated for pData must be freed by the caller
9233 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9235 HRESULT hRes;
9236 IStream *pStream;
9237 OLECONVERT_ISTORAGE_OLEPRES olePress;
9238 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9240 /* Initialize Default data for OLESTREAM */
9241 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9242 pOleStreamData[0].dwTypeID = 2;
9243 pOleStreamData[0].dwMetaFileWidth = 0;
9244 pOleStreamData[0].dwMetaFileHeight = 0;
9245 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9246 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9247 pOleStreamData[1].dwTypeID = 0;
9248 pOleStreamData[1].dwOleTypeNameLength = 0;
9249 pOleStreamData[1].strOleTypeName[0] = 0;
9250 pOleStreamData[1].dwMetaFileWidth = 0;
9251 pOleStreamData[1].dwMetaFileHeight = 0;
9252 pOleStreamData[1].pData = NULL;
9253 pOleStreamData[1].dwDataLength = 0;
9256 /* Open OlePress000 stream */
9257 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9258 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9259 if(hRes == S_OK)
9261 LARGE_INTEGER iSeekPos;
9262 METAFILEPICT16 MetaFilePict;
9263 static const char strMetafilePictName[] = "METAFILEPICT";
9265 /* Set the TypeID for a Metafile */
9266 pOleStreamData[1].dwTypeID = 5;
9268 /* Set the OleTypeName to Metafile */
9269 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9270 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9272 iSeekPos.u.HighPart = 0;
9273 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9275 /* Get Presentation Data */
9276 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9277 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9278 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9279 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9281 /*Set width and Height */
9282 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9283 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9284 if(olePress.dwSize > 0)
9286 /* Set Length */
9287 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9289 /* Set MetaFilePict struct */
9290 MetaFilePict.mm = 8;
9291 MetaFilePict.xExt = olePress.dwExtentX;
9292 MetaFilePict.yExt = olePress.dwExtentY;
9293 MetaFilePict.hMF = 0;
9295 /* Get Metafile Data */
9296 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9297 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9298 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9300 IStream_Release(pStream);
9304 /*************************************************************************
9305 * OleConvertOLESTREAMToIStorage [OLE32.@]
9307 * Read info on MSDN
9309 * TODO
9310 * DVTARGETDEVICE parameter is not handled
9311 * Still unsure of some mem fields for OLE 10 Stream
9312 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9313 * and "\001OLE" streams
9316 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9317 LPOLESTREAM pOleStream,
9318 LPSTORAGE pstg,
9319 const DVTARGETDEVICE* ptd)
9321 int i;
9322 HRESULT hRes=S_OK;
9323 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9325 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9327 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9329 if(ptd != NULL)
9331 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9334 if(pstg == NULL || pOleStream == NULL)
9336 hRes = E_INVALIDARG;
9339 if(hRes == S_OK)
9341 /* Load the OLESTREAM to Memory */
9342 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9345 if(hRes == S_OK)
9347 /* Load the OLESTREAM to Memory (part 2)*/
9348 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9351 if(hRes == S_OK)
9354 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9356 /* Do we have the IStorage Data in the OLESTREAM */
9357 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9359 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9360 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9362 else
9364 /* It must be an original OLE 1.0 source */
9365 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9368 else
9370 /* It must be an original OLE 1.0 source */
9371 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9374 /* Create CompObj Stream if necessary */
9375 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9376 if(hRes == S_OK)
9378 /*Create the Ole Stream if necessary */
9379 STORAGE_CreateOleStream(pstg, 0);
9384 /* Free allocated memory */
9385 for(i=0; i < 2; i++)
9387 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9388 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9389 pOleStreamData[i].pstrOleObjFileName = NULL;
9391 return hRes;
9394 /*************************************************************************
9395 * OleConvertIStorageToOLESTREAM [OLE32.@]
9397 * Read info on MSDN
9399 * Read info on MSDN
9401 * TODO
9402 * Still unsure of some mem fields for OLE 10 Stream
9403 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9404 * and "\001OLE" streams.
9407 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9408 LPSTORAGE pstg,
9409 LPOLESTREAM pOleStream)
9411 int i;
9412 HRESULT hRes = S_OK;
9413 IStream *pStream;
9414 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9415 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9417 TRACE("%p %p\n", pstg, pOleStream);
9419 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9421 if(pstg == NULL || pOleStream == NULL)
9423 hRes = E_INVALIDARG;
9425 if(hRes == S_OK)
9427 /* Get the ProgID */
9428 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9429 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9431 if(hRes == S_OK)
9433 /* Was it originally Ole10 */
9434 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9435 if(hRes == S_OK)
9437 IStream_Release(pStream);
9438 /* Get Presentation Data for Ole10Native */
9439 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9441 else
9443 /* Get Presentation Data (OLE20) */
9444 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9447 /* Save OLESTREAM */
9448 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9449 if(hRes == S_OK)
9451 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9456 /* Free allocated memory */
9457 for(i=0; i < 2; i++)
9459 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9462 return hRes;
9465 enum stream_1ole_flags {
9466 OleStream_LinkedObject = 0x00000001,
9467 OleStream_Convert = 0x00000004
9470 /***********************************************************************
9471 * GetConvertStg (OLE32.@)
9473 HRESULT WINAPI GetConvertStg(IStorage *stg)
9475 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9476 static const DWORD version_magic = 0x02000001;
9477 DWORD header[2];
9478 IStream *stream;
9479 HRESULT hr;
9481 TRACE("%p\n", stg);
9483 if (!stg) return E_INVALIDARG;
9485 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
9486 if (FAILED(hr)) return hr;
9488 hr = IStream_Read(stream, header, sizeof(header), NULL);
9489 IStream_Release(stream);
9490 if (FAILED(hr)) return hr;
9492 if (header[0] != version_magic)
9494 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
9495 return E_FAIL;
9498 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
9501 /***********************************************************************
9502 * SetConvertStg (OLE32.@)
9504 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
9506 DWORD flags = convert ? OleStream_Convert : 0;
9507 HRESULT hr;
9509 TRACE("(%p, %d)\n", storage, convert);
9511 hr = STORAGE_CreateOleStream(storage, flags);
9512 if (hr == STG_E_FILEALREADYEXISTS)
9514 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9515 IStream *stream;
9516 DWORD header[2];
9518 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
9519 if (FAILED(hr)) return hr;
9521 hr = IStream_Read(stream, header, sizeof(header), NULL);
9522 if (FAILED(hr))
9524 IStream_Release(stream);
9525 return hr;
9528 /* update flag if differs */
9529 if ((header[1] ^ flags) & OleStream_Convert)
9531 LARGE_INTEGER pos;
9533 if (header[1] & OleStream_Convert)
9534 flags = header[1] & ~OleStream_Convert;
9535 else
9536 flags = header[1] | OleStream_Convert;
9538 pos.QuadPart = sizeof(DWORD);
9539 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
9540 if (FAILED(hr))
9542 IStream_Release(stream);
9543 return hr;
9546 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
9548 IStream_Release(stream);
9551 return hr;
9554 /******************************************************************************
9555 * StgIsStorageFile [OLE32.@]
9556 * Verify if the file contains a storage object
9558 * PARAMS
9559 * fn [ I] Filename
9561 * RETURNS
9562 * S_OK if file has magic bytes as a storage object
9563 * S_FALSE if file is not storage
9565 HRESULT WINAPI
9566 StgIsStorageFile(LPCOLESTR fn)
9568 HANDLE hf;
9569 BYTE magic[8];
9570 DWORD bytes_read;
9572 TRACE("%s\n", debugstr_w(fn));
9573 hf = CreateFileW(fn, GENERIC_READ,
9574 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9575 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9577 if (hf == INVALID_HANDLE_VALUE)
9578 return STG_E_FILENOTFOUND;
9580 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9582 WARN(" unable to read file\n");
9583 CloseHandle(hf);
9584 return S_FALSE;
9587 CloseHandle(hf);
9589 if (bytes_read != 8) {
9590 TRACE(" too short\n");
9591 return S_FALSE;
9594 if (!memcmp(magic,STORAGE_magic,8)) {
9595 TRACE(" -> YES\n");
9596 return S_OK;
9599 TRACE(" -> Invalid header.\n");
9600 return S_FALSE;
9603 /***********************************************************************
9604 * WriteClassStm (OLE32.@)
9606 * Writes a CLSID to a stream.
9608 * PARAMS
9609 * pStm [I] Stream to write to.
9610 * rclsid [I] CLSID to write.
9612 * RETURNS
9613 * Success: S_OK.
9614 * Failure: HRESULT code.
9616 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9618 TRACE("(%p,%p)\n",pStm,rclsid);
9620 if (!pStm || !rclsid)
9621 return E_INVALIDARG;
9623 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9626 /***********************************************************************
9627 * ReadClassStm (OLE32.@)
9629 * Reads a CLSID from a stream.
9631 * PARAMS
9632 * pStm [I] Stream to read from.
9633 * rclsid [O] CLSID to read.
9635 * RETURNS
9636 * Success: S_OK.
9637 * Failure: HRESULT code.
9639 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9641 ULONG nbByte;
9642 HRESULT res;
9644 TRACE("(%p,%p)\n",pStm,pclsid);
9646 if (!pStm || !pclsid)
9647 return E_INVALIDARG;
9649 /* clear the output args */
9650 *pclsid = CLSID_NULL;
9652 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9654 if (FAILED(res))
9655 return res;
9657 if (nbByte != sizeof(CLSID))
9658 return STG_E_READFAULT;
9659 else
9660 return S_OK;