ole32: COM cleanup for IStorage interface of base storage implementation.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob4e802f57a79160c18153a278d4e1f3161a6534df
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
70 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
73 /****************************************************************************
74 * Storage32InternalImpl definitions.
76 * Definition of the implementation structure for the IStorage32 interface.
77 * This one implements the IStorage32 interface for storage that are
78 * inside another storage.
80 struct StorageInternalImpl
82 struct StorageBaseImpl base;
85 * Entry in the parent's stream tracking list
87 struct list ParentListEntry;
89 StorageBaseImpl *parentStorage;
91 typedef struct StorageInternalImpl StorageInternalImpl;
93 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
94 static const IStorageVtbl Storage32InternalImpl_Vtbl;
96 /* Method definitions for the Storage32InternalImpl class. */
97 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
98 DWORD openFlags, DirRef storageDirEntry);
99 static void StorageImpl_Destroy(StorageBaseImpl* iface);
100 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
101 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
102 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
103 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
104 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
105 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
106 static void StorageImpl_SaveFileHeader(StorageImpl* This);
108 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
109 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
110 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
111 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
112 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
114 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
115 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
116 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
118 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
119 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
120 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
121 ULONG blockIndex, ULONG offset, DWORD value);
122 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
123 ULONG blockIndex, ULONG offset, DWORD* value);
125 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
126 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
128 typedef struct TransactedDirEntry
130 /* If applicable, a reference to the original DirEntry in the transacted
131 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
132 DirRef transactedParentEntry;
134 /* True if this entry is being used. */
135 int inuse;
137 /* True if data is up to date. */
138 int read;
140 /* True if this entry has been modified. */
141 int dirty;
143 /* True if this entry's stream has been modified. */
144 int stream_dirty;
146 /* True if this entry has been deleted in the transacted storage, but the
147 * delete has not yet been committed. */
148 int deleted;
150 /* If this entry's stream has been modified, a reference to where the stream
151 * is stored in the snapshot file. */
152 DirRef stream_entry;
154 /* This directory entry's data, including any changes that have been made. */
155 DirEntry data;
157 /* A reference to the parent of this node. This is only valid while we are
158 * committing changes. */
159 DirRef parent;
161 /* A reference to a newly-created entry in the transacted parent. This is
162 * always equal to transactedParentEntry except when committing changes. */
163 DirRef newTransactedParentEntry;
164 } TransactedDirEntry;
166 /****************************************************************************
167 * Transacted storage object.
169 typedef struct TransactedSnapshotImpl
171 struct StorageBaseImpl base;
174 * Modified streams are temporarily saved to the scratch file.
176 StorageBaseImpl *scratch;
178 /* The directory structure is kept here, so that we can track how these
179 * entries relate to those in the parent storage. */
180 TransactedDirEntry *entries;
181 ULONG entries_size;
182 ULONG firstFreeEntry;
185 * Changes are committed to the transacted parent.
187 StorageBaseImpl *transactedParent;
188 } TransactedSnapshotImpl;
190 /* Generic function to create a transacted wrapper for a direct storage object. */
191 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
193 /* OLESTREAM memory structure to use for Get and Put Routines */
194 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
195 typedef struct
197 DWORD dwOleID;
198 DWORD dwTypeID;
199 DWORD dwOleTypeNameLength;
200 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
201 CHAR *pstrOleObjFileName;
202 DWORD dwOleObjFileNameLength;
203 DWORD dwMetaFileWidth;
204 DWORD dwMetaFileHeight;
205 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
206 DWORD dwDataLength;
207 BYTE *pData;
208 }OLECONVERT_OLESTREAM_DATA;
210 /* CompObj Stream structure */
211 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
212 typedef struct
214 BYTE byUnknown1[12];
215 CLSID clsid;
216 DWORD dwCLSIDNameLength;
217 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
218 DWORD dwOleTypeNameLength;
219 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
220 DWORD dwProgIDNameLength;
221 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
222 BYTE byUnknown2[16];
223 }OLECONVERT_ISTORAGE_COMPOBJ;
226 /* Ole Presentation Stream structure */
227 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
228 typedef struct
230 BYTE byUnknown1[28];
231 DWORD dwExtentX;
232 DWORD dwExtentY;
233 DWORD dwSize;
234 BYTE *pData;
235 }OLECONVERT_ISTORAGE_OLEPRES;
239 /***********************************************************************
240 * Forward declaration of internal functions used by the method DestroyElement
242 static HRESULT deleteStorageContents(
243 StorageBaseImpl *parentStorage,
244 DirRef indexToDelete,
245 DirEntry entryDataToDelete);
247 static HRESULT deleteStreamContents(
248 StorageBaseImpl *parentStorage,
249 DirRef indexToDelete,
250 DirEntry entryDataToDelete);
252 static HRESULT removeFromTree(
253 StorageBaseImpl *This,
254 DirRef parentStorageIndex,
255 DirRef deletedIndex);
257 /***********************************************************************
258 * Declaration of the functions used to manipulate DirEntry
261 static HRESULT insertIntoTree(
262 StorageBaseImpl *This,
263 DirRef parentStorageIndex,
264 DirRef newEntryIndex);
266 static LONG entryNameCmp(
267 const OLECHAR *name1,
268 const OLECHAR *name2);
270 static DirRef findElement(
271 StorageBaseImpl *storage,
272 DirRef storageEntry,
273 const OLECHAR *name,
274 DirEntry *data);
276 static HRESULT findTreeParent(
277 StorageBaseImpl *storage,
278 DirRef storageEntry,
279 const OLECHAR *childName,
280 DirEntry *parentData,
281 DirRef *parentEntry,
282 ULONG *relation);
284 /***********************************************************************
285 * Declaration of miscellaneous functions...
287 static HRESULT validateSTGM(DWORD stgmValue);
289 static DWORD GetShareModeFromSTGM(DWORD stgm);
290 static DWORD GetAccessModeFromSTGM(DWORD stgm);
291 static DWORD GetCreationModeFromSTGM(DWORD stgm);
293 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
296 /****************************************************************************
297 * IEnumSTATSTGImpl definitions.
299 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
300 * This class allows iterating through the content of a storage and to find
301 * specific items inside it.
303 struct IEnumSTATSTGImpl
305 IEnumSTATSTG IEnumSTATSTG_iface;
307 LONG ref; /* Reference count */
308 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
309 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
311 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
314 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
316 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
320 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
321 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
323 /************************************************************************
324 ** Block Functions
327 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
329 return (index+1) * This->bigBlockSize;
332 /************************************************************************
333 ** Storage32BaseImpl implementation
335 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
336 ULARGE_INTEGER offset,
337 void* buffer,
338 ULONG size,
339 ULONG* bytesRead)
341 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
344 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
345 ULARGE_INTEGER offset,
346 const void* buffer,
347 const ULONG size,
348 ULONG* bytesWritten)
350 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
353 /************************************************************************
354 * Storage32BaseImpl_QueryInterface (IUnknown)
356 * This method implements the common QueryInterface for all IStorage32
357 * implementations contained in this file.
359 * See Windows documentation for more details on IUnknown methods.
361 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
362 IStorage* iface,
363 REFIID riid,
364 void** ppvObject)
366 StorageBaseImpl *This = impl_from_IStorage(iface);
368 if ( (This==0) || (ppvObject==0) )
369 return E_INVALIDARG;
371 *ppvObject = 0;
373 if (IsEqualGUID(&IID_IUnknown, riid) ||
374 IsEqualGUID(&IID_IStorage, riid))
376 *ppvObject = This;
378 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
380 *ppvObject = &This->pssVtbl;
383 if ((*ppvObject)==0)
384 return E_NOINTERFACE;
386 IStorage_AddRef(iface);
388 return S_OK;
391 /************************************************************************
392 * Storage32BaseImpl_AddRef (IUnknown)
394 * This method implements the common AddRef for all IStorage32
395 * implementations contained in this file.
397 * See Windows documentation for more details on IUnknown methods.
399 static ULONG WINAPI StorageBaseImpl_AddRef(
400 IStorage* iface)
402 StorageBaseImpl *This = impl_from_IStorage(iface);
403 ULONG ref = InterlockedIncrement(&This->ref);
405 TRACE("(%p) AddRef to %d\n", This, ref);
407 return ref;
410 /************************************************************************
411 * Storage32BaseImpl_Release (IUnknown)
413 * This method implements the common Release for all IStorage32
414 * implementations contained in this file.
416 * See Windows documentation for more details on IUnknown methods.
418 static ULONG WINAPI StorageBaseImpl_Release(
419 IStorage* iface)
421 StorageBaseImpl *This = impl_from_IStorage(iface);
423 ULONG ref = InterlockedDecrement(&This->ref);
425 TRACE("(%p) ReleaseRef to %d\n", This, ref);
427 if (ref == 0)
430 * Since we are using a system of base-classes, we want to call the
431 * destructor of the appropriate derived class. To do this, we are
432 * using virtual functions to implement the destructor.
434 StorageBaseImpl_Destroy(This);
437 return ref;
440 /************************************************************************
441 * Storage32BaseImpl_OpenStream (IStorage)
443 * This method will open the specified stream object from the current storage.
445 * See Windows documentation for more details on IStorage methods.
447 static HRESULT WINAPI StorageBaseImpl_OpenStream(
448 IStorage* iface,
449 const OLECHAR* pwcsName, /* [string][in] */
450 void* reserved1, /* [unique][in] */
451 DWORD grfMode, /* [in] */
452 DWORD reserved2, /* [in] */
453 IStream** ppstm) /* [out] */
455 StorageBaseImpl *This = impl_from_IStorage(iface);
456 StgStreamImpl* newStream;
457 DirEntry currentEntry;
458 DirRef streamEntryRef;
459 HRESULT res = STG_E_UNKNOWN;
461 TRACE("(%p, %s, %p, %x, %d, %p)\n",
462 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
464 if ( (pwcsName==NULL) || (ppstm==0) )
466 res = E_INVALIDARG;
467 goto end;
470 *ppstm = NULL;
472 if ( FAILED( validateSTGM(grfMode) ) ||
473 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
475 res = STG_E_INVALIDFLAG;
476 goto end;
480 * As documented.
482 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
484 res = STG_E_INVALIDFUNCTION;
485 goto end;
488 if (This->reverted)
490 res = STG_E_REVERTED;
491 goto end;
495 * Check that we're compatible with the parent's storage mode, but
496 * only if we are not in transacted mode
498 if(!(This->openFlags & STGM_TRANSACTED)) {
499 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
501 res = STG_E_INVALIDFLAG;
502 goto end;
507 * Search for the element with the given name
509 streamEntryRef = findElement(
510 This,
511 This->storageDirEntry,
512 pwcsName,
513 &currentEntry);
516 * If it was found, construct the stream object and return a pointer to it.
518 if ( (streamEntryRef!=DIRENTRY_NULL) &&
519 (currentEntry.stgType==STGTY_STREAM) )
521 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
523 /* A single stream cannot be opened a second time. */
524 res = STG_E_ACCESSDENIED;
525 goto end;
528 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
530 if (newStream)
532 newStream->grfMode = grfMode;
533 *ppstm = &newStream->IStream_iface;
535 IStream_AddRef(*ppstm);
537 res = S_OK;
538 goto end;
541 res = E_OUTOFMEMORY;
542 goto end;
545 res = STG_E_FILENOTFOUND;
547 end:
548 if (res == S_OK)
549 TRACE("<-- IStream %p\n", *ppstm);
550 TRACE("<-- %08x\n", res);
551 return res;
554 /************************************************************************
555 * Storage32BaseImpl_OpenStorage (IStorage)
557 * This method will open a new storage object from the current storage.
559 * See Windows documentation for more details on IStorage methods.
561 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
562 IStorage* iface,
563 const OLECHAR* pwcsName, /* [string][unique][in] */
564 IStorage* pstgPriority, /* [unique][in] */
565 DWORD grfMode, /* [in] */
566 SNB snbExclude, /* [unique][in] */
567 DWORD reserved, /* [in] */
568 IStorage** ppstg) /* [out] */
570 StorageBaseImpl *This = impl_from_IStorage(iface);
571 StorageInternalImpl* newStorage;
572 StorageBaseImpl* newTransactedStorage;
573 DirEntry currentEntry;
574 DirRef storageEntryRef;
575 HRESULT res = STG_E_UNKNOWN;
577 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
578 iface, debugstr_w(pwcsName), pstgPriority,
579 grfMode, snbExclude, reserved, ppstg);
581 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
583 res = E_INVALIDARG;
584 goto end;
587 if (This->openFlags & STGM_SIMPLE)
589 res = STG_E_INVALIDFUNCTION;
590 goto end;
593 /* as documented */
594 if (snbExclude != NULL)
596 res = STG_E_INVALIDPARAMETER;
597 goto end;
600 if ( FAILED( validateSTGM(grfMode) ))
602 res = STG_E_INVALIDFLAG;
603 goto end;
607 * As documented.
609 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
610 (grfMode & STGM_DELETEONRELEASE) ||
611 (grfMode & STGM_PRIORITY) )
613 res = STG_E_INVALIDFUNCTION;
614 goto end;
617 if (This->reverted)
618 return STG_E_REVERTED;
621 * Check that we're compatible with the parent's storage mode,
622 * but only if we are not transacted
624 if(!(This->openFlags & STGM_TRANSACTED)) {
625 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
627 res = STG_E_ACCESSDENIED;
628 goto end;
632 *ppstg = NULL;
634 storageEntryRef = findElement(
635 This,
636 This->storageDirEntry,
637 pwcsName,
638 &currentEntry);
640 if ( (storageEntryRef!=DIRENTRY_NULL) &&
641 (currentEntry.stgType==STGTY_STORAGE) )
643 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
645 /* A single storage cannot be opened a second time. */
646 res = STG_E_ACCESSDENIED;
647 goto end;
650 newStorage = StorageInternalImpl_Construct(
651 This,
652 grfMode,
653 storageEntryRef);
655 if (newStorage != 0)
657 if (grfMode & STGM_TRANSACTED)
659 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
661 if (FAILED(res))
663 HeapFree(GetProcessHeap(), 0, newStorage);
664 goto end;
667 *ppstg = &newTransactedStorage->IStorage_iface;
669 else
671 *ppstg = &newStorage->base.IStorage_iface;
674 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
676 res = S_OK;
677 goto end;
680 res = STG_E_INSUFFICIENTMEMORY;
681 goto end;
684 res = STG_E_FILENOTFOUND;
686 end:
687 TRACE("<-- %08x\n", res);
688 return res;
691 /************************************************************************
692 * Storage32BaseImpl_EnumElements (IStorage)
694 * This method will create an enumerator object that can be used to
695 * retrieve information about all the elements in the storage object.
697 * See Windows documentation for more details on IStorage methods.
699 static HRESULT WINAPI StorageBaseImpl_EnumElements(
700 IStorage* iface,
701 DWORD reserved1, /* [in] */
702 void* reserved2, /* [size_is][unique][in] */
703 DWORD reserved3, /* [in] */
704 IEnumSTATSTG** ppenum) /* [out] */
706 StorageBaseImpl *This = impl_from_IStorage(iface);
707 IEnumSTATSTGImpl* newEnum;
709 TRACE("(%p, %d, %p, %d, %p)\n",
710 iface, reserved1, reserved2, reserved3, ppenum);
712 if ( (This==0) || (ppenum==0))
713 return E_INVALIDARG;
715 if (This->reverted)
716 return STG_E_REVERTED;
718 newEnum = IEnumSTATSTGImpl_Construct(
719 This,
720 This->storageDirEntry);
722 if (newEnum!=0)
724 *ppenum = &newEnum->IEnumSTATSTG_iface;
726 IEnumSTATSTG_AddRef(*ppenum);
728 return S_OK;
731 return E_OUTOFMEMORY;
734 /************************************************************************
735 * Storage32BaseImpl_Stat (IStorage)
737 * This method will retrieve information about this storage object.
739 * See Windows documentation for more details on IStorage methods.
741 static HRESULT WINAPI StorageBaseImpl_Stat(
742 IStorage* iface,
743 STATSTG* pstatstg, /* [out] */
744 DWORD grfStatFlag) /* [in] */
746 StorageBaseImpl *This = impl_from_IStorage(iface);
747 DirEntry currentEntry;
748 HRESULT res = STG_E_UNKNOWN;
750 TRACE("(%p, %p, %x)\n",
751 iface, pstatstg, grfStatFlag);
753 if ( (This==0) || (pstatstg==0))
755 res = E_INVALIDARG;
756 goto end;
759 if (This->reverted)
761 res = STG_E_REVERTED;
762 goto end;
765 res = StorageBaseImpl_ReadDirEntry(
766 This,
767 This->storageDirEntry,
768 &currentEntry);
770 if (SUCCEEDED(res))
772 StorageUtl_CopyDirEntryToSTATSTG(
773 This,
774 pstatstg,
775 &currentEntry,
776 grfStatFlag);
778 pstatstg->grfMode = This->openFlags;
779 pstatstg->grfStateBits = This->stateBits;
782 end:
783 if (res == S_OK)
785 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);
787 TRACE("<-- %08x\n", res);
788 return res;
791 /************************************************************************
792 * Storage32BaseImpl_RenameElement (IStorage)
794 * This method will rename the specified element.
796 * See Windows documentation for more details on IStorage methods.
798 static HRESULT WINAPI StorageBaseImpl_RenameElement(
799 IStorage* iface,
800 const OLECHAR* pwcsOldName, /* [in] */
801 const OLECHAR* pwcsNewName) /* [in] */
803 StorageBaseImpl *This = impl_from_IStorage(iface);
804 DirEntry currentEntry;
805 DirRef currentEntryRef;
807 TRACE("(%p, %s, %s)\n",
808 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
810 if (This->reverted)
811 return STG_E_REVERTED;
813 currentEntryRef = findElement(This,
814 This->storageDirEntry,
815 pwcsNewName,
816 &currentEntry);
818 if (currentEntryRef != DIRENTRY_NULL)
821 * There is already an element with the new name
823 return STG_E_FILEALREADYEXISTS;
827 * Search for the old element name
829 currentEntryRef = findElement(This,
830 This->storageDirEntry,
831 pwcsOldName,
832 &currentEntry);
834 if (currentEntryRef != DIRENTRY_NULL)
836 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
837 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
839 WARN("Element is already open; cannot rename.\n");
840 return STG_E_ACCESSDENIED;
843 /* Remove the element from its current position in the tree */
844 removeFromTree(This, This->storageDirEntry,
845 currentEntryRef);
847 /* Change the name of the element */
848 strcpyW(currentEntry.name, pwcsNewName);
850 /* Delete any sibling links */
851 currentEntry.leftChild = DIRENTRY_NULL;
852 currentEntry.rightChild = DIRENTRY_NULL;
854 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
855 &currentEntry);
857 /* Insert the element in a new position in the tree */
858 insertIntoTree(This, This->storageDirEntry,
859 currentEntryRef);
861 else
864 * There is no element with the old name
866 return STG_E_FILENOTFOUND;
869 return StorageBaseImpl_Flush(This);
872 /************************************************************************
873 * Storage32BaseImpl_CreateStream (IStorage)
875 * This method will create a stream object within this storage
877 * See Windows documentation for more details on IStorage methods.
879 static HRESULT WINAPI StorageBaseImpl_CreateStream(
880 IStorage* iface,
881 const OLECHAR* pwcsName, /* [string][in] */
882 DWORD grfMode, /* [in] */
883 DWORD reserved1, /* [in] */
884 DWORD reserved2, /* [in] */
885 IStream** ppstm) /* [out] */
887 StorageBaseImpl *This = impl_from_IStorage(iface);
888 StgStreamImpl* newStream;
889 DirEntry currentEntry, newStreamEntry;
890 DirRef currentEntryRef, newStreamEntryRef;
891 HRESULT hr;
893 TRACE("(%p, %s, %x, %d, %d, %p)\n",
894 iface, debugstr_w(pwcsName), grfMode,
895 reserved1, reserved2, ppstm);
897 if (ppstm == 0)
898 return STG_E_INVALIDPOINTER;
900 if (pwcsName == 0)
901 return STG_E_INVALIDNAME;
903 if (reserved1 || reserved2)
904 return STG_E_INVALIDPARAMETER;
906 if ( FAILED( validateSTGM(grfMode) ))
907 return STG_E_INVALIDFLAG;
909 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
910 return STG_E_INVALIDFLAG;
912 if (This->reverted)
913 return STG_E_REVERTED;
916 * As documented.
918 if ((grfMode & STGM_DELETEONRELEASE) ||
919 (grfMode & STGM_TRANSACTED))
920 return STG_E_INVALIDFUNCTION;
923 * Don't worry about permissions in transacted mode, as we can always write
924 * changes; we just can't always commit them.
926 if(!(This->openFlags & STGM_TRANSACTED)) {
927 /* Can't create a stream on read-only storage */
928 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
929 return STG_E_ACCESSDENIED;
931 /* Can't create a stream with greater access than the parent. */
932 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
933 return STG_E_ACCESSDENIED;
936 if(This->openFlags & STGM_SIMPLE)
937 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
939 *ppstm = 0;
941 currentEntryRef = findElement(This,
942 This->storageDirEntry,
943 pwcsName,
944 &currentEntry);
946 if (currentEntryRef != DIRENTRY_NULL)
949 * An element with this name already exists
951 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
953 IStorage_DestroyElement(iface, pwcsName);
955 else
956 return STG_E_FILEALREADYEXISTS;
960 * memset the empty entry
962 memset(&newStreamEntry, 0, sizeof(DirEntry));
964 newStreamEntry.sizeOfNameString =
965 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
967 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
968 return STG_E_INVALIDNAME;
970 strcpyW(newStreamEntry.name, pwcsName);
972 newStreamEntry.stgType = STGTY_STREAM;
973 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
974 newStreamEntry.size.u.LowPart = 0;
975 newStreamEntry.size.u.HighPart = 0;
977 newStreamEntry.leftChild = DIRENTRY_NULL;
978 newStreamEntry.rightChild = DIRENTRY_NULL;
979 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
981 /* call CoFileTime to get the current time
982 newStreamEntry.ctime
983 newStreamEntry.mtime
986 /* newStreamEntry.clsid */
989 * Create an entry with the new data
991 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
992 if (FAILED(hr))
993 return hr;
996 * Insert the new entry in the parent storage's tree.
998 hr = insertIntoTree(
999 This,
1000 This->storageDirEntry,
1001 newStreamEntryRef);
1002 if (FAILED(hr))
1004 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1005 return hr;
1009 * Open the stream to return it.
1011 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1013 if (newStream)
1015 *ppstm = &newStream->IStream_iface;
1016 IStream_AddRef(*ppstm);
1018 else
1020 return STG_E_INSUFFICIENTMEMORY;
1023 return StorageBaseImpl_Flush(This);
1026 /************************************************************************
1027 * Storage32BaseImpl_SetClass (IStorage)
1029 * This method will write the specified CLSID in the directory entry of this
1030 * storage.
1032 * See Windows documentation for more details on IStorage methods.
1034 static HRESULT WINAPI StorageBaseImpl_SetClass(
1035 IStorage* iface,
1036 REFCLSID clsid) /* [in] */
1038 StorageBaseImpl *This = impl_from_IStorage(iface);
1039 HRESULT hRes;
1040 DirEntry currentEntry;
1042 TRACE("(%p, %p)\n", iface, clsid);
1044 if (This->reverted)
1045 return STG_E_REVERTED;
1047 hRes = StorageBaseImpl_ReadDirEntry(This,
1048 This->storageDirEntry,
1049 &currentEntry);
1050 if (SUCCEEDED(hRes))
1052 currentEntry.clsid = *clsid;
1054 hRes = StorageBaseImpl_WriteDirEntry(This,
1055 This->storageDirEntry,
1056 &currentEntry);
1059 if (SUCCEEDED(hRes))
1060 hRes = StorageBaseImpl_Flush(This);
1062 return hRes;
1065 /************************************************************************
1066 ** Storage32Impl implementation
1069 /************************************************************************
1070 * Storage32BaseImpl_CreateStorage (IStorage)
1072 * This method will create the storage object within the provided storage.
1074 * See Windows documentation for more details on IStorage methods.
1076 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1077 IStorage* iface,
1078 const OLECHAR *pwcsName, /* [string][in] */
1079 DWORD grfMode, /* [in] */
1080 DWORD reserved1, /* [in] */
1081 DWORD reserved2, /* [in] */
1082 IStorage **ppstg) /* [out] */
1084 StorageBaseImpl* This = impl_from_IStorage(iface);
1086 DirEntry currentEntry;
1087 DirEntry newEntry;
1088 DirRef currentEntryRef;
1089 DirRef newEntryRef;
1090 HRESULT hr;
1092 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1093 iface, debugstr_w(pwcsName), grfMode,
1094 reserved1, reserved2, ppstg);
1096 if (ppstg == 0)
1097 return STG_E_INVALIDPOINTER;
1099 if (This->openFlags & STGM_SIMPLE)
1101 return STG_E_INVALIDFUNCTION;
1104 if (pwcsName == 0)
1105 return STG_E_INVALIDNAME;
1107 *ppstg = NULL;
1109 if ( FAILED( validateSTGM(grfMode) ) ||
1110 (grfMode & STGM_DELETEONRELEASE) )
1112 WARN("bad grfMode: 0x%x\n", grfMode);
1113 return STG_E_INVALIDFLAG;
1116 if (This->reverted)
1117 return STG_E_REVERTED;
1120 * Check that we're compatible with the parent's storage mode
1122 if ( !(This->openFlags & STGM_TRANSACTED) &&
1123 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1125 WARN("access denied\n");
1126 return STG_E_ACCESSDENIED;
1129 currentEntryRef = findElement(This,
1130 This->storageDirEntry,
1131 pwcsName,
1132 &currentEntry);
1134 if (currentEntryRef != DIRENTRY_NULL)
1137 * An element with this name already exists
1139 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1140 ((This->openFlags & STGM_TRANSACTED) ||
1141 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1143 hr = IStorage_DestroyElement(iface, pwcsName);
1144 if (FAILED(hr))
1145 return hr;
1147 else
1149 WARN("file already exists\n");
1150 return STG_E_FILEALREADYEXISTS;
1153 else if (!(This->openFlags & STGM_TRANSACTED) &&
1154 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1156 WARN("read-only storage\n");
1157 return STG_E_ACCESSDENIED;
1160 memset(&newEntry, 0, sizeof(DirEntry));
1162 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1164 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1166 FIXME("name too long\n");
1167 return STG_E_INVALIDNAME;
1170 strcpyW(newEntry.name, pwcsName);
1172 newEntry.stgType = STGTY_STORAGE;
1173 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1174 newEntry.size.u.LowPart = 0;
1175 newEntry.size.u.HighPart = 0;
1177 newEntry.leftChild = DIRENTRY_NULL;
1178 newEntry.rightChild = DIRENTRY_NULL;
1179 newEntry.dirRootEntry = DIRENTRY_NULL;
1181 /* call CoFileTime to get the current time
1182 newEntry.ctime
1183 newEntry.mtime
1186 /* newEntry.clsid */
1189 * Create a new directory entry for the storage
1191 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1192 if (FAILED(hr))
1193 return hr;
1196 * Insert the new directory entry into the parent storage's tree
1198 hr = insertIntoTree(
1199 This,
1200 This->storageDirEntry,
1201 newEntryRef);
1202 if (FAILED(hr))
1204 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1205 return hr;
1209 * Open it to get a pointer to return.
1211 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1213 if( (hr != S_OK) || (*ppstg == NULL))
1215 return hr;
1218 if (SUCCEEDED(hr))
1219 hr = StorageBaseImpl_Flush(This);
1221 return S_OK;
1225 /***************************************************************************
1227 * Internal Method
1229 * Reserve a directory entry in the file and initialize it.
1231 static HRESULT StorageImpl_CreateDirEntry(
1232 StorageBaseImpl *base,
1233 const DirEntry *newData,
1234 DirRef *index)
1236 StorageImpl *storage = (StorageImpl*)base;
1237 ULONG currentEntryIndex = 0;
1238 ULONG newEntryIndex = DIRENTRY_NULL;
1239 HRESULT hr = S_OK;
1240 BYTE currentData[RAW_DIRENTRY_SIZE];
1241 WORD sizeOfNameString;
1245 hr = StorageImpl_ReadRawDirEntry(storage,
1246 currentEntryIndex,
1247 currentData);
1249 if (SUCCEEDED(hr))
1251 StorageUtl_ReadWord(
1252 currentData,
1253 OFFSET_PS_NAMELENGTH,
1254 &sizeOfNameString);
1256 if (sizeOfNameString == 0)
1259 * The entry exists and is available, we found it.
1261 newEntryIndex = currentEntryIndex;
1264 else
1267 * We exhausted the directory entries, we will create more space below
1269 newEntryIndex = currentEntryIndex;
1271 currentEntryIndex++;
1273 } while (newEntryIndex == DIRENTRY_NULL);
1276 * grow the directory stream
1278 if (FAILED(hr))
1280 BYTE emptyData[RAW_DIRENTRY_SIZE];
1281 ULARGE_INTEGER newSize;
1282 ULONG entryIndex;
1283 ULONG lastEntry = 0;
1284 ULONG blockCount = 0;
1287 * obtain the new count of blocks in the directory stream
1289 blockCount = BlockChainStream_GetCount(
1290 storage->rootBlockChain)+1;
1293 * initialize the size used by the directory stream
1295 newSize.u.HighPart = 0;
1296 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1299 * add a block to the directory stream
1301 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1304 * memset the empty entry in order to initialize the unused newly
1305 * created entries
1307 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1310 * initialize them
1312 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1314 for(
1315 entryIndex = newEntryIndex + 1;
1316 entryIndex < lastEntry;
1317 entryIndex++)
1319 StorageImpl_WriteRawDirEntry(
1320 storage,
1321 entryIndex,
1322 emptyData);
1325 StorageImpl_SaveFileHeader(storage);
1328 UpdateRawDirEntry(currentData, newData);
1330 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1332 if (SUCCEEDED(hr))
1333 *index = newEntryIndex;
1335 return hr;
1338 /***************************************************************************
1340 * Internal Method
1342 * Mark a directory entry in the file as free.
1344 static HRESULT StorageImpl_DestroyDirEntry(
1345 StorageBaseImpl *base,
1346 DirRef index)
1348 BYTE emptyData[RAW_DIRENTRY_SIZE];
1349 StorageImpl *storage = (StorageImpl*)base;
1351 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1353 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1357 /****************************************************************************
1359 * Internal Method
1361 * Case insensitive comparison of DirEntry.name by first considering
1362 * their size.
1364 * Returns <0 when name1 < name2
1365 * >0 when name1 > name2
1366 * 0 when name1 == name2
1368 static LONG entryNameCmp(
1369 const OLECHAR *name1,
1370 const OLECHAR *name2)
1372 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1374 while (diff == 0 && *name1 != 0)
1377 * We compare the string themselves only when they are of the same length
1379 diff = toupperW(*name1++) - toupperW(*name2++);
1382 return diff;
1385 /****************************************************************************
1387 * Internal Method
1389 * Add a directory entry to a storage
1391 static HRESULT insertIntoTree(
1392 StorageBaseImpl *This,
1393 DirRef parentStorageIndex,
1394 DirRef newEntryIndex)
1396 DirEntry currentEntry;
1397 DirEntry newEntry;
1400 * Read the inserted entry
1402 StorageBaseImpl_ReadDirEntry(This,
1403 newEntryIndex,
1404 &newEntry);
1407 * Read the storage entry
1409 StorageBaseImpl_ReadDirEntry(This,
1410 parentStorageIndex,
1411 &currentEntry);
1413 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1416 * The root storage contains some element, therefore, start the research
1417 * for the appropriate location.
1419 BOOL found = 0;
1420 DirRef current, next, previous, currentEntryId;
1423 * Keep a reference to the root of the storage's element tree
1425 currentEntryId = currentEntry.dirRootEntry;
1428 * Read
1430 StorageBaseImpl_ReadDirEntry(This,
1431 currentEntry.dirRootEntry,
1432 &currentEntry);
1434 previous = currentEntry.leftChild;
1435 next = currentEntry.rightChild;
1436 current = currentEntryId;
1438 while (found == 0)
1440 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1442 if (diff < 0)
1444 if (previous != DIRENTRY_NULL)
1446 StorageBaseImpl_ReadDirEntry(This,
1447 previous,
1448 &currentEntry);
1449 current = previous;
1451 else
1453 currentEntry.leftChild = newEntryIndex;
1454 StorageBaseImpl_WriteDirEntry(This,
1455 current,
1456 &currentEntry);
1457 found = 1;
1460 else if (diff > 0)
1462 if (next != DIRENTRY_NULL)
1464 StorageBaseImpl_ReadDirEntry(This,
1465 next,
1466 &currentEntry);
1467 current = next;
1469 else
1471 currentEntry.rightChild = newEntryIndex;
1472 StorageBaseImpl_WriteDirEntry(This,
1473 current,
1474 &currentEntry);
1475 found = 1;
1478 else
1481 * Trying to insert an item with the same name in the
1482 * subtree structure.
1484 return STG_E_FILEALREADYEXISTS;
1487 previous = currentEntry.leftChild;
1488 next = currentEntry.rightChild;
1491 else
1494 * The storage is empty, make the new entry the root of its element tree
1496 currentEntry.dirRootEntry = newEntryIndex;
1497 StorageBaseImpl_WriteDirEntry(This,
1498 parentStorageIndex,
1499 &currentEntry);
1502 return S_OK;
1505 /****************************************************************************
1507 * Internal Method
1509 * Find and read the element of a storage with the given name.
1511 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1512 const OLECHAR *name, DirEntry *data)
1514 DirRef currentEntry;
1516 /* Read the storage entry to find the root of the tree. */
1517 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1519 currentEntry = data->dirRootEntry;
1521 while (currentEntry != DIRENTRY_NULL)
1523 LONG cmp;
1525 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1527 cmp = entryNameCmp(name, data->name);
1529 if (cmp == 0)
1530 /* found it */
1531 break;
1533 else if (cmp < 0)
1534 currentEntry = data->leftChild;
1536 else if (cmp > 0)
1537 currentEntry = data->rightChild;
1540 return currentEntry;
1543 /****************************************************************************
1545 * Internal Method
1547 * Find and read the binary tree parent of the element with the given name.
1549 * If there is no such element, find a place where it could be inserted and
1550 * return STG_E_FILENOTFOUND.
1552 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1553 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1554 ULONG *relation)
1556 DirRef childEntry;
1557 DirEntry childData;
1559 /* Read the storage entry to find the root of the tree. */
1560 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1562 *parentEntry = storageEntry;
1563 *relation = DIRENTRY_RELATION_DIR;
1565 childEntry = parentData->dirRootEntry;
1567 while (childEntry != DIRENTRY_NULL)
1569 LONG cmp;
1571 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1573 cmp = entryNameCmp(childName, childData.name);
1575 if (cmp == 0)
1576 /* found it */
1577 break;
1579 else if (cmp < 0)
1581 *parentData = childData;
1582 *parentEntry = childEntry;
1583 *relation = DIRENTRY_RELATION_PREVIOUS;
1585 childEntry = parentData->leftChild;
1588 else if (cmp > 0)
1590 *parentData = childData;
1591 *parentEntry = childEntry;
1592 *relation = DIRENTRY_RELATION_NEXT;
1594 childEntry = parentData->rightChild;
1598 if (childEntry == DIRENTRY_NULL)
1599 return STG_E_FILENOTFOUND;
1600 else
1601 return S_OK;
1605 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1606 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1607 SNB snbExclude, IStorage *pstgDest);
1609 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1610 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1611 SNB snbExclude, IStorage *pstgDest)
1613 DirEntry data;
1614 HRESULT hr;
1615 BOOL skip = FALSE;
1616 IStorage *pstgTmp;
1617 IStream *pstrChild, *pstrTmp;
1618 STATSTG strStat;
1620 if (srcEntry == DIRENTRY_NULL)
1621 return S_OK;
1623 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1625 if (FAILED(hr))
1626 return hr;
1628 if ( snbExclude )
1630 WCHAR **snb = snbExclude;
1632 while ( *snb != NULL && !skip )
1634 if ( lstrcmpW(data.name, *snb) == 0 )
1635 skip = TRUE;
1636 ++snb;
1640 if (!skip)
1642 if (data.stgType == STGTY_STORAGE && !skip_storage)
1645 * create a new storage in destination storage
1647 hr = IStorage_CreateStorage( pstgDest, data.name,
1648 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1649 0, 0,
1650 &pstgTmp );
1653 * if it already exist, don't create a new one use this one
1655 if (hr == STG_E_FILEALREADYEXISTS)
1657 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1658 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1659 NULL, 0, &pstgTmp );
1662 if (SUCCEEDED(hr))
1664 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1665 skip_stream, NULL, pstgTmp );
1667 IStorage_Release(pstgTmp);
1670 else if (data.stgType == STGTY_STREAM && !skip_stream)
1673 * create a new stream in destination storage. If the stream already
1674 * exist, it will be deleted and a new one will be created.
1676 hr = IStorage_CreateStream( pstgDest, data.name,
1677 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1678 0, 0, &pstrTmp );
1681 * open child stream storage. This operation must succeed even if the
1682 * stream is already open, so we use internal functions to do it.
1684 if (hr == S_OK)
1686 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1688 if (streamimpl)
1690 pstrChild = &streamimpl->IStream_iface;
1691 if (pstrChild)
1692 IStream_AddRef(pstrChild);
1694 else
1696 pstrChild = NULL;
1697 hr = E_OUTOFMEMORY;
1701 if (hr == S_OK)
1704 * Get the size of the source stream
1706 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1709 * Set the size of the destination stream.
1711 IStream_SetSize(pstrTmp, strStat.cbSize);
1714 * do the copy
1716 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1717 NULL, NULL );
1719 IStream_Release( pstrChild );
1722 IStream_Release( pstrTmp );
1726 /* copy siblings */
1727 if (SUCCEEDED(hr))
1728 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1729 skip_stream, snbExclude, pstgDest );
1731 if (SUCCEEDED(hr))
1732 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1733 skip_stream, snbExclude, pstgDest );
1735 return hr;
1738 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1739 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1740 SNB snbExclude, IStorage *pstgDest)
1742 DirEntry data;
1743 HRESULT hr;
1745 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1747 if (SUCCEEDED(hr))
1748 hr = IStorage_SetClass( pstgDest, &data.clsid );
1750 if (SUCCEEDED(hr))
1751 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1752 skip_stream, snbExclude, pstgDest );
1754 return hr;
1757 /*************************************************************************
1758 * CopyTo (IStorage)
1760 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1761 IStorage* iface,
1762 DWORD ciidExclude, /* [in] */
1763 const IID* rgiidExclude, /* [size_is][unique][in] */
1764 SNB snbExclude, /* [unique][in] */
1765 IStorage* pstgDest) /* [unique][in] */
1767 StorageBaseImpl *This = impl_from_IStorage(iface);
1769 BOOL skip_storage = FALSE, skip_stream = FALSE;
1770 int i;
1772 TRACE("(%p, %d, %p, %p, %p)\n",
1773 iface, ciidExclude, rgiidExclude,
1774 snbExclude, pstgDest);
1776 if ( pstgDest == 0 )
1777 return STG_E_INVALIDPOINTER;
1779 for(i = 0; i < ciidExclude; ++i)
1781 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1782 skip_storage = TRUE;
1783 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1784 skip_stream = TRUE;
1785 else
1786 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1789 if (!skip_storage)
1791 /* Give up early if it looks like this would be infinitely recursive.
1792 * Oddly enough, this includes some cases that aren't really recursive, like
1793 * copying to a transacted child. */
1794 IStorage *pstgDestAncestor = pstgDest;
1795 IStorage *pstgDestAncestorChild = NULL;
1797 /* Go up the chain from the destination until we find the source storage. */
1798 while (pstgDestAncestor != iface) {
1799 pstgDestAncestorChild = pstgDest;
1801 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1803 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
1805 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
1807 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1809 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
1811 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
1813 else
1814 break;
1817 if (pstgDestAncestor == iface)
1819 BOOL fail = TRUE;
1821 if (pstgDestAncestorChild && snbExclude)
1823 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1824 DirEntry data;
1825 WCHAR **snb = snbExclude;
1827 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1829 while ( *snb != NULL && fail )
1831 if ( lstrcmpW(data.name, *snb) == 0 )
1832 fail = FALSE;
1833 ++snb;
1837 if (fail)
1838 return STG_E_ACCESSDENIED;
1842 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1843 skip_storage, skip_stream, snbExclude, pstgDest );
1846 /*************************************************************************
1847 * MoveElementTo (IStorage)
1849 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1850 IStorage* iface,
1851 const OLECHAR *pwcsName, /* [string][in] */
1852 IStorage *pstgDest, /* [unique][in] */
1853 const OLECHAR *pwcsNewName,/* [string][in] */
1854 DWORD grfFlags) /* [in] */
1856 FIXME("(%p %s %p %s %u): stub\n", iface,
1857 debugstr_w(pwcsName), pstgDest,
1858 debugstr_w(pwcsNewName), grfFlags);
1859 return E_NOTIMPL;
1862 /*************************************************************************
1863 * Commit (IStorage)
1865 * Ensures that any changes made to a storage object open in transacted mode
1866 * are reflected in the parent storage
1868 * In a non-transacted mode, this ensures all cached writes are completed.
1870 static HRESULT WINAPI StorageImpl_Commit(
1871 IStorage* iface,
1872 DWORD grfCommitFlags)/* [in] */
1874 StorageBaseImpl* This = impl_from_IStorage(iface);
1875 TRACE("(%p %d)\n", iface, grfCommitFlags);
1876 return StorageBaseImpl_Flush(This);
1879 /*************************************************************************
1880 * Revert (IStorage)
1882 * Discard all changes that have been made since the last commit operation
1884 static HRESULT WINAPI StorageImpl_Revert(
1885 IStorage* iface)
1887 TRACE("(%p)\n", iface);
1888 return S_OK;
1891 /*************************************************************************
1892 * DestroyElement (IStorage)
1894 * Strategy: This implementation is built this way for simplicity not for speed.
1895 * I always delete the topmost element of the enumeration and adjust
1896 * the deleted element pointer all the time. This takes longer to
1897 * do but allow to reinvoke DestroyElement whenever we encounter a
1898 * storage object. The optimisation resides in the usage of another
1899 * enumeration strategy that would give all the leaves of a storage
1900 * first. (postfix order)
1902 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1903 IStorage* iface,
1904 const OLECHAR *pwcsName)/* [string][in] */
1906 StorageBaseImpl *This = impl_from_IStorage(iface);
1908 HRESULT hr = S_OK;
1909 DirEntry entryToDelete;
1910 DirRef entryToDeleteRef;
1912 TRACE("(%p, %s)\n",
1913 iface, debugstr_w(pwcsName));
1915 if (pwcsName==NULL)
1916 return STG_E_INVALIDPOINTER;
1918 if (This->reverted)
1919 return STG_E_REVERTED;
1921 if ( !(This->openFlags & STGM_TRANSACTED) &&
1922 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1923 return STG_E_ACCESSDENIED;
1925 entryToDeleteRef = findElement(
1926 This,
1927 This->storageDirEntry,
1928 pwcsName,
1929 &entryToDelete);
1931 if ( entryToDeleteRef == DIRENTRY_NULL )
1933 return STG_E_FILENOTFOUND;
1936 if ( entryToDelete.stgType == STGTY_STORAGE )
1938 hr = deleteStorageContents(
1939 This,
1940 entryToDeleteRef,
1941 entryToDelete);
1943 else if ( entryToDelete.stgType == STGTY_STREAM )
1945 hr = deleteStreamContents(
1946 This,
1947 entryToDeleteRef,
1948 entryToDelete);
1951 if (hr!=S_OK)
1952 return hr;
1955 * Remove the entry from its parent storage
1957 hr = removeFromTree(
1958 This,
1959 This->storageDirEntry,
1960 entryToDeleteRef);
1963 * Invalidate the entry
1965 if (SUCCEEDED(hr))
1966 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1968 if (SUCCEEDED(hr))
1969 hr = StorageBaseImpl_Flush(This);
1971 return hr;
1975 /******************************************************************************
1976 * Internal stream list handlers
1979 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1981 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1982 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1985 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1987 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1988 list_remove(&(strm->StrmListEntry));
1991 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1993 StgStreamImpl *strm;
1995 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1997 if (strm->dirEntry == streamEntry)
1999 return TRUE;
2003 return FALSE;
2006 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2008 StorageInternalImpl *childstg;
2010 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2012 if (childstg->base.storageDirEntry == storageEntry)
2014 return TRUE;
2018 return FALSE;
2021 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2023 struct list *cur, *cur2;
2024 StgStreamImpl *strm=NULL;
2025 StorageInternalImpl *childstg=NULL;
2027 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2028 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2029 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2030 strm->parentStorage = NULL;
2031 list_remove(cur);
2034 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2035 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2036 StorageBaseImpl_Invalidate( &childstg->base );
2039 if (stg->transactedChild)
2041 StorageBaseImpl_Invalidate(stg->transactedChild);
2043 stg->transactedChild = NULL;
2048 /*********************************************************************
2050 * Internal Method
2052 * Delete the contents of a storage entry.
2055 static HRESULT deleteStorageContents(
2056 StorageBaseImpl *parentStorage,
2057 DirRef indexToDelete,
2058 DirEntry entryDataToDelete)
2060 IEnumSTATSTG *elements = 0;
2061 IStorage *childStorage = 0;
2062 STATSTG currentElement;
2063 HRESULT hr;
2064 HRESULT destroyHr = S_OK;
2065 StorageInternalImpl *stg, *stg2;
2067 /* Invalidate any open storage objects. */
2068 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2070 if (stg->base.storageDirEntry == indexToDelete)
2072 StorageBaseImpl_Invalidate(&stg->base);
2077 * Open the storage and enumerate it
2079 hr = StorageBaseImpl_OpenStorage(
2080 &parentStorage->IStorage_iface,
2081 entryDataToDelete.name,
2083 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2086 &childStorage);
2088 if (hr != S_OK)
2090 return hr;
2094 * Enumerate the elements
2096 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2101 * Obtain the next element
2103 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2104 if (hr==S_OK)
2106 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2108 CoTaskMemFree(currentElement.pwcsName);
2112 * We need to Reset the enumeration every time because we delete elements
2113 * and the enumeration could be invalid
2115 IEnumSTATSTG_Reset(elements);
2117 } while ((hr == S_OK) && (destroyHr == S_OK));
2119 IStorage_Release(childStorage);
2120 IEnumSTATSTG_Release(elements);
2122 return destroyHr;
2125 /*********************************************************************
2127 * Internal Method
2129 * Perform the deletion of a stream's data
2132 static HRESULT deleteStreamContents(
2133 StorageBaseImpl *parentStorage,
2134 DirRef indexToDelete,
2135 DirEntry entryDataToDelete)
2137 IStream *pis;
2138 HRESULT hr;
2139 ULARGE_INTEGER size;
2140 StgStreamImpl *strm, *strm2;
2142 /* Invalidate any open stream objects. */
2143 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2145 if (strm->dirEntry == indexToDelete)
2147 TRACE("Stream deleted %p\n", strm);
2148 strm->parentStorage = NULL;
2149 list_remove(&strm->StrmListEntry);
2153 size.u.HighPart = 0;
2154 size.u.LowPart = 0;
2156 hr = StorageBaseImpl_OpenStream(&parentStorage->IStorage_iface,
2157 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2159 if (hr!=S_OK)
2161 return(hr);
2165 * Zap the stream
2167 hr = IStream_SetSize(pis, size);
2169 if(hr != S_OK)
2171 return hr;
2175 * Release the stream object.
2177 IStream_Release(pis);
2179 return S_OK;
2182 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2184 switch (relation)
2186 case DIRENTRY_RELATION_PREVIOUS:
2187 entry->leftChild = new_target;
2188 break;
2189 case DIRENTRY_RELATION_NEXT:
2190 entry->rightChild = new_target;
2191 break;
2192 case DIRENTRY_RELATION_DIR:
2193 entry->dirRootEntry = new_target;
2194 break;
2195 default:
2196 assert(0);
2200 /*************************************************************************
2202 * Internal Method
2204 * This method removes a directory entry from its parent storage tree without
2205 * freeing any resources attached to it.
2207 static HRESULT removeFromTree(
2208 StorageBaseImpl *This,
2209 DirRef parentStorageIndex,
2210 DirRef deletedIndex)
2212 HRESULT hr = S_OK;
2213 DirEntry entryToDelete;
2214 DirEntry parentEntry;
2215 DirRef parentEntryRef;
2216 ULONG typeOfRelation;
2218 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2220 if (hr != S_OK)
2221 return hr;
2224 * Find the element that links to the one we want to delete.
2226 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2227 &parentEntry, &parentEntryRef, &typeOfRelation);
2229 if (hr != S_OK)
2230 return hr;
2232 if (entryToDelete.leftChild != DIRENTRY_NULL)
2235 * Replace the deleted entry with its left child
2237 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2239 hr = StorageBaseImpl_WriteDirEntry(
2240 This,
2241 parentEntryRef,
2242 &parentEntry);
2243 if(FAILED(hr))
2245 return hr;
2248 if (entryToDelete.rightChild != DIRENTRY_NULL)
2251 * We need to reinsert the right child somewhere. We already know it and
2252 * its children are greater than everything in the left tree, so we
2253 * insert it at the rightmost point in the left tree.
2255 DirRef newRightChildParent = entryToDelete.leftChild;
2256 DirEntry newRightChildParentEntry;
2260 hr = StorageBaseImpl_ReadDirEntry(
2261 This,
2262 newRightChildParent,
2263 &newRightChildParentEntry);
2264 if (FAILED(hr))
2266 return hr;
2269 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2270 newRightChildParent = newRightChildParentEntry.rightChild;
2271 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2273 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2275 hr = StorageBaseImpl_WriteDirEntry(
2276 This,
2277 newRightChildParent,
2278 &newRightChildParentEntry);
2279 if (FAILED(hr))
2281 return hr;
2285 else
2288 * Replace the deleted entry with its right child
2290 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2292 hr = StorageBaseImpl_WriteDirEntry(
2293 This,
2294 parentEntryRef,
2295 &parentEntry);
2296 if(FAILED(hr))
2298 return hr;
2302 return hr;
2306 /******************************************************************************
2307 * SetElementTimes (IStorage)
2309 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2310 IStorage* iface,
2311 const OLECHAR *pwcsName,/* [string][in] */
2312 const FILETIME *pctime, /* [in] */
2313 const FILETIME *patime, /* [in] */
2314 const FILETIME *pmtime) /* [in] */
2316 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2317 return S_OK;
2320 /******************************************************************************
2321 * SetStateBits (IStorage)
2323 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2324 IStorage* iface,
2325 DWORD grfStateBits,/* [in] */
2326 DWORD grfMask) /* [in] */
2328 StorageBaseImpl *This = impl_from_IStorage(iface);
2330 if (This->reverted)
2331 return STG_E_REVERTED;
2333 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2334 return S_OK;
2337 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2338 DirRef index, const DirEntry *data)
2340 StorageImpl *This = (StorageImpl*)base;
2341 return StorageImpl_WriteDirEntry(This, index, data);
2344 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2345 DirRef index, DirEntry *data)
2347 StorageImpl *This = (StorageImpl*)base;
2348 return StorageImpl_ReadDirEntry(This, index, data);
2351 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2353 int i;
2355 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2357 if (!This->blockChainCache[i])
2359 return &This->blockChainCache[i];
2363 i = This->blockChainToEvict;
2365 BlockChainStream_Destroy(This->blockChainCache[i]);
2366 This->blockChainCache[i] = NULL;
2368 This->blockChainToEvict++;
2369 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2370 This->blockChainToEvict = 0;
2372 return &This->blockChainCache[i];
2375 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2376 DirRef index)
2378 int i, free_index=-1;
2380 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2382 if (!This->blockChainCache[i])
2384 if (free_index == -1) free_index = i;
2386 else if (This->blockChainCache[i]->ownerDirEntry == index)
2388 return &This->blockChainCache[i];
2392 if (free_index == -1)
2394 free_index = This->blockChainToEvict;
2396 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2397 This->blockChainCache[free_index] = NULL;
2399 This->blockChainToEvict++;
2400 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2401 This->blockChainToEvict = 0;
2404 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2405 return &This->blockChainCache[free_index];
2408 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2410 int i;
2412 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2414 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2416 BlockChainStream_Destroy(This->blockChainCache[i]);
2417 This->blockChainCache[i] = NULL;
2418 return;
2423 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2424 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2426 StorageImpl *This = (StorageImpl*)base;
2427 DirEntry data;
2428 HRESULT hr;
2429 ULONG bytesToRead;
2431 hr = StorageImpl_ReadDirEntry(This, index, &data);
2432 if (FAILED(hr)) return hr;
2434 if (data.size.QuadPart == 0)
2436 *bytesRead = 0;
2437 return S_OK;
2440 if (offset.QuadPart + size > data.size.QuadPart)
2442 bytesToRead = data.size.QuadPart - offset.QuadPart;
2444 else
2446 bytesToRead = size;
2449 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2451 SmallBlockChainStream *stream;
2453 stream = SmallBlockChainStream_Construct(This, NULL, index);
2454 if (!stream) return E_OUTOFMEMORY;
2456 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2458 SmallBlockChainStream_Destroy(stream);
2460 return hr;
2462 else
2464 BlockChainStream *stream = NULL;
2466 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2467 if (!stream) return E_OUTOFMEMORY;
2469 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2471 return hr;
2475 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2476 ULARGE_INTEGER newsize)
2478 StorageImpl *This = (StorageImpl*)base;
2479 DirEntry data;
2480 HRESULT hr;
2481 SmallBlockChainStream *smallblock=NULL;
2482 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2484 hr = StorageImpl_ReadDirEntry(This, index, &data);
2485 if (FAILED(hr)) return hr;
2487 /* In simple mode keep the stream size above the small block limit */
2488 if (This->base.openFlags & STGM_SIMPLE)
2489 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2491 if (data.size.QuadPart == newsize.QuadPart)
2492 return S_OK;
2494 /* Create a block chain object of the appropriate type */
2495 if (data.size.QuadPart == 0)
2497 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2499 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2500 if (!smallblock) return E_OUTOFMEMORY;
2502 else
2504 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2505 bigblock = *pbigblock;
2506 if (!bigblock) return E_OUTOFMEMORY;
2509 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2511 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2512 if (!smallblock) return E_OUTOFMEMORY;
2514 else
2516 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2517 bigblock = *pbigblock;
2518 if (!bigblock) return E_OUTOFMEMORY;
2521 /* Change the block chain type if necessary. */
2522 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2524 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2525 if (!bigblock)
2527 SmallBlockChainStream_Destroy(smallblock);
2528 return E_FAIL;
2531 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2532 *pbigblock = bigblock;
2534 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2536 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2537 if (!smallblock)
2538 return E_FAIL;
2541 /* Set the size of the block chain. */
2542 if (smallblock)
2544 SmallBlockChainStream_SetSize(smallblock, newsize);
2545 SmallBlockChainStream_Destroy(smallblock);
2547 else
2549 BlockChainStream_SetSize(bigblock, newsize);
2552 /* Set the size in the directory entry. */
2553 hr = StorageImpl_ReadDirEntry(This, index, &data);
2554 if (SUCCEEDED(hr))
2556 data.size = newsize;
2558 hr = StorageImpl_WriteDirEntry(This, index, &data);
2560 return hr;
2563 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2564 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2566 StorageImpl *This = (StorageImpl*)base;
2567 DirEntry data;
2568 HRESULT hr;
2569 ULARGE_INTEGER newSize;
2571 hr = StorageImpl_ReadDirEntry(This, index, &data);
2572 if (FAILED(hr)) return hr;
2574 /* Grow the stream if necessary */
2575 newSize.QuadPart = 0;
2576 newSize.QuadPart = offset.QuadPart + size;
2578 if (newSize.QuadPart > data.size.QuadPart)
2580 hr = StorageImpl_StreamSetSize(base, index, newSize);
2581 if (FAILED(hr))
2582 return hr;
2584 hr = StorageImpl_ReadDirEntry(This, index, &data);
2585 if (FAILED(hr)) return hr;
2588 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2590 SmallBlockChainStream *stream;
2592 stream = SmallBlockChainStream_Construct(This, NULL, index);
2593 if (!stream) return E_OUTOFMEMORY;
2595 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2597 SmallBlockChainStream_Destroy(stream);
2599 return hr;
2601 else
2603 BlockChainStream *stream;
2605 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2606 if (!stream) return E_OUTOFMEMORY;
2608 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2610 return hr;
2614 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2615 DirRef src)
2617 StorageImpl *This = (StorageImpl*)base;
2618 DirEntry dst_data, src_data;
2619 HRESULT hr;
2621 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2623 if (SUCCEEDED(hr))
2624 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2626 if (SUCCEEDED(hr))
2628 StorageImpl_DeleteCachedBlockChainStream(This, src);
2629 dst_data.startingBlock = src_data.startingBlock;
2630 dst_data.size = src_data.size;
2632 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2635 return hr;
2638 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2640 StorageImpl *This = (StorageImpl*) iface;
2641 STATSTG statstg;
2642 HRESULT hr;
2644 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2646 *result = statstg.pwcsName;
2648 return hr;
2652 * Virtual function table for the IStorage32Impl class.
2654 static const IStorageVtbl Storage32Impl_Vtbl =
2656 StorageBaseImpl_QueryInterface,
2657 StorageBaseImpl_AddRef,
2658 StorageBaseImpl_Release,
2659 StorageBaseImpl_CreateStream,
2660 StorageBaseImpl_OpenStream,
2661 StorageBaseImpl_CreateStorage,
2662 StorageBaseImpl_OpenStorage,
2663 StorageBaseImpl_CopyTo,
2664 StorageBaseImpl_MoveElementTo,
2665 StorageImpl_Commit,
2666 StorageImpl_Revert,
2667 StorageBaseImpl_EnumElements,
2668 StorageBaseImpl_DestroyElement,
2669 StorageBaseImpl_RenameElement,
2670 StorageBaseImpl_SetElementTimes,
2671 StorageBaseImpl_SetClass,
2672 StorageBaseImpl_SetStateBits,
2673 StorageBaseImpl_Stat
2676 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2678 StorageImpl_Destroy,
2679 StorageImpl_Invalidate,
2680 StorageImpl_Flush,
2681 StorageImpl_GetFilename,
2682 StorageImpl_CreateDirEntry,
2683 StorageImpl_BaseWriteDirEntry,
2684 StorageImpl_BaseReadDirEntry,
2685 StorageImpl_DestroyDirEntry,
2686 StorageImpl_StreamReadAt,
2687 StorageImpl_StreamWriteAt,
2688 StorageImpl_StreamSetSize,
2689 StorageImpl_StreamLink
2692 static HRESULT StorageImpl_Construct(
2693 HANDLE hFile,
2694 LPCOLESTR pwcsName,
2695 ILockBytes* pLkbyt,
2696 DWORD openFlags,
2697 BOOL fileBased,
2698 BOOL create,
2699 ULONG sector_size,
2700 StorageImpl** result)
2702 StorageImpl* This;
2703 HRESULT hr = S_OK;
2704 DirEntry currentEntry;
2705 DirRef currentEntryRef;
2707 if ( FAILED( validateSTGM(openFlags) ))
2708 return STG_E_INVALIDFLAG;
2710 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2711 if (!This)
2712 return E_OUTOFMEMORY;
2714 memset(This, 0, sizeof(StorageImpl));
2716 list_init(&This->base.strmHead);
2718 list_init(&This->base.storageHead);
2720 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
2721 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2722 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2723 This->base.openFlags = (openFlags & ~STGM_CREATE);
2724 This->base.ref = 1;
2725 This->base.create = create;
2727 This->base.reverted = 0;
2730 * Initialize the big block cache.
2732 This->bigBlockSize = sector_size;
2733 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2734 if (hFile)
2735 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2736 else
2738 This->lockBytes = pLkbyt;
2739 ILockBytes_AddRef(pLkbyt);
2742 if (FAILED(hr))
2743 goto end;
2745 if (create)
2747 ULARGE_INTEGER size;
2748 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2750 /* Discard any existing data. */
2751 size.QuadPart = 0;
2752 ILockBytes_SetSize(This->lockBytes, size);
2755 * Initialize all header variables:
2756 * - The big block depot consists of one block and it is at block 0
2757 * - The directory table starts at block 1
2758 * - There is no small block depot
2760 memset( This->bigBlockDepotStart,
2761 BLOCK_UNUSED,
2762 sizeof(This->bigBlockDepotStart));
2764 This->bigBlockDepotCount = 1;
2765 This->bigBlockDepotStart[0] = 0;
2766 This->rootStartBlock = 1;
2767 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2768 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2769 if (sector_size == 4096)
2770 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2771 else
2772 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2773 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2774 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2775 This->extBigBlockDepotCount = 0;
2777 StorageImpl_SaveFileHeader(This);
2780 * Add one block for the big block depot and one block for the directory table
2782 size.u.HighPart = 0;
2783 size.u.LowPart = This->bigBlockSize * 3;
2784 ILockBytes_SetSize(This->lockBytes, size);
2787 * Initialize the big block depot
2789 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2790 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2791 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2792 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2794 else
2797 * Load the header for the file.
2799 hr = StorageImpl_LoadFileHeader(This);
2801 if (FAILED(hr))
2803 goto end;
2808 * There is no block depot cached yet.
2810 This->indexBlockDepotCached = 0xFFFFFFFF;
2811 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2814 * Start searching for free blocks with block 0.
2816 This->prevFreeBlock = 0;
2818 This->firstFreeSmallBlock = 0;
2820 /* Read the extended big block depot locations. */
2821 if (This->extBigBlockDepotCount != 0)
2823 ULONG current_block = This->extBigBlockDepotStart;
2824 ULONG cache_size = This->extBigBlockDepotCount * 2;
2825 int i;
2827 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2828 if (!This->extBigBlockDepotLocations)
2830 hr = E_OUTOFMEMORY;
2831 goto end;
2834 This->extBigBlockDepotLocationsSize = cache_size;
2836 for (i=0; i<This->extBigBlockDepotCount; i++)
2838 if (current_block == BLOCK_END_OF_CHAIN)
2840 WARN("File has too few extended big block depot blocks.\n");
2841 hr = STG_E_DOCFILECORRUPT;
2842 goto end;
2844 This->extBigBlockDepotLocations[i] = current_block;
2845 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2848 else
2850 This->extBigBlockDepotLocations = NULL;
2851 This->extBigBlockDepotLocationsSize = 0;
2855 * Create the block chain abstractions.
2857 if(!(This->rootBlockChain =
2858 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2860 hr = STG_E_READFAULT;
2861 goto end;
2864 if(!(This->smallBlockDepotChain =
2865 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2866 DIRENTRY_NULL)))
2868 hr = STG_E_READFAULT;
2869 goto end;
2873 * Write the root storage entry (memory only)
2875 if (create)
2877 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
2878 DirEntry rootEntry;
2880 * Initialize the directory table
2882 memset(&rootEntry, 0, sizeof(rootEntry));
2883 strcpyW(rootEntry.name, rootentryW);
2884 rootEntry.sizeOfNameString = sizeof(rootentryW);
2885 rootEntry.stgType = STGTY_ROOT;
2886 rootEntry.leftChild = DIRENTRY_NULL;
2887 rootEntry.rightChild = DIRENTRY_NULL;
2888 rootEntry.dirRootEntry = DIRENTRY_NULL;
2889 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2890 rootEntry.size.u.HighPart = 0;
2891 rootEntry.size.u.LowPart = 0;
2893 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2897 * Find the ID of the root storage.
2899 currentEntryRef = 0;
2903 hr = StorageImpl_ReadDirEntry(
2904 This,
2905 currentEntryRef,
2906 &currentEntry);
2908 if (SUCCEEDED(hr))
2910 if ( (currentEntry.sizeOfNameString != 0 ) &&
2911 (currentEntry.stgType == STGTY_ROOT) )
2913 This->base.storageDirEntry = currentEntryRef;
2917 currentEntryRef++;
2919 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2921 if (FAILED(hr))
2923 hr = STG_E_READFAULT;
2924 goto end;
2928 * Create the block chain abstraction for the small block root chain.
2930 if(!(This->smallBlockRootChain =
2931 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2933 hr = STG_E_READFAULT;
2936 end:
2937 if (FAILED(hr))
2939 IStorage_Release(&This->base.IStorage_iface);
2940 *result = NULL;
2942 else
2944 StorageImpl_Flush((StorageBaseImpl*)This);
2945 *result = This;
2948 return hr;
2951 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2953 StorageImpl *This = (StorageImpl*) iface;
2955 StorageBaseImpl_DeleteAll(&This->base);
2957 This->base.reverted = 1;
2960 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2962 StorageImpl *This = (StorageImpl*) iface;
2963 int i;
2964 TRACE("(%p)\n", This);
2966 StorageImpl_Flush(iface);
2968 StorageImpl_Invalidate(iface);
2970 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
2972 BlockChainStream_Destroy(This->smallBlockRootChain);
2973 BlockChainStream_Destroy(This->rootBlockChain);
2974 BlockChainStream_Destroy(This->smallBlockDepotChain);
2976 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2977 BlockChainStream_Destroy(This->blockChainCache[i]);
2979 if (This->lockBytes)
2980 ILockBytes_Release(This->lockBytes);
2981 HeapFree(GetProcessHeap(), 0, This);
2984 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2986 StorageImpl *This = (StorageImpl*) iface;
2987 int i;
2988 HRESULT hr;
2989 TRACE("(%p)\n", This);
2991 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2993 if (SUCCEEDED(hr))
2994 hr = BlockChainStream_Flush(This->rootBlockChain);
2996 if (SUCCEEDED(hr))
2997 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2999 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
3000 if (This->blockChainCache[i])
3001 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3003 if (SUCCEEDED(hr))
3004 hr = ILockBytes_Flush(This->lockBytes);
3006 return hr;
3009 /******************************************************************************
3010 * Storage32Impl_GetNextFreeBigBlock
3012 * Returns the index of the next free big block.
3013 * If the big block depot is filled, this method will enlarge it.
3016 static ULONG StorageImpl_GetNextFreeBigBlock(
3017 StorageImpl* This)
3019 ULONG depotBlockIndexPos;
3020 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3021 BOOL success;
3022 ULONG depotBlockOffset;
3023 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3024 ULONG nextBlockIndex = BLOCK_SPECIAL;
3025 int depotIndex = 0;
3026 ULONG freeBlock = BLOCK_UNUSED;
3027 ULARGE_INTEGER neededSize;
3028 STATSTG statstg;
3030 depotIndex = This->prevFreeBlock / blocksPerDepot;
3031 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3034 * Scan the entire big block depot until we find a block marked free
3036 while (nextBlockIndex != BLOCK_UNUSED)
3038 if (depotIndex < COUNT_BBDEPOTINHEADER)
3040 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3043 * Grow the primary depot.
3045 if (depotBlockIndexPos == BLOCK_UNUSED)
3047 depotBlockIndexPos = depotIndex*blocksPerDepot;
3050 * Add a block depot.
3052 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3053 This->bigBlockDepotCount++;
3054 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3057 * Flag it as a block depot.
3059 StorageImpl_SetNextBlockInChain(This,
3060 depotBlockIndexPos,
3061 BLOCK_SPECIAL);
3063 /* Save new header information.
3065 StorageImpl_SaveFileHeader(This);
3068 else
3070 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3072 if (depotBlockIndexPos == BLOCK_UNUSED)
3075 * Grow the extended depot.
3077 ULONG extIndex = BLOCK_UNUSED;
3078 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3079 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3081 if (extBlockOffset == 0)
3083 /* We need an extended block.
3085 extIndex = Storage32Impl_AddExtBlockDepot(This);
3086 This->extBigBlockDepotCount++;
3087 depotBlockIndexPos = extIndex + 1;
3089 else
3090 depotBlockIndexPos = depotIndex * blocksPerDepot;
3093 * Add a block depot and mark it in the extended block.
3095 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3096 This->bigBlockDepotCount++;
3097 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3099 /* Flag the block depot.
3101 StorageImpl_SetNextBlockInChain(This,
3102 depotBlockIndexPos,
3103 BLOCK_SPECIAL);
3105 /* If necessary, flag the extended depot block.
3107 if (extIndex != BLOCK_UNUSED)
3108 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3110 /* Save header information.
3112 StorageImpl_SaveFileHeader(This);
3116 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3118 if (success)
3120 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3121 ( nextBlockIndex != BLOCK_UNUSED))
3123 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3125 if (nextBlockIndex == BLOCK_UNUSED)
3127 freeBlock = (depotIndex * blocksPerDepot) +
3128 (depotBlockOffset/sizeof(ULONG));
3131 depotBlockOffset += sizeof(ULONG);
3135 depotIndex++;
3136 depotBlockOffset = 0;
3140 * make sure that the block physically exists before using it
3142 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3144 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3146 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3147 ILockBytes_SetSize(This->lockBytes, neededSize);
3149 This->prevFreeBlock = freeBlock;
3151 return freeBlock;
3154 /******************************************************************************
3155 * Storage32Impl_AddBlockDepot
3157 * This will create a depot block, essentially it is a block initialized
3158 * to BLOCK_UNUSEDs.
3160 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3162 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3165 * Initialize blocks as free
3167 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3168 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3171 /******************************************************************************
3172 * Storage32Impl_GetExtDepotBlock
3174 * Returns the index of the block that corresponds to the specified depot
3175 * index. This method is only for depot indexes equal or greater than
3176 * COUNT_BBDEPOTINHEADER.
3178 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3180 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3181 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3182 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3183 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3184 ULONG blockIndex = BLOCK_UNUSED;
3185 ULONG extBlockIndex;
3186 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3187 int index, num_blocks;
3189 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3191 if (extBlockCount >= This->extBigBlockDepotCount)
3192 return BLOCK_UNUSED;
3194 if (This->indexExtBlockDepotCached != extBlockCount)
3196 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3198 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);
3200 num_blocks = This->bigBlockSize / 4;
3202 for (index = 0; index < num_blocks; index++)
3204 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3205 This->extBlockDepotCached[index] = blockIndex;
3208 This->indexExtBlockDepotCached = extBlockCount;
3211 blockIndex = This->extBlockDepotCached[extBlockOffset];
3213 return blockIndex;
3216 /******************************************************************************
3217 * Storage32Impl_SetExtDepotBlock
3219 * Associates the specified block index to the specified depot index.
3220 * This method is only for depot indexes equal or greater than
3221 * COUNT_BBDEPOTINHEADER.
3223 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3225 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3226 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3227 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3228 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3229 ULONG extBlockIndex;
3231 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3233 assert(extBlockCount < This->extBigBlockDepotCount);
3235 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3237 if (extBlockIndex != BLOCK_UNUSED)
3239 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3240 extBlockOffset * sizeof(ULONG),
3241 blockIndex);
3244 if (This->indexExtBlockDepotCached == extBlockCount)
3246 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3250 /******************************************************************************
3251 * Storage32Impl_AddExtBlockDepot
3253 * Creates an extended depot block.
3255 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3257 ULONG numExtBlocks = This->extBigBlockDepotCount;
3258 ULONG nextExtBlock = This->extBigBlockDepotStart;
3259 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3260 ULONG index = BLOCK_UNUSED;
3261 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3262 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3263 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3265 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3266 blocksPerDepotBlock;
3268 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3271 * The first extended block.
3273 This->extBigBlockDepotStart = index;
3275 else
3278 * Find the last existing extended block.
3280 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3283 * Add the new extended block to the chain.
3285 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3286 index);
3290 * Initialize this block.
3292 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3293 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3295 /* Add the block to our cache. */
3296 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3298 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3299 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3301 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3302 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3304 This->extBigBlockDepotLocations = new_cache;
3305 This->extBigBlockDepotLocationsSize = new_cache_size;
3307 This->extBigBlockDepotLocations[numExtBlocks] = index;
3309 return index;
3312 /******************************************************************************
3313 * Storage32Impl_FreeBigBlock
3315 * This method will flag the specified block as free in the big block depot.
3317 static void StorageImpl_FreeBigBlock(
3318 StorageImpl* This,
3319 ULONG blockIndex)
3321 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3323 if (blockIndex < This->prevFreeBlock)
3324 This->prevFreeBlock = blockIndex;
3327 /************************************************************************
3328 * Storage32Impl_GetNextBlockInChain
3330 * This method will retrieve the block index of the next big block in
3331 * in the chain.
3333 * Params: This - Pointer to the Storage object.
3334 * blockIndex - Index of the block to retrieve the chain
3335 * for.
3336 * nextBlockIndex - receives the return value.
3338 * Returns: This method returns the index of the next block in the chain.
3339 * It will return the constants:
3340 * BLOCK_SPECIAL - If the block given was not part of a
3341 * chain.
3342 * BLOCK_END_OF_CHAIN - If the block given was the last in
3343 * a chain.
3344 * BLOCK_UNUSED - If the block given was not past of a chain
3345 * and is available.
3346 * BLOCK_EXTBBDEPOT - This block is part of the extended
3347 * big block depot.
3349 * See Windows documentation for more details on IStorage methods.
3351 static HRESULT StorageImpl_GetNextBlockInChain(
3352 StorageImpl* This,
3353 ULONG blockIndex,
3354 ULONG* nextBlockIndex)
3356 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3357 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3358 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3359 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3360 BOOL success;
3361 ULONG depotBlockIndexPos;
3362 int index, num_blocks;
3364 *nextBlockIndex = BLOCK_SPECIAL;
3366 if(depotBlockCount >= This->bigBlockDepotCount)
3368 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3369 This->bigBlockDepotCount);
3370 return STG_E_READFAULT;
3374 * Cache the currently accessed depot block.
3376 if (depotBlockCount != This->indexBlockDepotCached)
3378 This->indexBlockDepotCached = depotBlockCount;
3380 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3382 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3384 else
3387 * We have to look in the extended depot.
3389 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3392 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3394 if (!success)
3395 return STG_E_READFAULT;
3397 num_blocks = This->bigBlockSize / 4;
3399 for (index = 0; index < num_blocks; index++)
3401 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3402 This->blockDepotCached[index] = *nextBlockIndex;
3406 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3408 return S_OK;
3411 /******************************************************************************
3412 * Storage32Impl_GetNextExtendedBlock
3414 * Given an extended block this method will return the next extended block.
3416 * NOTES:
3417 * The last ULONG of an extended block is the block index of the next
3418 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3419 * depot.
3421 * Return values:
3422 * - The index of the next extended block
3423 * - BLOCK_UNUSED: there is no next extended block.
3424 * - Any other return values denotes failure.
3426 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3428 ULONG nextBlockIndex = BLOCK_SPECIAL;
3429 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3431 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3432 &nextBlockIndex);
3434 return nextBlockIndex;
3437 /******************************************************************************
3438 * Storage32Impl_SetNextBlockInChain
3440 * This method will write the index of the specified block's next block
3441 * in the big block depot.
3443 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3444 * do the following
3446 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3447 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3448 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3451 static void StorageImpl_SetNextBlockInChain(
3452 StorageImpl* This,
3453 ULONG blockIndex,
3454 ULONG nextBlock)
3456 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3457 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3458 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3459 ULONG depotBlockIndexPos;
3461 assert(depotBlockCount < This->bigBlockDepotCount);
3462 assert(blockIndex != nextBlock);
3464 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3466 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3468 else
3471 * We have to look in the extended depot.
3473 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3476 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3477 nextBlock);
3479 * Update the cached block depot, if necessary.
3481 if (depotBlockCount == This->indexBlockDepotCached)
3483 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3487 /******************************************************************************
3488 * Storage32Impl_LoadFileHeader
3490 * This method will read in the file header
3492 static HRESULT StorageImpl_LoadFileHeader(
3493 StorageImpl* This)
3495 HRESULT hr;
3496 BYTE headerBigBlock[HEADER_SIZE];
3497 int index;
3498 ULARGE_INTEGER offset;
3499 DWORD bytes_read;
3501 TRACE("\n");
3503 * Get a pointer to the big block of data containing the header.
3505 offset.u.HighPart = 0;
3506 offset.u.LowPart = 0;
3507 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3508 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3509 hr = STG_E_FILENOTFOUND;
3512 * Extract the information from the header.
3514 if (SUCCEEDED(hr))
3517 * Check for the "magic number" signature and return an error if it is not
3518 * found.
3520 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3522 return STG_E_OLDFORMAT;
3525 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3527 return STG_E_INVALIDHEADER;
3530 StorageUtl_ReadWord(
3531 headerBigBlock,
3532 OFFSET_BIGBLOCKSIZEBITS,
3533 &This->bigBlockSizeBits);
3535 StorageUtl_ReadWord(
3536 headerBigBlock,
3537 OFFSET_SMALLBLOCKSIZEBITS,
3538 &This->smallBlockSizeBits);
3540 StorageUtl_ReadDWord(
3541 headerBigBlock,
3542 OFFSET_BBDEPOTCOUNT,
3543 &This->bigBlockDepotCount);
3545 StorageUtl_ReadDWord(
3546 headerBigBlock,
3547 OFFSET_ROOTSTARTBLOCK,
3548 &This->rootStartBlock);
3550 StorageUtl_ReadDWord(
3551 headerBigBlock,
3552 OFFSET_SMALLBLOCKLIMIT,
3553 &This->smallBlockLimit);
3555 StorageUtl_ReadDWord(
3556 headerBigBlock,
3557 OFFSET_SBDEPOTSTART,
3558 &This->smallBlockDepotStart);
3560 StorageUtl_ReadDWord(
3561 headerBigBlock,
3562 OFFSET_EXTBBDEPOTSTART,
3563 &This->extBigBlockDepotStart);
3565 StorageUtl_ReadDWord(
3566 headerBigBlock,
3567 OFFSET_EXTBBDEPOTCOUNT,
3568 &This->extBigBlockDepotCount);
3570 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3572 StorageUtl_ReadDWord(
3573 headerBigBlock,
3574 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3575 &(This->bigBlockDepotStart[index]));
3579 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3581 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3582 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3585 * Right now, the code is making some assumptions about the size of the
3586 * blocks, just make sure they are what we're expecting.
3588 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3589 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3590 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3592 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3593 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3594 hr = STG_E_INVALIDHEADER;
3596 else
3597 hr = S_OK;
3600 return hr;
3603 /******************************************************************************
3604 * Storage32Impl_SaveFileHeader
3606 * This method will save to the file the header
3608 static void StorageImpl_SaveFileHeader(
3609 StorageImpl* This)
3611 BYTE headerBigBlock[HEADER_SIZE];
3612 int index;
3613 HRESULT hr;
3614 ULARGE_INTEGER offset;
3615 DWORD bytes_read, bytes_written;
3616 DWORD major_version, dirsectorcount;
3619 * Get a pointer to the big block of data containing the header.
3621 offset.u.HighPart = 0;
3622 offset.u.LowPart = 0;
3623 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3624 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3625 hr = STG_E_FILENOTFOUND;
3627 if (This->bigBlockSizeBits == 0x9)
3628 major_version = 3;
3629 else if (This->bigBlockSizeBits == 0xc)
3630 major_version = 4;
3631 else
3633 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3634 major_version = 4;
3638 * If the block read failed, the file is probably new.
3640 if (FAILED(hr))
3643 * Initialize for all unknown fields.
3645 memset(headerBigBlock, 0, HEADER_SIZE);
3648 * Initialize the magic number.
3650 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3654 * Write the information to the header.
3656 StorageUtl_WriteWord(
3657 headerBigBlock,
3658 OFFSET_MINORVERSION,
3659 0x3e);
3661 StorageUtl_WriteWord(
3662 headerBigBlock,
3663 OFFSET_MAJORVERSION,
3664 major_version);
3666 StorageUtl_WriteWord(
3667 headerBigBlock,
3668 OFFSET_BYTEORDERMARKER,
3669 (WORD)-2);
3671 StorageUtl_WriteWord(
3672 headerBigBlock,
3673 OFFSET_BIGBLOCKSIZEBITS,
3674 This->bigBlockSizeBits);
3676 StorageUtl_WriteWord(
3677 headerBigBlock,
3678 OFFSET_SMALLBLOCKSIZEBITS,
3679 This->smallBlockSizeBits);
3681 if (major_version >= 4)
3683 if (This->rootBlockChain)
3684 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3685 else
3686 /* This file is being created, and it will start out with one block. */
3687 dirsectorcount = 1;
3689 else
3690 /* This field must be 0 in versions older than 4 */
3691 dirsectorcount = 0;
3693 StorageUtl_WriteDWord(
3694 headerBigBlock,
3695 OFFSET_DIRSECTORCOUNT,
3696 dirsectorcount);
3698 StorageUtl_WriteDWord(
3699 headerBigBlock,
3700 OFFSET_BBDEPOTCOUNT,
3701 This->bigBlockDepotCount);
3703 StorageUtl_WriteDWord(
3704 headerBigBlock,
3705 OFFSET_ROOTSTARTBLOCK,
3706 This->rootStartBlock);
3708 StorageUtl_WriteDWord(
3709 headerBigBlock,
3710 OFFSET_SMALLBLOCKLIMIT,
3711 This->smallBlockLimit);
3713 StorageUtl_WriteDWord(
3714 headerBigBlock,
3715 OFFSET_SBDEPOTSTART,
3716 This->smallBlockDepotStart);
3718 StorageUtl_WriteDWord(
3719 headerBigBlock,
3720 OFFSET_SBDEPOTCOUNT,
3721 This->smallBlockDepotChain ?
3722 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3724 StorageUtl_WriteDWord(
3725 headerBigBlock,
3726 OFFSET_EXTBBDEPOTSTART,
3727 This->extBigBlockDepotStart);
3729 StorageUtl_WriteDWord(
3730 headerBigBlock,
3731 OFFSET_EXTBBDEPOTCOUNT,
3732 This->extBigBlockDepotCount);
3734 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3736 StorageUtl_WriteDWord(
3737 headerBigBlock,
3738 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3739 (This->bigBlockDepotStart[index]));
3743 * Write the big block back to the file.
3745 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3748 /******************************************************************************
3749 * StorageImpl_ReadRawDirEntry
3751 * This method will read the raw data from a directory entry in the file.
3753 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3755 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3757 ULARGE_INTEGER offset;
3758 HRESULT hr;
3759 ULONG bytesRead;
3761 offset.u.HighPart = 0;
3762 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3764 hr = BlockChainStream_ReadAt(
3765 This->rootBlockChain,
3766 offset,
3767 RAW_DIRENTRY_SIZE,
3768 buffer,
3769 &bytesRead);
3771 if (bytesRead != RAW_DIRENTRY_SIZE)
3772 return STG_E_READFAULT;
3774 return hr;
3777 /******************************************************************************
3778 * StorageImpl_WriteRawDirEntry
3780 * This method will write the raw data from a directory entry in the file.
3782 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3784 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3786 ULARGE_INTEGER offset;
3787 HRESULT hr;
3788 ULONG bytesRead;
3790 offset.u.HighPart = 0;
3791 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3793 hr = BlockChainStream_WriteAt(
3794 This->rootBlockChain,
3795 offset,
3796 RAW_DIRENTRY_SIZE,
3797 buffer,
3798 &bytesRead);
3800 return hr;
3803 /******************************************************************************
3804 * UpdateRawDirEntry
3806 * Update raw directory entry data from the fields in newData.
3808 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3810 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3812 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3814 memcpy(
3815 buffer + OFFSET_PS_NAME,
3816 newData->name,
3817 DIRENTRY_NAME_BUFFER_LEN );
3819 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3821 StorageUtl_WriteWord(
3822 buffer,
3823 OFFSET_PS_NAMELENGTH,
3824 newData->sizeOfNameString);
3826 StorageUtl_WriteDWord(
3827 buffer,
3828 OFFSET_PS_LEFTCHILD,
3829 newData->leftChild);
3831 StorageUtl_WriteDWord(
3832 buffer,
3833 OFFSET_PS_RIGHTCHILD,
3834 newData->rightChild);
3836 StorageUtl_WriteDWord(
3837 buffer,
3838 OFFSET_PS_DIRROOT,
3839 newData->dirRootEntry);
3841 StorageUtl_WriteGUID(
3842 buffer,
3843 OFFSET_PS_GUID,
3844 &newData->clsid);
3846 StorageUtl_WriteDWord(
3847 buffer,
3848 OFFSET_PS_CTIMELOW,
3849 newData->ctime.dwLowDateTime);
3851 StorageUtl_WriteDWord(
3852 buffer,
3853 OFFSET_PS_CTIMEHIGH,
3854 newData->ctime.dwHighDateTime);
3856 StorageUtl_WriteDWord(
3857 buffer,
3858 OFFSET_PS_MTIMELOW,
3859 newData->mtime.dwLowDateTime);
3861 StorageUtl_WriteDWord(
3862 buffer,
3863 OFFSET_PS_MTIMEHIGH,
3864 newData->ctime.dwHighDateTime);
3866 StorageUtl_WriteDWord(
3867 buffer,
3868 OFFSET_PS_STARTBLOCK,
3869 newData->startingBlock);
3871 StorageUtl_WriteDWord(
3872 buffer,
3873 OFFSET_PS_SIZE,
3874 newData->size.u.LowPart);
3877 /******************************************************************************
3878 * Storage32Impl_ReadDirEntry
3880 * This method will read the specified directory entry.
3882 HRESULT StorageImpl_ReadDirEntry(
3883 StorageImpl* This,
3884 DirRef index,
3885 DirEntry* buffer)
3887 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3888 HRESULT readRes;
3890 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3892 if (SUCCEEDED(readRes))
3894 memset(buffer->name, 0, sizeof(buffer->name));
3895 memcpy(
3896 buffer->name,
3897 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3898 DIRENTRY_NAME_BUFFER_LEN );
3899 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3901 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3903 StorageUtl_ReadWord(
3904 currentEntry,
3905 OFFSET_PS_NAMELENGTH,
3906 &buffer->sizeOfNameString);
3908 StorageUtl_ReadDWord(
3909 currentEntry,
3910 OFFSET_PS_LEFTCHILD,
3911 &buffer->leftChild);
3913 StorageUtl_ReadDWord(
3914 currentEntry,
3915 OFFSET_PS_RIGHTCHILD,
3916 &buffer->rightChild);
3918 StorageUtl_ReadDWord(
3919 currentEntry,
3920 OFFSET_PS_DIRROOT,
3921 &buffer->dirRootEntry);
3923 StorageUtl_ReadGUID(
3924 currentEntry,
3925 OFFSET_PS_GUID,
3926 &buffer->clsid);
3928 StorageUtl_ReadDWord(
3929 currentEntry,
3930 OFFSET_PS_CTIMELOW,
3931 &buffer->ctime.dwLowDateTime);
3933 StorageUtl_ReadDWord(
3934 currentEntry,
3935 OFFSET_PS_CTIMEHIGH,
3936 &buffer->ctime.dwHighDateTime);
3938 StorageUtl_ReadDWord(
3939 currentEntry,
3940 OFFSET_PS_MTIMELOW,
3941 &buffer->mtime.dwLowDateTime);
3943 StorageUtl_ReadDWord(
3944 currentEntry,
3945 OFFSET_PS_MTIMEHIGH,
3946 &buffer->mtime.dwHighDateTime);
3948 StorageUtl_ReadDWord(
3949 currentEntry,
3950 OFFSET_PS_STARTBLOCK,
3951 &buffer->startingBlock);
3953 StorageUtl_ReadDWord(
3954 currentEntry,
3955 OFFSET_PS_SIZE,
3956 &buffer->size.u.LowPart);
3958 buffer->size.u.HighPart = 0;
3961 return readRes;
3964 /*********************************************************************
3965 * Write the specified directory entry to the file
3967 HRESULT StorageImpl_WriteDirEntry(
3968 StorageImpl* This,
3969 DirRef index,
3970 const DirEntry* buffer)
3972 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3973 HRESULT writeRes;
3975 UpdateRawDirEntry(currentEntry, buffer);
3977 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3978 return writeRes;
3981 static BOOL StorageImpl_ReadBigBlock(
3982 StorageImpl* This,
3983 ULONG blockIndex,
3984 void* buffer)
3986 ULARGE_INTEGER ulOffset;
3987 DWORD read=0;
3989 ulOffset.u.HighPart = 0;
3990 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3992 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3994 if (read && read < This->bigBlockSize)
3996 /* File ends during this block; fill the rest with 0's. */
3997 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
4000 return (read != 0);
4003 static BOOL StorageImpl_ReadDWordFromBigBlock(
4004 StorageImpl* This,
4005 ULONG blockIndex,
4006 ULONG offset,
4007 DWORD* value)
4009 ULARGE_INTEGER ulOffset;
4010 DWORD read;
4011 DWORD tmp;
4013 ulOffset.u.HighPart = 0;
4014 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4015 ulOffset.u.LowPart += offset;
4017 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4018 *value = lendian32toh(tmp);
4019 return (read == sizeof(DWORD));
4022 static BOOL StorageImpl_WriteBigBlock(
4023 StorageImpl* This,
4024 ULONG blockIndex,
4025 const void* buffer)
4027 ULARGE_INTEGER ulOffset;
4028 DWORD wrote;
4030 ulOffset.u.HighPart = 0;
4031 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4033 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4034 return (wrote == This->bigBlockSize);
4037 static BOOL StorageImpl_WriteDWordToBigBlock(
4038 StorageImpl* This,
4039 ULONG blockIndex,
4040 ULONG offset,
4041 DWORD value)
4043 ULARGE_INTEGER ulOffset;
4044 DWORD wrote;
4046 ulOffset.u.HighPart = 0;
4047 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4048 ulOffset.u.LowPart += offset;
4050 value = htole32(value);
4051 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4052 return (wrote == sizeof(DWORD));
4055 /******************************************************************************
4056 * Storage32Impl_SmallBlocksToBigBlocks
4058 * This method will convert a small block chain to a big block chain.
4059 * The small block chain will be destroyed.
4061 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4062 StorageImpl* This,
4063 SmallBlockChainStream** ppsbChain)
4065 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4066 ULARGE_INTEGER size, offset;
4067 ULONG cbRead, cbWritten;
4068 ULARGE_INTEGER cbTotalRead;
4069 DirRef streamEntryRef;
4070 HRESULT resWrite = S_OK;
4071 HRESULT resRead;
4072 DirEntry streamEntry;
4073 BYTE *buffer;
4074 BlockChainStream *bbTempChain = NULL;
4075 BlockChainStream *bigBlockChain = NULL;
4078 * Create a temporary big block chain that doesn't have
4079 * an associated directory entry. This temporary chain will be
4080 * used to copy data from small blocks to big blocks.
4082 bbTempChain = BlockChainStream_Construct(This,
4083 &bbHeadOfChain,
4084 DIRENTRY_NULL);
4085 if(!bbTempChain) return NULL;
4087 * Grow the big block chain.
4089 size = SmallBlockChainStream_GetSize(*ppsbChain);
4090 BlockChainStream_SetSize(bbTempChain, size);
4093 * Copy the contents of the small block chain to the big block chain
4094 * by small block size increments.
4096 offset.u.LowPart = 0;
4097 offset.u.HighPart = 0;
4098 cbTotalRead.QuadPart = 0;
4100 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4103 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4104 offset,
4105 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4106 buffer,
4107 &cbRead);
4108 if (FAILED(resRead))
4109 break;
4111 if (cbRead > 0)
4113 cbTotalRead.QuadPart += cbRead;
4115 resWrite = BlockChainStream_WriteAt(bbTempChain,
4116 offset,
4117 cbRead,
4118 buffer,
4119 &cbWritten);
4121 if (FAILED(resWrite))
4122 break;
4124 offset.u.LowPart += cbRead;
4126 else
4128 resRead = STG_E_READFAULT;
4129 break;
4131 } while (cbTotalRead.QuadPart < size.QuadPart);
4132 HeapFree(GetProcessHeap(),0,buffer);
4134 size.u.HighPart = 0;
4135 size.u.LowPart = 0;
4137 if (FAILED(resRead) || FAILED(resWrite))
4139 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4140 BlockChainStream_SetSize(bbTempChain, size);
4141 BlockChainStream_Destroy(bbTempChain);
4142 return NULL;
4146 * Destroy the small block chain.
4148 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4149 SmallBlockChainStream_SetSize(*ppsbChain, size);
4150 SmallBlockChainStream_Destroy(*ppsbChain);
4151 *ppsbChain = 0;
4154 * Change the directory entry. This chain is now a big block chain
4155 * and it doesn't reside in the small blocks chain anymore.
4157 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4159 streamEntry.startingBlock = bbHeadOfChain;
4161 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4164 * Destroy the temporary entryless big block chain.
4165 * Create a new big block chain associated with this entry.
4167 BlockChainStream_Destroy(bbTempChain);
4168 bigBlockChain = BlockChainStream_Construct(This,
4169 NULL,
4170 streamEntryRef);
4172 return bigBlockChain;
4175 /******************************************************************************
4176 * Storage32Impl_BigBlocksToSmallBlocks
4178 * This method will convert a big block chain to a small block chain.
4179 * The big block chain will be destroyed on success.
4181 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4182 StorageImpl* This,
4183 BlockChainStream** ppbbChain,
4184 ULARGE_INTEGER newSize)
4186 ULARGE_INTEGER size, offset, cbTotalRead;
4187 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4188 DirRef streamEntryRef;
4189 HRESULT resWrite = S_OK, resRead = S_OK;
4190 DirEntry streamEntry;
4191 BYTE* buffer;
4192 SmallBlockChainStream* sbTempChain;
4194 TRACE("%p %p\n", This, ppbbChain);
4196 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4197 DIRENTRY_NULL);
4199 if(!sbTempChain)
4200 return NULL;
4202 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4203 size = BlockChainStream_GetSize(*ppbbChain);
4204 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4206 offset.u.HighPart = 0;
4207 offset.u.LowPart = 0;
4208 cbTotalRead.QuadPart = 0;
4209 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4210 while(cbTotalRead.QuadPart < size.QuadPart)
4212 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4213 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4214 buffer, &cbRead);
4216 if(FAILED(resRead))
4217 break;
4219 if(cbRead > 0)
4221 cbTotalRead.QuadPart += cbRead;
4223 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4224 cbRead, buffer, &cbWritten);
4226 if(FAILED(resWrite))
4227 break;
4229 offset.u.LowPart += cbRead;
4231 else
4233 resRead = STG_E_READFAULT;
4234 break;
4237 HeapFree(GetProcessHeap(), 0, buffer);
4239 size.u.HighPart = 0;
4240 size.u.LowPart = 0;
4242 if(FAILED(resRead) || FAILED(resWrite))
4244 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4245 SmallBlockChainStream_SetSize(sbTempChain, size);
4246 SmallBlockChainStream_Destroy(sbTempChain);
4247 return NULL;
4250 /* destroy the original big block chain */
4251 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4252 BlockChainStream_SetSize(*ppbbChain, size);
4253 BlockChainStream_Destroy(*ppbbChain);
4254 *ppbbChain = NULL;
4256 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4257 streamEntry.startingBlock = sbHeadOfChain;
4258 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4260 SmallBlockChainStream_Destroy(sbTempChain);
4261 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4264 static HRESULT StorageBaseImpl_CopyStream(
4265 StorageBaseImpl *dst, DirRef dst_entry,
4266 StorageBaseImpl *src, DirRef src_entry)
4268 HRESULT hr;
4269 BYTE data[4096];
4270 DirEntry srcdata;
4271 ULARGE_INTEGER bytes_copied;
4272 ULONG bytestocopy, bytesread, byteswritten;
4274 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4276 if (SUCCEEDED(hr))
4278 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4280 bytes_copied.QuadPart = 0;
4281 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4283 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4285 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4286 data, &bytesread);
4287 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4289 if (SUCCEEDED(hr))
4290 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4291 data, &byteswritten);
4292 if (SUCCEEDED(hr))
4294 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4295 bytes_copied.QuadPart += byteswritten;
4300 return hr;
4303 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4305 DirRef result=This->firstFreeEntry;
4307 while (result < This->entries_size && This->entries[result].inuse)
4308 result++;
4310 if (result == This->entries_size)
4312 ULONG new_size = This->entries_size * 2;
4313 TransactedDirEntry *new_entries;
4315 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4316 if (!new_entries) return DIRENTRY_NULL;
4318 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4319 HeapFree(GetProcessHeap(), 0, This->entries);
4321 This->entries = new_entries;
4322 This->entries_size = new_size;
4325 This->entries[result].inuse = 1;
4327 This->firstFreeEntry = result+1;
4329 return result;
4332 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4333 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4335 DirRef stubEntryRef;
4336 TransactedDirEntry *entry;
4338 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4340 if (stubEntryRef != DIRENTRY_NULL)
4342 entry = &This->entries[stubEntryRef];
4344 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4346 entry->read = 0;
4349 return stubEntryRef;
4352 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4353 TransactedSnapshotImpl *This, DirRef entry)
4355 HRESULT hr=S_OK;
4356 DirEntry data;
4358 if (!This->entries[entry].read)
4360 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4361 This->entries[entry].transactedParentEntry,
4362 &data);
4364 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4366 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4368 if (data.leftChild == DIRENTRY_NULL)
4369 hr = E_OUTOFMEMORY;
4372 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4374 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4376 if (data.rightChild == DIRENTRY_NULL)
4377 hr = E_OUTOFMEMORY;
4380 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4382 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4384 if (data.dirRootEntry == DIRENTRY_NULL)
4385 hr = E_OUTOFMEMORY;
4388 if (SUCCEEDED(hr))
4390 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4391 This->entries[entry].read = 1;
4395 return hr;
4398 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4399 TransactedSnapshotImpl *This, DirRef entry)
4401 HRESULT hr = S_OK;
4403 if (!This->entries[entry].stream_dirty)
4405 DirEntry new_entrydata;
4407 memset(&new_entrydata, 0, sizeof(DirEntry));
4408 new_entrydata.name[0] = 'S';
4409 new_entrydata.sizeOfNameString = 1;
4410 new_entrydata.stgType = STGTY_STREAM;
4411 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4412 new_entrydata.leftChild = DIRENTRY_NULL;
4413 new_entrydata.rightChild = DIRENTRY_NULL;
4414 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4416 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4417 &This->entries[entry].stream_entry);
4419 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4421 hr = StorageBaseImpl_CopyStream(
4422 This->scratch, This->entries[entry].stream_entry,
4423 This->transactedParent, This->entries[entry].transactedParentEntry);
4425 if (FAILED(hr))
4426 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4429 if (SUCCEEDED(hr))
4430 This->entries[entry].stream_dirty = 1;
4432 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4434 /* Since this entry is modified, and we aren't using its stream data, we
4435 * no longer care about the original entry. */
4436 DirRef delete_ref;
4437 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4439 if (delete_ref != DIRENTRY_NULL)
4440 This->entries[delete_ref].deleted = 1;
4442 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4446 return hr;
4449 /* Find the first entry in a depth-first traversal. */
4450 static DirRef TransactedSnapshotImpl_FindFirstChild(
4451 TransactedSnapshotImpl* This, DirRef parent)
4453 DirRef cursor, prev;
4454 TransactedDirEntry *entry;
4456 cursor = parent;
4457 entry = &This->entries[cursor];
4458 while (entry->read)
4460 if (entry->data.leftChild != DIRENTRY_NULL)
4462 prev = cursor;
4463 cursor = entry->data.leftChild;
4464 entry = &This->entries[cursor];
4465 entry->parent = prev;
4467 else if (entry->data.rightChild != DIRENTRY_NULL)
4469 prev = cursor;
4470 cursor = entry->data.rightChild;
4471 entry = &This->entries[cursor];
4472 entry->parent = prev;
4474 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4476 prev = cursor;
4477 cursor = entry->data.dirRootEntry;
4478 entry = &This->entries[cursor];
4479 entry->parent = prev;
4481 else
4482 break;
4485 return cursor;
4488 /* Find the next entry in a depth-first traversal. */
4489 static DirRef TransactedSnapshotImpl_FindNextChild(
4490 TransactedSnapshotImpl* This, DirRef current)
4492 DirRef parent;
4493 TransactedDirEntry *parent_entry;
4495 parent = This->entries[current].parent;
4496 parent_entry = &This->entries[parent];
4498 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4500 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4502 This->entries[parent_entry->data.rightChild].parent = parent;
4503 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4506 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4508 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4509 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4513 return parent;
4516 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4517 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4518 TransactedSnapshotImpl* This, DirRef entry)
4520 return entry != DIRENTRY_NULL &&
4521 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4524 /* Destroy the entries created by CopyTree. */
4525 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4526 TransactedSnapshotImpl* This, DirRef stop)
4528 DirRef cursor;
4529 TransactedDirEntry *entry;
4530 ULARGE_INTEGER zero;
4532 zero.QuadPart = 0;
4534 if (!This->entries[This->base.storageDirEntry].read)
4535 return;
4537 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4539 if (cursor == DIRENTRY_NULL)
4540 return;
4542 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4544 while (cursor != DIRENTRY_NULL && cursor != stop)
4546 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4548 entry = &This->entries[cursor];
4550 if (entry->stream_dirty)
4551 StorageBaseImpl_StreamSetSize(This->transactedParent,
4552 entry->newTransactedParentEntry, zero);
4554 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4555 entry->newTransactedParentEntry);
4557 entry->newTransactedParentEntry = entry->transactedParentEntry;
4560 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4564 /* Make a copy of our edited tree that we can use in the parent. */
4565 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4567 DirRef cursor;
4568 TransactedDirEntry *entry;
4569 HRESULT hr = S_OK;
4571 cursor = This->base.storageDirEntry;
4572 entry = &This->entries[cursor];
4573 entry->parent = DIRENTRY_NULL;
4574 entry->newTransactedParentEntry = entry->transactedParentEntry;
4576 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4577 return S_OK;
4579 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4581 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4582 entry = &This->entries[cursor];
4584 while (cursor != DIRENTRY_NULL)
4586 /* Make a copy of this entry in the transacted parent. */
4587 if (!entry->read ||
4588 (!entry->dirty && !entry->stream_dirty &&
4589 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4590 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4591 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4592 entry->newTransactedParentEntry = entry->transactedParentEntry;
4593 else
4595 DirEntry newData;
4597 memcpy(&newData, &entry->data, sizeof(DirEntry));
4599 newData.size.QuadPart = 0;
4600 newData.startingBlock = BLOCK_END_OF_CHAIN;
4602 if (newData.leftChild != DIRENTRY_NULL)
4603 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4605 if (newData.rightChild != DIRENTRY_NULL)
4606 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4608 if (newData.dirRootEntry != DIRENTRY_NULL)
4609 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4611 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4612 &entry->newTransactedParentEntry);
4613 if (FAILED(hr))
4615 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4616 return hr;
4619 if (entry->stream_dirty)
4621 hr = StorageBaseImpl_CopyStream(
4622 This->transactedParent, entry->newTransactedParentEntry,
4623 This->scratch, entry->stream_entry);
4625 else if (entry->data.size.QuadPart)
4627 hr = StorageBaseImpl_StreamLink(
4628 This->transactedParent, entry->newTransactedParentEntry,
4629 entry->transactedParentEntry);
4632 if (FAILED(hr))
4634 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4635 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4636 return hr;
4640 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4641 entry = &This->entries[cursor];
4644 return hr;
4647 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4648 IStorage* iface,
4649 DWORD grfCommitFlags) /* [in] */
4651 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4652 TransactedDirEntry *root_entry;
4653 DirRef i, dir_root_ref;
4654 DirEntry data;
4655 ULARGE_INTEGER zero;
4656 HRESULT hr;
4658 zero.QuadPart = 0;
4660 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4662 /* Cannot commit a read-only transacted storage */
4663 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4664 return STG_E_ACCESSDENIED;
4666 /* To prevent data loss, we create the new structure in the file before we
4667 * delete the old one, so that in case of errors the old data is intact. We
4668 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4669 * needed in the rare situation where we have just enough free disk space to
4670 * overwrite the existing data. */
4672 root_entry = &This->entries[This->base.storageDirEntry];
4674 if (!root_entry->read)
4675 return S_OK;
4677 hr = TransactedSnapshotImpl_CopyTree(This);
4678 if (FAILED(hr)) return hr;
4680 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4681 dir_root_ref = DIRENTRY_NULL;
4682 else
4683 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4685 hr = StorageBaseImpl_Flush(This->transactedParent);
4687 /* Update the storage to use the new data in one step. */
4688 if (SUCCEEDED(hr))
4689 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4690 root_entry->transactedParentEntry, &data);
4692 if (SUCCEEDED(hr))
4694 data.dirRootEntry = dir_root_ref;
4695 data.clsid = root_entry->data.clsid;
4696 data.ctime = root_entry->data.ctime;
4697 data.mtime = root_entry->data.mtime;
4699 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4700 root_entry->transactedParentEntry, &data);
4703 /* Try to flush after updating the root storage, but if the flush fails, keep
4704 * going, on the theory that it'll either succeed later or the subsequent
4705 * writes will fail. */
4706 StorageBaseImpl_Flush(This->transactedParent);
4708 if (SUCCEEDED(hr))
4710 /* Destroy the old now-orphaned data. */
4711 for (i=0; i<This->entries_size; i++)
4713 TransactedDirEntry *entry = &This->entries[i];
4714 if (entry->inuse)
4716 if (entry->deleted)
4718 StorageBaseImpl_StreamSetSize(This->transactedParent,
4719 entry->transactedParentEntry, zero);
4720 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4721 entry->transactedParentEntry);
4722 memset(entry, 0, sizeof(TransactedDirEntry));
4723 This->firstFreeEntry = min(i, This->firstFreeEntry);
4725 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4727 if (entry->transactedParentEntry != DIRENTRY_NULL)
4728 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4729 entry->transactedParentEntry);
4730 if (entry->stream_dirty)
4732 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4733 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4734 entry->stream_dirty = 0;
4736 entry->dirty = 0;
4737 entry->transactedParentEntry = entry->newTransactedParentEntry;
4742 else
4744 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4747 if (SUCCEEDED(hr))
4748 hr = StorageBaseImpl_Flush(This->transactedParent);
4750 return hr;
4753 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4754 IStorage* iface)
4756 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4757 ULARGE_INTEGER zero;
4758 ULONG i;
4760 TRACE("(%p)\n", iface);
4762 /* Destroy the open objects. */
4763 StorageBaseImpl_DeleteAll(&This->base);
4765 /* Clear out the scratch file. */
4766 zero.QuadPart = 0;
4767 for (i=0; i<This->entries_size; i++)
4769 if (This->entries[i].stream_dirty)
4771 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4772 zero);
4774 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4778 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4780 This->firstFreeEntry = 0;
4781 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4783 return S_OK;
4786 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4788 if (!This->reverted)
4790 TRACE("Storage invalidated (stg=%p)\n", This);
4792 This->reverted = 1;
4794 StorageBaseImpl_DeleteAll(This);
4798 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4800 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4802 TransactedSnapshotImpl_Revert(&This->base.IStorage_iface);
4804 IStorage_Release(&This->transactedParent->IStorage_iface);
4806 IStorage_Release(&This->scratch->IStorage_iface);
4808 HeapFree(GetProcessHeap(), 0, This->entries);
4810 HeapFree(GetProcessHeap(), 0, This);
4813 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4815 /* We only need to flush when committing. */
4816 return S_OK;
4819 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4821 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4823 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4826 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4827 const DirEntry *newData, DirRef *index)
4829 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4830 DirRef new_ref;
4831 TransactedDirEntry *new_entry;
4833 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4834 if (new_ref == DIRENTRY_NULL)
4835 return E_OUTOFMEMORY;
4837 new_entry = &This->entries[new_ref];
4839 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4840 new_entry->read = 1;
4841 new_entry->dirty = 1;
4842 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4844 *index = new_ref;
4846 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4848 return S_OK;
4851 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4852 DirRef index, const DirEntry *data)
4854 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4855 HRESULT hr;
4857 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4859 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4860 if (FAILED(hr)) return hr;
4862 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4864 if (index != This->base.storageDirEntry)
4866 This->entries[index].dirty = 1;
4868 if (data->size.QuadPart == 0 &&
4869 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4871 /* Since this entry is modified, and we aren't using its stream data, we
4872 * no longer care about the original entry. */
4873 DirRef delete_ref;
4874 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4876 if (delete_ref != DIRENTRY_NULL)
4877 This->entries[delete_ref].deleted = 1;
4879 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4883 return S_OK;
4886 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4887 DirRef index, DirEntry *data)
4889 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4890 HRESULT hr;
4892 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4893 if (FAILED(hr)) return hr;
4895 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4897 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4899 return S_OK;
4902 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4903 DirRef index)
4905 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4907 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4908 This->entries[index].data.size.QuadPart != 0)
4910 /* If we deleted this entry while it has stream data. We must have left the
4911 * data because some other entry is using it, and we need to leave the
4912 * original entry alone. */
4913 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4914 This->firstFreeEntry = min(index, This->firstFreeEntry);
4916 else
4918 This->entries[index].deleted = 1;
4921 return S_OK;
4924 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4925 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4927 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4929 if (This->entries[index].stream_dirty)
4931 return StorageBaseImpl_StreamReadAt(This->scratch,
4932 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4934 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4936 /* This stream doesn't live in the parent, and we haven't allocated storage
4937 * for it yet */
4938 *bytesRead = 0;
4939 return S_OK;
4941 else
4943 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4944 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4948 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4949 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4951 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4952 HRESULT hr;
4954 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4955 if (FAILED(hr)) return hr;
4957 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4958 if (FAILED(hr)) return hr;
4960 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4961 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4963 if (SUCCEEDED(hr) && size != 0)
4964 This->entries[index].data.size.QuadPart = max(
4965 This->entries[index].data.size.QuadPart,
4966 offset.QuadPart + size);
4968 return hr;
4971 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4972 DirRef index, ULARGE_INTEGER newsize)
4974 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4975 HRESULT hr;
4977 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4978 if (FAILED(hr)) return hr;
4980 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4981 return S_OK;
4983 if (newsize.QuadPart == 0)
4985 /* Destroy any parent references or entries in the scratch file. */
4986 if (This->entries[index].stream_dirty)
4988 ULARGE_INTEGER zero;
4989 zero.QuadPart = 0;
4990 StorageBaseImpl_StreamSetSize(This->scratch,
4991 This->entries[index].stream_entry, zero);
4992 StorageBaseImpl_DestroyDirEntry(This->scratch,
4993 This->entries[index].stream_entry);
4994 This->entries[index].stream_dirty = 0;
4996 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4998 DirRef delete_ref;
4999 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5001 if (delete_ref != DIRENTRY_NULL)
5002 This->entries[delete_ref].deleted = 1;
5004 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5007 else
5009 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5010 if (FAILED(hr)) return hr;
5012 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5013 This->entries[index].stream_entry, newsize);
5016 if (SUCCEEDED(hr))
5017 This->entries[index].data.size = newsize;
5019 return hr;
5022 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5023 DirRef dst, DirRef src)
5025 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5026 HRESULT hr;
5027 TransactedDirEntry *dst_entry, *src_entry;
5029 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5030 if (FAILED(hr)) return hr;
5032 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5033 if (FAILED(hr)) return hr;
5035 dst_entry = &This->entries[dst];
5036 src_entry = &This->entries[src];
5038 dst_entry->stream_dirty = src_entry->stream_dirty;
5039 dst_entry->stream_entry = src_entry->stream_entry;
5040 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5041 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5042 dst_entry->data.size = src_entry->data.size;
5044 return S_OK;
5047 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5049 StorageBaseImpl_QueryInterface,
5050 StorageBaseImpl_AddRef,
5051 StorageBaseImpl_Release,
5052 StorageBaseImpl_CreateStream,
5053 StorageBaseImpl_OpenStream,
5054 StorageBaseImpl_CreateStorage,
5055 StorageBaseImpl_OpenStorage,
5056 StorageBaseImpl_CopyTo,
5057 StorageBaseImpl_MoveElementTo,
5058 TransactedSnapshotImpl_Commit,
5059 TransactedSnapshotImpl_Revert,
5060 StorageBaseImpl_EnumElements,
5061 StorageBaseImpl_DestroyElement,
5062 StorageBaseImpl_RenameElement,
5063 StorageBaseImpl_SetElementTimes,
5064 StorageBaseImpl_SetClass,
5065 StorageBaseImpl_SetStateBits,
5066 StorageBaseImpl_Stat
5069 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5071 TransactedSnapshotImpl_Destroy,
5072 TransactedSnapshotImpl_Invalidate,
5073 TransactedSnapshotImpl_Flush,
5074 TransactedSnapshotImpl_GetFilename,
5075 TransactedSnapshotImpl_CreateDirEntry,
5076 TransactedSnapshotImpl_WriteDirEntry,
5077 TransactedSnapshotImpl_ReadDirEntry,
5078 TransactedSnapshotImpl_DestroyDirEntry,
5079 TransactedSnapshotImpl_StreamReadAt,
5080 TransactedSnapshotImpl_StreamWriteAt,
5081 TransactedSnapshotImpl_StreamSetSize,
5082 TransactedSnapshotImpl_StreamLink
5085 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5086 TransactedSnapshotImpl** result)
5088 HRESULT hr;
5090 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5091 if (*result)
5093 IStorage *scratch;
5095 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5097 /* This is OK because the property set storage functions use the IStorage functions. */
5098 (*result)->base.pssVtbl = parentStorage->pssVtbl;
5100 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5102 list_init(&(*result)->base.strmHead);
5104 list_init(&(*result)->base.storageHead);
5106 (*result)->base.ref = 1;
5108 (*result)->base.openFlags = parentStorage->openFlags;
5110 /* Create a new temporary storage to act as the scratch file. */
5111 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5112 0, &scratch);
5113 (*result)->scratch = impl_from_IStorage(scratch);
5115 if (SUCCEEDED(hr))
5117 ULONG num_entries = 20;
5119 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5120 (*result)->entries_size = num_entries;
5121 (*result)->firstFreeEntry = 0;
5123 if ((*result)->entries)
5125 /* parentStorage already has 1 reference, which we take over here. */
5126 (*result)->transactedParent = parentStorage;
5128 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5130 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5132 else
5134 IStorage_Release(scratch);
5136 hr = E_OUTOFMEMORY;
5140 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5142 return hr;
5144 else
5145 return E_OUTOFMEMORY;
5148 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5149 StorageBaseImpl** result)
5151 static int fixme=0;
5153 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5155 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5158 return TransactedSnapshotImpl_Construct(parentStorage,
5159 (TransactedSnapshotImpl**)result);
5162 static HRESULT Storage_Construct(
5163 HANDLE hFile,
5164 LPCOLESTR pwcsName,
5165 ILockBytes* pLkbyt,
5166 DWORD openFlags,
5167 BOOL fileBased,
5168 BOOL create,
5169 ULONG sector_size,
5170 StorageBaseImpl** result)
5172 StorageImpl *newStorage;
5173 StorageBaseImpl *newTransactedStorage;
5174 HRESULT hr;
5176 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5177 if (FAILED(hr)) goto end;
5179 if (openFlags & STGM_TRANSACTED)
5181 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5182 if (FAILED(hr))
5183 IStorage_Release(&newStorage->base.IStorage_iface);
5184 else
5185 *result = newTransactedStorage;
5187 else
5188 *result = &newStorage->base;
5190 end:
5191 return hr;
5194 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5196 StorageInternalImpl* This = (StorageInternalImpl*) base;
5198 if (!This->base.reverted)
5200 TRACE("Storage invalidated (stg=%p)\n", This);
5202 This->base.reverted = 1;
5204 This->parentStorage = NULL;
5206 StorageBaseImpl_DeleteAll(&This->base);
5208 list_remove(&This->ParentListEntry);
5212 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5214 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5216 StorageInternalImpl_Invalidate(&This->base);
5218 HeapFree(GetProcessHeap(), 0, This);
5221 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5223 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5225 return StorageBaseImpl_Flush(This->parentStorage);
5228 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5230 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5232 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5235 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5236 const DirEntry *newData, DirRef *index)
5238 StorageInternalImpl* This = (StorageInternalImpl*) base;
5240 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5241 newData, index);
5244 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5245 DirRef index, const DirEntry *data)
5247 StorageInternalImpl* This = (StorageInternalImpl*) base;
5249 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5250 index, data);
5253 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5254 DirRef index, DirEntry *data)
5256 StorageInternalImpl* This = (StorageInternalImpl*) base;
5258 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5259 index, data);
5262 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5263 DirRef index)
5265 StorageInternalImpl* This = (StorageInternalImpl*) base;
5267 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5268 index);
5271 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5272 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5274 StorageInternalImpl* This = (StorageInternalImpl*) base;
5276 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5277 index, offset, size, buffer, bytesRead);
5280 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5281 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5283 StorageInternalImpl* This = (StorageInternalImpl*) base;
5285 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5286 index, offset, size, buffer, bytesWritten);
5289 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5290 DirRef index, ULARGE_INTEGER newsize)
5292 StorageInternalImpl* This = (StorageInternalImpl*) base;
5294 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5295 index, newsize);
5298 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5299 DirRef dst, DirRef src)
5301 StorageInternalImpl* This = (StorageInternalImpl*) base;
5303 return StorageBaseImpl_StreamLink(This->parentStorage,
5304 dst, src);
5307 /******************************************************************************
5309 ** Storage32InternalImpl_Commit
5312 static HRESULT WINAPI StorageInternalImpl_Commit(
5313 IStorage* iface,
5314 DWORD grfCommitFlags) /* [in] */
5316 StorageBaseImpl* This = impl_from_IStorage(iface);
5317 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5318 return StorageBaseImpl_Flush(This);
5321 /******************************************************************************
5323 ** Storage32InternalImpl_Revert
5326 static HRESULT WINAPI StorageInternalImpl_Revert(
5327 IStorage* iface)
5329 FIXME("(%p): stub\n", iface);
5330 return S_OK;
5333 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5335 IStorage_Release(&This->parentStorage->IStorage_iface);
5336 HeapFree(GetProcessHeap(), 0, This);
5339 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5340 IEnumSTATSTG* iface,
5341 REFIID riid,
5342 void** ppvObject)
5344 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5346 if (ppvObject==0)
5347 return E_INVALIDARG;
5349 *ppvObject = 0;
5351 if (IsEqualGUID(&IID_IUnknown, riid) ||
5352 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5354 *ppvObject = This;
5355 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5356 return S_OK;
5359 return E_NOINTERFACE;
5362 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5363 IEnumSTATSTG* iface)
5365 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5366 return InterlockedIncrement(&This->ref);
5369 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5370 IEnumSTATSTG* iface)
5372 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5374 ULONG newRef;
5376 newRef = InterlockedDecrement(&This->ref);
5378 if (newRef==0)
5380 IEnumSTATSTGImpl_Destroy(This);
5383 return newRef;
5386 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5387 IEnumSTATSTGImpl* This,
5388 DirRef *ref)
5390 DirRef result = DIRENTRY_NULL;
5391 DirRef searchNode;
5392 DirEntry entry;
5393 HRESULT hr;
5394 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5396 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5397 This->parentStorage->storageDirEntry, &entry);
5398 searchNode = entry.dirRootEntry;
5400 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5402 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5404 if (SUCCEEDED(hr))
5406 LONG diff = entryNameCmp( entry.name, This->name);
5408 if (diff <= 0)
5410 searchNode = entry.rightChild;
5412 else
5414 result = searchNode;
5415 memcpy(result_name, entry.name, sizeof(result_name));
5416 searchNode = entry.leftChild;
5421 if (SUCCEEDED(hr))
5423 *ref = result;
5424 if (result != DIRENTRY_NULL)
5425 memcpy(This->name, result_name, sizeof(result_name));
5428 return hr;
5431 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5432 IEnumSTATSTG* iface,
5433 ULONG celt,
5434 STATSTG* rgelt,
5435 ULONG* pceltFetched)
5437 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5439 DirEntry currentEntry;
5440 STATSTG* currentReturnStruct = rgelt;
5441 ULONG objectFetched = 0;
5442 DirRef currentSearchNode;
5443 HRESULT hr=S_OK;
5445 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5446 return E_INVALIDARG;
5448 if (This->parentStorage->reverted)
5449 return STG_E_REVERTED;
5452 * To avoid the special case, get another pointer to a ULONG value if
5453 * the caller didn't supply one.
5455 if (pceltFetched==0)
5456 pceltFetched = &objectFetched;
5459 * Start the iteration, we will iterate until we hit the end of the
5460 * linked list or until we hit the number of items to iterate through
5462 *pceltFetched = 0;
5464 while ( *pceltFetched < celt )
5466 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5468 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5469 break;
5472 * Read the entry from the storage.
5474 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5475 currentSearchNode,
5476 &currentEntry);
5479 * Copy the information to the return buffer.
5481 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5482 currentReturnStruct,
5483 &currentEntry,
5484 STATFLAG_DEFAULT);
5487 * Step to the next item in the iteration
5489 (*pceltFetched)++;
5490 currentReturnStruct++;
5493 if (SUCCEEDED(hr) && *pceltFetched != celt)
5494 hr = S_FALSE;
5496 return hr;
5500 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5501 IEnumSTATSTG* iface,
5502 ULONG celt)
5504 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5506 ULONG objectFetched = 0;
5507 DirRef currentSearchNode;
5508 HRESULT hr=S_OK;
5510 if (This->parentStorage->reverted)
5511 return STG_E_REVERTED;
5513 while ( (objectFetched < celt) )
5515 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5517 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5518 break;
5520 objectFetched++;
5523 if (SUCCEEDED(hr) && objectFetched != celt)
5524 return S_FALSE;
5526 return hr;
5529 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5530 IEnumSTATSTG* iface)
5532 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5534 if (This->parentStorage->reverted)
5535 return STG_E_REVERTED;
5537 This->name[0] = 0;
5539 return S_OK;
5542 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5543 IEnumSTATSTG* iface,
5544 IEnumSTATSTG** ppenum)
5546 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5548 IEnumSTATSTGImpl* newClone;
5550 if (This->parentStorage->reverted)
5551 return STG_E_REVERTED;
5554 * Perform a sanity check on the parameters.
5556 if (ppenum==0)
5557 return E_INVALIDARG;
5559 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5560 This->storageDirEntry);
5564 * The new clone enumeration must point to the same current node as
5565 * the ole one.
5567 memcpy(newClone->name, This->name, sizeof(newClone->name));
5569 *ppenum = &newClone->IEnumSTATSTG_iface;
5572 * Don't forget to nail down a reference to the clone before
5573 * returning it.
5575 IEnumSTATSTGImpl_AddRef(*ppenum);
5577 return S_OK;
5581 * Virtual function table for the IEnumSTATSTGImpl class.
5583 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5585 IEnumSTATSTGImpl_QueryInterface,
5586 IEnumSTATSTGImpl_AddRef,
5587 IEnumSTATSTGImpl_Release,
5588 IEnumSTATSTGImpl_Next,
5589 IEnumSTATSTGImpl_Skip,
5590 IEnumSTATSTGImpl_Reset,
5591 IEnumSTATSTGImpl_Clone
5594 /******************************************************************************
5595 ** IEnumSTATSTGImpl implementation
5598 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5599 StorageBaseImpl* parentStorage,
5600 DirRef storageDirEntry)
5602 IEnumSTATSTGImpl* newEnumeration;
5604 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5606 if (newEnumeration!=0)
5609 * Set-up the virtual function table and reference count.
5611 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5612 newEnumeration->ref = 0;
5615 * We want to nail-down the reference to the storage in case the
5616 * enumeration out-lives the storage in the client application.
5618 newEnumeration->parentStorage = parentStorage;
5619 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
5621 newEnumeration->storageDirEntry = storageDirEntry;
5624 * Make sure the current node of the iterator is the first one.
5626 IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface);
5629 return newEnumeration;
5633 * Virtual function table for the Storage32InternalImpl class.
5635 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5637 StorageBaseImpl_QueryInterface,
5638 StorageBaseImpl_AddRef,
5639 StorageBaseImpl_Release,
5640 StorageBaseImpl_CreateStream,
5641 StorageBaseImpl_OpenStream,
5642 StorageBaseImpl_CreateStorage,
5643 StorageBaseImpl_OpenStorage,
5644 StorageBaseImpl_CopyTo,
5645 StorageBaseImpl_MoveElementTo,
5646 StorageInternalImpl_Commit,
5647 StorageInternalImpl_Revert,
5648 StorageBaseImpl_EnumElements,
5649 StorageBaseImpl_DestroyElement,
5650 StorageBaseImpl_RenameElement,
5651 StorageBaseImpl_SetElementTimes,
5652 StorageBaseImpl_SetClass,
5653 StorageBaseImpl_SetStateBits,
5654 StorageBaseImpl_Stat
5657 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5659 StorageInternalImpl_Destroy,
5660 StorageInternalImpl_Invalidate,
5661 StorageInternalImpl_Flush,
5662 StorageInternalImpl_GetFilename,
5663 StorageInternalImpl_CreateDirEntry,
5664 StorageInternalImpl_WriteDirEntry,
5665 StorageInternalImpl_ReadDirEntry,
5666 StorageInternalImpl_DestroyDirEntry,
5667 StorageInternalImpl_StreamReadAt,
5668 StorageInternalImpl_StreamWriteAt,
5669 StorageInternalImpl_StreamSetSize,
5670 StorageInternalImpl_StreamLink
5673 /******************************************************************************
5674 ** Storage32InternalImpl implementation
5677 static StorageInternalImpl* StorageInternalImpl_Construct(
5678 StorageBaseImpl* parentStorage,
5679 DWORD openFlags,
5680 DirRef storageDirEntry)
5682 StorageInternalImpl* newStorage;
5684 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5686 if (newStorage!=0)
5688 list_init(&newStorage->base.strmHead);
5690 list_init(&newStorage->base.storageHead);
5693 * Initialize the virtual function table.
5695 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
5696 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5697 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5698 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5700 newStorage->base.reverted = 0;
5702 newStorage->base.ref = 1;
5704 newStorage->parentStorage = parentStorage;
5707 * Keep a reference to the directory entry of this storage
5709 newStorage->base.storageDirEntry = storageDirEntry;
5711 newStorage->base.create = 0;
5713 return newStorage;
5716 return 0;
5719 /******************************************************************************
5720 ** StorageUtl implementation
5723 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5725 WORD tmp;
5727 memcpy(&tmp, buffer+offset, sizeof(WORD));
5728 *value = lendian16toh(tmp);
5731 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5733 value = htole16(value);
5734 memcpy(buffer+offset, &value, sizeof(WORD));
5737 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5739 DWORD tmp;
5741 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5742 *value = lendian32toh(tmp);
5745 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5747 value = htole32(value);
5748 memcpy(buffer+offset, &value, sizeof(DWORD));
5751 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5752 ULARGE_INTEGER* value)
5754 #ifdef WORDS_BIGENDIAN
5755 ULARGE_INTEGER tmp;
5757 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5758 value->u.LowPart = htole32(tmp.u.HighPart);
5759 value->u.HighPart = htole32(tmp.u.LowPart);
5760 #else
5761 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5762 #endif
5765 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5766 const ULARGE_INTEGER *value)
5768 #ifdef WORDS_BIGENDIAN
5769 ULARGE_INTEGER tmp;
5771 tmp.u.LowPart = htole32(value->u.HighPart);
5772 tmp.u.HighPart = htole32(value->u.LowPart);
5773 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5774 #else
5775 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5776 #endif
5779 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5781 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5782 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5783 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5785 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5788 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5790 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5791 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5792 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5794 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5797 void StorageUtl_CopyDirEntryToSTATSTG(
5798 StorageBaseImpl* storage,
5799 STATSTG* destination,
5800 const DirEntry* source,
5801 int statFlags)
5804 * The copy of the string occurs only when the flag is not set
5806 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5808 /* Use the filename for the root storage. */
5809 destination->pwcsName = 0;
5810 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5812 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5813 (source->name[0] == 0) )
5815 destination->pwcsName = 0;
5817 else
5819 destination->pwcsName =
5820 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5822 strcpyW(destination->pwcsName, source->name);
5825 switch (source->stgType)
5827 case STGTY_STORAGE:
5828 case STGTY_ROOT:
5829 destination->type = STGTY_STORAGE;
5830 break;
5831 case STGTY_STREAM:
5832 destination->type = STGTY_STREAM;
5833 break;
5834 default:
5835 destination->type = STGTY_STREAM;
5836 break;
5839 destination->cbSize = source->size;
5841 currentReturnStruct->mtime = {0}; TODO
5842 currentReturnStruct->ctime = {0};
5843 currentReturnStruct->atime = {0};
5845 destination->grfMode = 0;
5846 destination->grfLocksSupported = 0;
5847 destination->clsid = source->clsid;
5848 destination->grfStateBits = 0;
5849 destination->reserved = 0;
5852 /******************************************************************************
5853 ** BlockChainStream implementation
5856 /* Read and save the index of all blocks in this stream. */
5857 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5859 ULONG next_sector, next_offset;
5860 HRESULT hr;
5861 struct BlockChainRun *last_run;
5863 if (This->indexCacheLen == 0)
5865 last_run = NULL;
5866 next_offset = 0;
5867 next_sector = BlockChainStream_GetHeadOfChain(This);
5869 else
5871 last_run = &This->indexCache[This->indexCacheLen-1];
5872 next_offset = last_run->lastOffset+1;
5873 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5874 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5875 &next_sector);
5876 if (FAILED(hr)) return hr;
5879 while (next_sector != BLOCK_END_OF_CHAIN)
5881 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5883 /* Add the current block to the cache. */
5884 if (This->indexCacheSize == 0)
5886 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5887 if (!This->indexCache) return E_OUTOFMEMORY;
5888 This->indexCacheSize = 16;
5890 else if (This->indexCacheSize == This->indexCacheLen)
5892 struct BlockChainRun *new_cache;
5893 ULONG new_size;
5895 new_size = This->indexCacheSize * 2;
5896 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5897 if (!new_cache) return E_OUTOFMEMORY;
5898 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5900 HeapFree(GetProcessHeap(), 0, This->indexCache);
5901 This->indexCache = new_cache;
5902 This->indexCacheSize = new_size;
5905 This->indexCacheLen++;
5906 last_run = &This->indexCache[This->indexCacheLen-1];
5907 last_run->firstSector = next_sector;
5908 last_run->firstOffset = next_offset;
5911 last_run->lastOffset = next_offset;
5913 /* Find the next block. */
5914 next_offset++;
5915 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5916 if (FAILED(hr)) return hr;
5919 if (This->indexCacheLen)
5921 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5922 This->numBlocks = last_run->lastOffset+1;
5924 else
5926 This->tailIndex = BLOCK_END_OF_CHAIN;
5927 This->numBlocks = 0;
5930 return S_OK;
5933 /* Locate the nth block in this stream. */
5934 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5936 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5937 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5939 if (offset >= This->numBlocks)
5940 return BLOCK_END_OF_CHAIN;
5942 while (min_run < max_run)
5944 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5945 if (offset < This->indexCache[run_to_check].firstOffset)
5947 max_offset = This->indexCache[run_to_check].firstOffset-1;
5948 max_run = run_to_check-1;
5950 else if (offset > This->indexCache[run_to_check].lastOffset)
5952 min_offset = This->indexCache[run_to_check].lastOffset+1;
5953 min_run = run_to_check+1;
5955 else
5956 /* Block is in this run. */
5957 min_run = max_run = run_to_check;
5960 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5963 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5964 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5966 BlockChainBlock *result=NULL;
5967 int i;
5969 for (i=0; i<2; i++)
5970 if (This->cachedBlocks[i].index == index)
5972 *sector = This->cachedBlocks[i].sector;
5973 *block = &This->cachedBlocks[i];
5974 return S_OK;
5977 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5978 if (*sector == BLOCK_END_OF_CHAIN)
5979 return STG_E_DOCFILECORRUPT;
5981 if (create)
5983 if (This->cachedBlocks[0].index == 0xffffffff)
5984 result = &This->cachedBlocks[0];
5985 else if (This->cachedBlocks[1].index == 0xffffffff)
5986 result = &This->cachedBlocks[1];
5987 else
5989 result = &This->cachedBlocks[This->blockToEvict++];
5990 if (This->blockToEvict == 2)
5991 This->blockToEvict = 0;
5994 if (result->dirty)
5996 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5997 return STG_E_WRITEFAULT;
5998 result->dirty = 0;
6001 result->read = 0;
6002 result->index = index;
6003 result->sector = *sector;
6006 *block = result;
6007 return S_OK;
6010 BlockChainStream* BlockChainStream_Construct(
6011 StorageImpl* parentStorage,
6012 ULONG* headOfStreamPlaceHolder,
6013 DirRef dirEntry)
6015 BlockChainStream* newStream;
6017 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6019 newStream->parentStorage = parentStorage;
6020 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6021 newStream->ownerDirEntry = dirEntry;
6022 newStream->indexCache = NULL;
6023 newStream->indexCacheLen = 0;
6024 newStream->indexCacheSize = 0;
6025 newStream->cachedBlocks[0].index = 0xffffffff;
6026 newStream->cachedBlocks[0].dirty = 0;
6027 newStream->cachedBlocks[1].index = 0xffffffff;
6028 newStream->cachedBlocks[1].dirty = 0;
6029 newStream->blockToEvict = 0;
6031 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6033 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6034 HeapFree(GetProcessHeap(), 0, newStream);
6035 return NULL;
6038 return newStream;
6041 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6043 int i;
6044 if (!This) return S_OK;
6045 for (i=0; i<2; i++)
6047 if (This->cachedBlocks[i].dirty)
6049 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6050 This->cachedBlocks[i].dirty = 0;
6051 else
6052 return STG_E_WRITEFAULT;
6055 return S_OK;
6058 void BlockChainStream_Destroy(BlockChainStream* This)
6060 if (This)
6062 BlockChainStream_Flush(This);
6063 HeapFree(GetProcessHeap(), 0, This->indexCache);
6065 HeapFree(GetProcessHeap(), 0, This);
6068 /******************************************************************************
6069 * BlockChainStream_GetHeadOfChain
6071 * Returns the head of this stream chain.
6072 * Some special chains don't have directory entries, their heads are kept in
6073 * This->headOfStreamPlaceHolder.
6076 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6078 DirEntry chainEntry;
6079 HRESULT hr;
6081 if (This->headOfStreamPlaceHolder != 0)
6082 return *(This->headOfStreamPlaceHolder);
6084 if (This->ownerDirEntry != DIRENTRY_NULL)
6086 hr = StorageImpl_ReadDirEntry(
6087 This->parentStorage,
6088 This->ownerDirEntry,
6089 &chainEntry);
6091 if (SUCCEEDED(hr))
6093 return chainEntry.startingBlock;
6097 return BLOCK_END_OF_CHAIN;
6100 /******************************************************************************
6101 * BlockChainStream_GetCount
6103 * Returns the number of blocks that comprises this chain.
6104 * This is not the size of the stream as the last block may not be full!
6106 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6108 return This->numBlocks;
6111 /******************************************************************************
6112 * BlockChainStream_ReadAt
6114 * Reads a specified number of bytes from this chain at the specified offset.
6115 * bytesRead may be NULL.
6116 * Failure will be returned if the specified number of bytes has not been read.
6118 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6119 ULARGE_INTEGER offset,
6120 ULONG size,
6121 void* buffer,
6122 ULONG* bytesRead)
6124 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6125 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6126 ULONG bytesToReadInBuffer;
6127 ULONG blockIndex;
6128 BYTE* bufferWalker;
6129 ULARGE_INTEGER stream_size;
6130 HRESULT hr;
6131 BlockChainBlock *cachedBlock;
6133 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6136 * Find the first block in the stream that contains part of the buffer.
6138 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6140 *bytesRead = 0;
6142 stream_size = BlockChainStream_GetSize(This);
6143 if (stream_size.QuadPart > offset.QuadPart)
6144 size = min(stream_size.QuadPart - offset.QuadPart, size);
6145 else
6146 return S_OK;
6149 * Start reading the buffer.
6151 bufferWalker = buffer;
6153 while (size > 0)
6155 ULARGE_INTEGER ulOffset;
6156 DWORD bytesReadAt;
6159 * Calculate how many bytes we can copy from this big block.
6161 bytesToReadInBuffer =
6162 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6164 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6166 if (FAILED(hr))
6167 return hr;
6169 if (!cachedBlock)
6171 /* Not in cache, and we're going to read past the end of the block. */
6172 ulOffset.u.HighPart = 0;
6173 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6174 offsetInBlock;
6176 StorageImpl_ReadAt(This->parentStorage,
6177 ulOffset,
6178 bufferWalker,
6179 bytesToReadInBuffer,
6180 &bytesReadAt);
6182 else
6184 if (!cachedBlock->read)
6186 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6187 return STG_E_READFAULT;
6189 cachedBlock->read = 1;
6192 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6193 bytesReadAt = bytesToReadInBuffer;
6196 blockNoInSequence++;
6197 bufferWalker += bytesReadAt;
6198 size -= bytesReadAt;
6199 *bytesRead += bytesReadAt;
6200 offsetInBlock = 0; /* There is no offset on the next block */
6202 if (bytesToReadInBuffer != bytesReadAt)
6203 break;
6206 return S_OK;
6209 /******************************************************************************
6210 * BlockChainStream_WriteAt
6212 * Writes the specified number of bytes to this chain at the specified offset.
6213 * Will fail if not all specified number of bytes have been written.
6215 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6216 ULARGE_INTEGER offset,
6217 ULONG size,
6218 const void* buffer,
6219 ULONG* bytesWritten)
6221 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6222 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6223 ULONG bytesToWrite;
6224 ULONG blockIndex;
6225 const BYTE* bufferWalker;
6226 HRESULT hr;
6227 BlockChainBlock *cachedBlock;
6229 *bytesWritten = 0;
6230 bufferWalker = buffer;
6232 while (size > 0)
6234 ULARGE_INTEGER ulOffset;
6235 DWORD bytesWrittenAt;
6238 * Calculate how many bytes we can copy to this big block.
6240 bytesToWrite =
6241 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6243 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6245 /* BlockChainStream_SetSize should have already been called to ensure we have
6246 * enough blocks in the chain to write into */
6247 if (FAILED(hr))
6249 ERR("not enough blocks in chain to write data\n");
6250 return hr;
6253 if (!cachedBlock)
6255 /* Not in cache, and we're going to write past the end of the block. */
6256 ulOffset.u.HighPart = 0;
6257 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6258 offsetInBlock;
6260 StorageImpl_WriteAt(This->parentStorage,
6261 ulOffset,
6262 bufferWalker,
6263 bytesToWrite,
6264 &bytesWrittenAt);
6266 else
6268 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6270 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6271 return STG_E_READFAULT;
6274 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6275 bytesWrittenAt = bytesToWrite;
6276 cachedBlock->read = 1;
6277 cachedBlock->dirty = 1;
6280 blockNoInSequence++;
6281 bufferWalker += bytesWrittenAt;
6282 size -= bytesWrittenAt;
6283 *bytesWritten += bytesWrittenAt;
6284 offsetInBlock = 0; /* There is no offset on the next block */
6286 if (bytesWrittenAt != bytesToWrite)
6287 break;
6290 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6293 /******************************************************************************
6294 * BlockChainStream_Shrink
6296 * Shrinks this chain in the big block depot.
6298 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6299 ULARGE_INTEGER newSize)
6301 ULONG blockIndex;
6302 ULONG numBlocks;
6303 int i;
6306 * Figure out how many blocks are needed to contain the new size
6308 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6310 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6311 numBlocks++;
6313 if (numBlocks)
6316 * Go to the new end of chain
6318 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6320 /* Mark the new end of chain */
6321 StorageImpl_SetNextBlockInChain(
6322 This->parentStorage,
6323 blockIndex,
6324 BLOCK_END_OF_CHAIN);
6326 This->tailIndex = blockIndex;
6328 else
6330 if (This->headOfStreamPlaceHolder != 0)
6332 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6334 else
6336 DirEntry chainEntry;
6337 assert(This->ownerDirEntry != DIRENTRY_NULL);
6339 StorageImpl_ReadDirEntry(
6340 This->parentStorage,
6341 This->ownerDirEntry,
6342 &chainEntry);
6344 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6346 StorageImpl_WriteDirEntry(
6347 This->parentStorage,
6348 This->ownerDirEntry,
6349 &chainEntry);
6352 This->tailIndex = BLOCK_END_OF_CHAIN;
6355 This->numBlocks = numBlocks;
6358 * Mark the extra blocks as free
6360 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6362 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6363 StorageImpl_FreeBigBlock(This->parentStorage,
6364 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6365 if (last_run->lastOffset == last_run->firstOffset)
6366 This->indexCacheLen--;
6367 else
6368 last_run->lastOffset--;
6372 * Reset the last accessed block cache.
6374 for (i=0; i<2; i++)
6376 if (This->cachedBlocks[i].index >= numBlocks)
6378 This->cachedBlocks[i].index = 0xffffffff;
6379 This->cachedBlocks[i].dirty = 0;
6383 return TRUE;
6386 /******************************************************************************
6387 * BlockChainStream_Enlarge
6389 * Grows this chain in the big block depot.
6391 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6392 ULARGE_INTEGER newSize)
6394 ULONG blockIndex, currentBlock;
6395 ULONG newNumBlocks;
6396 ULONG oldNumBlocks = 0;
6398 blockIndex = BlockChainStream_GetHeadOfChain(This);
6401 * Empty chain. Create the head.
6403 if (blockIndex == BLOCK_END_OF_CHAIN)
6405 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6406 StorageImpl_SetNextBlockInChain(This->parentStorage,
6407 blockIndex,
6408 BLOCK_END_OF_CHAIN);
6410 if (This->headOfStreamPlaceHolder != 0)
6412 *(This->headOfStreamPlaceHolder) = blockIndex;
6414 else
6416 DirEntry chainEntry;
6417 assert(This->ownerDirEntry != DIRENTRY_NULL);
6419 StorageImpl_ReadDirEntry(
6420 This->parentStorage,
6421 This->ownerDirEntry,
6422 &chainEntry);
6424 chainEntry.startingBlock = blockIndex;
6426 StorageImpl_WriteDirEntry(
6427 This->parentStorage,
6428 This->ownerDirEntry,
6429 &chainEntry);
6432 This->tailIndex = blockIndex;
6433 This->numBlocks = 1;
6437 * Figure out how many blocks are needed to contain this stream
6439 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6441 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6442 newNumBlocks++;
6445 * Go to the current end of chain
6447 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6449 currentBlock = blockIndex;
6451 while (blockIndex != BLOCK_END_OF_CHAIN)
6453 This->numBlocks++;
6454 currentBlock = blockIndex;
6456 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6457 &blockIndex)))
6458 return FALSE;
6461 This->tailIndex = currentBlock;
6464 currentBlock = This->tailIndex;
6465 oldNumBlocks = This->numBlocks;
6468 * Add new blocks to the chain
6470 if (oldNumBlocks < newNumBlocks)
6472 while (oldNumBlocks < newNumBlocks)
6474 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6476 StorageImpl_SetNextBlockInChain(
6477 This->parentStorage,
6478 currentBlock,
6479 blockIndex);
6481 StorageImpl_SetNextBlockInChain(
6482 This->parentStorage,
6483 blockIndex,
6484 BLOCK_END_OF_CHAIN);
6486 currentBlock = blockIndex;
6487 oldNumBlocks++;
6490 This->tailIndex = blockIndex;
6491 This->numBlocks = newNumBlocks;
6494 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6495 return FALSE;
6497 return TRUE;
6500 /******************************************************************************
6501 * BlockChainStream_SetSize
6503 * Sets the size of this stream. The big block depot will be updated.
6504 * The file will grow if we grow the chain.
6506 * TODO: Free the actual blocks in the file when we shrink the chain.
6507 * Currently, the blocks are still in the file. So the file size
6508 * doesn't shrink even if we shrink streams.
6510 BOOL BlockChainStream_SetSize(
6511 BlockChainStream* This,
6512 ULARGE_INTEGER newSize)
6514 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6516 if (newSize.u.LowPart == size.u.LowPart)
6517 return TRUE;
6519 if (newSize.u.LowPart < size.u.LowPart)
6521 BlockChainStream_Shrink(This, newSize);
6523 else
6525 BlockChainStream_Enlarge(This, newSize);
6528 return TRUE;
6531 /******************************************************************************
6532 * BlockChainStream_GetSize
6534 * Returns the size of this chain.
6535 * Will return the block count if this chain doesn't have a directory entry.
6537 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6539 DirEntry chainEntry;
6541 if(This->headOfStreamPlaceHolder == NULL)
6544 * This chain has a directory entry so use the size value from there.
6546 StorageImpl_ReadDirEntry(
6547 This->parentStorage,
6548 This->ownerDirEntry,
6549 &chainEntry);
6551 return chainEntry.size;
6553 else
6556 * this chain is a chain that does not have a directory entry, figure out the
6557 * size by making the product number of used blocks times the
6558 * size of them
6560 ULARGE_INTEGER result;
6561 result.u.HighPart = 0;
6563 result.u.LowPart =
6564 BlockChainStream_GetCount(This) *
6565 This->parentStorage->bigBlockSize;
6567 return result;
6571 /******************************************************************************
6572 ** SmallBlockChainStream implementation
6575 SmallBlockChainStream* SmallBlockChainStream_Construct(
6576 StorageImpl* parentStorage,
6577 ULONG* headOfStreamPlaceHolder,
6578 DirRef dirEntry)
6580 SmallBlockChainStream* newStream;
6582 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6584 newStream->parentStorage = parentStorage;
6585 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6586 newStream->ownerDirEntry = dirEntry;
6588 return newStream;
6591 void SmallBlockChainStream_Destroy(
6592 SmallBlockChainStream* This)
6594 HeapFree(GetProcessHeap(), 0, This);
6597 /******************************************************************************
6598 * SmallBlockChainStream_GetHeadOfChain
6600 * Returns the head of this chain of small blocks.
6602 static ULONG SmallBlockChainStream_GetHeadOfChain(
6603 SmallBlockChainStream* This)
6605 DirEntry chainEntry;
6606 HRESULT hr;
6608 if (This->headOfStreamPlaceHolder != NULL)
6609 return *(This->headOfStreamPlaceHolder);
6611 if (This->ownerDirEntry)
6613 hr = StorageImpl_ReadDirEntry(
6614 This->parentStorage,
6615 This->ownerDirEntry,
6616 &chainEntry);
6618 if (SUCCEEDED(hr))
6620 return chainEntry.startingBlock;
6625 return BLOCK_END_OF_CHAIN;
6628 /******************************************************************************
6629 * SmallBlockChainStream_GetNextBlockInChain
6631 * Returns the index of the next small block in this chain.
6633 * Return Values:
6634 * - BLOCK_END_OF_CHAIN: end of this chain
6635 * - BLOCK_UNUSED: small block 'blockIndex' is free
6637 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6638 SmallBlockChainStream* This,
6639 ULONG blockIndex,
6640 ULONG* nextBlockInChain)
6642 ULARGE_INTEGER offsetOfBlockInDepot;
6643 DWORD buffer;
6644 ULONG bytesRead;
6645 HRESULT res;
6647 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6649 offsetOfBlockInDepot.u.HighPart = 0;
6650 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6653 * Read those bytes in the buffer from the small block file.
6655 res = BlockChainStream_ReadAt(
6656 This->parentStorage->smallBlockDepotChain,
6657 offsetOfBlockInDepot,
6658 sizeof(DWORD),
6659 &buffer,
6660 &bytesRead);
6662 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6663 res = STG_E_READFAULT;
6665 if (SUCCEEDED(res))
6667 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6668 return S_OK;
6671 return res;
6674 /******************************************************************************
6675 * SmallBlockChainStream_SetNextBlockInChain
6677 * Writes the index of the next block of the specified block in the small
6678 * block depot.
6679 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6680 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6682 static void SmallBlockChainStream_SetNextBlockInChain(
6683 SmallBlockChainStream* This,
6684 ULONG blockIndex,
6685 ULONG nextBlock)
6687 ULARGE_INTEGER offsetOfBlockInDepot;
6688 DWORD buffer;
6689 ULONG bytesWritten;
6691 offsetOfBlockInDepot.u.HighPart = 0;
6692 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6694 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6697 * Read those bytes in the buffer from the small block file.
6699 BlockChainStream_WriteAt(
6700 This->parentStorage->smallBlockDepotChain,
6701 offsetOfBlockInDepot,
6702 sizeof(DWORD),
6703 &buffer,
6704 &bytesWritten);
6707 /******************************************************************************
6708 * SmallBlockChainStream_FreeBlock
6710 * Flag small block 'blockIndex' as free in the small block depot.
6712 static void SmallBlockChainStream_FreeBlock(
6713 SmallBlockChainStream* This,
6714 ULONG blockIndex)
6716 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6719 /******************************************************************************
6720 * SmallBlockChainStream_GetNextFreeBlock
6722 * Returns the index of a free small block. The small block depot will be
6723 * enlarged if necessary. The small block chain will also be enlarged if
6724 * necessary.
6726 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6727 SmallBlockChainStream* This)
6729 ULARGE_INTEGER offsetOfBlockInDepot;
6730 DWORD buffer;
6731 ULONG bytesRead;
6732 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6733 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6734 HRESULT res = S_OK;
6735 ULONG smallBlocksPerBigBlock;
6736 DirEntry rootEntry;
6737 ULONG blocksRequired;
6738 ULARGE_INTEGER old_size, size_required;
6740 offsetOfBlockInDepot.u.HighPart = 0;
6743 * Scan the small block depot for a free block
6745 while (nextBlockIndex != BLOCK_UNUSED)
6747 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6749 res = BlockChainStream_ReadAt(
6750 This->parentStorage->smallBlockDepotChain,
6751 offsetOfBlockInDepot,
6752 sizeof(DWORD),
6753 &buffer,
6754 &bytesRead);
6757 * If we run out of space for the small block depot, enlarge it
6759 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6761 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6763 if (nextBlockIndex != BLOCK_UNUSED)
6764 blockIndex++;
6766 else
6768 ULONG count =
6769 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6771 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6772 ULARGE_INTEGER newSize, offset;
6773 ULONG bytesWritten;
6775 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6776 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6779 * Initialize all the small blocks to free
6781 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6782 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6783 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6784 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6786 StorageImpl_SaveFileHeader(This->parentStorage);
6790 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6792 smallBlocksPerBigBlock =
6793 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6796 * Verify if we have to allocate big blocks to contain small blocks
6798 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6800 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6802 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6804 if (size_required.QuadPart > old_size.QuadPart)
6806 BlockChainStream_SetSize(
6807 This->parentStorage->smallBlockRootChain,
6808 size_required);
6810 StorageImpl_ReadDirEntry(
6811 This->parentStorage,
6812 This->parentStorage->base.storageDirEntry,
6813 &rootEntry);
6815 rootEntry.size = size_required;
6817 StorageImpl_WriteDirEntry(
6818 This->parentStorage,
6819 This->parentStorage->base.storageDirEntry,
6820 &rootEntry);
6823 return blockIndex;
6826 /******************************************************************************
6827 * SmallBlockChainStream_ReadAt
6829 * Reads a specified number of bytes from this chain at the specified offset.
6830 * bytesRead may be NULL.
6831 * Failure will be returned if the specified number of bytes has not been read.
6833 HRESULT SmallBlockChainStream_ReadAt(
6834 SmallBlockChainStream* This,
6835 ULARGE_INTEGER offset,
6836 ULONG size,
6837 void* buffer,
6838 ULONG* bytesRead)
6840 HRESULT rc = S_OK;
6841 ULARGE_INTEGER offsetInBigBlockFile;
6842 ULONG blockNoInSequence =
6843 offset.u.LowPart / This->parentStorage->smallBlockSize;
6845 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6846 ULONG bytesToReadInBuffer;
6847 ULONG blockIndex;
6848 ULONG bytesReadFromBigBlockFile;
6849 BYTE* bufferWalker;
6850 ULARGE_INTEGER stream_size;
6853 * This should never happen on a small block file.
6855 assert(offset.u.HighPart==0);
6857 *bytesRead = 0;
6859 stream_size = SmallBlockChainStream_GetSize(This);
6860 if (stream_size.QuadPart > offset.QuadPart)
6861 size = min(stream_size.QuadPart - offset.QuadPart, size);
6862 else
6863 return S_OK;
6866 * Find the first block in the stream that contains part of the buffer.
6868 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6870 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6872 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6873 if(FAILED(rc))
6874 return rc;
6875 blockNoInSequence--;
6879 * Start reading the buffer.
6881 bufferWalker = buffer;
6883 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6886 * Calculate how many bytes we can copy from this small block.
6888 bytesToReadInBuffer =
6889 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6892 * Calculate the offset of the small block in the small block file.
6894 offsetInBigBlockFile.u.HighPart = 0;
6895 offsetInBigBlockFile.u.LowPart =
6896 blockIndex * This->parentStorage->smallBlockSize;
6898 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6901 * Read those bytes in the buffer from the small block file.
6902 * The small block has already been identified so it shouldn't fail
6903 * unless the file is corrupt.
6905 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6906 offsetInBigBlockFile,
6907 bytesToReadInBuffer,
6908 bufferWalker,
6909 &bytesReadFromBigBlockFile);
6911 if (FAILED(rc))
6912 return rc;
6914 if (!bytesReadFromBigBlockFile)
6915 return STG_E_DOCFILECORRUPT;
6918 * Step to the next big block.
6920 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6921 if(FAILED(rc))
6922 return STG_E_DOCFILECORRUPT;
6924 bufferWalker += bytesReadFromBigBlockFile;
6925 size -= bytesReadFromBigBlockFile;
6926 *bytesRead += bytesReadFromBigBlockFile;
6927 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6930 return S_OK;
6933 /******************************************************************************
6934 * SmallBlockChainStream_WriteAt
6936 * Writes the specified number of bytes to this chain at the specified offset.
6937 * Will fail if not all specified number of bytes have been written.
6939 HRESULT SmallBlockChainStream_WriteAt(
6940 SmallBlockChainStream* This,
6941 ULARGE_INTEGER offset,
6942 ULONG size,
6943 const void* buffer,
6944 ULONG* bytesWritten)
6946 ULARGE_INTEGER offsetInBigBlockFile;
6947 ULONG blockNoInSequence =
6948 offset.u.LowPart / This->parentStorage->smallBlockSize;
6950 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6951 ULONG bytesToWriteInBuffer;
6952 ULONG blockIndex;
6953 ULONG bytesWrittenToBigBlockFile;
6954 const BYTE* bufferWalker;
6955 HRESULT res;
6958 * This should never happen on a small block file.
6960 assert(offset.u.HighPart==0);
6963 * Find the first block in the stream that contains part of the buffer.
6965 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6967 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6969 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6970 return STG_E_DOCFILECORRUPT;
6971 blockNoInSequence--;
6975 * Start writing the buffer.
6977 *bytesWritten = 0;
6978 bufferWalker = buffer;
6979 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6982 * Calculate how many bytes we can copy to this small block.
6984 bytesToWriteInBuffer =
6985 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6988 * Calculate the offset of the small block in the small block file.
6990 offsetInBigBlockFile.u.HighPart = 0;
6991 offsetInBigBlockFile.u.LowPart =
6992 blockIndex * This->parentStorage->smallBlockSize;
6994 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6997 * Write those bytes in the buffer to the small block file.
6999 res = BlockChainStream_WriteAt(
7000 This->parentStorage->smallBlockRootChain,
7001 offsetInBigBlockFile,
7002 bytesToWriteInBuffer,
7003 bufferWalker,
7004 &bytesWrittenToBigBlockFile);
7005 if (FAILED(res))
7006 return res;
7009 * Step to the next big block.
7011 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7012 &blockIndex)))
7013 return FALSE;
7014 bufferWalker += bytesWrittenToBigBlockFile;
7015 size -= bytesWrittenToBigBlockFile;
7016 *bytesWritten += bytesWrittenToBigBlockFile;
7017 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7020 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7023 /******************************************************************************
7024 * SmallBlockChainStream_Shrink
7026 * Shrinks this chain in the small block depot.
7028 static BOOL SmallBlockChainStream_Shrink(
7029 SmallBlockChainStream* This,
7030 ULARGE_INTEGER newSize)
7032 ULONG blockIndex, extraBlock;
7033 ULONG numBlocks;
7034 ULONG count = 0;
7036 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7038 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7039 numBlocks++;
7041 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7044 * Go to the new end of chain
7046 while (count < numBlocks)
7048 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7049 &blockIndex)))
7050 return FALSE;
7051 count++;
7055 * If the count is 0, we have a special case, the head of the chain was
7056 * just freed.
7058 if (count == 0)
7060 DirEntry chainEntry;
7062 StorageImpl_ReadDirEntry(This->parentStorage,
7063 This->ownerDirEntry,
7064 &chainEntry);
7066 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7068 StorageImpl_WriteDirEntry(This->parentStorage,
7069 This->ownerDirEntry,
7070 &chainEntry);
7073 * We start freeing the chain at the head block.
7075 extraBlock = blockIndex;
7077 else
7079 /* Get the next block before marking the new end */
7080 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7081 &extraBlock)))
7082 return FALSE;
7084 /* Mark the new end of chain */
7085 SmallBlockChainStream_SetNextBlockInChain(
7086 This,
7087 blockIndex,
7088 BLOCK_END_OF_CHAIN);
7092 * Mark the extra blocks as free
7094 while (extraBlock != BLOCK_END_OF_CHAIN)
7096 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7097 &blockIndex)))
7098 return FALSE;
7099 SmallBlockChainStream_FreeBlock(This, extraBlock);
7100 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7101 extraBlock = blockIndex;
7104 return TRUE;
7107 /******************************************************************************
7108 * SmallBlockChainStream_Enlarge
7110 * Grows this chain in the small block depot.
7112 static BOOL SmallBlockChainStream_Enlarge(
7113 SmallBlockChainStream* This,
7114 ULARGE_INTEGER newSize)
7116 ULONG blockIndex, currentBlock;
7117 ULONG newNumBlocks;
7118 ULONG oldNumBlocks = 0;
7120 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7123 * Empty chain. Create the head.
7125 if (blockIndex == BLOCK_END_OF_CHAIN)
7127 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7128 SmallBlockChainStream_SetNextBlockInChain(
7129 This,
7130 blockIndex,
7131 BLOCK_END_OF_CHAIN);
7133 if (This->headOfStreamPlaceHolder != NULL)
7135 *(This->headOfStreamPlaceHolder) = blockIndex;
7137 else
7139 DirEntry chainEntry;
7141 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7142 &chainEntry);
7144 chainEntry.startingBlock = blockIndex;
7146 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7147 &chainEntry);
7151 currentBlock = blockIndex;
7154 * Figure out how many blocks are needed to contain this stream
7156 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7158 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7159 newNumBlocks++;
7162 * Go to the current end of chain
7164 while (blockIndex != BLOCK_END_OF_CHAIN)
7166 oldNumBlocks++;
7167 currentBlock = blockIndex;
7168 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7169 return FALSE;
7173 * Add new blocks to the chain
7175 while (oldNumBlocks < newNumBlocks)
7177 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7178 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7180 SmallBlockChainStream_SetNextBlockInChain(
7181 This,
7182 blockIndex,
7183 BLOCK_END_OF_CHAIN);
7185 currentBlock = blockIndex;
7186 oldNumBlocks++;
7189 return TRUE;
7192 /******************************************************************************
7193 * SmallBlockChainStream_SetSize
7195 * Sets the size of this stream.
7196 * The file will grow if we grow the chain.
7198 * TODO: Free the actual blocks in the file when we shrink the chain.
7199 * Currently, the blocks are still in the file. So the file size
7200 * doesn't shrink even if we shrink streams.
7202 BOOL SmallBlockChainStream_SetSize(
7203 SmallBlockChainStream* This,
7204 ULARGE_INTEGER newSize)
7206 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7208 if (newSize.u.LowPart == size.u.LowPart)
7209 return TRUE;
7211 if (newSize.u.LowPart < size.u.LowPart)
7213 SmallBlockChainStream_Shrink(This, newSize);
7215 else
7217 SmallBlockChainStream_Enlarge(This, newSize);
7220 return TRUE;
7223 /******************************************************************************
7224 * SmallBlockChainStream_GetCount
7226 * Returns the number of small blocks that comprises this chain.
7227 * This is not the size of the stream as the last block may not be full!
7230 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7232 ULONG blockIndex;
7233 ULONG count = 0;
7235 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7237 while(blockIndex != BLOCK_END_OF_CHAIN)
7239 count++;
7241 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7242 blockIndex, &blockIndex)))
7243 return 0;
7246 return count;
7249 /******************************************************************************
7250 * SmallBlockChainStream_GetSize
7252 * Returns the size of this chain.
7254 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7256 DirEntry chainEntry;
7258 if(This->headOfStreamPlaceHolder != NULL)
7260 ULARGE_INTEGER result;
7261 result.u.HighPart = 0;
7263 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7264 This->parentStorage->smallBlockSize;
7266 return result;
7269 StorageImpl_ReadDirEntry(
7270 This->parentStorage,
7271 This->ownerDirEntry,
7272 &chainEntry);
7274 return chainEntry.size;
7277 static HRESULT create_storagefile(
7278 LPCOLESTR pwcsName,
7279 DWORD grfMode,
7280 DWORD grfAttrs,
7281 STGOPTIONS* pStgOptions,
7282 REFIID riid,
7283 void** ppstgOpen)
7285 StorageBaseImpl* newStorage = 0;
7286 HANDLE hFile = INVALID_HANDLE_VALUE;
7287 HRESULT hr = STG_E_INVALIDFLAG;
7288 DWORD shareMode;
7289 DWORD accessMode;
7290 DWORD creationMode;
7291 DWORD fileAttributes;
7292 WCHAR tempFileName[MAX_PATH];
7294 if (ppstgOpen == 0)
7295 return STG_E_INVALIDPOINTER;
7297 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7298 return STG_E_INVALIDPARAMETER;
7300 /* if no share mode given then DENY_NONE is the default */
7301 if (STGM_SHARE_MODE(grfMode) == 0)
7302 grfMode |= STGM_SHARE_DENY_NONE;
7304 if ( FAILED( validateSTGM(grfMode) ))
7305 goto end;
7307 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7308 switch(STGM_ACCESS_MODE(grfMode))
7310 case STGM_WRITE:
7311 case STGM_READWRITE:
7312 break;
7313 default:
7314 goto end;
7317 /* in direct mode, can only use SHARE_EXCLUSIVE */
7318 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7319 goto end;
7321 /* but in transacted mode, any share mode is valid */
7324 * Generate a unique name.
7326 if (pwcsName == 0)
7328 WCHAR tempPath[MAX_PATH];
7329 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7331 memset(tempPath, 0, sizeof(tempPath));
7332 memset(tempFileName, 0, sizeof(tempFileName));
7334 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7335 tempPath[0] = '.';
7337 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7338 pwcsName = tempFileName;
7339 else
7341 hr = STG_E_INSUFFICIENTMEMORY;
7342 goto end;
7345 creationMode = TRUNCATE_EXISTING;
7347 else
7349 creationMode = GetCreationModeFromSTGM(grfMode);
7353 * Interpret the STGM value grfMode
7355 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7356 accessMode = GetAccessModeFromSTGM(grfMode);
7358 if (grfMode & STGM_DELETEONRELEASE)
7359 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7360 else
7361 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7363 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7365 static int fixme;
7366 if (!fixme++)
7367 FIXME("Storage share mode not implemented.\n");
7370 *ppstgOpen = 0;
7372 hFile = CreateFileW(pwcsName,
7373 accessMode,
7374 shareMode,
7375 NULL,
7376 creationMode,
7377 fileAttributes,
7380 if (hFile == INVALID_HANDLE_VALUE)
7382 if(GetLastError() == ERROR_FILE_EXISTS)
7383 hr = STG_E_FILEALREADYEXISTS;
7384 else
7385 hr = E_FAIL;
7386 goto end;
7390 * Allocate and initialize the new IStorage32object.
7392 hr = Storage_Construct(
7393 hFile,
7394 pwcsName,
7395 NULL,
7396 grfMode,
7397 TRUE,
7398 TRUE,
7399 pStgOptions->ulSectorSize,
7400 &newStorage);
7402 if (FAILED(hr))
7404 goto end;
7407 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
7408 IStorage_Release(&newStorage->IStorage_iface);
7410 end:
7411 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7413 return hr;
7416 /******************************************************************************
7417 * StgCreateDocfile [OLE32.@]
7418 * Creates a new compound file storage object
7420 * PARAMS
7421 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7422 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7423 * reserved [ ?] unused?, usually 0
7424 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7426 * RETURNS
7427 * S_OK if the file was successfully created
7428 * some STG_E_ value if error
7429 * NOTES
7430 * if pwcsName is NULL, create file with new unique name
7431 * the function can returns
7432 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7433 * (unrealized now)
7435 HRESULT WINAPI StgCreateDocfile(
7436 LPCOLESTR pwcsName,
7437 DWORD grfMode,
7438 DWORD reserved,
7439 IStorage **ppstgOpen)
7441 STGOPTIONS stgoptions = {1, 0, 512};
7443 TRACE("(%s, %x, %d, %p)\n",
7444 debugstr_w(pwcsName), grfMode,
7445 reserved, ppstgOpen);
7447 if (ppstgOpen == 0)
7448 return STG_E_INVALIDPOINTER;
7449 if (reserved != 0)
7450 return STG_E_INVALIDPARAMETER;
7452 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7455 /******************************************************************************
7456 * StgCreateStorageEx [OLE32.@]
7458 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7460 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7461 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7463 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7465 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7466 return STG_E_INVALIDPARAMETER;
7469 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7471 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7472 return STG_E_INVALIDPARAMETER;
7475 if (stgfmt == STGFMT_FILE)
7477 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7478 return STG_E_INVALIDPARAMETER;
7481 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7483 STGOPTIONS defaultOptions = {1, 0, 512};
7485 if (!pStgOptions) pStgOptions = &defaultOptions;
7486 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7490 ERR("Invalid stgfmt argument\n");
7491 return STG_E_INVALIDPARAMETER;
7494 /******************************************************************************
7495 * StgCreatePropSetStg [OLE32.@]
7497 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7498 IPropertySetStorage **propset)
7500 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
7501 if (reserved)
7502 return STG_E_INVALIDPARAMETER;
7504 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
7507 /******************************************************************************
7508 * StgOpenStorageEx [OLE32.@]
7510 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7512 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7513 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7515 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7517 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7518 return STG_E_INVALIDPARAMETER;
7521 switch (stgfmt)
7523 case STGFMT_FILE:
7524 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7525 return STG_E_INVALIDPARAMETER;
7527 case STGFMT_STORAGE:
7528 break;
7530 case STGFMT_DOCFILE:
7531 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7533 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7534 return STG_E_INVALIDPARAMETER;
7536 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7537 break;
7539 case STGFMT_ANY:
7540 WARN("STGFMT_ANY assuming storage\n");
7541 break;
7543 default:
7544 return STG_E_INVALIDPARAMETER;
7547 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7551 /******************************************************************************
7552 * StgOpenStorage [OLE32.@]
7554 HRESULT WINAPI StgOpenStorage(
7555 const OLECHAR *pwcsName,
7556 IStorage *pstgPriority,
7557 DWORD grfMode,
7558 SNB snbExclude,
7559 DWORD reserved,
7560 IStorage **ppstgOpen)
7562 StorageBaseImpl* newStorage = 0;
7563 HRESULT hr = S_OK;
7564 HANDLE hFile = 0;
7565 DWORD shareMode;
7566 DWORD accessMode;
7568 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7569 debugstr_w(pwcsName), pstgPriority, grfMode,
7570 snbExclude, reserved, ppstgOpen);
7572 if (pwcsName == 0)
7574 hr = STG_E_INVALIDNAME;
7575 goto end;
7578 if (ppstgOpen == 0)
7580 hr = STG_E_INVALIDPOINTER;
7581 goto end;
7584 if (reserved)
7586 hr = STG_E_INVALIDPARAMETER;
7587 goto end;
7590 if (grfMode & STGM_PRIORITY)
7592 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7593 return STG_E_INVALIDFLAG;
7594 if (grfMode & STGM_DELETEONRELEASE)
7595 return STG_E_INVALIDFUNCTION;
7596 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7597 return STG_E_INVALIDFLAG;
7598 grfMode &= ~0xf0; /* remove the existing sharing mode */
7599 grfMode |= STGM_SHARE_DENY_NONE;
7601 /* STGM_PRIORITY stops other IStorage objects on the same file from
7602 * committing until the STGM_PRIORITY IStorage is closed. it also
7603 * stops non-transacted mode StgOpenStorage calls with write access from
7604 * succeeding. obviously, both of these cannot be achieved through just
7605 * file share flags */
7606 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7610 * Validate the sharing mode
7612 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7613 switch(STGM_SHARE_MODE(grfMode))
7615 case STGM_SHARE_EXCLUSIVE:
7616 case STGM_SHARE_DENY_WRITE:
7617 break;
7618 default:
7619 hr = STG_E_INVALIDFLAG;
7620 goto end;
7623 if ( FAILED( validateSTGM(grfMode) ) ||
7624 (grfMode&STGM_CREATE))
7626 hr = STG_E_INVALIDFLAG;
7627 goto end;
7630 /* shared reading requires transacted mode */
7631 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7632 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7633 !(grfMode&STGM_TRANSACTED) )
7635 hr = STG_E_INVALIDFLAG;
7636 goto end;
7640 * Interpret the STGM value grfMode
7642 shareMode = GetShareModeFromSTGM(grfMode);
7643 accessMode = GetAccessModeFromSTGM(grfMode);
7645 *ppstgOpen = 0;
7647 hFile = CreateFileW( pwcsName,
7648 accessMode,
7649 shareMode,
7650 NULL,
7651 OPEN_EXISTING,
7652 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7655 if (hFile==INVALID_HANDLE_VALUE)
7657 DWORD last_error = GetLastError();
7659 hr = E_FAIL;
7661 switch (last_error)
7663 case ERROR_FILE_NOT_FOUND:
7664 hr = STG_E_FILENOTFOUND;
7665 break;
7667 case ERROR_PATH_NOT_FOUND:
7668 hr = STG_E_PATHNOTFOUND;
7669 break;
7671 case ERROR_ACCESS_DENIED:
7672 case ERROR_WRITE_PROTECT:
7673 hr = STG_E_ACCESSDENIED;
7674 break;
7676 case ERROR_SHARING_VIOLATION:
7677 hr = STG_E_SHAREVIOLATION;
7678 break;
7680 default:
7681 hr = E_FAIL;
7684 goto end;
7688 * Refuse to open the file if it's too small to be a structured storage file
7689 * FIXME: verify the file when reading instead of here
7691 if (GetFileSize(hFile, NULL) < 0x100)
7693 CloseHandle(hFile);
7694 hr = STG_E_FILEALREADYEXISTS;
7695 goto end;
7699 * Allocate and initialize the new IStorage32object.
7701 hr = Storage_Construct(
7702 hFile,
7703 pwcsName,
7704 NULL,
7705 grfMode,
7706 TRUE,
7707 FALSE,
7708 512,
7709 &newStorage);
7711 if (FAILED(hr))
7714 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7716 if(hr == STG_E_INVALIDHEADER)
7717 hr = STG_E_FILEALREADYEXISTS;
7718 goto end;
7721 *ppstgOpen = &newStorage->IStorage_iface;
7723 end:
7724 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7725 return hr;
7728 /******************************************************************************
7729 * StgCreateDocfileOnILockBytes [OLE32.@]
7731 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7732 ILockBytes *plkbyt,
7733 DWORD grfMode,
7734 DWORD reserved,
7735 IStorage** ppstgOpen)
7737 StorageBaseImpl* newStorage = 0;
7738 HRESULT hr = S_OK;
7740 if ((ppstgOpen == 0) || (plkbyt == 0))
7741 return STG_E_INVALIDPOINTER;
7744 * Allocate and initialize the new IStorage object.
7746 hr = Storage_Construct(
7749 plkbyt,
7750 grfMode,
7751 FALSE,
7752 TRUE,
7753 512,
7754 &newStorage);
7756 if (FAILED(hr))
7758 return hr;
7761 *ppstgOpen = &newStorage->IStorage_iface;
7763 return hr;
7766 /******************************************************************************
7767 * StgOpenStorageOnILockBytes [OLE32.@]
7769 HRESULT WINAPI StgOpenStorageOnILockBytes(
7770 ILockBytes *plkbyt,
7771 IStorage *pstgPriority,
7772 DWORD grfMode,
7773 SNB snbExclude,
7774 DWORD reserved,
7775 IStorage **ppstgOpen)
7777 StorageBaseImpl* newStorage = 0;
7778 HRESULT hr = S_OK;
7780 if ((plkbyt == 0) || (ppstgOpen == 0))
7781 return STG_E_INVALIDPOINTER;
7783 if ( FAILED( validateSTGM(grfMode) ))
7784 return STG_E_INVALIDFLAG;
7786 *ppstgOpen = 0;
7789 * Allocate and initialize the new IStorage object.
7791 hr = Storage_Construct(
7794 plkbyt,
7795 grfMode,
7796 FALSE,
7797 FALSE,
7798 512,
7799 &newStorage);
7801 if (FAILED(hr))
7803 return hr;
7806 *ppstgOpen = &newStorage->IStorage_iface;
7808 return hr;
7811 /******************************************************************************
7812 * StgSetTimes [ole32.@]
7813 * StgSetTimes [OLE32.@]
7817 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7818 FILETIME const *patime, FILETIME const *pmtime)
7820 IStorage *stg = NULL;
7821 HRESULT r;
7823 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7825 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7826 0, 0, &stg);
7827 if( SUCCEEDED(r) )
7829 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7830 IStorage_Release(stg);
7833 return r;
7836 /******************************************************************************
7837 * StgIsStorageILockBytes [OLE32.@]
7839 * Determines if the ILockBytes contains a storage object.
7841 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7843 BYTE sig[sizeof(STORAGE_magic)];
7844 ULARGE_INTEGER offset;
7845 ULONG read = 0;
7847 offset.u.HighPart = 0;
7848 offset.u.LowPart = 0;
7850 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
7852 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
7853 return S_OK;
7855 return S_FALSE;
7858 /******************************************************************************
7859 * WriteClassStg [OLE32.@]
7861 * This method will store the specified CLSID in the specified storage object
7863 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7865 if(!pStg)
7866 return E_INVALIDARG;
7868 if(!rclsid)
7869 return STG_E_INVALIDPOINTER;
7871 return IStorage_SetClass(pStg, rclsid);
7874 /***********************************************************************
7875 * ReadClassStg (OLE32.@)
7877 * This method reads the CLSID previously written to a storage object with
7878 * the WriteClassStg.
7880 * PARAMS
7881 * pstg [I] IStorage pointer
7882 * pclsid [O] Pointer to where the CLSID is written
7884 * RETURNS
7885 * Success: S_OK.
7886 * Failure: HRESULT code.
7888 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7890 STATSTG pstatstg;
7891 HRESULT hRes;
7893 TRACE("(%p, %p)\n", pstg, pclsid);
7895 if(!pstg || !pclsid)
7896 return E_INVALIDARG;
7899 * read a STATSTG structure (contains the clsid) from the storage
7901 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7903 if(SUCCEEDED(hRes))
7904 *pclsid=pstatstg.clsid;
7906 return hRes;
7909 /***********************************************************************
7910 * OleLoadFromStream (OLE32.@)
7912 * This function loads an object from stream
7914 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7916 CLSID clsid;
7917 HRESULT res;
7918 LPPERSISTSTREAM xstm;
7920 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7922 res=ReadClassStm(pStm,&clsid);
7923 if (FAILED(res))
7924 return res;
7925 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7926 if (FAILED(res))
7927 return res;
7928 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7929 if (FAILED(res)) {
7930 IUnknown_Release((IUnknown*)*ppvObj);
7931 return res;
7933 res=IPersistStream_Load(xstm,pStm);
7934 IPersistStream_Release(xstm);
7935 /* FIXME: all refcounts ok at this point? I think they should be:
7936 * pStm : unchanged
7937 * ppvObj : 1
7938 * xstm : 0 (released)
7940 return res;
7943 /***********************************************************************
7944 * OleSaveToStream (OLE32.@)
7946 * This function saves an object with the IPersistStream interface on it
7947 * to the specified stream.
7949 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7952 CLSID clsid;
7953 HRESULT res;
7955 TRACE("(%p,%p)\n",pPStm,pStm);
7957 res=IPersistStream_GetClassID(pPStm,&clsid);
7959 if (SUCCEEDED(res)){
7961 res=WriteClassStm(pStm,&clsid);
7963 if (SUCCEEDED(res))
7965 res=IPersistStream_Save(pPStm,pStm,TRUE);
7968 TRACE("Finished Save\n");
7969 return res;
7972 /****************************************************************************
7973 * This method validate a STGM parameter that can contain the values below
7975 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7976 * The stgm values contained in 0xffff0000 are bitmasks.
7978 * STGM_DIRECT 0x00000000
7979 * STGM_TRANSACTED 0x00010000
7980 * STGM_SIMPLE 0x08000000
7982 * STGM_READ 0x00000000
7983 * STGM_WRITE 0x00000001
7984 * STGM_READWRITE 0x00000002
7986 * STGM_SHARE_DENY_NONE 0x00000040
7987 * STGM_SHARE_DENY_READ 0x00000030
7988 * STGM_SHARE_DENY_WRITE 0x00000020
7989 * STGM_SHARE_EXCLUSIVE 0x00000010
7991 * STGM_PRIORITY 0x00040000
7992 * STGM_DELETEONRELEASE 0x04000000
7994 * STGM_CREATE 0x00001000
7995 * STGM_CONVERT 0x00020000
7996 * STGM_FAILIFTHERE 0x00000000
7998 * STGM_NOSCRATCH 0x00100000
7999 * STGM_NOSNAPSHOT 0x00200000
8001 static HRESULT validateSTGM(DWORD stgm)
8003 DWORD access = STGM_ACCESS_MODE(stgm);
8004 DWORD share = STGM_SHARE_MODE(stgm);
8005 DWORD create = STGM_CREATE_MODE(stgm);
8007 if (stgm&~STGM_KNOWN_FLAGS)
8009 ERR("unknown flags %08x\n", stgm);
8010 return E_FAIL;
8013 switch (access)
8015 case STGM_READ:
8016 case STGM_WRITE:
8017 case STGM_READWRITE:
8018 break;
8019 default:
8020 return E_FAIL;
8023 switch (share)
8025 case STGM_SHARE_DENY_NONE:
8026 case STGM_SHARE_DENY_READ:
8027 case STGM_SHARE_DENY_WRITE:
8028 case STGM_SHARE_EXCLUSIVE:
8029 break;
8030 default:
8031 return E_FAIL;
8034 switch (create)
8036 case STGM_CREATE:
8037 case STGM_FAILIFTHERE:
8038 break;
8039 default:
8040 return E_FAIL;
8044 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8046 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8047 return E_FAIL;
8050 * STGM_CREATE | STGM_CONVERT
8051 * if both are false, STGM_FAILIFTHERE is set to TRUE
8053 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8054 return E_FAIL;
8057 * STGM_NOSCRATCH requires STGM_TRANSACTED
8059 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8060 return E_FAIL;
8063 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8064 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8066 if ( (stgm & STGM_NOSNAPSHOT) &&
8067 (!(stgm & STGM_TRANSACTED) ||
8068 share == STGM_SHARE_EXCLUSIVE ||
8069 share == STGM_SHARE_DENY_WRITE) )
8070 return E_FAIL;
8072 return S_OK;
8075 /****************************************************************************
8076 * GetShareModeFromSTGM
8078 * This method will return a share mode flag from a STGM value.
8079 * The STGM value is assumed valid.
8081 static DWORD GetShareModeFromSTGM(DWORD stgm)
8083 switch (STGM_SHARE_MODE(stgm))
8085 case STGM_SHARE_DENY_NONE:
8086 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8087 case STGM_SHARE_DENY_READ:
8088 return FILE_SHARE_WRITE;
8089 case STGM_SHARE_DENY_WRITE:
8090 return FILE_SHARE_READ;
8091 case STGM_SHARE_EXCLUSIVE:
8092 return 0;
8094 ERR("Invalid share mode!\n");
8095 assert(0);
8096 return 0;
8099 /****************************************************************************
8100 * GetAccessModeFromSTGM
8102 * This method will return an access mode flag from a STGM value.
8103 * The STGM value is assumed valid.
8105 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8107 switch (STGM_ACCESS_MODE(stgm))
8109 case STGM_READ:
8110 return GENERIC_READ;
8111 case STGM_WRITE:
8112 case STGM_READWRITE:
8113 return GENERIC_READ | GENERIC_WRITE;
8115 ERR("Invalid access mode!\n");
8116 assert(0);
8117 return 0;
8120 /****************************************************************************
8121 * GetCreationModeFromSTGM
8123 * This method will return a creation mode flag from a STGM value.
8124 * The STGM value is assumed valid.
8126 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8128 switch(STGM_CREATE_MODE(stgm))
8130 case STGM_CREATE:
8131 return CREATE_ALWAYS;
8132 case STGM_CONVERT:
8133 FIXME("STGM_CONVERT not implemented!\n");
8134 return CREATE_NEW;
8135 case STGM_FAILIFTHERE:
8136 return CREATE_NEW;
8138 ERR("Invalid create mode!\n");
8139 assert(0);
8140 return 0;
8144 /*************************************************************************
8145 * OLECONVERT_LoadOLE10 [Internal]
8147 * Loads the OLE10 STREAM to memory
8149 * PARAMS
8150 * pOleStream [I] The OLESTREAM
8151 * pData [I] Data Structure for the OLESTREAM Data
8153 * RETURNS
8154 * Success: S_OK
8155 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8156 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8158 * NOTES
8159 * This function is used by OleConvertOLESTREAMToIStorage only.
8161 * Memory allocated for pData must be freed by the caller
8163 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8165 DWORD dwSize;
8166 HRESULT hRes = S_OK;
8167 int nTryCnt=0;
8168 int max_try = 6;
8170 pData->pData = NULL;
8171 pData->pstrOleObjFileName = NULL;
8173 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8175 /* Get the OleID */
8176 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8177 if(dwSize != sizeof(pData->dwOleID))
8179 hRes = CONVERT10_E_OLESTREAM_GET;
8181 else if(pData->dwOleID != OLESTREAM_ID)
8183 hRes = CONVERT10_E_OLESTREAM_FMT;
8185 else
8187 hRes = S_OK;
8188 break;
8192 if(hRes == S_OK)
8194 /* Get the TypeID... more info needed for this field */
8195 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8196 if(dwSize != sizeof(pData->dwTypeID))
8198 hRes = CONVERT10_E_OLESTREAM_GET;
8201 if(hRes == S_OK)
8203 if(pData->dwTypeID != 0)
8205 /* Get the length of the OleTypeName */
8206 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8207 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8209 hRes = CONVERT10_E_OLESTREAM_GET;
8212 if(hRes == S_OK)
8214 if(pData->dwOleTypeNameLength > 0)
8216 /* Get the OleTypeName */
8217 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8218 if(dwSize != pData->dwOleTypeNameLength)
8220 hRes = CONVERT10_E_OLESTREAM_GET;
8224 if(bStrem1)
8226 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8227 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8229 hRes = CONVERT10_E_OLESTREAM_GET;
8231 if(hRes == S_OK)
8233 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8234 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8235 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8236 if(pData->pstrOleObjFileName)
8238 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8239 if(dwSize != pData->dwOleObjFileNameLength)
8241 hRes = CONVERT10_E_OLESTREAM_GET;
8244 else
8245 hRes = CONVERT10_E_OLESTREAM_GET;
8248 else
8250 /* Get the Width of the Metafile */
8251 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8252 if(dwSize != sizeof(pData->dwMetaFileWidth))
8254 hRes = CONVERT10_E_OLESTREAM_GET;
8256 if(hRes == S_OK)
8258 /* Get the Height of the Metafile */
8259 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8260 if(dwSize != sizeof(pData->dwMetaFileHeight))
8262 hRes = CONVERT10_E_OLESTREAM_GET;
8266 if(hRes == S_OK)
8268 /* Get the Length of the Data */
8269 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8270 if(dwSize != sizeof(pData->dwDataLength))
8272 hRes = CONVERT10_E_OLESTREAM_GET;
8276 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8278 if(!bStrem1) /* if it is a second OLE stream data */
8280 pData->dwDataLength -= 8;
8281 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8282 if(dwSize != sizeof(pData->strUnknown))
8284 hRes = CONVERT10_E_OLESTREAM_GET;
8288 if(hRes == S_OK)
8290 if(pData->dwDataLength > 0)
8292 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8294 /* Get Data (ex. IStorage, Metafile, or BMP) */
8295 if(pData->pData)
8297 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8298 if(dwSize != pData->dwDataLength)
8300 hRes = CONVERT10_E_OLESTREAM_GET;
8303 else
8305 hRes = CONVERT10_E_OLESTREAM_GET;
8311 return hRes;
8314 /*************************************************************************
8315 * OLECONVERT_SaveOLE10 [Internal]
8317 * Saves the OLE10 STREAM From memory
8319 * PARAMS
8320 * pData [I] Data Structure for the OLESTREAM Data
8321 * pOleStream [I] The OLESTREAM to save
8323 * RETURNS
8324 * Success: S_OK
8325 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8327 * NOTES
8328 * This function is used by OleConvertIStorageToOLESTREAM only.
8331 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8333 DWORD dwSize;
8334 HRESULT hRes = S_OK;
8337 /* Set the OleID */
8338 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8339 if(dwSize != sizeof(pData->dwOleID))
8341 hRes = CONVERT10_E_OLESTREAM_PUT;
8344 if(hRes == S_OK)
8346 /* Set the TypeID */
8347 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8348 if(dwSize != sizeof(pData->dwTypeID))
8350 hRes = CONVERT10_E_OLESTREAM_PUT;
8354 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8356 /* Set the Length of the OleTypeName */
8357 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8358 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8360 hRes = CONVERT10_E_OLESTREAM_PUT;
8363 if(hRes == S_OK)
8365 if(pData->dwOleTypeNameLength > 0)
8367 /* Set the OleTypeName */
8368 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8369 if(dwSize != pData->dwOleTypeNameLength)
8371 hRes = CONVERT10_E_OLESTREAM_PUT;
8376 if(hRes == S_OK)
8378 /* Set the width of the Metafile */
8379 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8380 if(dwSize != sizeof(pData->dwMetaFileWidth))
8382 hRes = CONVERT10_E_OLESTREAM_PUT;
8386 if(hRes == S_OK)
8388 /* Set the height of the Metafile */
8389 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8390 if(dwSize != sizeof(pData->dwMetaFileHeight))
8392 hRes = CONVERT10_E_OLESTREAM_PUT;
8396 if(hRes == S_OK)
8398 /* Set the length of the Data */
8399 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8400 if(dwSize != sizeof(pData->dwDataLength))
8402 hRes = CONVERT10_E_OLESTREAM_PUT;
8406 if(hRes == S_OK)
8408 if(pData->dwDataLength > 0)
8410 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8411 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8412 if(dwSize != pData->dwDataLength)
8414 hRes = CONVERT10_E_OLESTREAM_PUT;
8419 return hRes;
8422 /*************************************************************************
8423 * OLECONVERT_GetOLE20FromOLE10[Internal]
8425 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8426 * opens it, and copies the content to the dest IStorage for
8427 * OleConvertOLESTREAMToIStorage
8430 * PARAMS
8431 * pDestStorage [I] The IStorage to copy the data to
8432 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8433 * nBufferLength [I] The size of the buffer
8435 * RETURNS
8436 * Nothing
8438 * NOTES
8442 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8444 HRESULT hRes;
8445 HANDLE hFile;
8446 IStorage *pTempStorage;
8447 DWORD dwNumOfBytesWritten;
8448 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8449 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8451 /* Create a temp File */
8452 GetTempPathW(MAX_PATH, wstrTempDir);
8453 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8454 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8456 if(hFile != INVALID_HANDLE_VALUE)
8458 /* Write IStorage Data to File */
8459 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8460 CloseHandle(hFile);
8462 /* Open and copy temp storage to the Dest Storage */
8463 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8464 if(hRes == S_OK)
8466 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8467 IStorage_Release(pTempStorage);
8469 DeleteFileW(wstrTempFile);
8474 /*************************************************************************
8475 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8477 * Saves the OLE10 STREAM From memory
8479 * PARAMS
8480 * pStorage [I] The Src IStorage to copy
8481 * pData [I] The Dest Memory to write to.
8483 * RETURNS
8484 * The size in bytes allocated for pData
8486 * NOTES
8487 * Memory allocated for pData must be freed by the caller
8489 * Used by OleConvertIStorageToOLESTREAM only.
8492 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8494 HANDLE hFile;
8495 HRESULT hRes;
8496 DWORD nDataLength = 0;
8497 IStorage *pTempStorage;
8498 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8499 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8501 *pData = NULL;
8503 /* Create temp Storage */
8504 GetTempPathW(MAX_PATH, wstrTempDir);
8505 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8506 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8508 if(hRes == S_OK)
8510 /* Copy Src Storage to the Temp Storage */
8511 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8512 IStorage_Release(pTempStorage);
8514 /* Open Temp Storage as a file and copy to memory */
8515 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8516 if(hFile != INVALID_HANDLE_VALUE)
8518 nDataLength = GetFileSize(hFile, NULL);
8519 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8520 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8521 CloseHandle(hFile);
8523 DeleteFileW(wstrTempFile);
8525 return nDataLength;
8528 /*************************************************************************
8529 * OLECONVERT_CreateOleStream [Internal]
8531 * Creates the "\001OLE" stream in the IStorage if necessary.
8533 * PARAMS
8534 * pStorage [I] Dest storage to create the stream in
8536 * RETURNS
8537 * Nothing
8539 * NOTES
8540 * This function is used by OleConvertOLESTREAMToIStorage only.
8542 * This stream is still unknown, MS Word seems to have extra data
8543 * but since the data is stored in the OLESTREAM there should be
8544 * no need to recreate the stream. If the stream is manually
8545 * deleted it will create it with this default data.
8548 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8550 HRESULT hRes;
8551 IStream *pStream;
8552 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8553 BYTE pOleStreamHeader [] =
8555 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8556 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8557 0x00, 0x00, 0x00, 0x00
8560 /* Create stream if not present */
8561 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8562 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8564 if(hRes == S_OK)
8566 /* Write default Data */
8567 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8568 IStream_Release(pStream);
8572 /* write a string to a stream, preceded by its length */
8573 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8575 HRESULT r;
8576 LPSTR str;
8577 DWORD len = 0;
8579 if( string )
8580 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8581 r = IStream_Write( stm, &len, sizeof(len), NULL);
8582 if( FAILED( r ) )
8583 return r;
8584 if(len == 0)
8585 return r;
8586 str = CoTaskMemAlloc( len );
8587 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8588 r = IStream_Write( stm, str, len, NULL);
8589 CoTaskMemFree( str );
8590 return r;
8593 /* read a string preceded by its length from a stream */
8594 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8596 HRESULT r;
8597 DWORD len, count = 0;
8598 LPSTR str;
8599 LPWSTR wstr;
8601 r = IStream_Read( stm, &len, sizeof(len), &count );
8602 if( FAILED( r ) )
8603 return r;
8604 if( count != sizeof(len) )
8605 return E_OUTOFMEMORY;
8607 TRACE("%d bytes\n",len);
8609 str = CoTaskMemAlloc( len );
8610 if( !str )
8611 return E_OUTOFMEMORY;
8612 count = 0;
8613 r = IStream_Read( stm, str, len, &count );
8614 if( FAILED( r ) )
8615 return r;
8616 if( count != len )
8618 CoTaskMemFree( str );
8619 return E_OUTOFMEMORY;
8622 TRACE("Read string %s\n",debugstr_an(str,len));
8624 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8625 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8626 if( wstr )
8627 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8628 CoTaskMemFree( str );
8630 *string = wstr;
8632 return r;
8636 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8637 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8639 IStream *pstm;
8640 HRESULT r = S_OK;
8641 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8643 static const BYTE unknown1[12] =
8644 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8645 0xFF, 0xFF, 0xFF, 0xFF};
8646 static const BYTE unknown2[16] =
8647 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8648 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8650 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8651 debugstr_w(lpszUserType), debugstr_w(szClipName),
8652 debugstr_w(szProgIDName));
8654 /* Create a CompObj stream */
8655 r = IStorage_CreateStream(pstg, szwStreamName,
8656 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8657 if( FAILED (r) )
8658 return r;
8660 /* Write CompObj Structure to stream */
8661 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8663 if( SUCCEEDED( r ) )
8664 r = WriteClassStm( pstm, clsid );
8666 if( SUCCEEDED( r ) )
8667 r = STREAM_WriteString( pstm, lpszUserType );
8668 if( SUCCEEDED( r ) )
8669 r = STREAM_WriteString( pstm, szClipName );
8670 if( SUCCEEDED( r ) )
8671 r = STREAM_WriteString( pstm, szProgIDName );
8672 if( SUCCEEDED( r ) )
8673 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8675 IStream_Release( pstm );
8677 return r;
8680 /***********************************************************************
8681 * WriteFmtUserTypeStg (OLE32.@)
8683 HRESULT WINAPI WriteFmtUserTypeStg(
8684 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8686 HRESULT r;
8687 WCHAR szwClipName[0x40];
8688 CLSID clsid = CLSID_NULL;
8689 LPWSTR wstrProgID = NULL;
8690 DWORD n;
8692 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8694 /* get the clipboard format name */
8695 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8696 szwClipName[n]=0;
8698 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8700 /* FIXME: There's room to save a CLSID and its ProgID, but
8701 the CLSID is not looked up in the registry and in all the
8702 tests I wrote it was CLSID_NULL. Where does it come from?
8705 /* get the real program ID. This may fail, but that's fine */
8706 ProgIDFromCLSID(&clsid, &wstrProgID);
8708 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8710 r = STORAGE_WriteCompObj( pstg, &clsid,
8711 lpszUserType, szwClipName, wstrProgID );
8713 CoTaskMemFree(wstrProgID);
8715 return r;
8719 /******************************************************************************
8720 * ReadFmtUserTypeStg [OLE32.@]
8722 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8724 HRESULT r;
8725 IStream *stm = 0;
8726 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8727 unsigned char unknown1[12];
8728 unsigned char unknown2[16];
8729 DWORD count;
8730 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8731 CLSID clsid;
8733 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8735 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8736 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8737 if( FAILED ( r ) )
8739 WARN("Failed to open stream r = %08x\n", r);
8740 return r;
8743 /* read the various parts of the structure */
8744 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8745 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8746 goto end;
8747 r = ReadClassStm( stm, &clsid );
8748 if( FAILED( r ) )
8749 goto end;
8751 r = STREAM_ReadString( stm, &szCLSIDName );
8752 if( FAILED( r ) )
8753 goto end;
8755 r = STREAM_ReadString( stm, &szOleTypeName );
8756 if( FAILED( r ) )
8757 goto end;
8759 r = STREAM_ReadString( stm, &szProgIDName );
8760 if( FAILED( r ) )
8761 goto end;
8763 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8764 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8765 goto end;
8767 /* ok, success... now we just need to store what we found */
8768 if( pcf )
8769 *pcf = RegisterClipboardFormatW( szOleTypeName );
8770 CoTaskMemFree( szOleTypeName );
8772 if( lplpszUserType )
8773 *lplpszUserType = szCLSIDName;
8774 CoTaskMemFree( szProgIDName );
8776 end:
8777 IStream_Release( stm );
8779 return r;
8783 /*************************************************************************
8784 * OLECONVERT_CreateCompObjStream [Internal]
8786 * Creates a "\001CompObj" is the destination IStorage if necessary.
8788 * PARAMS
8789 * pStorage [I] The dest IStorage to create the CompObj Stream
8790 * if necessary.
8791 * strOleTypeName [I] The ProgID
8793 * RETURNS
8794 * Success: S_OK
8795 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8797 * NOTES
8798 * This function is used by OleConvertOLESTREAMToIStorage only.
8800 * The stream data is stored in the OLESTREAM and there should be
8801 * no need to recreate the stream. If the stream is manually
8802 * deleted it will attempt to create it by querying the registry.
8806 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8808 IStream *pStream;
8809 HRESULT hStorageRes, hRes = S_OK;
8810 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8811 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8812 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8814 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8815 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8817 /* Initialize the CompObj structure */
8818 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8819 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8820 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8823 /* Create a CompObj stream if it doesn't exist */
8824 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8825 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8826 if(hStorageRes == S_OK)
8828 /* copy the OleTypeName to the compobj struct */
8829 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8830 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8832 /* copy the OleTypeName to the compobj struct */
8833 /* Note: in the test made, these were Identical */
8834 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8835 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8837 /* Get the CLSID */
8838 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8839 bufferW, OLESTREAM_MAX_STR_LEN );
8840 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8842 if(hRes == S_OK)
8844 HKEY hKey;
8845 LONG hErr;
8846 /* Get the CLSID Default Name from the Registry */
8847 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8848 if(hErr == ERROR_SUCCESS)
8850 char strTemp[OLESTREAM_MAX_STR_LEN];
8851 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8852 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8853 if(hErr == ERROR_SUCCESS)
8855 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8857 RegCloseKey(hKey);
8861 /* Write CompObj Structure to stream */
8862 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8864 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8866 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8867 if(IStorageCompObj.dwCLSIDNameLength > 0)
8869 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8871 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8872 if(IStorageCompObj.dwOleTypeNameLength > 0)
8874 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8876 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8877 if(IStorageCompObj.dwProgIDNameLength > 0)
8879 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8881 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8882 IStream_Release(pStream);
8884 return hRes;
8888 /*************************************************************************
8889 * OLECONVERT_CreateOlePresStream[Internal]
8891 * Creates the "\002OlePres000" Stream with the Metafile data
8893 * PARAMS
8894 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8895 * dwExtentX [I] Width of the Metafile
8896 * dwExtentY [I] Height of the Metafile
8897 * pData [I] Metafile data
8898 * dwDataLength [I] Size of the Metafile data
8900 * RETURNS
8901 * Success: S_OK
8902 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8904 * NOTES
8905 * This function is used by OleConvertOLESTREAMToIStorage only.
8908 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8910 HRESULT hRes;
8911 IStream *pStream;
8912 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8913 BYTE pOlePresStreamHeader [] =
8915 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8916 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8917 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8918 0x00, 0x00, 0x00, 0x00
8921 BYTE pOlePresStreamHeaderEmpty [] =
8923 0x00, 0x00, 0x00, 0x00,
8924 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8925 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8926 0x00, 0x00, 0x00, 0x00
8929 /* Create the OlePres000 Stream */
8930 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8931 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8933 if(hRes == S_OK)
8935 DWORD nHeaderSize;
8936 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8938 memset(&OlePres, 0, sizeof(OlePres));
8939 /* Do we have any metafile data to save */
8940 if(dwDataLength > 0)
8942 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8943 nHeaderSize = sizeof(pOlePresStreamHeader);
8945 else
8947 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8948 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8950 /* Set width and height of the metafile */
8951 OlePres.dwExtentX = dwExtentX;
8952 OlePres.dwExtentY = -dwExtentY;
8954 /* Set Data and Length */
8955 if(dwDataLength > sizeof(METAFILEPICT16))
8957 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8958 OlePres.pData = &(pData[8]);
8960 /* Save OlePres000 Data to Stream */
8961 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8962 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8963 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8964 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8965 if(OlePres.dwSize > 0)
8967 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8969 IStream_Release(pStream);
8973 /*************************************************************************
8974 * OLECONVERT_CreateOle10NativeStream [Internal]
8976 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8978 * PARAMS
8979 * pStorage [I] Dest storage to create the stream in
8980 * pData [I] Ole10 Native Data (ex. bmp)
8981 * dwDataLength [I] Size of the Ole10 Native Data
8983 * RETURNS
8984 * Nothing
8986 * NOTES
8987 * This function is used by OleConvertOLESTREAMToIStorage only.
8989 * Might need to verify the data and return appropriate error message
8992 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8994 HRESULT hRes;
8995 IStream *pStream;
8996 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8998 /* Create the Ole10Native Stream */
8999 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9000 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9002 if(hRes == S_OK)
9004 /* Write info to stream */
9005 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9006 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9007 IStream_Release(pStream);
9012 /*************************************************************************
9013 * OLECONVERT_GetOLE10ProgID [Internal]
9015 * Finds the ProgID (or OleTypeID) from the IStorage
9017 * PARAMS
9018 * pStorage [I] The Src IStorage to get the ProgID
9019 * strProgID [I] the ProgID string to get
9020 * dwSize [I] the size of the string
9022 * RETURNS
9023 * Success: S_OK
9024 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9026 * NOTES
9027 * This function is used by OleConvertIStorageToOLESTREAM only.
9031 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9033 HRESULT hRes;
9034 IStream *pStream;
9035 LARGE_INTEGER iSeekPos;
9036 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9037 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9039 /* Open the CompObj Stream */
9040 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9041 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9042 if(hRes == S_OK)
9045 /*Get the OleType from the CompObj Stream */
9046 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9047 iSeekPos.u.HighPart = 0;
9049 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9050 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9051 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9052 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9053 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9054 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9055 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9057 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9058 if(*dwSize > 0)
9060 IStream_Read(pStream, strProgID, *dwSize, NULL);
9062 IStream_Release(pStream);
9064 else
9066 STATSTG stat;
9067 LPOLESTR wstrProgID;
9069 /* Get the OleType from the registry */
9070 REFCLSID clsid = &(stat.clsid);
9071 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9072 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9073 if(hRes == S_OK)
9075 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9079 return hRes;
9082 /*************************************************************************
9083 * OLECONVERT_GetOle10PresData [Internal]
9085 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9087 * PARAMS
9088 * pStorage [I] Src IStroage
9089 * pOleStream [I] Dest OleStream Mem Struct
9091 * RETURNS
9092 * Nothing
9094 * NOTES
9095 * This function is used by OleConvertIStorageToOLESTREAM only.
9097 * Memory allocated for pData must be freed by the caller
9101 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9104 HRESULT hRes;
9105 IStream *pStream;
9106 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9108 /* Initialize Default data for OLESTREAM */
9109 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9110 pOleStreamData[0].dwTypeID = 2;
9111 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9112 pOleStreamData[1].dwTypeID = 0;
9113 pOleStreamData[0].dwMetaFileWidth = 0;
9114 pOleStreamData[0].dwMetaFileHeight = 0;
9115 pOleStreamData[0].pData = NULL;
9116 pOleStreamData[1].pData = NULL;
9118 /* Open Ole10Native Stream */
9119 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9120 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9121 if(hRes == S_OK)
9124 /* Read Size and Data */
9125 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9126 if(pOleStreamData->dwDataLength > 0)
9128 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9129 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9131 IStream_Release(pStream);
9137 /*************************************************************************
9138 * OLECONVERT_GetOle20PresData[Internal]
9140 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9142 * PARAMS
9143 * pStorage [I] Src IStroage
9144 * pOleStreamData [I] Dest OleStream Mem Struct
9146 * RETURNS
9147 * Nothing
9149 * NOTES
9150 * This function is used by OleConvertIStorageToOLESTREAM only.
9152 * Memory allocated for pData must be freed by the caller
9154 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9156 HRESULT hRes;
9157 IStream *pStream;
9158 OLECONVERT_ISTORAGE_OLEPRES olePress;
9159 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9161 /* Initialize Default data for OLESTREAM */
9162 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9163 pOleStreamData[0].dwTypeID = 2;
9164 pOleStreamData[0].dwMetaFileWidth = 0;
9165 pOleStreamData[0].dwMetaFileHeight = 0;
9166 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9167 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9168 pOleStreamData[1].dwTypeID = 0;
9169 pOleStreamData[1].dwOleTypeNameLength = 0;
9170 pOleStreamData[1].strOleTypeName[0] = 0;
9171 pOleStreamData[1].dwMetaFileWidth = 0;
9172 pOleStreamData[1].dwMetaFileHeight = 0;
9173 pOleStreamData[1].pData = NULL;
9174 pOleStreamData[1].dwDataLength = 0;
9177 /* Open OlePress000 stream */
9178 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9179 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9180 if(hRes == S_OK)
9182 LARGE_INTEGER iSeekPos;
9183 METAFILEPICT16 MetaFilePict;
9184 static const char strMetafilePictName[] = "METAFILEPICT";
9186 /* Set the TypeID for a Metafile */
9187 pOleStreamData[1].dwTypeID = 5;
9189 /* Set the OleTypeName to Metafile */
9190 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9191 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9193 iSeekPos.u.HighPart = 0;
9194 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9196 /* Get Presentation Data */
9197 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9198 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9199 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9200 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9202 /*Set width and Height */
9203 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9204 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9205 if(olePress.dwSize > 0)
9207 /* Set Length */
9208 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9210 /* Set MetaFilePict struct */
9211 MetaFilePict.mm = 8;
9212 MetaFilePict.xExt = olePress.dwExtentX;
9213 MetaFilePict.yExt = olePress.dwExtentY;
9214 MetaFilePict.hMF = 0;
9216 /* Get Metafile Data */
9217 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9218 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9219 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9221 IStream_Release(pStream);
9225 /*************************************************************************
9226 * OleConvertOLESTREAMToIStorage [OLE32.@]
9228 * Read info on MSDN
9230 * TODO
9231 * DVTARGETDEVICE parameter is not handled
9232 * Still unsure of some mem fields for OLE 10 Stream
9233 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9234 * and "\001OLE" streams
9237 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9238 LPOLESTREAM pOleStream,
9239 LPSTORAGE pstg,
9240 const DVTARGETDEVICE* ptd)
9242 int i;
9243 HRESULT hRes=S_OK;
9244 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9246 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9248 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9250 if(ptd != NULL)
9252 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9255 if(pstg == NULL || pOleStream == NULL)
9257 hRes = E_INVALIDARG;
9260 if(hRes == S_OK)
9262 /* Load the OLESTREAM to Memory */
9263 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9266 if(hRes == S_OK)
9268 /* Load the OLESTREAM to Memory (part 2)*/
9269 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9272 if(hRes == S_OK)
9275 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9277 /* Do we have the IStorage Data in the OLESTREAM */
9278 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9280 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9281 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9283 else
9285 /* It must be an original OLE 1.0 source */
9286 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9289 else
9291 /* It must be an original OLE 1.0 source */
9292 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9295 /* Create CompObj Stream if necessary */
9296 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9297 if(hRes == S_OK)
9299 /*Create the Ole Stream if necessary */
9300 OLECONVERT_CreateOleStream(pstg);
9305 /* Free allocated memory */
9306 for(i=0; i < 2; i++)
9308 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9309 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9310 pOleStreamData[i].pstrOleObjFileName = NULL;
9312 return hRes;
9315 /*************************************************************************
9316 * OleConvertIStorageToOLESTREAM [OLE32.@]
9318 * Read info on MSDN
9320 * Read info on MSDN
9322 * TODO
9323 * Still unsure of some mem fields for OLE 10 Stream
9324 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9325 * and "\001OLE" streams.
9328 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9329 LPSTORAGE pstg,
9330 LPOLESTREAM pOleStream)
9332 int i;
9333 HRESULT hRes = S_OK;
9334 IStream *pStream;
9335 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9336 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9338 TRACE("%p %p\n", pstg, pOleStream);
9340 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9342 if(pstg == NULL || pOleStream == NULL)
9344 hRes = E_INVALIDARG;
9346 if(hRes == S_OK)
9348 /* Get the ProgID */
9349 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9350 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9352 if(hRes == S_OK)
9354 /* Was it originally Ole10 */
9355 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9356 if(hRes == S_OK)
9358 IStream_Release(pStream);
9359 /* Get Presentation Data for Ole10Native */
9360 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9362 else
9364 /* Get Presentation Data (OLE20) */
9365 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9368 /* Save OLESTREAM */
9369 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9370 if(hRes == S_OK)
9372 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9377 /* Free allocated memory */
9378 for(i=0; i < 2; i++)
9380 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9383 return hRes;
9386 enum stream_1ole_flags {
9387 OleStream_LinkedObject = 0x00000001,
9388 OleStream_Convert = 0x00000100
9391 /***********************************************************************
9392 * GetConvertStg (OLE32.@)
9394 HRESULT WINAPI GetConvertStg(IStorage *stg)
9396 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9397 static const DWORD version_magic = 0x02000001;
9398 DWORD header[2];
9399 IStream *stream;
9400 HRESULT hr;
9401 ULONG len;
9403 TRACE("%p\n", stg);
9405 if (!stg) return E_INVALIDARG;
9407 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
9408 if (FAILED(hr)) return hr;
9410 len = 0;
9411 hr = IStream_Read(stream, header, sizeof(header), &len);
9412 IStream_Release(stream);
9413 if (FAILED(hr)) return hr;
9415 if (header[0] != version_magic)
9417 ERR("got wrong version magic for \1Ole stream, 0x%08x\n", header[0]);
9418 return E_FAIL;
9421 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
9424 /******************************************************************************
9425 * StgIsStorageFile [OLE32.@]
9426 * Verify if the file contains a storage object
9428 * PARAMS
9429 * fn [ I] Filename
9431 * RETURNS
9432 * S_OK if file has magic bytes as a storage object
9433 * S_FALSE if file is not storage
9435 HRESULT WINAPI
9436 StgIsStorageFile(LPCOLESTR fn)
9438 HANDLE hf;
9439 BYTE magic[8];
9440 DWORD bytes_read;
9442 TRACE("%s\n", debugstr_w(fn));
9443 hf = CreateFileW(fn, GENERIC_READ,
9444 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9445 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9447 if (hf == INVALID_HANDLE_VALUE)
9448 return STG_E_FILENOTFOUND;
9450 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9452 WARN(" unable to read file\n");
9453 CloseHandle(hf);
9454 return S_FALSE;
9457 CloseHandle(hf);
9459 if (bytes_read != 8) {
9460 TRACE(" too short\n");
9461 return S_FALSE;
9464 if (!memcmp(magic,STORAGE_magic,8)) {
9465 TRACE(" -> YES\n");
9466 return S_OK;
9469 TRACE(" -> Invalid header.\n");
9470 return S_FALSE;
9473 /***********************************************************************
9474 * WriteClassStm (OLE32.@)
9476 * Writes a CLSID to a stream.
9478 * PARAMS
9479 * pStm [I] Stream to write to.
9480 * rclsid [I] CLSID to write.
9482 * RETURNS
9483 * Success: S_OK.
9484 * Failure: HRESULT code.
9486 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9488 TRACE("(%p,%p)\n",pStm,rclsid);
9490 if (!pStm || !rclsid)
9491 return E_INVALIDARG;
9493 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9496 /***********************************************************************
9497 * ReadClassStm (OLE32.@)
9499 * Reads a CLSID from a stream.
9501 * PARAMS
9502 * pStm [I] Stream to read from.
9503 * rclsid [O] CLSID to read.
9505 * RETURNS
9506 * Success: S_OK.
9507 * Failure: HRESULT code.
9509 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9511 ULONG nbByte;
9512 HRESULT res;
9514 TRACE("(%p,%p)\n",pStm,pclsid);
9516 if (!pStm || !pclsid)
9517 return E_INVALIDARG;
9519 /* clear the output args */
9520 *pclsid = CLSID_NULL;
9522 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9524 if (FAILED(res))
9525 return res;
9527 if (nbByte != sizeof(CLSID))
9528 return STG_E_READFAULT;
9529 else
9530 return S_OK;