msvcrt: Add __ExceptionPtr* functions implementation.
[wine.git] / dlls / ole32 / storage32.c
blob7f1affbfaca58832d8cdf3c3946ddb232b25ce1d
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
55 #include "compobj_private.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(storage);
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
60 #define OLESTREAM_ID 0x501
61 #define OLESTREAM_MAX_STR_LEN 255
64 * These are signatures to detect the type of Document file.
66 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
67 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
69 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
71 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
74 static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
76 return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
79 /****************************************************************************
80 * Storage32InternalImpl definitions.
82 * Definition of the implementation structure for the IStorage32 interface.
83 * This one implements the IStorage32 interface for storage that are
84 * inside another storage.
86 struct StorageInternalImpl
88 struct StorageBaseImpl base;
91 * Entry in the parent's stream tracking list
93 struct list ParentListEntry;
95 StorageBaseImpl *parentStorage;
97 typedef struct StorageInternalImpl StorageInternalImpl;
99 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
100 static const IStorageVtbl Storage32InternalImpl_Vtbl;
102 /* Method definitions for the Storage32InternalImpl class. */
103 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
104 DWORD openFlags, DirRef storageDirEntry);
105 static void StorageImpl_Destroy(StorageBaseImpl* iface);
106 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
107 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
108 static HRESULT StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer, ULONG *read );
109 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
110 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
111 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
112 static void StorageImpl_SaveFileHeader(StorageImpl* This);
113 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, ULARGE_INTEGER cb, DWORD dwLockType);
115 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex);
116 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
117 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
118 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
119 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
121 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
122 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
123 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
125 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
126 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
127 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
128 ULONG blockIndex, ULONG offset, DWORD value);
129 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
130 ULONG blockIndex, ULONG offset, DWORD* value);
132 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
133 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
135 typedef struct TransactedDirEntry
137 /* If applicable, a reference to the original DirEntry in the transacted
138 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
139 DirRef transactedParentEntry;
141 /* True if this entry is being used. */
142 BOOL inuse;
144 /* True if data is up to date. */
145 BOOL read;
147 /* True if this entry has been modified. */
148 BOOL dirty;
150 /* True if this entry's stream has been modified. */
151 BOOL stream_dirty;
153 /* True if this entry has been deleted in the transacted storage, but the
154 * delete has not yet been committed. */
155 BOOL deleted;
157 /* If this entry's stream has been modified, a reference to where the stream
158 * is stored in the snapshot file. */
159 DirRef stream_entry;
161 /* This directory entry's data, including any changes that have been made. */
162 DirEntry data;
164 /* A reference to the parent of this node. This is only valid while we are
165 * committing changes. */
166 DirRef parent;
168 /* A reference to a newly-created entry in the transacted parent. This is
169 * always equal to transactedParentEntry except when committing changes. */
170 DirRef newTransactedParentEntry;
171 } TransactedDirEntry;
173 /****************************************************************************
174 * Transacted storage object.
176 typedef struct TransactedSnapshotImpl
178 struct StorageBaseImpl base;
181 * Modified streams are temporarily saved to the scratch file.
183 StorageBaseImpl *scratch;
185 /* The directory structure is kept here, so that we can track how these
186 * entries relate to those in the parent storage. */
187 TransactedDirEntry *entries;
188 ULONG entries_size;
189 ULONG firstFreeEntry;
192 * Changes are committed to the transacted parent.
194 StorageBaseImpl *transactedParent;
196 /* The transaction signature from when we last committed */
197 ULONG lastTransactionSig;
198 } TransactedSnapshotImpl;
200 /* Generic function to create a transacted wrapper for a direct storage object. */
201 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
203 /* OLESTREAM memory structure to use for Get and Put Routines */
204 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
205 typedef struct
207 DWORD dwOleID;
208 DWORD dwTypeID;
209 DWORD dwOleTypeNameLength;
210 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
211 CHAR *pstrOleObjFileName;
212 DWORD dwOleObjFileNameLength;
213 DWORD dwMetaFileWidth;
214 DWORD dwMetaFileHeight;
215 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
216 DWORD dwDataLength;
217 BYTE *pData;
218 }OLECONVERT_OLESTREAM_DATA;
220 /* CompObj Stream structure */
221 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
222 typedef struct
224 BYTE byUnknown1[12];
225 CLSID clsid;
226 DWORD dwCLSIDNameLength;
227 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
228 DWORD dwOleTypeNameLength;
229 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
230 DWORD dwProgIDNameLength;
231 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
232 BYTE byUnknown2[16];
233 }OLECONVERT_ISTORAGE_COMPOBJ;
236 /* Ole Presentation Stream structure */
237 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
238 typedef struct
240 BYTE byUnknown1[28];
241 DWORD dwExtentX;
242 DWORD dwExtentY;
243 DWORD dwSize;
244 BYTE *pData;
245 }OLECONVERT_ISTORAGE_OLEPRES;
249 /***********************************************************************
250 * Forward declaration of internal functions used by the method DestroyElement
252 static HRESULT deleteStorageContents(
253 StorageBaseImpl *parentStorage,
254 DirRef indexToDelete,
255 DirEntry entryDataToDelete);
257 static HRESULT deleteStreamContents(
258 StorageBaseImpl *parentStorage,
259 DirRef indexToDelete,
260 DirEntry entryDataToDelete);
262 static HRESULT removeFromTree(
263 StorageBaseImpl *This,
264 DirRef parentStorageIndex,
265 DirRef deletedIndex);
267 /***********************************************************************
268 * Declaration of the functions used to manipulate DirEntry
271 static HRESULT insertIntoTree(
272 StorageBaseImpl *This,
273 DirRef parentStorageIndex,
274 DirRef newEntryIndex);
276 static LONG entryNameCmp(
277 const OLECHAR *name1,
278 const OLECHAR *name2);
280 static DirRef findElement(
281 StorageBaseImpl *storage,
282 DirRef storageEntry,
283 const OLECHAR *name,
284 DirEntry *data);
286 static HRESULT findTreeParent(
287 StorageBaseImpl *storage,
288 DirRef storageEntry,
289 const OLECHAR *childName,
290 DirEntry *parentData,
291 DirRef *parentEntry,
292 ULONG *relation);
294 /***********************************************************************
295 * Declaration of miscellaneous functions...
297 static HRESULT validateSTGM(DWORD stgmValue);
299 static DWORD GetShareModeFromSTGM(DWORD stgm);
300 static DWORD GetAccessModeFromSTGM(DWORD stgm);
301 static DWORD GetCreationModeFromSTGM(DWORD stgm);
303 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
306 /****************************************************************************
307 * IEnumSTATSTGImpl definitions.
309 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
310 * This class allows iterating through the content of a storage and to find
311 * specific items inside it.
313 struct IEnumSTATSTGImpl
315 IEnumSTATSTG IEnumSTATSTG_iface;
317 LONG ref; /* Reference count */
318 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
319 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
321 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
324 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
326 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
330 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
331 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
333 /************************************************************************
334 ** Block Functions
337 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
339 return (index+1) * This->bigBlockSize;
342 /************************************************************************
343 ** Storage32BaseImpl implementation
345 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
346 ULARGE_INTEGER offset,
347 void* buffer,
348 ULONG size,
349 ULONG* bytesRead)
351 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
354 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
355 ULARGE_INTEGER offset,
356 const void* buffer,
357 const ULONG size,
358 ULONG* bytesWritten)
360 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
363 /************************************************************************
364 * Storage32BaseImpl_QueryInterface (IUnknown)
366 * This method implements the common QueryInterface for all IStorage32
367 * implementations contained in this file.
369 * See Windows documentation for more details on IUnknown methods.
371 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
372 IStorage* iface,
373 REFIID riid,
374 void** ppvObject)
376 StorageBaseImpl *This = impl_from_IStorage(iface);
378 if (!ppvObject)
379 return E_INVALIDARG;
381 *ppvObject = 0;
383 if (IsEqualGUID(&IID_IUnknown, riid) ||
384 IsEqualGUID(&IID_IStorage, riid))
386 *ppvObject = &This->IStorage_iface;
388 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
390 *ppvObject = &This->IPropertySetStorage_iface;
392 /* locking interface is reported for writer only */
393 else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
395 *ppvObject = &This->IDirectWriterLock_iface;
397 else
398 return E_NOINTERFACE;
400 IStorage_AddRef(iface);
402 return S_OK;
405 /************************************************************************
406 * Storage32BaseImpl_AddRef (IUnknown)
408 * This method implements the common AddRef for all IStorage32
409 * implementations contained in this file.
411 * See Windows documentation for more details on IUnknown methods.
413 static ULONG WINAPI StorageBaseImpl_AddRef(
414 IStorage* iface)
416 StorageBaseImpl *This = impl_from_IStorage(iface);
417 ULONG ref = InterlockedIncrement(&This->ref);
419 TRACE("(%p) AddRef to %d\n", This, ref);
421 return ref;
424 /************************************************************************
425 * Storage32BaseImpl_Release (IUnknown)
427 * This method implements the common Release for all IStorage32
428 * implementations contained in this file.
430 * See Windows documentation for more details on IUnknown methods.
432 static ULONG WINAPI StorageBaseImpl_Release(
433 IStorage* iface)
435 StorageBaseImpl *This = impl_from_IStorage(iface);
437 ULONG ref = InterlockedDecrement(&This->ref);
439 TRACE("(%p) ReleaseRef to %d\n", This, ref);
441 if (ref == 0)
444 * Since we are using a system of base-classes, we want to call the
445 * destructor of the appropriate derived class. To do this, we are
446 * using virtual functions to implement the destructor.
448 StorageBaseImpl_Destroy(This);
451 return ref;
454 /************************************************************************
455 * Storage32BaseImpl_OpenStream (IStorage)
457 * This method will open the specified stream object from the current storage.
459 * See Windows documentation for more details on IStorage methods.
461 static HRESULT WINAPI StorageBaseImpl_OpenStream(
462 IStorage* iface,
463 const OLECHAR* pwcsName, /* [string][in] */
464 void* reserved1, /* [unique][in] */
465 DWORD grfMode, /* [in] */
466 DWORD reserved2, /* [in] */
467 IStream** ppstm) /* [out] */
469 StorageBaseImpl *This = impl_from_IStorage(iface);
470 StgStreamImpl* newStream;
471 DirEntry currentEntry;
472 DirRef streamEntryRef;
473 HRESULT res = STG_E_UNKNOWN;
475 TRACE("(%p, %s, %p, %x, %d, %p)\n",
476 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
478 if ( (pwcsName==NULL) || (ppstm==0) )
480 res = E_INVALIDARG;
481 goto end;
484 *ppstm = NULL;
486 if ( FAILED( validateSTGM(grfMode) ) ||
487 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
489 res = STG_E_INVALIDFLAG;
490 goto end;
494 * As documented.
496 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
498 res = STG_E_INVALIDFUNCTION;
499 goto end;
502 if (This->reverted)
504 res = STG_E_REVERTED;
505 goto end;
509 * Check that we're compatible with the parent's storage mode, but
510 * only if we are not in transacted mode
512 if(!(This->openFlags & STGM_TRANSACTED)) {
513 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
515 res = STG_E_INVALIDFLAG;
516 goto end;
521 * Search for the element with the given name
523 streamEntryRef = findElement(
524 This,
525 This->storageDirEntry,
526 pwcsName,
527 &currentEntry);
530 * If it was found, construct the stream object and return a pointer to it.
532 if ( (streamEntryRef!=DIRENTRY_NULL) &&
533 (currentEntry.stgType==STGTY_STREAM) )
535 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
537 /* A single stream cannot be opened a second time. */
538 res = STG_E_ACCESSDENIED;
539 goto end;
542 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
544 if (newStream)
546 newStream->grfMode = grfMode;
547 *ppstm = &newStream->IStream_iface;
549 IStream_AddRef(*ppstm);
551 res = S_OK;
552 goto end;
555 res = E_OUTOFMEMORY;
556 goto end;
559 res = STG_E_FILENOTFOUND;
561 end:
562 if (res == S_OK)
563 TRACE("<-- IStream %p\n", *ppstm);
564 TRACE("<-- %08x\n", res);
565 return res;
568 /************************************************************************
569 * Storage32BaseImpl_OpenStorage (IStorage)
571 * This method will open a new storage object from the current storage.
573 * See Windows documentation for more details on IStorage methods.
575 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
576 IStorage* iface,
577 const OLECHAR* pwcsName, /* [string][unique][in] */
578 IStorage* pstgPriority, /* [unique][in] */
579 DWORD grfMode, /* [in] */
580 SNB snbExclude, /* [unique][in] */
581 DWORD reserved, /* [in] */
582 IStorage** ppstg) /* [out] */
584 StorageBaseImpl *This = impl_from_IStorage(iface);
585 StorageInternalImpl* newStorage;
586 StorageBaseImpl* newTransactedStorage;
587 DirEntry currentEntry;
588 DirRef storageEntryRef;
589 HRESULT res = STG_E_UNKNOWN;
591 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
592 iface, debugstr_w(pwcsName), pstgPriority,
593 grfMode, snbExclude, reserved, ppstg);
595 if ((pwcsName==NULL) || (ppstg==0) )
597 res = E_INVALIDARG;
598 goto end;
601 if (This->openFlags & STGM_SIMPLE)
603 res = STG_E_INVALIDFUNCTION;
604 goto end;
607 /* as documented */
608 if (snbExclude != NULL)
610 res = STG_E_INVALIDPARAMETER;
611 goto end;
614 if ( FAILED( validateSTGM(grfMode) ))
616 res = STG_E_INVALIDFLAG;
617 goto end;
621 * As documented.
623 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
624 (grfMode & STGM_DELETEONRELEASE) ||
625 (grfMode & STGM_PRIORITY) )
627 res = STG_E_INVALIDFUNCTION;
628 goto end;
631 if (This->reverted)
632 return STG_E_REVERTED;
635 * Check that we're compatible with the parent's storage mode,
636 * but only if we are not transacted
638 if(!(This->openFlags & STGM_TRANSACTED)) {
639 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
641 res = STG_E_ACCESSDENIED;
642 goto end;
646 *ppstg = NULL;
648 storageEntryRef = findElement(
649 This,
650 This->storageDirEntry,
651 pwcsName,
652 &currentEntry);
654 if ( (storageEntryRef!=DIRENTRY_NULL) &&
655 (currentEntry.stgType==STGTY_STORAGE) )
657 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
659 /* A single storage cannot be opened a second time. */
660 res = STG_E_ACCESSDENIED;
661 goto end;
664 newStorage = StorageInternalImpl_Construct(
665 This,
666 grfMode,
667 storageEntryRef);
669 if (newStorage != 0)
671 if (grfMode & STGM_TRANSACTED)
673 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
675 if (FAILED(res))
677 HeapFree(GetProcessHeap(), 0, newStorage);
678 goto end;
681 *ppstg = &newTransactedStorage->IStorage_iface;
683 else
685 *ppstg = &newStorage->base.IStorage_iface;
688 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
690 res = S_OK;
691 goto end;
694 res = STG_E_INSUFFICIENTMEMORY;
695 goto end;
698 res = STG_E_FILENOTFOUND;
700 end:
701 TRACE("<-- %08x\n", res);
702 return res;
705 /************************************************************************
706 * Storage32BaseImpl_EnumElements (IStorage)
708 * This method will create an enumerator object that can be used to
709 * retrieve information about all the elements in the storage object.
711 * See Windows documentation for more details on IStorage methods.
713 static HRESULT WINAPI StorageBaseImpl_EnumElements(
714 IStorage* iface,
715 DWORD reserved1, /* [in] */
716 void* reserved2, /* [size_is][unique][in] */
717 DWORD reserved3, /* [in] */
718 IEnumSTATSTG** ppenum) /* [out] */
720 StorageBaseImpl *This = impl_from_IStorage(iface);
721 IEnumSTATSTGImpl* newEnum;
723 TRACE("(%p, %d, %p, %d, %p)\n",
724 iface, reserved1, reserved2, reserved3, ppenum);
726 if (!ppenum)
727 return E_INVALIDARG;
729 if (This->reverted)
730 return STG_E_REVERTED;
732 newEnum = IEnumSTATSTGImpl_Construct(
733 This,
734 This->storageDirEntry);
736 if (newEnum)
738 *ppenum = &newEnum->IEnumSTATSTG_iface;
739 return S_OK;
742 return E_OUTOFMEMORY;
745 /************************************************************************
746 * Storage32BaseImpl_Stat (IStorage)
748 * This method will retrieve information about this storage object.
750 * See Windows documentation for more details on IStorage methods.
752 static HRESULT WINAPI StorageBaseImpl_Stat(
753 IStorage* iface,
754 STATSTG* pstatstg, /* [out] */
755 DWORD grfStatFlag) /* [in] */
757 StorageBaseImpl *This = impl_from_IStorage(iface);
758 DirEntry currentEntry;
759 HRESULT res = STG_E_UNKNOWN;
761 TRACE("(%p, %p, %x)\n",
762 iface, pstatstg, grfStatFlag);
764 if (!pstatstg)
766 res = E_INVALIDARG;
767 goto end;
770 if (This->reverted)
772 res = STG_E_REVERTED;
773 goto end;
776 res = StorageBaseImpl_ReadDirEntry(
777 This,
778 This->storageDirEntry,
779 &currentEntry);
781 if (SUCCEEDED(res))
783 StorageUtl_CopyDirEntryToSTATSTG(
784 This,
785 pstatstg,
786 &currentEntry,
787 grfStatFlag);
789 pstatstg->grfMode = This->openFlags;
790 pstatstg->grfStateBits = This->stateBits;
793 end:
794 if (res == S_OK)
796 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);
798 TRACE("<-- %08x\n", res);
799 return res;
802 /************************************************************************
803 * Storage32BaseImpl_RenameElement (IStorage)
805 * This method will rename the specified element.
807 * See Windows documentation for more details on IStorage methods.
809 static HRESULT WINAPI StorageBaseImpl_RenameElement(
810 IStorage* iface,
811 const OLECHAR* pwcsOldName, /* [in] */
812 const OLECHAR* pwcsNewName) /* [in] */
814 StorageBaseImpl *This = impl_from_IStorage(iface);
815 DirEntry currentEntry;
816 DirRef currentEntryRef;
818 TRACE("(%p, %s, %s)\n",
819 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
821 if (This->reverted)
822 return STG_E_REVERTED;
824 currentEntryRef = findElement(This,
825 This->storageDirEntry,
826 pwcsNewName,
827 &currentEntry);
829 if (currentEntryRef != DIRENTRY_NULL)
832 * There is already an element with the new name
834 return STG_E_FILEALREADYEXISTS;
838 * Search for the old element name
840 currentEntryRef = findElement(This,
841 This->storageDirEntry,
842 pwcsOldName,
843 &currentEntry);
845 if (currentEntryRef != DIRENTRY_NULL)
847 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
848 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
850 WARN("Element is already open; cannot rename.\n");
851 return STG_E_ACCESSDENIED;
854 /* Remove the element from its current position in the tree */
855 removeFromTree(This, This->storageDirEntry,
856 currentEntryRef);
858 /* Change the name of the element */
859 strcpyW(currentEntry.name, pwcsNewName);
861 /* Delete any sibling links */
862 currentEntry.leftChild = DIRENTRY_NULL;
863 currentEntry.rightChild = DIRENTRY_NULL;
865 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
866 &currentEntry);
868 /* Insert the element in a new position in the tree */
869 insertIntoTree(This, This->storageDirEntry,
870 currentEntryRef);
872 else
875 * There is no element with the old name
877 return STG_E_FILENOTFOUND;
880 return StorageBaseImpl_Flush(This);
883 /************************************************************************
884 * Storage32BaseImpl_CreateStream (IStorage)
886 * This method will create a stream object within this storage
888 * See Windows documentation for more details on IStorage methods.
890 static HRESULT WINAPI StorageBaseImpl_CreateStream(
891 IStorage* iface,
892 const OLECHAR* pwcsName, /* [string][in] */
893 DWORD grfMode, /* [in] */
894 DWORD reserved1, /* [in] */
895 DWORD reserved2, /* [in] */
896 IStream** ppstm) /* [out] */
898 StorageBaseImpl *This = impl_from_IStorage(iface);
899 StgStreamImpl* newStream;
900 DirEntry currentEntry, newStreamEntry;
901 DirRef currentEntryRef, newStreamEntryRef;
902 HRESULT hr;
904 TRACE("(%p, %s, %x, %d, %d, %p)\n",
905 iface, debugstr_w(pwcsName), grfMode,
906 reserved1, reserved2, ppstm);
908 if (ppstm == 0)
909 return STG_E_INVALIDPOINTER;
911 if (pwcsName == 0)
912 return STG_E_INVALIDNAME;
914 if (reserved1 || reserved2)
915 return STG_E_INVALIDPARAMETER;
917 if ( FAILED( validateSTGM(grfMode) ))
918 return STG_E_INVALIDFLAG;
920 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
921 return STG_E_INVALIDFLAG;
923 if (This->reverted)
924 return STG_E_REVERTED;
927 * As documented.
929 if ((grfMode & STGM_DELETEONRELEASE) ||
930 (grfMode & STGM_TRANSACTED))
931 return STG_E_INVALIDFUNCTION;
934 * Don't worry about permissions in transacted mode, as we can always write
935 * changes; we just can't always commit them.
937 if(!(This->openFlags & STGM_TRANSACTED)) {
938 /* Can't create a stream on read-only storage */
939 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
940 return STG_E_ACCESSDENIED;
942 /* Can't create a stream with greater access than the parent. */
943 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
944 return STG_E_ACCESSDENIED;
947 if(This->openFlags & STGM_SIMPLE)
948 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
950 *ppstm = 0;
952 currentEntryRef = findElement(This,
953 This->storageDirEntry,
954 pwcsName,
955 &currentEntry);
957 if (currentEntryRef != DIRENTRY_NULL)
960 * An element with this name already exists
962 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
964 IStorage_DestroyElement(iface, pwcsName);
966 else
967 return STG_E_FILEALREADYEXISTS;
971 * memset the empty entry
973 memset(&newStreamEntry, 0, sizeof(DirEntry));
975 newStreamEntry.sizeOfNameString =
976 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
978 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
979 return STG_E_INVALIDNAME;
981 strcpyW(newStreamEntry.name, pwcsName);
983 newStreamEntry.stgType = STGTY_STREAM;
984 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
985 newStreamEntry.size.u.LowPart = 0;
986 newStreamEntry.size.u.HighPart = 0;
988 newStreamEntry.leftChild = DIRENTRY_NULL;
989 newStreamEntry.rightChild = DIRENTRY_NULL;
990 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
992 /* call CoFileTime to get the current time
993 newStreamEntry.ctime
994 newStreamEntry.mtime
997 /* newStreamEntry.clsid */
1000 * Create an entry with the new data
1002 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
1003 if (FAILED(hr))
1004 return hr;
1007 * Insert the new entry in the parent storage's tree.
1009 hr = insertIntoTree(
1010 This,
1011 This->storageDirEntry,
1012 newStreamEntryRef);
1013 if (FAILED(hr))
1015 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1016 return hr;
1020 * Open the stream to return it.
1022 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1024 if (newStream)
1026 *ppstm = &newStream->IStream_iface;
1027 IStream_AddRef(*ppstm);
1029 else
1031 return STG_E_INSUFFICIENTMEMORY;
1034 return StorageBaseImpl_Flush(This);
1037 /************************************************************************
1038 * Storage32BaseImpl_SetClass (IStorage)
1040 * This method will write the specified CLSID in the directory entry of this
1041 * storage.
1043 * See Windows documentation for more details on IStorage methods.
1045 static HRESULT WINAPI StorageBaseImpl_SetClass(
1046 IStorage* iface,
1047 REFCLSID clsid) /* [in] */
1049 StorageBaseImpl *This = impl_from_IStorage(iface);
1050 HRESULT hRes;
1051 DirEntry currentEntry;
1053 TRACE("(%p, %p)\n", iface, clsid);
1055 if (This->reverted)
1056 return STG_E_REVERTED;
1058 hRes = StorageBaseImpl_ReadDirEntry(This,
1059 This->storageDirEntry,
1060 &currentEntry);
1061 if (SUCCEEDED(hRes))
1063 currentEntry.clsid = *clsid;
1065 hRes = StorageBaseImpl_WriteDirEntry(This,
1066 This->storageDirEntry,
1067 &currentEntry);
1070 if (SUCCEEDED(hRes))
1071 hRes = StorageBaseImpl_Flush(This);
1073 return hRes;
1076 /************************************************************************
1077 ** Storage32Impl implementation
1080 /************************************************************************
1081 * Storage32BaseImpl_CreateStorage (IStorage)
1083 * This method will create the storage object within the provided storage.
1085 * See Windows documentation for more details on IStorage methods.
1087 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1088 IStorage* iface,
1089 const OLECHAR *pwcsName, /* [string][in] */
1090 DWORD grfMode, /* [in] */
1091 DWORD reserved1, /* [in] */
1092 DWORD reserved2, /* [in] */
1093 IStorage **ppstg) /* [out] */
1095 StorageBaseImpl* This = impl_from_IStorage(iface);
1097 DirEntry currentEntry;
1098 DirEntry newEntry;
1099 DirRef currentEntryRef;
1100 DirRef newEntryRef;
1101 HRESULT hr;
1103 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1104 iface, debugstr_w(pwcsName), grfMode,
1105 reserved1, reserved2, ppstg);
1107 if (ppstg == 0)
1108 return STG_E_INVALIDPOINTER;
1110 if (This->openFlags & STGM_SIMPLE)
1112 return STG_E_INVALIDFUNCTION;
1115 if (pwcsName == 0)
1116 return STG_E_INVALIDNAME;
1118 *ppstg = NULL;
1120 if ( FAILED( validateSTGM(grfMode) ) ||
1121 (grfMode & STGM_DELETEONRELEASE) )
1123 WARN("bad grfMode: 0x%x\n", grfMode);
1124 return STG_E_INVALIDFLAG;
1127 if (This->reverted)
1128 return STG_E_REVERTED;
1131 * Check that we're compatible with the parent's storage mode
1133 if ( !(This->openFlags & STGM_TRANSACTED) &&
1134 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1136 WARN("access denied\n");
1137 return STG_E_ACCESSDENIED;
1140 currentEntryRef = findElement(This,
1141 This->storageDirEntry,
1142 pwcsName,
1143 &currentEntry);
1145 if (currentEntryRef != DIRENTRY_NULL)
1148 * An element with this name already exists
1150 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1151 ((This->openFlags & STGM_TRANSACTED) ||
1152 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1154 hr = IStorage_DestroyElement(iface, pwcsName);
1155 if (FAILED(hr))
1156 return hr;
1158 else
1160 WARN("file already exists\n");
1161 return STG_E_FILEALREADYEXISTS;
1164 else if (!(This->openFlags & STGM_TRANSACTED) &&
1165 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1167 WARN("read-only storage\n");
1168 return STG_E_ACCESSDENIED;
1171 memset(&newEntry, 0, sizeof(DirEntry));
1173 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1175 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1177 FIXME("name too long\n");
1178 return STG_E_INVALIDNAME;
1181 strcpyW(newEntry.name, pwcsName);
1183 newEntry.stgType = STGTY_STORAGE;
1184 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1185 newEntry.size.u.LowPart = 0;
1186 newEntry.size.u.HighPart = 0;
1188 newEntry.leftChild = DIRENTRY_NULL;
1189 newEntry.rightChild = DIRENTRY_NULL;
1190 newEntry.dirRootEntry = DIRENTRY_NULL;
1192 /* call CoFileTime to get the current time
1193 newEntry.ctime
1194 newEntry.mtime
1197 /* newEntry.clsid */
1200 * Create a new directory entry for the storage
1202 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1203 if (FAILED(hr))
1204 return hr;
1207 * Insert the new directory entry into the parent storage's tree
1209 hr = insertIntoTree(
1210 This,
1211 This->storageDirEntry,
1212 newEntryRef);
1213 if (FAILED(hr))
1215 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1216 return hr;
1220 * Open it to get a pointer to return.
1222 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1224 if( (hr != S_OK) || (*ppstg == NULL))
1226 return hr;
1229 if (SUCCEEDED(hr))
1230 hr = StorageBaseImpl_Flush(This);
1232 return S_OK;
1236 /***************************************************************************
1238 * Internal Method
1240 * Reserve a directory entry in the file and initialize it.
1242 static HRESULT StorageImpl_CreateDirEntry(
1243 StorageBaseImpl *base,
1244 const DirEntry *newData,
1245 DirRef *index)
1247 StorageImpl *storage = (StorageImpl*)base;
1248 ULONG currentEntryIndex = 0;
1249 ULONG newEntryIndex = DIRENTRY_NULL;
1250 HRESULT hr = S_OK;
1251 BYTE currentData[RAW_DIRENTRY_SIZE];
1252 WORD sizeOfNameString;
1256 hr = StorageImpl_ReadRawDirEntry(storage,
1257 currentEntryIndex,
1258 currentData);
1260 if (SUCCEEDED(hr))
1262 StorageUtl_ReadWord(
1263 currentData,
1264 OFFSET_PS_NAMELENGTH,
1265 &sizeOfNameString);
1267 if (sizeOfNameString == 0)
1270 * The entry exists and is available, we found it.
1272 newEntryIndex = currentEntryIndex;
1275 else
1278 * We exhausted the directory entries, we will create more space below
1280 newEntryIndex = currentEntryIndex;
1282 currentEntryIndex++;
1284 } while (newEntryIndex == DIRENTRY_NULL);
1287 * grow the directory stream
1289 if (FAILED(hr))
1291 BYTE emptyData[RAW_DIRENTRY_SIZE];
1292 ULARGE_INTEGER newSize;
1293 ULONG entryIndex;
1294 ULONG lastEntry = 0;
1295 ULONG blockCount = 0;
1298 * obtain the new count of blocks in the directory stream
1300 blockCount = BlockChainStream_GetCount(
1301 storage->rootBlockChain)+1;
1304 * initialize the size used by the directory stream
1306 newSize.u.HighPart = 0;
1307 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1310 * add a block to the directory stream
1312 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1315 * memset the empty entry in order to initialize the unused newly
1316 * created entries
1318 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1321 * initialize them
1323 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1325 for(
1326 entryIndex = newEntryIndex + 1;
1327 entryIndex < lastEntry;
1328 entryIndex++)
1330 StorageImpl_WriteRawDirEntry(
1331 storage,
1332 entryIndex,
1333 emptyData);
1336 StorageImpl_SaveFileHeader(storage);
1339 UpdateRawDirEntry(currentData, newData);
1341 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1343 if (SUCCEEDED(hr))
1344 *index = newEntryIndex;
1346 return hr;
1349 /***************************************************************************
1351 * Internal Method
1353 * Mark a directory entry in the file as free.
1355 static HRESULT StorageImpl_DestroyDirEntry(
1356 StorageBaseImpl *base,
1357 DirRef index)
1359 BYTE emptyData[RAW_DIRENTRY_SIZE];
1360 StorageImpl *storage = (StorageImpl*)base;
1362 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1364 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1368 /****************************************************************************
1370 * Internal Method
1372 * Case insensitive comparison of DirEntry.name by first considering
1373 * their size.
1375 * Returns <0 when name1 < name2
1376 * >0 when name1 > name2
1377 * 0 when name1 == name2
1379 static LONG entryNameCmp(
1380 const OLECHAR *name1,
1381 const OLECHAR *name2)
1383 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1385 while (diff == 0 && *name1 != 0)
1388 * We compare the string themselves only when they are of the same length
1390 diff = toupperW(*name1++) - toupperW(*name2++);
1393 return diff;
1396 /****************************************************************************
1398 * Internal Method
1400 * Add a directory entry to a storage
1402 static HRESULT insertIntoTree(
1403 StorageBaseImpl *This,
1404 DirRef parentStorageIndex,
1405 DirRef newEntryIndex)
1407 DirEntry currentEntry;
1408 DirEntry newEntry;
1411 * Read the inserted entry
1413 StorageBaseImpl_ReadDirEntry(This,
1414 newEntryIndex,
1415 &newEntry);
1418 * Read the storage entry
1420 StorageBaseImpl_ReadDirEntry(This,
1421 parentStorageIndex,
1422 &currentEntry);
1424 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1427 * The root storage contains some element, therefore, start the research
1428 * for the appropriate location.
1430 BOOL found = FALSE;
1431 DirRef current, next, previous, currentEntryId;
1434 * Keep a reference to the root of the storage's element tree
1436 currentEntryId = currentEntry.dirRootEntry;
1439 * Read
1441 StorageBaseImpl_ReadDirEntry(This,
1442 currentEntry.dirRootEntry,
1443 &currentEntry);
1445 previous = currentEntry.leftChild;
1446 next = currentEntry.rightChild;
1447 current = currentEntryId;
1449 while (!found)
1451 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1453 if (diff < 0)
1455 if (previous != DIRENTRY_NULL)
1457 StorageBaseImpl_ReadDirEntry(This,
1458 previous,
1459 &currentEntry);
1460 current = previous;
1462 else
1464 currentEntry.leftChild = newEntryIndex;
1465 StorageBaseImpl_WriteDirEntry(This,
1466 current,
1467 &currentEntry);
1468 found = TRUE;
1471 else if (diff > 0)
1473 if (next != DIRENTRY_NULL)
1475 StorageBaseImpl_ReadDirEntry(This,
1476 next,
1477 &currentEntry);
1478 current = next;
1480 else
1482 currentEntry.rightChild = newEntryIndex;
1483 StorageBaseImpl_WriteDirEntry(This,
1484 current,
1485 &currentEntry);
1486 found = TRUE;
1489 else
1492 * Trying to insert an item with the same name in the
1493 * subtree structure.
1495 return STG_E_FILEALREADYEXISTS;
1498 previous = currentEntry.leftChild;
1499 next = currentEntry.rightChild;
1502 else
1505 * The storage is empty, make the new entry the root of its element tree
1507 currentEntry.dirRootEntry = newEntryIndex;
1508 StorageBaseImpl_WriteDirEntry(This,
1509 parentStorageIndex,
1510 &currentEntry);
1513 return S_OK;
1516 /****************************************************************************
1518 * Internal Method
1520 * Find and read the element of a storage with the given name.
1522 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1523 const OLECHAR *name, DirEntry *data)
1525 DirRef currentEntry;
1527 /* Read the storage entry to find the root of the tree. */
1528 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1530 currentEntry = data->dirRootEntry;
1532 while (currentEntry != DIRENTRY_NULL)
1534 LONG cmp;
1536 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1538 cmp = entryNameCmp(name, data->name);
1540 if (cmp == 0)
1541 /* found it */
1542 break;
1544 else if (cmp < 0)
1545 currentEntry = data->leftChild;
1547 else if (cmp > 0)
1548 currentEntry = data->rightChild;
1551 return currentEntry;
1554 /****************************************************************************
1556 * Internal Method
1558 * Find and read the binary tree parent of the element with the given name.
1560 * If there is no such element, find a place where it could be inserted and
1561 * return STG_E_FILENOTFOUND.
1563 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1564 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1565 ULONG *relation)
1567 DirRef childEntry;
1568 DirEntry childData;
1570 /* Read the storage entry to find the root of the tree. */
1571 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1573 *parentEntry = storageEntry;
1574 *relation = DIRENTRY_RELATION_DIR;
1576 childEntry = parentData->dirRootEntry;
1578 while (childEntry != DIRENTRY_NULL)
1580 LONG cmp;
1582 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1584 cmp = entryNameCmp(childName, childData.name);
1586 if (cmp == 0)
1587 /* found it */
1588 break;
1590 else if (cmp < 0)
1592 *parentData = childData;
1593 *parentEntry = childEntry;
1594 *relation = DIRENTRY_RELATION_PREVIOUS;
1596 childEntry = parentData->leftChild;
1599 else if (cmp > 0)
1601 *parentData = childData;
1602 *parentEntry = childEntry;
1603 *relation = DIRENTRY_RELATION_NEXT;
1605 childEntry = parentData->rightChild;
1609 if (childEntry == DIRENTRY_NULL)
1610 return STG_E_FILENOTFOUND;
1611 else
1612 return S_OK;
1616 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1617 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1618 SNB snbExclude, IStorage *pstgDest);
1620 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1621 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1622 SNB snbExclude, IStorage *pstgDest)
1624 DirEntry data;
1625 HRESULT hr;
1626 BOOL skip = FALSE;
1627 IStorage *pstgTmp;
1628 IStream *pstrChild, *pstrTmp;
1629 STATSTG strStat;
1631 if (srcEntry == DIRENTRY_NULL)
1632 return S_OK;
1634 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1636 if (FAILED(hr))
1637 return hr;
1639 if ( snbExclude )
1641 WCHAR **snb = snbExclude;
1643 while ( *snb != NULL && !skip )
1645 if ( lstrcmpW(data.name, *snb) == 0 )
1646 skip = TRUE;
1647 ++snb;
1651 if (!skip)
1653 if (data.stgType == STGTY_STORAGE && !skip_storage)
1656 * create a new storage in destination storage
1658 hr = IStorage_CreateStorage( pstgDest, data.name,
1659 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1660 0, 0,
1661 &pstgTmp );
1664 * if it already exist, don't create a new one use this one
1666 if (hr == STG_E_FILEALREADYEXISTS)
1668 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1669 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1670 NULL, 0, &pstgTmp );
1673 if (SUCCEEDED(hr))
1675 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1676 skip_stream, NULL, pstgTmp );
1678 IStorage_Release(pstgTmp);
1681 else if (data.stgType == STGTY_STREAM && !skip_stream)
1684 * create a new stream in destination storage. If the stream already
1685 * exist, it will be deleted and a new one will be created.
1687 hr = IStorage_CreateStream( pstgDest, data.name,
1688 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1689 0, 0, &pstrTmp );
1692 * open child stream storage. This operation must succeed even if the
1693 * stream is already open, so we use internal functions to do it.
1695 if (hr == S_OK)
1697 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1699 if (streamimpl)
1701 pstrChild = &streamimpl->IStream_iface;
1702 if (pstrChild)
1703 IStream_AddRef(pstrChild);
1705 else
1707 pstrChild = NULL;
1708 hr = E_OUTOFMEMORY;
1712 if (hr == S_OK)
1715 * Get the size of the source stream
1717 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1720 * Set the size of the destination stream.
1722 IStream_SetSize(pstrTmp, strStat.cbSize);
1725 * do the copy
1727 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1728 NULL, NULL );
1730 IStream_Release( pstrChild );
1733 IStream_Release( pstrTmp );
1737 /* copy siblings */
1738 if (SUCCEEDED(hr))
1739 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1740 skip_stream, snbExclude, pstgDest );
1742 if (SUCCEEDED(hr))
1743 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1744 skip_stream, snbExclude, pstgDest );
1746 return hr;
1749 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1750 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1751 SNB snbExclude, IStorage *pstgDest)
1753 DirEntry data;
1754 HRESULT hr;
1756 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1758 if (SUCCEEDED(hr))
1759 hr = IStorage_SetClass( pstgDest, &data.clsid );
1761 if (SUCCEEDED(hr))
1762 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1763 skip_stream, snbExclude, pstgDest );
1765 return hr;
1768 /*************************************************************************
1769 * CopyTo (IStorage)
1771 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1772 IStorage* iface,
1773 DWORD ciidExclude, /* [in] */
1774 const IID* rgiidExclude, /* [size_is][unique][in] */
1775 SNB snbExclude, /* [unique][in] */
1776 IStorage* pstgDest) /* [unique][in] */
1778 StorageBaseImpl *This = impl_from_IStorage(iface);
1780 BOOL skip_storage = FALSE, skip_stream = FALSE;
1781 DWORD i;
1783 TRACE("(%p, %d, %p, %p, %p)\n",
1784 iface, ciidExclude, rgiidExclude,
1785 snbExclude, pstgDest);
1787 if ( pstgDest == 0 )
1788 return STG_E_INVALIDPOINTER;
1790 for(i = 0; i < ciidExclude; ++i)
1792 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1793 skip_storage = TRUE;
1794 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1795 skip_stream = TRUE;
1796 else
1797 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1800 if (!skip_storage)
1802 /* Give up early if it looks like this would be infinitely recursive.
1803 * Oddly enough, this includes some cases that aren't really recursive, like
1804 * copying to a transacted child. */
1805 IStorage *pstgDestAncestor = pstgDest;
1806 IStorage *pstgDestAncestorChild = NULL;
1808 /* Go up the chain from the destination until we find the source storage. */
1809 while (pstgDestAncestor != iface) {
1810 pstgDestAncestorChild = pstgDest;
1812 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1814 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
1816 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
1818 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1820 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
1822 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
1824 else
1825 break;
1828 if (pstgDestAncestor == iface)
1830 BOOL fail = TRUE;
1832 if (pstgDestAncestorChild && snbExclude)
1834 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1835 DirEntry data;
1836 WCHAR **snb = snbExclude;
1838 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1840 while ( *snb != NULL && fail )
1842 if ( lstrcmpW(data.name, *snb) == 0 )
1843 fail = FALSE;
1844 ++snb;
1848 if (fail)
1849 return STG_E_ACCESSDENIED;
1853 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1854 skip_storage, skip_stream, snbExclude, pstgDest );
1857 /*************************************************************************
1858 * MoveElementTo (IStorage)
1860 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1861 IStorage* iface,
1862 const OLECHAR *pwcsName, /* [string][in] */
1863 IStorage *pstgDest, /* [unique][in] */
1864 const OLECHAR *pwcsNewName,/* [string][in] */
1865 DWORD grfFlags) /* [in] */
1867 FIXME("(%p %s %p %s %u): stub\n", iface,
1868 debugstr_w(pwcsName), pstgDest,
1869 debugstr_w(pwcsNewName), grfFlags);
1870 return E_NOTIMPL;
1873 /*************************************************************************
1874 * Commit (IStorage)
1876 * Ensures that any changes made to a storage object open in transacted mode
1877 * are reflected in the parent storage
1879 * In a non-transacted mode, this ensures all cached writes are completed.
1881 static HRESULT WINAPI StorageImpl_Commit(
1882 IStorage* iface,
1883 DWORD grfCommitFlags)/* [in] */
1885 StorageBaseImpl* This = impl_from_IStorage(iface);
1886 TRACE("(%p %d)\n", iface, grfCommitFlags);
1887 return StorageBaseImpl_Flush(This);
1890 /*************************************************************************
1891 * Revert (IStorage)
1893 * Discard all changes that have been made since the last commit operation
1895 static HRESULT WINAPI StorageImpl_Revert(
1896 IStorage* iface)
1898 TRACE("(%p)\n", iface);
1899 return S_OK;
1902 /*************************************************************************
1903 * DestroyElement (IStorage)
1905 * Strategy: This implementation is built this way for simplicity not for speed.
1906 * I always delete the topmost element of the enumeration and adjust
1907 * the deleted element pointer all the time. This takes longer to
1908 * do but allow to reinvoke DestroyElement whenever we encounter a
1909 * storage object. The optimisation resides in the usage of another
1910 * enumeration strategy that would give all the leaves of a storage
1911 * first. (postfix order)
1913 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1914 IStorage* iface,
1915 const OLECHAR *pwcsName)/* [string][in] */
1917 StorageBaseImpl *This = impl_from_IStorage(iface);
1919 HRESULT hr = S_OK;
1920 DirEntry entryToDelete;
1921 DirRef entryToDeleteRef;
1923 TRACE("(%p, %s)\n",
1924 iface, debugstr_w(pwcsName));
1926 if (pwcsName==NULL)
1927 return STG_E_INVALIDPOINTER;
1929 if (This->reverted)
1930 return STG_E_REVERTED;
1932 if ( !(This->openFlags & STGM_TRANSACTED) &&
1933 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1934 return STG_E_ACCESSDENIED;
1936 entryToDeleteRef = findElement(
1937 This,
1938 This->storageDirEntry,
1939 pwcsName,
1940 &entryToDelete);
1942 if ( entryToDeleteRef == DIRENTRY_NULL )
1944 return STG_E_FILENOTFOUND;
1947 if ( entryToDelete.stgType == STGTY_STORAGE )
1949 hr = deleteStorageContents(
1950 This,
1951 entryToDeleteRef,
1952 entryToDelete);
1954 else if ( entryToDelete.stgType == STGTY_STREAM )
1956 hr = deleteStreamContents(
1957 This,
1958 entryToDeleteRef,
1959 entryToDelete);
1962 if (hr!=S_OK)
1963 return hr;
1966 * Remove the entry from its parent storage
1968 hr = removeFromTree(
1969 This,
1970 This->storageDirEntry,
1971 entryToDeleteRef);
1974 * Invalidate the entry
1976 if (SUCCEEDED(hr))
1977 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1979 if (SUCCEEDED(hr))
1980 hr = StorageBaseImpl_Flush(This);
1982 return hr;
1986 /******************************************************************************
1987 * Internal stream list handlers
1990 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1992 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1993 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1996 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1998 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1999 list_remove(&(strm->StrmListEntry));
2002 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
2004 StgStreamImpl *strm;
2006 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
2008 if (strm->dirEntry == streamEntry)
2010 return TRUE;
2014 return FALSE;
2017 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2019 StorageInternalImpl *childstg;
2021 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2023 if (childstg->base.storageDirEntry == storageEntry)
2025 return TRUE;
2029 return FALSE;
2032 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2034 struct list *cur, *cur2;
2035 StgStreamImpl *strm=NULL;
2036 StorageInternalImpl *childstg=NULL;
2038 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2039 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2040 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2041 strm->parentStorage = NULL;
2042 list_remove(cur);
2045 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2046 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2047 StorageBaseImpl_Invalidate( &childstg->base );
2050 if (stg->transactedChild)
2052 StorageBaseImpl_Invalidate(stg->transactedChild);
2054 stg->transactedChild = NULL;
2059 /*********************************************************************
2061 * Internal Method
2063 * Delete the contents of a storage entry.
2066 static HRESULT deleteStorageContents(
2067 StorageBaseImpl *parentStorage,
2068 DirRef indexToDelete,
2069 DirEntry entryDataToDelete)
2071 IEnumSTATSTG *elements = 0;
2072 IStorage *childStorage = 0;
2073 STATSTG currentElement;
2074 HRESULT hr;
2075 HRESULT destroyHr = S_OK;
2076 StorageInternalImpl *stg, *stg2;
2078 /* Invalidate any open storage objects. */
2079 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2081 if (stg->base.storageDirEntry == indexToDelete)
2083 StorageBaseImpl_Invalidate(&stg->base);
2088 * Open the storage and enumerate it
2090 hr = IStorage_OpenStorage(
2091 &parentStorage->IStorage_iface,
2092 entryDataToDelete.name,
2094 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2097 &childStorage);
2099 if (hr != S_OK)
2101 return hr;
2105 * Enumerate the elements
2107 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2112 * Obtain the next element
2114 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2115 if (hr==S_OK)
2117 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2119 CoTaskMemFree(currentElement.pwcsName);
2123 * We need to Reset the enumeration every time because we delete elements
2124 * and the enumeration could be invalid
2126 IEnumSTATSTG_Reset(elements);
2128 } while ((hr == S_OK) && (destroyHr == S_OK));
2130 IStorage_Release(childStorage);
2131 IEnumSTATSTG_Release(elements);
2133 return destroyHr;
2136 /*********************************************************************
2138 * Internal Method
2140 * Perform the deletion of a stream's data
2143 static HRESULT deleteStreamContents(
2144 StorageBaseImpl *parentStorage,
2145 DirRef indexToDelete,
2146 DirEntry entryDataToDelete)
2148 IStream *pis;
2149 HRESULT hr;
2150 ULARGE_INTEGER size;
2151 StgStreamImpl *strm, *strm2;
2153 /* Invalidate any open stream objects. */
2154 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2156 if (strm->dirEntry == indexToDelete)
2158 TRACE("Stream deleted %p\n", strm);
2159 strm->parentStorage = NULL;
2160 list_remove(&strm->StrmListEntry);
2164 size.u.HighPart = 0;
2165 size.u.LowPart = 0;
2167 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2168 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2170 if (hr!=S_OK)
2172 return(hr);
2176 * Zap the stream
2178 hr = IStream_SetSize(pis, size);
2180 if(hr != S_OK)
2182 return hr;
2186 * Release the stream object.
2188 IStream_Release(pis);
2190 return S_OK;
2193 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2195 switch (relation)
2197 case DIRENTRY_RELATION_PREVIOUS:
2198 entry->leftChild = new_target;
2199 break;
2200 case DIRENTRY_RELATION_NEXT:
2201 entry->rightChild = new_target;
2202 break;
2203 case DIRENTRY_RELATION_DIR:
2204 entry->dirRootEntry = new_target;
2205 break;
2206 default:
2207 assert(0);
2211 /*************************************************************************
2213 * Internal Method
2215 * This method removes a directory entry from its parent storage tree without
2216 * freeing any resources attached to it.
2218 static HRESULT removeFromTree(
2219 StorageBaseImpl *This,
2220 DirRef parentStorageIndex,
2221 DirRef deletedIndex)
2223 DirEntry entryToDelete;
2224 DirEntry parentEntry;
2225 DirRef parentEntryRef;
2226 ULONG typeOfRelation;
2227 HRESULT hr;
2229 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2231 if (hr != S_OK)
2232 return hr;
2235 * Find the element that links to the one we want to delete.
2237 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2238 &parentEntry, &parentEntryRef, &typeOfRelation);
2240 if (hr != S_OK)
2241 return hr;
2243 if (entryToDelete.leftChild != DIRENTRY_NULL)
2246 * Replace the deleted entry with its left child
2248 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2250 hr = StorageBaseImpl_WriteDirEntry(
2251 This,
2252 parentEntryRef,
2253 &parentEntry);
2254 if(FAILED(hr))
2256 return hr;
2259 if (entryToDelete.rightChild != DIRENTRY_NULL)
2262 * We need to reinsert the right child somewhere. We already know it and
2263 * its children are greater than everything in the left tree, so we
2264 * insert it at the rightmost point in the left tree.
2266 DirRef newRightChildParent = entryToDelete.leftChild;
2267 DirEntry newRightChildParentEntry;
2271 hr = StorageBaseImpl_ReadDirEntry(
2272 This,
2273 newRightChildParent,
2274 &newRightChildParentEntry);
2275 if (FAILED(hr))
2277 return hr;
2280 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2281 newRightChildParent = newRightChildParentEntry.rightChild;
2282 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2284 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2286 hr = StorageBaseImpl_WriteDirEntry(
2287 This,
2288 newRightChildParent,
2289 &newRightChildParentEntry);
2290 if (FAILED(hr))
2292 return hr;
2296 else
2299 * Replace the deleted entry with its right child
2301 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2303 hr = StorageBaseImpl_WriteDirEntry(
2304 This,
2305 parentEntryRef,
2306 &parentEntry);
2307 if(FAILED(hr))
2309 return hr;
2313 return hr;
2317 /******************************************************************************
2318 * SetElementTimes (IStorage)
2320 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2321 IStorage* iface,
2322 const OLECHAR *pwcsName,/* [string][in] */
2323 const FILETIME *pctime, /* [in] */
2324 const FILETIME *patime, /* [in] */
2325 const FILETIME *pmtime) /* [in] */
2327 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2328 return S_OK;
2331 /******************************************************************************
2332 * SetStateBits (IStorage)
2334 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2335 IStorage* iface,
2336 DWORD grfStateBits,/* [in] */
2337 DWORD grfMask) /* [in] */
2339 StorageBaseImpl *This = impl_from_IStorage(iface);
2341 if (This->reverted)
2342 return STG_E_REVERTED;
2344 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2345 return S_OK;
2348 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2349 DirRef index, const DirEntry *data)
2351 StorageImpl *This = (StorageImpl*)base;
2352 return StorageImpl_WriteDirEntry(This, index, data);
2355 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2356 DirRef index, DirEntry *data)
2358 StorageImpl *This = (StorageImpl*)base;
2359 return StorageImpl_ReadDirEntry(This, index, data);
2362 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2364 int i;
2366 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2368 if (!This->blockChainCache[i])
2370 return &This->blockChainCache[i];
2374 i = This->blockChainToEvict;
2376 BlockChainStream_Destroy(This->blockChainCache[i]);
2377 This->blockChainCache[i] = NULL;
2379 This->blockChainToEvict++;
2380 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2381 This->blockChainToEvict = 0;
2383 return &This->blockChainCache[i];
2386 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2387 DirRef index)
2389 int i, free_index=-1;
2391 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2393 if (!This->blockChainCache[i])
2395 if (free_index == -1) free_index = i;
2397 else if (This->blockChainCache[i]->ownerDirEntry == index)
2399 return &This->blockChainCache[i];
2403 if (free_index == -1)
2405 free_index = This->blockChainToEvict;
2407 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2408 This->blockChainCache[free_index] = NULL;
2410 This->blockChainToEvict++;
2411 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2412 This->blockChainToEvict = 0;
2415 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2416 return &This->blockChainCache[free_index];
2419 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2421 int i;
2423 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2425 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2427 BlockChainStream_Destroy(This->blockChainCache[i]);
2428 This->blockChainCache[i] = NULL;
2429 return;
2434 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2435 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2437 StorageImpl *This = (StorageImpl*)base;
2438 DirEntry data;
2439 HRESULT hr;
2440 ULONG bytesToRead;
2442 hr = StorageImpl_ReadDirEntry(This, index, &data);
2443 if (FAILED(hr)) return hr;
2445 if (data.size.QuadPart == 0)
2447 *bytesRead = 0;
2448 return S_OK;
2451 if (offset.QuadPart + size > data.size.QuadPart)
2453 bytesToRead = data.size.QuadPart - offset.QuadPart;
2455 else
2457 bytesToRead = size;
2460 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2462 SmallBlockChainStream *stream;
2464 stream = SmallBlockChainStream_Construct(This, NULL, index);
2465 if (!stream) return E_OUTOFMEMORY;
2467 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2469 SmallBlockChainStream_Destroy(stream);
2471 return hr;
2473 else
2475 BlockChainStream *stream = NULL;
2477 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2478 if (!stream) return E_OUTOFMEMORY;
2480 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2482 return hr;
2486 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2487 ULARGE_INTEGER newsize)
2489 StorageImpl *This = (StorageImpl*)base;
2490 DirEntry data;
2491 HRESULT hr;
2492 SmallBlockChainStream *smallblock=NULL;
2493 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2495 hr = StorageImpl_ReadDirEntry(This, index, &data);
2496 if (FAILED(hr)) return hr;
2498 /* In simple mode keep the stream size above the small block limit */
2499 if (This->base.openFlags & STGM_SIMPLE)
2500 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2502 if (data.size.QuadPart == newsize.QuadPart)
2503 return S_OK;
2505 /* Create a block chain object of the appropriate type */
2506 if (data.size.QuadPart == 0)
2508 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2510 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2511 if (!smallblock) return E_OUTOFMEMORY;
2513 else
2515 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2516 bigblock = *pbigblock;
2517 if (!bigblock) return E_OUTOFMEMORY;
2520 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2522 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2523 if (!smallblock) return E_OUTOFMEMORY;
2525 else
2527 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2528 bigblock = *pbigblock;
2529 if (!bigblock) return E_OUTOFMEMORY;
2532 /* Change the block chain type if necessary. */
2533 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2535 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2536 if (!bigblock)
2538 SmallBlockChainStream_Destroy(smallblock);
2539 return E_FAIL;
2542 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2543 *pbigblock = bigblock;
2545 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2547 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2548 if (!smallblock)
2549 return E_FAIL;
2552 /* Set the size of the block chain. */
2553 if (smallblock)
2555 SmallBlockChainStream_SetSize(smallblock, newsize);
2556 SmallBlockChainStream_Destroy(smallblock);
2558 else
2560 BlockChainStream_SetSize(bigblock, newsize);
2563 /* Set the size in the directory entry. */
2564 hr = StorageImpl_ReadDirEntry(This, index, &data);
2565 if (SUCCEEDED(hr))
2567 data.size = newsize;
2569 hr = StorageImpl_WriteDirEntry(This, index, &data);
2571 return hr;
2574 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2575 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2577 StorageImpl *This = (StorageImpl*)base;
2578 DirEntry data;
2579 HRESULT hr;
2580 ULARGE_INTEGER newSize;
2582 hr = StorageImpl_ReadDirEntry(This, index, &data);
2583 if (FAILED(hr)) return hr;
2585 /* Grow the stream if necessary */
2586 newSize.QuadPart = 0;
2587 newSize.QuadPart = offset.QuadPart + size;
2589 if (newSize.QuadPart > data.size.QuadPart)
2591 hr = StorageImpl_StreamSetSize(base, index, newSize);
2592 if (FAILED(hr))
2593 return hr;
2595 hr = StorageImpl_ReadDirEntry(This, index, &data);
2596 if (FAILED(hr)) return hr;
2599 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2601 SmallBlockChainStream *stream;
2603 stream = SmallBlockChainStream_Construct(This, NULL, index);
2604 if (!stream) return E_OUTOFMEMORY;
2606 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2608 SmallBlockChainStream_Destroy(stream);
2610 return hr;
2612 else
2614 BlockChainStream *stream;
2616 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2617 if (!stream) return E_OUTOFMEMORY;
2619 return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2623 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2624 DirRef src)
2626 StorageImpl *This = (StorageImpl*)base;
2627 DirEntry dst_data, src_data;
2628 HRESULT hr;
2630 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2632 if (SUCCEEDED(hr))
2633 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2635 if (SUCCEEDED(hr))
2637 StorageImpl_DeleteCachedBlockChainStream(This, src);
2638 dst_data.startingBlock = src_data.startingBlock;
2639 dst_data.size = src_data.size;
2641 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2644 return hr;
2647 static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
2648 ULONG* result, BOOL refresh)
2650 StorageImpl *This = (StorageImpl*)base;
2651 HRESULT hr=S_OK;
2653 if (refresh)
2655 ULARGE_INTEGER offset;
2656 ULONG bytes_read;
2657 BYTE data[4];
2659 offset.u.HighPart = 0;
2660 offset.u.LowPart = OFFSET_TRANSACTIONSIG;
2661 hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
2663 if (SUCCEEDED(hr))
2665 /* FIXME: Throw out everything and reload the file if this changed. */
2666 StorageUtl_ReadDWord(data, 0, &This->transactionSig);
2670 *result = This->transactionSig;
2672 return hr;
2675 static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
2676 ULONG value)
2678 StorageImpl *This = (StorageImpl*)base;
2680 This->transactionSig = value;
2681 StorageImpl_SaveFileHeader(This);
2683 return S_OK;
2686 static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base)
2688 StorageImpl *This = (StorageImpl*)base;
2689 HRESULT hr;
2690 ULARGE_INTEGER offset, cb;
2692 /* Synchronous grab of both priority ranges, the commit lock, and the
2693 * lock-checking lock. */
2694 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
2695 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
2697 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
2699 if (hr == STG_E_INVALIDFUNCTION)
2700 hr = S_OK;
2702 return hr;
2705 static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base)
2707 StorageImpl *This = (StorageImpl*)base;
2708 HRESULT hr;
2709 ULARGE_INTEGER offset, cb;
2711 offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
2712 cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
2714 hr = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2716 if (hr == STG_E_INVALIDFUNCTION)
2717 hr = S_OK;
2719 return hr;
2722 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2724 StorageImpl *This = (StorageImpl*) iface;
2725 STATSTG statstg;
2726 HRESULT hr;
2728 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2730 *result = statstg.pwcsName;
2732 return hr;
2735 static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
2737 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2738 return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
2741 static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
2743 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2744 return IStorage_AddRef(&This->IStorage_iface);
2747 static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
2749 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2750 return IStorage_Release(&This->IStorage_iface);
2753 static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
2755 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2756 FIXME("(%p)->(%d): stub\n", This, timeout);
2757 return E_NOTIMPL;
2760 static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
2762 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2763 FIXME("(%p): stub\n", This);
2764 return E_NOTIMPL;
2767 static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
2769 StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
2770 FIXME("(%p): stub\n", This);
2771 return E_NOTIMPL;
2774 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
2776 directwriterlock_QueryInterface,
2777 directwriterlock_AddRef,
2778 directwriterlock_Release,
2779 directwriterlock_WaitForWriteAccess,
2780 directwriterlock_ReleaseWriteAccess,
2781 directwriterlock_HaveWriteAccess
2785 * Virtual function table for the IStorage32Impl class.
2787 static const IStorageVtbl Storage32Impl_Vtbl =
2789 StorageBaseImpl_QueryInterface,
2790 StorageBaseImpl_AddRef,
2791 StorageBaseImpl_Release,
2792 StorageBaseImpl_CreateStream,
2793 StorageBaseImpl_OpenStream,
2794 StorageBaseImpl_CreateStorage,
2795 StorageBaseImpl_OpenStorage,
2796 StorageBaseImpl_CopyTo,
2797 StorageBaseImpl_MoveElementTo,
2798 StorageImpl_Commit,
2799 StorageImpl_Revert,
2800 StorageBaseImpl_EnumElements,
2801 StorageBaseImpl_DestroyElement,
2802 StorageBaseImpl_RenameElement,
2803 StorageBaseImpl_SetElementTimes,
2804 StorageBaseImpl_SetClass,
2805 StorageBaseImpl_SetStateBits,
2806 StorageBaseImpl_Stat
2809 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2811 StorageImpl_Destroy,
2812 StorageImpl_Invalidate,
2813 StorageImpl_Flush,
2814 StorageImpl_GetFilename,
2815 StorageImpl_CreateDirEntry,
2816 StorageImpl_BaseWriteDirEntry,
2817 StorageImpl_BaseReadDirEntry,
2818 StorageImpl_DestroyDirEntry,
2819 StorageImpl_StreamReadAt,
2820 StorageImpl_StreamWriteAt,
2821 StorageImpl_StreamSetSize,
2822 StorageImpl_StreamLink,
2823 StorageImpl_GetTransactionSig,
2824 StorageImpl_SetTransactionSig,
2825 StorageImpl_LockTransaction,
2826 StorageImpl_UnlockTransaction
2829 static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
2830 ULARGE_INTEGER cb, DWORD dwLockType)
2832 HRESULT hr;
2834 /* potential optimization: if we have an HFILE use LockFileEx in blocking mode directly */
2838 int delay=0;
2840 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
2842 if (hr == STG_E_ACCESSDENIED)
2844 Sleep(delay);
2845 if (delay < 150) delay++;
2847 } while (hr == STG_E_ACCESSDENIED);
2849 return hr;
2852 static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
2853 ULONG end, HRESULT fail_hr)
2855 HRESULT hr;
2856 ULARGE_INTEGER offset, cb;
2858 offset.QuadPart = start;
2859 cb.QuadPart = 1 + end - start;
2861 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2862 if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2864 if (hr == STG_E_ACCESSDENIED)
2865 return fail_hr;
2866 else
2867 return S_OK;
2870 static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
2872 HRESULT hr=S_OK;
2873 int i, j;
2874 ULARGE_INTEGER offset, cb;
2876 cb.QuadPart = 1;
2878 for (i=start; i<=end; i++)
2880 offset.QuadPart = i;
2881 hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2882 if (hr != STG_E_ACCESSDENIED)
2883 break;
2886 if (SUCCEEDED(hr))
2888 for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
2890 if (This->locked_bytes[j] == 0)
2892 This->locked_bytes[j] = i;
2893 break;
2898 return hr;
2901 static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
2903 HRESULT hr;
2904 ULARGE_INTEGER offset;
2905 ULARGE_INTEGER cb;
2906 DWORD share_mode = STGM_SHARE_MODE(openFlags);
2908 /* Wrap all other locking inside a single lock so we can check ranges safely */
2909 offset.QuadPart = RANGELOCK_CHECKLOCKS;
2910 cb.QuadPart = 1;
2911 hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
2913 /* If the ILockBytes doesn't support locking that's ok. */
2914 if (FAILED(hr)) return S_OK;
2916 hr = S_OK;
2918 /* First check for any conflicting locks. */
2919 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2920 hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
2922 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2923 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
2925 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
2926 hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
2928 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
2929 hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
2931 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
2932 hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
2934 /* Then grab our locks. */
2935 if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
2937 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
2938 if (SUCCEEDED(hr))
2939 hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
2942 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
2943 hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
2945 if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
2946 hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
2948 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
2949 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
2951 if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
2952 hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
2954 offset.QuadPart = RANGELOCK_CHECKLOCKS;
2955 cb.QuadPart = 1;
2956 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
2958 return hr;
2961 static HRESULT StorageImpl_Construct(
2962 HANDLE hFile,
2963 LPCOLESTR pwcsName,
2964 ILockBytes* pLkbyt,
2965 DWORD openFlags,
2966 BOOL fileBased,
2967 BOOL create,
2968 ULONG sector_size,
2969 StorageImpl** result)
2971 StorageImpl* This;
2972 HRESULT hr = S_OK;
2973 DirEntry currentEntry;
2974 DirRef currentEntryRef;
2976 if ( FAILED( validateSTGM(openFlags) ))
2977 return STG_E_INVALIDFLAG;
2979 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2980 if (!This)
2981 return E_OUTOFMEMORY;
2983 memset(This, 0, sizeof(StorageImpl));
2985 list_init(&This->base.strmHead);
2987 list_init(&This->base.storageHead);
2989 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
2990 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
2991 This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
2992 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2993 This->base.openFlags = (openFlags & ~STGM_CREATE);
2994 This->base.ref = 1;
2995 This->base.create = create;
2997 if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
2998 This->base.lockingrole = SWMR_Writer;
2999 else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
3000 This->base.lockingrole = SWMR_Reader;
3001 else
3002 This->base.lockingrole = SWMR_None;
3004 This->base.reverted = FALSE;
3007 * Initialize the big block cache.
3009 This->bigBlockSize = sector_size;
3010 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
3011 if (hFile)
3012 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
3013 else
3015 This->lockBytes = pLkbyt;
3016 ILockBytes_AddRef(pLkbyt);
3019 if (FAILED(hr))
3020 goto end;
3022 hr = StorageImpl_GrabLocks(This, openFlags);
3024 if (FAILED(hr))
3025 goto end;
3027 if (create)
3029 ULARGE_INTEGER size;
3030 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
3032 /* Discard any existing data. */
3033 size.QuadPart = 0;
3034 ILockBytes_SetSize(This->lockBytes, size);
3037 * Initialize all header variables:
3038 * - The big block depot consists of one block and it is at block 0
3039 * - The directory table starts at block 1
3040 * - There is no small block depot
3042 memset( This->bigBlockDepotStart,
3043 BLOCK_UNUSED,
3044 sizeof(This->bigBlockDepotStart));
3046 This->bigBlockDepotCount = 1;
3047 This->bigBlockDepotStart[0] = 0;
3048 This->rootStartBlock = 1;
3049 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
3050 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
3051 if (sector_size == 4096)
3052 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
3053 else
3054 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
3055 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
3056 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
3057 This->extBigBlockDepotCount = 0;
3059 StorageImpl_SaveFileHeader(This);
3062 * Add one block for the big block depot and one block for the directory table
3064 size.u.HighPart = 0;
3065 size.u.LowPart = This->bigBlockSize * 3;
3066 ILockBytes_SetSize(This->lockBytes, size);
3069 * Initialize the big block depot
3071 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3072 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
3073 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
3074 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
3076 else
3079 * Load the header for the file.
3081 hr = StorageImpl_LoadFileHeader(This);
3083 if (FAILED(hr))
3085 goto end;
3090 * There is no block depot cached yet.
3092 This->indexBlockDepotCached = 0xFFFFFFFF;
3093 This->indexExtBlockDepotCached = 0xFFFFFFFF;
3096 * Start searching for free blocks with block 0.
3098 This->prevFreeBlock = 0;
3100 This->firstFreeSmallBlock = 0;
3102 /* Read the extended big block depot locations. */
3103 if (This->extBigBlockDepotCount != 0)
3105 ULONG current_block = This->extBigBlockDepotStart;
3106 ULONG cache_size = This->extBigBlockDepotCount * 2;
3107 ULONG i;
3109 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
3110 if (!This->extBigBlockDepotLocations)
3112 hr = E_OUTOFMEMORY;
3113 goto end;
3116 This->extBigBlockDepotLocationsSize = cache_size;
3118 for (i=0; i<This->extBigBlockDepotCount; i++)
3120 if (current_block == BLOCK_END_OF_CHAIN)
3122 WARN("File has too few extended big block depot blocks.\n");
3123 hr = STG_E_DOCFILECORRUPT;
3124 goto end;
3126 This->extBigBlockDepotLocations[i] = current_block;
3127 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
3130 else
3132 This->extBigBlockDepotLocations = NULL;
3133 This->extBigBlockDepotLocationsSize = 0;
3137 * Create the block chain abstractions.
3139 if(!(This->rootBlockChain =
3140 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
3142 hr = STG_E_READFAULT;
3143 goto end;
3146 if(!(This->smallBlockDepotChain =
3147 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
3148 DIRENTRY_NULL)))
3150 hr = STG_E_READFAULT;
3151 goto end;
3155 * Write the root storage entry (memory only)
3157 if (create)
3159 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
3160 DirEntry rootEntry;
3162 * Initialize the directory table
3164 memset(&rootEntry, 0, sizeof(rootEntry));
3165 strcpyW(rootEntry.name, rootentryW);
3166 rootEntry.sizeOfNameString = sizeof(rootentryW);
3167 rootEntry.stgType = STGTY_ROOT;
3168 rootEntry.leftChild = DIRENTRY_NULL;
3169 rootEntry.rightChild = DIRENTRY_NULL;
3170 rootEntry.dirRootEntry = DIRENTRY_NULL;
3171 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
3172 rootEntry.size.u.HighPart = 0;
3173 rootEntry.size.u.LowPart = 0;
3175 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
3179 * Find the ID of the root storage.
3181 currentEntryRef = 0;
3185 hr = StorageImpl_ReadDirEntry(
3186 This,
3187 currentEntryRef,
3188 &currentEntry);
3190 if (SUCCEEDED(hr))
3192 if ( (currentEntry.sizeOfNameString != 0 ) &&
3193 (currentEntry.stgType == STGTY_ROOT) )
3195 This->base.storageDirEntry = currentEntryRef;
3199 currentEntryRef++;
3201 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
3203 if (FAILED(hr))
3205 hr = STG_E_READFAULT;
3206 goto end;
3210 * Create the block chain abstraction for the small block root chain.
3212 if(!(This->smallBlockRootChain =
3213 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
3215 hr = STG_E_READFAULT;
3218 end:
3219 if (FAILED(hr))
3221 IStorage_Release(&This->base.IStorage_iface);
3222 *result = NULL;
3224 else
3226 StorageImpl_Flush(&This->base);
3227 *result = This;
3230 return hr;
3233 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
3235 StorageImpl *This = (StorageImpl*) iface;
3237 StorageBaseImpl_DeleteAll(&This->base);
3239 This->base.reverted = TRUE;
3242 static void StorageImpl_Destroy(StorageBaseImpl* iface)
3244 StorageImpl *This = (StorageImpl*) iface;
3245 int i;
3246 TRACE("(%p)\n", This);
3248 StorageImpl_Flush(iface);
3250 StorageImpl_Invalidate(iface);
3252 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3254 BlockChainStream_Destroy(This->smallBlockRootChain);
3255 BlockChainStream_Destroy(This->rootBlockChain);
3256 BlockChainStream_Destroy(This->smallBlockDepotChain);
3258 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
3259 BlockChainStream_Destroy(This->blockChainCache[i]);
3261 for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
3263 ULARGE_INTEGER offset, cb;
3264 cb.QuadPart = 1;
3265 if (This->locked_bytes[i] != 0)
3267 offset.QuadPart = This->locked_bytes[i];
3268 ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
3272 if (This->lockBytes)
3273 ILockBytes_Release(This->lockBytes);
3274 HeapFree(GetProcessHeap(), 0, This);
3277 static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
3279 StorageImpl *This = (StorageImpl*)storage;
3280 int i;
3281 HRESULT hr;
3282 TRACE("(%p)\n", This);
3284 hr = BlockChainStream_Flush(This->smallBlockRootChain);
3286 if (SUCCEEDED(hr))
3287 hr = BlockChainStream_Flush(This->rootBlockChain);
3289 if (SUCCEEDED(hr))
3290 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
3292 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
3293 if (This->blockChainCache[i])
3294 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3296 if (SUCCEEDED(hr))
3297 hr = ILockBytes_Flush(This->lockBytes);
3299 return hr;
3302 /******************************************************************************
3303 * Storage32Impl_GetNextFreeBigBlock
3305 * Returns the index of the next free big block.
3306 * If the big block depot is filled, this method will enlarge it.
3309 static ULONG StorageImpl_GetNextFreeBigBlock(
3310 StorageImpl* This)
3312 ULONG depotBlockIndexPos;
3313 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3314 ULONG depotBlockOffset;
3315 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3316 ULONG nextBlockIndex = BLOCK_SPECIAL;
3317 int depotIndex = 0;
3318 ULONG freeBlock = BLOCK_UNUSED;
3319 ULONG read;
3320 ULARGE_INTEGER neededSize;
3321 STATSTG statstg;
3323 depotIndex = This->prevFreeBlock / blocksPerDepot;
3324 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3327 * Scan the entire big block depot until we find a block marked free
3329 while (nextBlockIndex != BLOCK_UNUSED)
3331 if (depotIndex < COUNT_BBDEPOTINHEADER)
3333 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3336 * Grow the primary depot.
3338 if (depotBlockIndexPos == BLOCK_UNUSED)
3340 depotBlockIndexPos = depotIndex*blocksPerDepot;
3343 * Add a block depot.
3345 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3346 This->bigBlockDepotCount++;
3347 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3350 * Flag it as a block depot.
3352 StorageImpl_SetNextBlockInChain(This,
3353 depotBlockIndexPos,
3354 BLOCK_SPECIAL);
3356 /* Save new header information.
3358 StorageImpl_SaveFileHeader(This);
3361 else
3363 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3365 if (depotBlockIndexPos == BLOCK_UNUSED)
3368 * Grow the extended depot.
3370 ULONG extIndex = BLOCK_UNUSED;
3371 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3372 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3374 if (extBlockOffset == 0)
3376 /* We need an extended block.
3378 extIndex = Storage32Impl_AddExtBlockDepot(This);
3379 This->extBigBlockDepotCount++;
3380 depotBlockIndexPos = extIndex + 1;
3382 else
3383 depotBlockIndexPos = depotIndex * blocksPerDepot;
3386 * Add a block depot and mark it in the extended block.
3388 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
3389 This->bigBlockDepotCount++;
3390 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3392 /* Flag the block depot.
3394 StorageImpl_SetNextBlockInChain(This,
3395 depotBlockIndexPos,
3396 BLOCK_SPECIAL);
3398 /* If necessary, flag the extended depot block.
3400 if (extIndex != BLOCK_UNUSED)
3401 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3403 /* Save header information.
3405 StorageImpl_SaveFileHeader(This);
3409 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3411 if (read)
3413 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3414 ( nextBlockIndex != BLOCK_UNUSED))
3416 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3418 if (nextBlockIndex == BLOCK_UNUSED)
3420 freeBlock = (depotIndex * blocksPerDepot) +
3421 (depotBlockOffset/sizeof(ULONG));
3424 depotBlockOffset += sizeof(ULONG);
3428 depotIndex++;
3429 depotBlockOffset = 0;
3433 * make sure that the block physically exists before using it
3435 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3437 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3439 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3440 ILockBytes_SetSize(This->lockBytes, neededSize);
3442 This->prevFreeBlock = freeBlock;
3444 return freeBlock;
3447 /******************************************************************************
3448 * Storage32Impl_AddBlockDepot
3450 * This will create a depot block, essentially it is a block initialized
3451 * to BLOCK_UNUSEDs.
3453 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3455 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3456 ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3457 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3458 ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3461 * Initialize blocks as free
3463 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3465 /* Reserve the range lock sector */
3466 if (depotIndex == rangeLockDepot)
3468 ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3471 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3474 /******************************************************************************
3475 * Storage32Impl_GetExtDepotBlock
3477 * Returns the index of the block that corresponds to the specified depot
3478 * index. This method is only for depot indexes equal or greater than
3479 * COUNT_BBDEPOTINHEADER.
3481 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3483 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3484 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3485 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3486 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3487 ULONG blockIndex = BLOCK_UNUSED;
3488 ULONG extBlockIndex;
3489 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3490 int index, num_blocks;
3492 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3494 if (extBlockCount >= This->extBigBlockDepotCount)
3495 return BLOCK_UNUSED;
3497 if (This->indexExtBlockDepotCached != extBlockCount)
3499 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3501 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3503 num_blocks = This->bigBlockSize / 4;
3505 for (index = 0; index < num_blocks; index++)
3507 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3508 This->extBlockDepotCached[index] = blockIndex;
3511 This->indexExtBlockDepotCached = extBlockCount;
3514 blockIndex = This->extBlockDepotCached[extBlockOffset];
3516 return blockIndex;
3519 /******************************************************************************
3520 * Storage32Impl_SetExtDepotBlock
3522 * Associates the specified block index to the specified depot index.
3523 * This method is only for depot indexes equal or greater than
3524 * COUNT_BBDEPOTINHEADER.
3526 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3528 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3529 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3530 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3531 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3532 ULONG extBlockIndex;
3534 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3536 assert(extBlockCount < This->extBigBlockDepotCount);
3538 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3540 if (extBlockIndex != BLOCK_UNUSED)
3542 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3543 extBlockOffset * sizeof(ULONG),
3544 blockIndex);
3547 if (This->indexExtBlockDepotCached == extBlockCount)
3549 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3553 /******************************************************************************
3554 * Storage32Impl_AddExtBlockDepot
3556 * Creates an extended depot block.
3558 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3560 ULONG numExtBlocks = This->extBigBlockDepotCount;
3561 ULONG nextExtBlock = This->extBigBlockDepotStart;
3562 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3563 ULONG index = BLOCK_UNUSED;
3564 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3565 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3566 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3568 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3569 blocksPerDepotBlock;
3571 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3574 * The first extended block.
3576 This->extBigBlockDepotStart = index;
3578 else
3581 * Find the last existing extended block.
3583 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3586 * Add the new extended block to the chain.
3588 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3589 index);
3593 * Initialize this block.
3595 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3596 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3598 /* Add the block to our cache. */
3599 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3601 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3602 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3604 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3605 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3607 This->extBigBlockDepotLocations = new_cache;
3608 This->extBigBlockDepotLocationsSize = new_cache_size;
3610 This->extBigBlockDepotLocations[numExtBlocks] = index;
3612 return index;
3615 /******************************************************************************
3616 * Storage32Impl_FreeBigBlock
3618 * This method will flag the specified block as free in the big block depot.
3620 static void StorageImpl_FreeBigBlock(
3621 StorageImpl* This,
3622 ULONG blockIndex)
3624 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3626 if (blockIndex < This->prevFreeBlock)
3627 This->prevFreeBlock = blockIndex;
3630 /************************************************************************
3631 * Storage32Impl_GetNextBlockInChain
3633 * This method will retrieve the block index of the next big block in
3634 * in the chain.
3636 * Params: This - Pointer to the Storage object.
3637 * blockIndex - Index of the block to retrieve the chain
3638 * for.
3639 * nextBlockIndex - receives the return value.
3641 * Returns: This method returns the index of the next block in the chain.
3642 * It will return the constants:
3643 * BLOCK_SPECIAL - If the block given was not part of a
3644 * chain.
3645 * BLOCK_END_OF_CHAIN - If the block given was the last in
3646 * a chain.
3647 * BLOCK_UNUSED - If the block given was not past of a chain
3648 * and is available.
3649 * BLOCK_EXTBBDEPOT - This block is part of the extended
3650 * big block depot.
3652 * See Windows documentation for more details on IStorage methods.
3654 static HRESULT StorageImpl_GetNextBlockInChain(
3655 StorageImpl* This,
3656 ULONG blockIndex,
3657 ULONG* nextBlockIndex)
3659 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3660 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3661 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3662 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3663 ULONG read;
3664 ULONG depotBlockIndexPos;
3665 int index, num_blocks;
3667 *nextBlockIndex = BLOCK_SPECIAL;
3669 if(depotBlockCount >= This->bigBlockDepotCount)
3671 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3672 This->bigBlockDepotCount);
3673 return STG_E_READFAULT;
3677 * Cache the currently accessed depot block.
3679 if (depotBlockCount != This->indexBlockDepotCached)
3681 This->indexBlockDepotCached = depotBlockCount;
3683 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3685 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3687 else
3690 * We have to look in the extended depot.
3692 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3695 StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
3697 if (!read)
3698 return STG_E_READFAULT;
3700 num_blocks = This->bigBlockSize / 4;
3702 for (index = 0; index < num_blocks; index++)
3704 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3705 This->blockDepotCached[index] = *nextBlockIndex;
3709 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3711 return S_OK;
3714 /******************************************************************************
3715 * Storage32Impl_GetNextExtendedBlock
3717 * Given an extended block this method will return the next extended block.
3719 * NOTES:
3720 * The last ULONG of an extended block is the block index of the next
3721 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3722 * depot.
3724 * Return values:
3725 * - The index of the next extended block
3726 * - BLOCK_UNUSED: there is no next extended block.
3727 * - Any other return values denotes failure.
3729 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3731 ULONG nextBlockIndex = BLOCK_SPECIAL;
3732 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3734 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3735 &nextBlockIndex);
3737 return nextBlockIndex;
3740 /******************************************************************************
3741 * Storage32Impl_SetNextBlockInChain
3743 * This method will write the index of the specified block's next block
3744 * in the big block depot.
3746 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3747 * do the following
3749 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3750 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3751 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3754 static void StorageImpl_SetNextBlockInChain(
3755 StorageImpl* This,
3756 ULONG blockIndex,
3757 ULONG nextBlock)
3759 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3760 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3761 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3762 ULONG depotBlockIndexPos;
3764 assert(depotBlockCount < This->bigBlockDepotCount);
3765 assert(blockIndex != nextBlock);
3767 if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
3768 /* This should never happen (storage file format spec forbids it), but
3769 * older versions of Wine may have generated broken files. We don't want to
3770 * assert and potentially lose data, but we do want to know if this ever
3771 * happens in a newly-created file. */
3772 ERR("Using range lock page\n");
3774 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3776 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3778 else
3781 * We have to look in the extended depot.
3783 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3786 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3787 nextBlock);
3789 * Update the cached block depot, if necessary.
3791 if (depotBlockCount == This->indexBlockDepotCached)
3793 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3797 /******************************************************************************
3798 * Storage32Impl_LoadFileHeader
3800 * This method will read in the file header
3802 static HRESULT StorageImpl_LoadFileHeader(
3803 StorageImpl* This)
3805 HRESULT hr;
3806 BYTE headerBigBlock[HEADER_SIZE];
3807 int index;
3808 ULARGE_INTEGER offset;
3809 DWORD bytes_read;
3811 TRACE("\n");
3813 * Get a pointer to the big block of data containing the header.
3815 offset.u.HighPart = 0;
3816 offset.u.LowPart = 0;
3817 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3818 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3819 hr = STG_E_FILENOTFOUND;
3822 * Extract the information from the header.
3824 if (SUCCEEDED(hr))
3827 * Check for the "magic number" signature and return an error if it is not
3828 * found.
3830 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3832 return STG_E_OLDFORMAT;
3835 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3837 return STG_E_INVALIDHEADER;
3840 StorageUtl_ReadWord(
3841 headerBigBlock,
3842 OFFSET_BIGBLOCKSIZEBITS,
3843 &This->bigBlockSizeBits);
3845 StorageUtl_ReadWord(
3846 headerBigBlock,
3847 OFFSET_SMALLBLOCKSIZEBITS,
3848 &This->smallBlockSizeBits);
3850 StorageUtl_ReadDWord(
3851 headerBigBlock,
3852 OFFSET_BBDEPOTCOUNT,
3853 &This->bigBlockDepotCount);
3855 StorageUtl_ReadDWord(
3856 headerBigBlock,
3857 OFFSET_ROOTSTARTBLOCK,
3858 &This->rootStartBlock);
3860 StorageUtl_ReadDWord(
3861 headerBigBlock,
3862 OFFSET_TRANSACTIONSIG,
3863 &This->transactionSig);
3865 StorageUtl_ReadDWord(
3866 headerBigBlock,
3867 OFFSET_SMALLBLOCKLIMIT,
3868 &This->smallBlockLimit);
3870 StorageUtl_ReadDWord(
3871 headerBigBlock,
3872 OFFSET_SBDEPOTSTART,
3873 &This->smallBlockDepotStart);
3875 StorageUtl_ReadDWord(
3876 headerBigBlock,
3877 OFFSET_EXTBBDEPOTSTART,
3878 &This->extBigBlockDepotStart);
3880 StorageUtl_ReadDWord(
3881 headerBigBlock,
3882 OFFSET_EXTBBDEPOTCOUNT,
3883 &This->extBigBlockDepotCount);
3885 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3887 StorageUtl_ReadDWord(
3888 headerBigBlock,
3889 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3890 &(This->bigBlockDepotStart[index]));
3894 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3896 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3897 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3900 * Right now, the code is making some assumptions about the size of the
3901 * blocks, just make sure they are what we're expecting.
3903 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3904 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3905 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3907 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3908 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3909 hr = STG_E_INVALIDHEADER;
3911 else
3912 hr = S_OK;
3915 return hr;
3918 /******************************************************************************
3919 * Storage32Impl_SaveFileHeader
3921 * This method will save to the file the header
3923 static void StorageImpl_SaveFileHeader(
3924 StorageImpl* This)
3926 BYTE headerBigBlock[HEADER_SIZE];
3927 int index;
3928 HRESULT hr;
3929 ULARGE_INTEGER offset;
3930 DWORD bytes_read, bytes_written;
3931 DWORD major_version, dirsectorcount;
3934 * Get a pointer to the big block of data containing the header.
3936 offset.u.HighPart = 0;
3937 offset.u.LowPart = 0;
3938 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3939 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3940 hr = STG_E_FILENOTFOUND;
3942 if (This->bigBlockSizeBits == 0x9)
3943 major_version = 3;
3944 else if (This->bigBlockSizeBits == 0xc)
3945 major_version = 4;
3946 else
3948 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3949 major_version = 4;
3953 * If the block read failed, the file is probably new.
3955 if (FAILED(hr))
3958 * Initialize for all unknown fields.
3960 memset(headerBigBlock, 0, HEADER_SIZE);
3963 * Initialize the magic number.
3965 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3969 * Write the information to the header.
3971 StorageUtl_WriteWord(
3972 headerBigBlock,
3973 OFFSET_MINORVERSION,
3974 0x3e);
3976 StorageUtl_WriteWord(
3977 headerBigBlock,
3978 OFFSET_MAJORVERSION,
3979 major_version);
3981 StorageUtl_WriteWord(
3982 headerBigBlock,
3983 OFFSET_BYTEORDERMARKER,
3984 (WORD)-2);
3986 StorageUtl_WriteWord(
3987 headerBigBlock,
3988 OFFSET_BIGBLOCKSIZEBITS,
3989 This->bigBlockSizeBits);
3991 StorageUtl_WriteWord(
3992 headerBigBlock,
3993 OFFSET_SMALLBLOCKSIZEBITS,
3994 This->smallBlockSizeBits);
3996 if (major_version >= 4)
3998 if (This->rootBlockChain)
3999 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
4000 else
4001 /* This file is being created, and it will start out with one block. */
4002 dirsectorcount = 1;
4004 else
4005 /* This field must be 0 in versions older than 4 */
4006 dirsectorcount = 0;
4008 StorageUtl_WriteDWord(
4009 headerBigBlock,
4010 OFFSET_DIRSECTORCOUNT,
4011 dirsectorcount);
4013 StorageUtl_WriteDWord(
4014 headerBigBlock,
4015 OFFSET_BBDEPOTCOUNT,
4016 This->bigBlockDepotCount);
4018 StorageUtl_WriteDWord(
4019 headerBigBlock,
4020 OFFSET_ROOTSTARTBLOCK,
4021 This->rootStartBlock);
4023 StorageUtl_WriteDWord(
4024 headerBigBlock,
4025 OFFSET_TRANSACTIONSIG,
4026 This->transactionSig);
4028 StorageUtl_WriteDWord(
4029 headerBigBlock,
4030 OFFSET_SMALLBLOCKLIMIT,
4031 This->smallBlockLimit);
4033 StorageUtl_WriteDWord(
4034 headerBigBlock,
4035 OFFSET_SBDEPOTSTART,
4036 This->smallBlockDepotStart);
4038 StorageUtl_WriteDWord(
4039 headerBigBlock,
4040 OFFSET_SBDEPOTCOUNT,
4041 This->smallBlockDepotChain ?
4042 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
4044 StorageUtl_WriteDWord(
4045 headerBigBlock,
4046 OFFSET_EXTBBDEPOTSTART,
4047 This->extBigBlockDepotStart);
4049 StorageUtl_WriteDWord(
4050 headerBigBlock,
4051 OFFSET_EXTBBDEPOTCOUNT,
4052 This->extBigBlockDepotCount);
4054 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
4056 StorageUtl_WriteDWord(
4057 headerBigBlock,
4058 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
4059 (This->bigBlockDepotStart[index]));
4063 * Write the big block back to the file.
4065 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
4068 /******************************************************************************
4069 * StorageImpl_ReadRawDirEntry
4071 * This method will read the raw data from a directory entry in the file.
4073 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4075 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
4077 ULARGE_INTEGER offset;
4078 HRESULT hr;
4079 ULONG bytesRead;
4081 offset.u.HighPart = 0;
4082 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
4084 hr = BlockChainStream_ReadAt(
4085 This->rootBlockChain,
4086 offset,
4087 RAW_DIRENTRY_SIZE,
4088 buffer,
4089 &bytesRead);
4091 if (bytesRead != RAW_DIRENTRY_SIZE)
4092 return STG_E_READFAULT;
4094 return hr;
4097 /******************************************************************************
4098 * StorageImpl_WriteRawDirEntry
4100 * This method will write the raw data from a directory entry in the file.
4102 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4104 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
4106 ULARGE_INTEGER offset;
4107 ULONG bytesRead;
4109 offset.u.HighPart = 0;
4110 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
4112 return BlockChainStream_WriteAt(
4113 This->rootBlockChain,
4114 offset,
4115 RAW_DIRENTRY_SIZE,
4116 buffer,
4117 &bytesRead);
4120 /******************************************************************************
4121 * UpdateRawDirEntry
4123 * Update raw directory entry data from the fields in newData.
4125 * buffer must be RAW_DIRENTRY_SIZE bytes long.
4127 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
4129 memset(buffer, 0, RAW_DIRENTRY_SIZE);
4131 memcpy(
4132 buffer + OFFSET_PS_NAME,
4133 newData->name,
4134 DIRENTRY_NAME_BUFFER_LEN );
4136 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
4138 StorageUtl_WriteWord(
4139 buffer,
4140 OFFSET_PS_NAMELENGTH,
4141 newData->sizeOfNameString);
4143 StorageUtl_WriteDWord(
4144 buffer,
4145 OFFSET_PS_LEFTCHILD,
4146 newData->leftChild);
4148 StorageUtl_WriteDWord(
4149 buffer,
4150 OFFSET_PS_RIGHTCHILD,
4151 newData->rightChild);
4153 StorageUtl_WriteDWord(
4154 buffer,
4155 OFFSET_PS_DIRROOT,
4156 newData->dirRootEntry);
4158 StorageUtl_WriteGUID(
4159 buffer,
4160 OFFSET_PS_GUID,
4161 &newData->clsid);
4163 StorageUtl_WriteDWord(
4164 buffer,
4165 OFFSET_PS_CTIMELOW,
4166 newData->ctime.dwLowDateTime);
4168 StorageUtl_WriteDWord(
4169 buffer,
4170 OFFSET_PS_CTIMEHIGH,
4171 newData->ctime.dwHighDateTime);
4173 StorageUtl_WriteDWord(
4174 buffer,
4175 OFFSET_PS_MTIMELOW,
4176 newData->mtime.dwLowDateTime);
4178 StorageUtl_WriteDWord(
4179 buffer,
4180 OFFSET_PS_MTIMEHIGH,
4181 newData->ctime.dwHighDateTime);
4183 StorageUtl_WriteDWord(
4184 buffer,
4185 OFFSET_PS_STARTBLOCK,
4186 newData->startingBlock);
4188 StorageUtl_WriteDWord(
4189 buffer,
4190 OFFSET_PS_SIZE,
4191 newData->size.u.LowPart);
4194 /******************************************************************************
4195 * Storage32Impl_ReadDirEntry
4197 * This method will read the specified directory entry.
4199 HRESULT StorageImpl_ReadDirEntry(
4200 StorageImpl* This,
4201 DirRef index,
4202 DirEntry* buffer)
4204 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4205 HRESULT readRes;
4207 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
4209 if (SUCCEEDED(readRes))
4211 memset(buffer->name, 0, sizeof(buffer->name));
4212 memcpy(
4213 buffer->name,
4214 (WCHAR *)currentEntry+OFFSET_PS_NAME,
4215 DIRENTRY_NAME_BUFFER_LEN );
4216 TRACE("storage name: %s\n", debugstr_w(buffer->name));
4218 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
4220 StorageUtl_ReadWord(
4221 currentEntry,
4222 OFFSET_PS_NAMELENGTH,
4223 &buffer->sizeOfNameString);
4225 StorageUtl_ReadDWord(
4226 currentEntry,
4227 OFFSET_PS_LEFTCHILD,
4228 &buffer->leftChild);
4230 StorageUtl_ReadDWord(
4231 currentEntry,
4232 OFFSET_PS_RIGHTCHILD,
4233 &buffer->rightChild);
4235 StorageUtl_ReadDWord(
4236 currentEntry,
4237 OFFSET_PS_DIRROOT,
4238 &buffer->dirRootEntry);
4240 StorageUtl_ReadGUID(
4241 currentEntry,
4242 OFFSET_PS_GUID,
4243 &buffer->clsid);
4245 StorageUtl_ReadDWord(
4246 currentEntry,
4247 OFFSET_PS_CTIMELOW,
4248 &buffer->ctime.dwLowDateTime);
4250 StorageUtl_ReadDWord(
4251 currentEntry,
4252 OFFSET_PS_CTIMEHIGH,
4253 &buffer->ctime.dwHighDateTime);
4255 StorageUtl_ReadDWord(
4256 currentEntry,
4257 OFFSET_PS_MTIMELOW,
4258 &buffer->mtime.dwLowDateTime);
4260 StorageUtl_ReadDWord(
4261 currentEntry,
4262 OFFSET_PS_MTIMEHIGH,
4263 &buffer->mtime.dwHighDateTime);
4265 StorageUtl_ReadDWord(
4266 currentEntry,
4267 OFFSET_PS_STARTBLOCK,
4268 &buffer->startingBlock);
4270 StorageUtl_ReadDWord(
4271 currentEntry,
4272 OFFSET_PS_SIZE,
4273 &buffer->size.u.LowPart);
4275 buffer->size.u.HighPart = 0;
4278 return readRes;
4281 /*********************************************************************
4282 * Write the specified directory entry to the file
4284 HRESULT StorageImpl_WriteDirEntry(
4285 StorageImpl* This,
4286 DirRef index,
4287 const DirEntry* buffer)
4289 BYTE currentEntry[RAW_DIRENTRY_SIZE];
4291 UpdateRawDirEntry(currentEntry, buffer);
4293 return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
4296 static HRESULT StorageImpl_ReadBigBlock(
4297 StorageImpl* This,
4298 ULONG blockIndex,
4299 void* buffer,
4300 ULONG* out_read)
4302 ULARGE_INTEGER ulOffset;
4303 DWORD read=0;
4304 HRESULT hr;
4306 ulOffset.u.HighPart = 0;
4307 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4309 hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
4311 if (SUCCEEDED(hr) && read < This->bigBlockSize)
4313 /* File ends during this block; fill the rest with 0's. */
4314 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
4317 if (out_read) *out_read = read;
4319 return hr;
4322 static BOOL StorageImpl_ReadDWordFromBigBlock(
4323 StorageImpl* This,
4324 ULONG blockIndex,
4325 ULONG offset,
4326 DWORD* value)
4328 ULARGE_INTEGER ulOffset;
4329 DWORD read;
4330 DWORD tmp;
4332 ulOffset.u.HighPart = 0;
4333 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4334 ulOffset.u.LowPart += offset;
4336 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4337 *value = lendian32toh(tmp);
4338 return (read == sizeof(DWORD));
4341 static BOOL StorageImpl_WriteBigBlock(
4342 StorageImpl* This,
4343 ULONG blockIndex,
4344 const void* buffer)
4346 ULARGE_INTEGER ulOffset;
4347 DWORD wrote;
4349 ulOffset.u.HighPart = 0;
4350 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4352 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4353 return (wrote == This->bigBlockSize);
4356 static BOOL StorageImpl_WriteDWordToBigBlock(
4357 StorageImpl* This,
4358 ULONG blockIndex,
4359 ULONG offset,
4360 DWORD value)
4362 ULARGE_INTEGER ulOffset;
4363 DWORD wrote;
4365 ulOffset.u.HighPart = 0;
4366 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4367 ulOffset.u.LowPart += offset;
4369 value = htole32(value);
4370 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4371 return (wrote == sizeof(DWORD));
4374 /******************************************************************************
4375 * Storage32Impl_SmallBlocksToBigBlocks
4377 * This method will convert a small block chain to a big block chain.
4378 * The small block chain will be destroyed.
4380 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4381 StorageImpl* This,
4382 SmallBlockChainStream** ppsbChain)
4384 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4385 ULARGE_INTEGER size, offset;
4386 ULONG cbRead, cbWritten;
4387 ULARGE_INTEGER cbTotalRead;
4388 DirRef streamEntryRef;
4389 HRESULT resWrite = S_OK;
4390 HRESULT resRead;
4391 DirEntry streamEntry;
4392 BYTE *buffer;
4393 BlockChainStream *bbTempChain = NULL;
4394 BlockChainStream *bigBlockChain = NULL;
4397 * Create a temporary big block chain that doesn't have
4398 * an associated directory entry. This temporary chain will be
4399 * used to copy data from small blocks to big blocks.
4401 bbTempChain = BlockChainStream_Construct(This,
4402 &bbHeadOfChain,
4403 DIRENTRY_NULL);
4404 if(!bbTempChain) return NULL;
4406 * Grow the big block chain.
4408 size = SmallBlockChainStream_GetSize(*ppsbChain);
4409 BlockChainStream_SetSize(bbTempChain, size);
4412 * Copy the contents of the small block chain to the big block chain
4413 * by small block size increments.
4415 offset.u.LowPart = 0;
4416 offset.u.HighPart = 0;
4417 cbTotalRead.QuadPart = 0;
4419 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4422 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4423 offset,
4424 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4425 buffer,
4426 &cbRead);
4427 if (FAILED(resRead))
4428 break;
4430 if (cbRead > 0)
4432 cbTotalRead.QuadPart += cbRead;
4434 resWrite = BlockChainStream_WriteAt(bbTempChain,
4435 offset,
4436 cbRead,
4437 buffer,
4438 &cbWritten);
4440 if (FAILED(resWrite))
4441 break;
4443 offset.u.LowPart += cbRead;
4445 else
4447 resRead = STG_E_READFAULT;
4448 break;
4450 } while (cbTotalRead.QuadPart < size.QuadPart);
4451 HeapFree(GetProcessHeap(),0,buffer);
4453 size.u.HighPart = 0;
4454 size.u.LowPart = 0;
4456 if (FAILED(resRead) || FAILED(resWrite))
4458 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4459 BlockChainStream_SetSize(bbTempChain, size);
4460 BlockChainStream_Destroy(bbTempChain);
4461 return NULL;
4465 * Destroy the small block chain.
4467 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4468 SmallBlockChainStream_SetSize(*ppsbChain, size);
4469 SmallBlockChainStream_Destroy(*ppsbChain);
4470 *ppsbChain = 0;
4473 * Change the directory entry. This chain is now a big block chain
4474 * and it doesn't reside in the small blocks chain anymore.
4476 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4478 streamEntry.startingBlock = bbHeadOfChain;
4480 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4483 * Destroy the temporary entryless big block chain.
4484 * Create a new big block chain associated with this entry.
4486 BlockChainStream_Destroy(bbTempChain);
4487 bigBlockChain = BlockChainStream_Construct(This,
4488 NULL,
4489 streamEntryRef);
4491 return bigBlockChain;
4494 /******************************************************************************
4495 * Storage32Impl_BigBlocksToSmallBlocks
4497 * This method will convert a big block chain to a small block chain.
4498 * The big block chain will be destroyed on success.
4500 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4501 StorageImpl* This,
4502 BlockChainStream** ppbbChain,
4503 ULARGE_INTEGER newSize)
4505 ULARGE_INTEGER size, offset, cbTotalRead;
4506 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4507 DirRef streamEntryRef;
4508 HRESULT resWrite = S_OK, resRead = S_OK;
4509 DirEntry streamEntry;
4510 BYTE* buffer;
4511 SmallBlockChainStream* sbTempChain;
4513 TRACE("%p %p\n", This, ppbbChain);
4515 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4516 DIRENTRY_NULL);
4518 if(!sbTempChain)
4519 return NULL;
4521 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4522 size = BlockChainStream_GetSize(*ppbbChain);
4523 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4525 offset.u.HighPart = 0;
4526 offset.u.LowPart = 0;
4527 cbTotalRead.QuadPart = 0;
4528 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4529 while(cbTotalRead.QuadPart < size.QuadPart)
4531 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4532 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4533 buffer, &cbRead);
4535 if(FAILED(resRead))
4536 break;
4538 if(cbRead > 0)
4540 cbTotalRead.QuadPart += cbRead;
4542 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4543 cbRead, buffer, &cbWritten);
4545 if(FAILED(resWrite))
4546 break;
4548 offset.u.LowPart += cbRead;
4550 else
4552 resRead = STG_E_READFAULT;
4553 break;
4556 HeapFree(GetProcessHeap(), 0, buffer);
4558 size.u.HighPart = 0;
4559 size.u.LowPart = 0;
4561 if(FAILED(resRead) || FAILED(resWrite))
4563 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4564 SmallBlockChainStream_SetSize(sbTempChain, size);
4565 SmallBlockChainStream_Destroy(sbTempChain);
4566 return NULL;
4569 /* destroy the original big block chain */
4570 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4571 BlockChainStream_SetSize(*ppbbChain, size);
4572 BlockChainStream_Destroy(*ppbbChain);
4573 *ppbbChain = NULL;
4575 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4576 streamEntry.startingBlock = sbHeadOfChain;
4577 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4579 SmallBlockChainStream_Destroy(sbTempChain);
4580 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4583 static HRESULT StorageBaseImpl_CopyStream(
4584 StorageBaseImpl *dst, DirRef dst_entry,
4585 StorageBaseImpl *src, DirRef src_entry)
4587 HRESULT hr;
4588 BYTE data[4096];
4589 DirEntry srcdata;
4590 ULARGE_INTEGER bytes_copied;
4591 ULONG bytestocopy, bytesread, byteswritten;
4593 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4595 if (SUCCEEDED(hr))
4597 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4599 bytes_copied.QuadPart = 0;
4600 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4602 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4604 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4605 data, &bytesread);
4606 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4608 if (SUCCEEDED(hr))
4609 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4610 data, &byteswritten);
4611 if (SUCCEEDED(hr))
4613 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4614 bytes_copied.QuadPart += byteswritten;
4619 return hr;
4622 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4624 DirRef result=This->firstFreeEntry;
4626 while (result < This->entries_size && This->entries[result].inuse)
4627 result++;
4629 if (result == This->entries_size)
4631 ULONG new_size = This->entries_size * 2;
4632 TransactedDirEntry *new_entries;
4634 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4635 if (!new_entries) return DIRENTRY_NULL;
4637 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4638 HeapFree(GetProcessHeap(), 0, This->entries);
4640 This->entries = new_entries;
4641 This->entries_size = new_size;
4644 This->entries[result].inuse = TRUE;
4646 This->firstFreeEntry = result+1;
4648 return result;
4651 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4652 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4654 DirRef stubEntryRef;
4655 TransactedDirEntry *entry;
4657 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4659 if (stubEntryRef != DIRENTRY_NULL)
4661 entry = &This->entries[stubEntryRef];
4663 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4665 entry->read = FALSE;
4668 return stubEntryRef;
4671 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4672 TransactedSnapshotImpl *This, DirRef entry)
4674 HRESULT hr=S_OK;
4675 DirEntry data;
4677 if (!This->entries[entry].read)
4679 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4680 This->entries[entry].transactedParentEntry,
4681 &data);
4683 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4685 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4687 if (data.leftChild == DIRENTRY_NULL)
4688 hr = E_OUTOFMEMORY;
4691 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4693 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4695 if (data.rightChild == DIRENTRY_NULL)
4696 hr = E_OUTOFMEMORY;
4699 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4701 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4703 if (data.dirRootEntry == DIRENTRY_NULL)
4704 hr = E_OUTOFMEMORY;
4707 if (SUCCEEDED(hr))
4709 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4710 This->entries[entry].read = TRUE;
4714 return hr;
4717 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4718 TransactedSnapshotImpl *This, DirRef entry)
4720 HRESULT hr = S_OK;
4722 if (!This->entries[entry].stream_dirty)
4724 DirEntry new_entrydata;
4726 memset(&new_entrydata, 0, sizeof(DirEntry));
4727 new_entrydata.name[0] = 'S';
4728 new_entrydata.sizeOfNameString = 1;
4729 new_entrydata.stgType = STGTY_STREAM;
4730 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4731 new_entrydata.leftChild = DIRENTRY_NULL;
4732 new_entrydata.rightChild = DIRENTRY_NULL;
4733 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4735 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4736 &This->entries[entry].stream_entry);
4738 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4740 hr = StorageBaseImpl_CopyStream(
4741 This->scratch, This->entries[entry].stream_entry,
4742 This->transactedParent, This->entries[entry].transactedParentEntry);
4744 if (FAILED(hr))
4745 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4748 if (SUCCEEDED(hr))
4749 This->entries[entry].stream_dirty = TRUE;
4751 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4753 /* Since this entry is modified, and we aren't using its stream data, we
4754 * no longer care about the original entry. */
4755 DirRef delete_ref;
4756 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4758 if (delete_ref != DIRENTRY_NULL)
4759 This->entries[delete_ref].deleted = TRUE;
4761 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4765 return hr;
4768 /* Find the first entry in a depth-first traversal. */
4769 static DirRef TransactedSnapshotImpl_FindFirstChild(
4770 TransactedSnapshotImpl* This, DirRef parent)
4772 DirRef cursor, prev;
4773 TransactedDirEntry *entry;
4775 cursor = parent;
4776 entry = &This->entries[cursor];
4777 while (entry->read)
4779 if (entry->data.leftChild != DIRENTRY_NULL)
4781 prev = cursor;
4782 cursor = entry->data.leftChild;
4783 entry = &This->entries[cursor];
4784 entry->parent = prev;
4786 else if (entry->data.rightChild != DIRENTRY_NULL)
4788 prev = cursor;
4789 cursor = entry->data.rightChild;
4790 entry = &This->entries[cursor];
4791 entry->parent = prev;
4793 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4795 prev = cursor;
4796 cursor = entry->data.dirRootEntry;
4797 entry = &This->entries[cursor];
4798 entry->parent = prev;
4800 else
4801 break;
4804 return cursor;
4807 /* Find the next entry in a depth-first traversal. */
4808 static DirRef TransactedSnapshotImpl_FindNextChild(
4809 TransactedSnapshotImpl* This, DirRef current)
4811 DirRef parent;
4812 TransactedDirEntry *parent_entry;
4814 parent = This->entries[current].parent;
4815 parent_entry = &This->entries[parent];
4817 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4819 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4821 This->entries[parent_entry->data.rightChild].parent = parent;
4822 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4825 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4827 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4828 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4832 return parent;
4835 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4836 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4837 TransactedSnapshotImpl* This, DirRef entry)
4839 return entry != DIRENTRY_NULL &&
4840 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4843 /* Destroy the entries created by CopyTree. */
4844 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4845 TransactedSnapshotImpl* This, DirRef stop)
4847 DirRef cursor;
4848 TransactedDirEntry *entry;
4849 ULARGE_INTEGER zero;
4851 zero.QuadPart = 0;
4853 if (!This->entries[This->base.storageDirEntry].read)
4854 return;
4856 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4858 if (cursor == DIRENTRY_NULL)
4859 return;
4861 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4863 while (cursor != DIRENTRY_NULL && cursor != stop)
4865 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4867 entry = &This->entries[cursor];
4869 if (entry->stream_dirty)
4870 StorageBaseImpl_StreamSetSize(This->transactedParent,
4871 entry->newTransactedParentEntry, zero);
4873 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4874 entry->newTransactedParentEntry);
4876 entry->newTransactedParentEntry = entry->transactedParentEntry;
4879 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4883 /* Make a copy of our edited tree that we can use in the parent. */
4884 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4886 DirRef cursor;
4887 TransactedDirEntry *entry;
4888 HRESULT hr = S_OK;
4890 cursor = This->base.storageDirEntry;
4891 entry = &This->entries[cursor];
4892 entry->parent = DIRENTRY_NULL;
4893 entry->newTransactedParentEntry = entry->transactedParentEntry;
4895 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4896 return S_OK;
4898 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4900 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4901 entry = &This->entries[cursor];
4903 while (cursor != DIRENTRY_NULL)
4905 /* Make a copy of this entry in the transacted parent. */
4906 if (!entry->read ||
4907 (!entry->dirty && !entry->stream_dirty &&
4908 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4909 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4910 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4911 entry->newTransactedParentEntry = entry->transactedParentEntry;
4912 else
4914 DirEntry newData;
4916 memcpy(&newData, &entry->data, sizeof(DirEntry));
4918 newData.size.QuadPart = 0;
4919 newData.startingBlock = BLOCK_END_OF_CHAIN;
4921 if (newData.leftChild != DIRENTRY_NULL)
4922 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4924 if (newData.rightChild != DIRENTRY_NULL)
4925 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4927 if (newData.dirRootEntry != DIRENTRY_NULL)
4928 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4930 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4931 &entry->newTransactedParentEntry);
4932 if (FAILED(hr))
4934 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4935 return hr;
4938 if (entry->stream_dirty)
4940 hr = StorageBaseImpl_CopyStream(
4941 This->transactedParent, entry->newTransactedParentEntry,
4942 This->scratch, entry->stream_entry);
4944 else if (entry->data.size.QuadPart)
4946 hr = StorageBaseImpl_StreamLink(
4947 This->transactedParent, entry->newTransactedParentEntry,
4948 entry->transactedParentEntry);
4951 if (FAILED(hr))
4953 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4954 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4955 return hr;
4959 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4960 entry = &This->entries[cursor];
4963 return hr;
4966 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4967 IStorage* iface,
4968 DWORD grfCommitFlags) /* [in] */
4970 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4971 TransactedDirEntry *root_entry;
4972 DirRef i, dir_root_ref;
4973 DirEntry data;
4974 ULARGE_INTEGER zero;
4975 HRESULT hr;
4976 ULONG transactionSig;
4978 zero.QuadPart = 0;
4980 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4982 /* Cannot commit a read-only transacted storage */
4983 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4984 return STG_E_ACCESSDENIED;
4986 hr = StorageBaseImpl_LockTransaction(This->transactedParent);
4987 if (hr == E_NOTIMPL) hr = S_OK;
4988 if (SUCCEEDED(hr))
4990 hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
4991 if (SUCCEEDED(hr))
4993 if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
4994 hr = STG_E_NOTCURRENT;
4996 if (SUCCEEDED(hr))
4998 This->lastTransactionSig = transactionSig+1;
4999 hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
5002 else if (hr == E_NOTIMPL)
5003 hr = S_OK;
5005 if (FAILED(hr)) goto end;
5007 /* To prevent data loss, we create the new structure in the file before we
5008 * delete the old one, so that in case of errors the old data is intact. We
5009 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
5010 * needed in the rare situation where we have just enough free disk space to
5011 * overwrite the existing data. */
5013 root_entry = &This->entries[This->base.storageDirEntry];
5015 if (!root_entry->read)
5016 goto end;
5018 hr = TransactedSnapshotImpl_CopyTree(This);
5019 if (FAILED(hr)) goto end;
5021 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
5022 dir_root_ref = DIRENTRY_NULL;
5023 else
5024 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
5026 hr = StorageBaseImpl_Flush(This->transactedParent);
5028 /* Update the storage to use the new data in one step. */
5029 if (SUCCEEDED(hr))
5030 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
5031 root_entry->transactedParentEntry, &data);
5033 if (SUCCEEDED(hr))
5035 data.dirRootEntry = dir_root_ref;
5036 data.clsid = root_entry->data.clsid;
5037 data.ctime = root_entry->data.ctime;
5038 data.mtime = root_entry->data.mtime;
5040 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
5041 root_entry->transactedParentEntry, &data);
5044 /* Try to flush after updating the root storage, but if the flush fails, keep
5045 * going, on the theory that it'll either succeed later or the subsequent
5046 * writes will fail. */
5047 StorageBaseImpl_Flush(This->transactedParent);
5049 if (SUCCEEDED(hr))
5051 /* Destroy the old now-orphaned data. */
5052 for (i=0; i<This->entries_size; i++)
5054 TransactedDirEntry *entry = &This->entries[i];
5055 if (entry->inuse)
5057 if (entry->deleted)
5059 StorageBaseImpl_StreamSetSize(This->transactedParent,
5060 entry->transactedParentEntry, zero);
5061 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5062 entry->transactedParentEntry);
5063 memset(entry, 0, sizeof(TransactedDirEntry));
5064 This->firstFreeEntry = min(i, This->firstFreeEntry);
5066 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
5068 if (entry->transactedParentEntry != DIRENTRY_NULL)
5069 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
5070 entry->transactedParentEntry);
5071 if (entry->stream_dirty)
5073 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
5074 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
5075 entry->stream_dirty = FALSE;
5077 entry->dirty = FALSE;
5078 entry->transactedParentEntry = entry->newTransactedParentEntry;
5083 else
5085 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
5088 if (SUCCEEDED(hr))
5089 hr = StorageBaseImpl_Flush(This->transactedParent);
5090 end:
5091 StorageBaseImpl_UnlockTransaction(This->transactedParent);
5094 return hr;
5097 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
5098 IStorage* iface)
5100 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
5101 ULARGE_INTEGER zero;
5102 ULONG i;
5104 TRACE("(%p)\n", iface);
5106 /* Destroy the open objects. */
5107 StorageBaseImpl_DeleteAll(&This->base);
5109 /* Clear out the scratch file. */
5110 zero.QuadPart = 0;
5111 for (i=0; i<This->entries_size; i++)
5113 if (This->entries[i].stream_dirty)
5115 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
5116 zero);
5118 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
5122 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
5124 This->firstFreeEntry = 0;
5125 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
5127 return S_OK;
5130 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
5132 if (!This->reverted)
5134 TRACE("Storage invalidated (stg=%p)\n", This);
5136 This->reverted = TRUE;
5138 StorageBaseImpl_DeleteAll(This);
5142 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
5144 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
5146 IStorage_Revert(&This->base.IStorage_iface);
5147 IStorage_Release(&This->transactedParent->IStorage_iface);
5148 IStorage_Release(&This->scratch->IStorage_iface);
5149 HeapFree(GetProcessHeap(), 0, This->entries);
5150 HeapFree(GetProcessHeap(), 0, This);
5153 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
5155 /* We only need to flush when committing. */
5156 return S_OK;
5159 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5161 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
5163 return StorageBaseImpl_GetFilename(This->transactedParent, result);
5166 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
5167 const DirEntry *newData, DirRef *index)
5169 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5170 DirRef new_ref;
5171 TransactedDirEntry *new_entry;
5173 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
5174 if (new_ref == DIRENTRY_NULL)
5175 return E_OUTOFMEMORY;
5177 new_entry = &This->entries[new_ref];
5179 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
5180 new_entry->read = TRUE;
5181 new_entry->dirty = TRUE;
5182 memcpy(&new_entry->data, newData, sizeof(DirEntry));
5184 *index = new_ref;
5186 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
5188 return S_OK;
5191 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
5192 DirRef index, const DirEntry *data)
5194 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5195 HRESULT hr;
5197 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
5199 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5200 if (FAILED(hr)) return hr;
5202 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
5204 if (index != This->base.storageDirEntry)
5206 This->entries[index].dirty = TRUE;
5208 if (data->size.QuadPart == 0 &&
5209 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5211 /* Since this entry is modified, and we aren't using its stream data, we
5212 * no longer care about the original entry. */
5213 DirRef delete_ref;
5214 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5216 if (delete_ref != DIRENTRY_NULL)
5217 This->entries[delete_ref].deleted = TRUE;
5219 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5223 return S_OK;
5226 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
5227 DirRef index, DirEntry *data)
5229 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5230 HRESULT hr;
5232 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5233 if (FAILED(hr)) return hr;
5235 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
5237 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
5239 return S_OK;
5242 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
5243 DirRef index)
5245 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5247 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
5248 This->entries[index].data.size.QuadPart != 0)
5250 /* If we deleted this entry while it has stream data. We must have left the
5251 * data because some other entry is using it, and we need to leave the
5252 * original entry alone. */
5253 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
5254 This->firstFreeEntry = min(index, This->firstFreeEntry);
5256 else
5258 This->entries[index].deleted = TRUE;
5261 return S_OK;
5264 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
5265 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5267 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5269 if (This->entries[index].stream_dirty)
5271 return StorageBaseImpl_StreamReadAt(This->scratch,
5272 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
5274 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
5276 /* This stream doesn't live in the parent, and we haven't allocated storage
5277 * for it yet */
5278 *bytesRead = 0;
5279 return S_OK;
5281 else
5283 return StorageBaseImpl_StreamReadAt(This->transactedParent,
5284 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
5288 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
5289 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5291 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5292 HRESULT hr;
5294 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5295 if (FAILED(hr)) return hr;
5297 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5298 if (FAILED(hr)) return hr;
5300 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
5301 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
5303 if (SUCCEEDED(hr) && size != 0)
5304 This->entries[index].data.size.QuadPart = max(
5305 This->entries[index].data.size.QuadPart,
5306 offset.QuadPart + size);
5308 return hr;
5311 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
5312 DirRef index, ULARGE_INTEGER newsize)
5314 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5315 HRESULT hr;
5317 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
5318 if (FAILED(hr)) return hr;
5320 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
5321 return S_OK;
5323 if (newsize.QuadPart == 0)
5325 /* Destroy any parent references or entries in the scratch file. */
5326 if (This->entries[index].stream_dirty)
5328 ULARGE_INTEGER zero;
5329 zero.QuadPart = 0;
5330 StorageBaseImpl_StreamSetSize(This->scratch,
5331 This->entries[index].stream_entry, zero);
5332 StorageBaseImpl_DestroyDirEntry(This->scratch,
5333 This->entries[index].stream_entry);
5334 This->entries[index].stream_dirty = FALSE;
5336 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
5338 DirRef delete_ref;
5339 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5341 if (delete_ref != DIRENTRY_NULL)
5342 This->entries[delete_ref].deleted = TRUE;
5344 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5347 else
5349 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5350 if (FAILED(hr)) return hr;
5352 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5353 This->entries[index].stream_entry, newsize);
5356 if (SUCCEEDED(hr))
5357 This->entries[index].data.size = newsize;
5359 return hr;
5362 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5363 DirRef dst, DirRef src)
5365 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5366 HRESULT hr;
5367 TransactedDirEntry *dst_entry, *src_entry;
5369 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5370 if (FAILED(hr)) return hr;
5372 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5373 if (FAILED(hr)) return hr;
5375 dst_entry = &This->entries[dst];
5376 src_entry = &This->entries[src];
5378 dst_entry->stream_dirty = src_entry->stream_dirty;
5379 dst_entry->stream_entry = src_entry->stream_entry;
5380 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5381 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5382 dst_entry->data.size = src_entry->data.size;
5384 return S_OK;
5387 static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
5388 ULONG* result, BOOL refresh)
5390 return E_NOTIMPL;
5393 static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
5394 ULONG value)
5396 return E_NOTIMPL;
5399 static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base)
5401 return E_NOTIMPL;
5404 static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base)
5406 return E_NOTIMPL;
5409 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5411 StorageBaseImpl_QueryInterface,
5412 StorageBaseImpl_AddRef,
5413 StorageBaseImpl_Release,
5414 StorageBaseImpl_CreateStream,
5415 StorageBaseImpl_OpenStream,
5416 StorageBaseImpl_CreateStorage,
5417 StorageBaseImpl_OpenStorage,
5418 StorageBaseImpl_CopyTo,
5419 StorageBaseImpl_MoveElementTo,
5420 TransactedSnapshotImpl_Commit,
5421 TransactedSnapshotImpl_Revert,
5422 StorageBaseImpl_EnumElements,
5423 StorageBaseImpl_DestroyElement,
5424 StorageBaseImpl_RenameElement,
5425 StorageBaseImpl_SetElementTimes,
5426 StorageBaseImpl_SetClass,
5427 StorageBaseImpl_SetStateBits,
5428 StorageBaseImpl_Stat
5431 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5433 TransactedSnapshotImpl_Destroy,
5434 TransactedSnapshotImpl_Invalidate,
5435 TransactedSnapshotImpl_Flush,
5436 TransactedSnapshotImpl_GetFilename,
5437 TransactedSnapshotImpl_CreateDirEntry,
5438 TransactedSnapshotImpl_WriteDirEntry,
5439 TransactedSnapshotImpl_ReadDirEntry,
5440 TransactedSnapshotImpl_DestroyDirEntry,
5441 TransactedSnapshotImpl_StreamReadAt,
5442 TransactedSnapshotImpl_StreamWriteAt,
5443 TransactedSnapshotImpl_StreamSetSize,
5444 TransactedSnapshotImpl_StreamLink,
5445 TransactedSnapshotImpl_GetTransactionSig,
5446 TransactedSnapshotImpl_SetTransactionSig,
5447 TransactedSnapshotImpl_LockTransaction,
5448 TransactedSnapshotImpl_UnlockTransaction
5451 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5452 TransactedSnapshotImpl** result)
5454 HRESULT hr;
5456 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5457 if (*result)
5459 IStorage *scratch;
5461 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5463 /* This is OK because the property set storage functions use the IStorage functions. */
5464 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5465 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5467 list_init(&(*result)->base.strmHead);
5469 list_init(&(*result)->base.storageHead);
5471 (*result)->base.ref = 1;
5473 (*result)->base.openFlags = parentStorage->openFlags;
5475 /* This cannot fail, except with E_NOTIMPL in which case we don't care */
5476 StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
5478 /* Create a new temporary storage to act as the scratch file. */
5479 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5480 0, &scratch);
5481 (*result)->scratch = impl_from_IStorage(scratch);
5483 if (SUCCEEDED(hr))
5485 ULONG num_entries = 20;
5487 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5488 (*result)->entries_size = num_entries;
5489 (*result)->firstFreeEntry = 0;
5491 if ((*result)->entries)
5493 /* parentStorage already has 1 reference, which we take over here. */
5494 (*result)->transactedParent = parentStorage;
5496 parentStorage->transactedChild = &(*result)->base;
5498 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5500 else
5502 IStorage_Release(scratch);
5504 hr = E_OUTOFMEMORY;
5508 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
5510 return hr;
5512 else
5513 return E_OUTOFMEMORY;
5516 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5517 StorageBaseImpl** result)
5519 static int fixme=0;
5521 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5523 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5526 return TransactedSnapshotImpl_Construct(parentStorage,
5527 (TransactedSnapshotImpl**)result);
5530 static HRESULT Storage_Construct(
5531 HANDLE hFile,
5532 LPCOLESTR pwcsName,
5533 ILockBytes* pLkbyt,
5534 DWORD openFlags,
5535 BOOL fileBased,
5536 BOOL create,
5537 ULONG sector_size,
5538 StorageBaseImpl** result)
5540 StorageImpl *newStorage;
5541 StorageBaseImpl *newTransactedStorage;
5542 HRESULT hr;
5544 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5545 if (FAILED(hr)) goto end;
5547 if (openFlags & STGM_TRANSACTED)
5549 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5550 if (FAILED(hr))
5551 IStorage_Release(&newStorage->base.IStorage_iface);
5552 else
5553 *result = newTransactedStorage;
5555 else
5556 *result = &newStorage->base;
5558 end:
5559 return hr;
5562 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5564 StorageInternalImpl* This = (StorageInternalImpl*) base;
5566 if (!This->base.reverted)
5568 TRACE("Storage invalidated (stg=%p)\n", This);
5570 This->base.reverted = TRUE;
5572 This->parentStorage = NULL;
5574 StorageBaseImpl_DeleteAll(&This->base);
5576 list_remove(&This->ParentListEntry);
5580 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5582 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5584 StorageInternalImpl_Invalidate(&This->base);
5586 HeapFree(GetProcessHeap(), 0, This);
5589 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5591 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5593 return StorageBaseImpl_Flush(This->parentStorage);
5596 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5598 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5600 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5603 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5604 const DirEntry *newData, DirRef *index)
5606 StorageInternalImpl* This = (StorageInternalImpl*) base;
5608 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5609 newData, index);
5612 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5613 DirRef index, const DirEntry *data)
5615 StorageInternalImpl* This = (StorageInternalImpl*) base;
5617 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5618 index, data);
5621 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5622 DirRef index, DirEntry *data)
5624 StorageInternalImpl* This = (StorageInternalImpl*) base;
5626 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5627 index, data);
5630 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5631 DirRef index)
5633 StorageInternalImpl* This = (StorageInternalImpl*) base;
5635 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5636 index);
5639 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5640 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5642 StorageInternalImpl* This = (StorageInternalImpl*) base;
5644 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5645 index, offset, size, buffer, bytesRead);
5648 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5649 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5651 StorageInternalImpl* This = (StorageInternalImpl*) base;
5653 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5654 index, offset, size, buffer, bytesWritten);
5657 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5658 DirRef index, ULARGE_INTEGER newsize)
5660 StorageInternalImpl* This = (StorageInternalImpl*) base;
5662 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5663 index, newsize);
5666 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5667 DirRef dst, DirRef src)
5669 StorageInternalImpl* This = (StorageInternalImpl*) base;
5671 return StorageBaseImpl_StreamLink(This->parentStorage,
5672 dst, src);
5675 static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
5676 ULONG* result, BOOL refresh)
5678 return E_NOTIMPL;
5681 static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
5682 ULONG value)
5684 return E_NOTIMPL;
5687 static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base)
5689 return E_NOTIMPL;
5692 static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base)
5694 return E_NOTIMPL;
5697 /******************************************************************************
5699 ** Storage32InternalImpl_Commit
5702 static HRESULT WINAPI StorageInternalImpl_Commit(
5703 IStorage* iface,
5704 DWORD grfCommitFlags) /* [in] */
5706 StorageBaseImpl* This = impl_from_IStorage(iface);
5707 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5708 return StorageBaseImpl_Flush(This);
5711 /******************************************************************************
5713 ** Storage32InternalImpl_Revert
5716 static HRESULT WINAPI StorageInternalImpl_Revert(
5717 IStorage* iface)
5719 FIXME("(%p): stub\n", iface);
5720 return S_OK;
5723 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5725 IStorage_Release(&This->parentStorage->IStorage_iface);
5726 HeapFree(GetProcessHeap(), 0, This);
5729 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5730 IEnumSTATSTG* iface,
5731 REFIID riid,
5732 void** ppvObject)
5734 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5736 if (ppvObject==0)
5737 return E_INVALIDARG;
5739 *ppvObject = 0;
5741 if (IsEqualGUID(&IID_IUnknown, riid) ||
5742 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5744 *ppvObject = This;
5745 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5746 return S_OK;
5749 return E_NOINTERFACE;
5752 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5753 IEnumSTATSTG* iface)
5755 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5756 return InterlockedIncrement(&This->ref);
5759 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5760 IEnumSTATSTG* iface)
5762 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5764 ULONG newRef;
5766 newRef = InterlockedDecrement(&This->ref);
5768 if (newRef==0)
5770 IEnumSTATSTGImpl_Destroy(This);
5773 return newRef;
5776 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5777 IEnumSTATSTGImpl* This,
5778 DirRef *ref)
5780 DirRef result = DIRENTRY_NULL;
5781 DirRef searchNode;
5782 DirEntry entry;
5783 HRESULT hr;
5784 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5786 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5787 This->parentStorage->storageDirEntry, &entry);
5788 searchNode = entry.dirRootEntry;
5790 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5792 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5794 if (SUCCEEDED(hr))
5796 LONG diff = entryNameCmp( entry.name, This->name);
5798 if (diff <= 0)
5800 searchNode = entry.rightChild;
5802 else
5804 result = searchNode;
5805 memcpy(result_name, entry.name, sizeof(result_name));
5806 searchNode = entry.leftChild;
5811 if (SUCCEEDED(hr))
5813 *ref = result;
5814 if (result != DIRENTRY_NULL)
5815 memcpy(This->name, result_name, sizeof(result_name));
5818 return hr;
5821 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5822 IEnumSTATSTG* iface,
5823 ULONG celt,
5824 STATSTG* rgelt,
5825 ULONG* pceltFetched)
5827 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5829 DirEntry currentEntry;
5830 STATSTG* currentReturnStruct = rgelt;
5831 ULONG objectFetched = 0;
5832 DirRef currentSearchNode;
5833 HRESULT hr=S_OK;
5835 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5836 return E_INVALIDARG;
5838 if (This->parentStorage->reverted)
5839 return STG_E_REVERTED;
5842 * To avoid the special case, get another pointer to a ULONG value if
5843 * the caller didn't supply one.
5845 if (pceltFetched==0)
5846 pceltFetched = &objectFetched;
5849 * Start the iteration, we will iterate until we hit the end of the
5850 * linked list or until we hit the number of items to iterate through
5852 *pceltFetched = 0;
5854 while ( *pceltFetched < celt )
5856 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5858 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5859 break;
5862 * Read the entry from the storage.
5864 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5865 currentSearchNode,
5866 &currentEntry);
5869 * Copy the information to the return buffer.
5871 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5872 currentReturnStruct,
5873 &currentEntry,
5874 STATFLAG_DEFAULT);
5877 * Step to the next item in the iteration
5879 (*pceltFetched)++;
5880 currentReturnStruct++;
5883 if (SUCCEEDED(hr) && *pceltFetched != celt)
5884 hr = S_FALSE;
5886 return hr;
5890 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5891 IEnumSTATSTG* iface,
5892 ULONG celt)
5894 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5896 ULONG objectFetched = 0;
5897 DirRef currentSearchNode;
5898 HRESULT hr=S_OK;
5900 if (This->parentStorage->reverted)
5901 return STG_E_REVERTED;
5903 while ( (objectFetched < celt) )
5905 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5907 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5908 break;
5910 objectFetched++;
5913 if (SUCCEEDED(hr) && objectFetched != celt)
5914 return S_FALSE;
5916 return hr;
5919 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5920 IEnumSTATSTG* iface)
5922 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5924 if (This->parentStorage->reverted)
5925 return STG_E_REVERTED;
5927 This->name[0] = 0;
5929 return S_OK;
5932 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5933 IEnumSTATSTG* iface,
5934 IEnumSTATSTG** ppenum)
5936 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5937 IEnumSTATSTGImpl* newClone;
5939 if (This->parentStorage->reverted)
5940 return STG_E_REVERTED;
5942 if (ppenum==0)
5943 return E_INVALIDARG;
5945 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5946 This->storageDirEntry);
5947 if (!newClone)
5949 *ppenum = NULL;
5950 return E_OUTOFMEMORY;
5954 * The new clone enumeration must point to the same current node as
5955 * the old one.
5957 memcpy(newClone->name, This->name, sizeof(newClone->name));
5959 *ppenum = &newClone->IEnumSTATSTG_iface;
5961 return S_OK;
5965 * Virtual function table for the IEnumSTATSTGImpl class.
5967 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5969 IEnumSTATSTGImpl_QueryInterface,
5970 IEnumSTATSTGImpl_AddRef,
5971 IEnumSTATSTGImpl_Release,
5972 IEnumSTATSTGImpl_Next,
5973 IEnumSTATSTGImpl_Skip,
5974 IEnumSTATSTGImpl_Reset,
5975 IEnumSTATSTGImpl_Clone
5978 /******************************************************************************
5979 ** IEnumSTATSTGImpl implementation
5982 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5983 StorageBaseImpl* parentStorage,
5984 DirRef storageDirEntry)
5986 IEnumSTATSTGImpl* newEnumeration;
5988 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5990 if (newEnumeration)
5992 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5993 newEnumeration->ref = 1;
5994 newEnumeration->name[0] = 0;
5997 * We want to nail-down the reference to the storage in case the
5998 * enumeration out-lives the storage in the client application.
6000 newEnumeration->parentStorage = parentStorage;
6001 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
6003 newEnumeration->storageDirEntry = storageDirEntry;
6006 return newEnumeration;
6010 * Virtual function table for the Storage32InternalImpl class.
6012 static const IStorageVtbl Storage32InternalImpl_Vtbl =
6014 StorageBaseImpl_QueryInterface,
6015 StorageBaseImpl_AddRef,
6016 StorageBaseImpl_Release,
6017 StorageBaseImpl_CreateStream,
6018 StorageBaseImpl_OpenStream,
6019 StorageBaseImpl_CreateStorage,
6020 StorageBaseImpl_OpenStorage,
6021 StorageBaseImpl_CopyTo,
6022 StorageBaseImpl_MoveElementTo,
6023 StorageInternalImpl_Commit,
6024 StorageInternalImpl_Revert,
6025 StorageBaseImpl_EnumElements,
6026 StorageBaseImpl_DestroyElement,
6027 StorageBaseImpl_RenameElement,
6028 StorageBaseImpl_SetElementTimes,
6029 StorageBaseImpl_SetClass,
6030 StorageBaseImpl_SetStateBits,
6031 StorageBaseImpl_Stat
6034 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
6036 StorageInternalImpl_Destroy,
6037 StorageInternalImpl_Invalidate,
6038 StorageInternalImpl_Flush,
6039 StorageInternalImpl_GetFilename,
6040 StorageInternalImpl_CreateDirEntry,
6041 StorageInternalImpl_WriteDirEntry,
6042 StorageInternalImpl_ReadDirEntry,
6043 StorageInternalImpl_DestroyDirEntry,
6044 StorageInternalImpl_StreamReadAt,
6045 StorageInternalImpl_StreamWriteAt,
6046 StorageInternalImpl_StreamSetSize,
6047 StorageInternalImpl_StreamLink,
6048 StorageInternalImpl_GetTransactionSig,
6049 StorageInternalImpl_SetTransactionSig,
6050 StorageInternalImpl_LockTransaction,
6051 StorageInternalImpl_UnlockTransaction
6054 /******************************************************************************
6055 ** Storage32InternalImpl implementation
6058 static StorageInternalImpl* StorageInternalImpl_Construct(
6059 StorageBaseImpl* parentStorage,
6060 DWORD openFlags,
6061 DirRef storageDirEntry)
6063 StorageInternalImpl* newStorage;
6065 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
6067 if (newStorage!=0)
6069 list_init(&newStorage->base.strmHead);
6071 list_init(&newStorage->base.storageHead);
6074 * Initialize the virtual function table.
6076 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
6077 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
6078 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
6079 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
6081 newStorage->base.reverted = FALSE;
6083 newStorage->base.ref = 1;
6085 newStorage->parentStorage = parentStorage;
6088 * Keep a reference to the directory entry of this storage
6090 newStorage->base.storageDirEntry = storageDirEntry;
6092 newStorage->base.create = FALSE;
6094 return newStorage;
6097 return 0;
6100 /******************************************************************************
6101 ** StorageUtl implementation
6104 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
6106 WORD tmp;
6108 memcpy(&tmp, buffer+offset, sizeof(WORD));
6109 *value = lendian16toh(tmp);
6112 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
6114 value = htole16(value);
6115 memcpy(buffer+offset, &value, sizeof(WORD));
6118 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
6120 DWORD tmp;
6122 memcpy(&tmp, buffer+offset, sizeof(DWORD));
6123 *value = lendian32toh(tmp);
6126 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
6128 value = htole32(value);
6129 memcpy(buffer+offset, &value, sizeof(DWORD));
6132 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
6133 ULARGE_INTEGER* value)
6135 #ifdef WORDS_BIGENDIAN
6136 ULARGE_INTEGER tmp;
6138 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
6139 value->u.LowPart = htole32(tmp.u.HighPart);
6140 value->u.HighPart = htole32(tmp.u.LowPart);
6141 #else
6142 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
6143 #endif
6146 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
6147 const ULARGE_INTEGER *value)
6149 #ifdef WORDS_BIGENDIAN
6150 ULARGE_INTEGER tmp;
6152 tmp.u.LowPart = htole32(value->u.HighPart);
6153 tmp.u.HighPart = htole32(value->u.LowPart);
6154 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
6155 #else
6156 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
6157 #endif
6160 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
6162 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
6163 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
6164 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
6166 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
6169 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
6171 StorageUtl_WriteDWord(buffer, offset, value->Data1);
6172 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
6173 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
6175 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
6178 void StorageUtl_CopyDirEntryToSTATSTG(
6179 StorageBaseImpl* storage,
6180 STATSTG* destination,
6181 const DirEntry* source,
6182 int statFlags)
6185 * The copy of the string occurs only when the flag is not set
6187 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
6189 /* Use the filename for the root storage. */
6190 destination->pwcsName = 0;
6191 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
6193 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
6194 (source->name[0] == 0) )
6196 destination->pwcsName = 0;
6198 else
6200 destination->pwcsName =
6201 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
6203 strcpyW(destination->pwcsName, source->name);
6206 switch (source->stgType)
6208 case STGTY_STORAGE:
6209 case STGTY_ROOT:
6210 destination->type = STGTY_STORAGE;
6211 break;
6212 case STGTY_STREAM:
6213 destination->type = STGTY_STREAM;
6214 break;
6215 default:
6216 destination->type = STGTY_STREAM;
6217 break;
6220 destination->cbSize = source->size;
6222 currentReturnStruct->mtime = {0}; TODO
6223 currentReturnStruct->ctime = {0};
6224 currentReturnStruct->atime = {0};
6226 destination->grfMode = 0;
6227 destination->grfLocksSupported = 0;
6228 destination->clsid = source->clsid;
6229 destination->grfStateBits = 0;
6230 destination->reserved = 0;
6233 /******************************************************************************
6234 ** BlockChainStream implementation
6237 /* Read and save the index of all blocks in this stream. */
6238 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
6240 ULONG next_sector, next_offset;
6241 HRESULT hr;
6242 struct BlockChainRun *last_run;
6244 if (This->indexCacheLen == 0)
6246 last_run = NULL;
6247 next_offset = 0;
6248 next_sector = BlockChainStream_GetHeadOfChain(This);
6250 else
6252 last_run = &This->indexCache[This->indexCacheLen-1];
6253 next_offset = last_run->lastOffset+1;
6254 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
6255 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
6256 &next_sector);
6257 if (FAILED(hr)) return hr;
6260 while (next_sector != BLOCK_END_OF_CHAIN)
6262 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
6264 /* Add the current block to the cache. */
6265 if (This->indexCacheSize == 0)
6267 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
6268 if (!This->indexCache) return E_OUTOFMEMORY;
6269 This->indexCacheSize = 16;
6271 else if (This->indexCacheSize == This->indexCacheLen)
6273 struct BlockChainRun *new_cache;
6274 ULONG new_size;
6276 new_size = This->indexCacheSize * 2;
6277 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
6278 if (!new_cache) return E_OUTOFMEMORY;
6279 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
6281 HeapFree(GetProcessHeap(), 0, This->indexCache);
6282 This->indexCache = new_cache;
6283 This->indexCacheSize = new_size;
6286 This->indexCacheLen++;
6287 last_run = &This->indexCache[This->indexCacheLen-1];
6288 last_run->firstSector = next_sector;
6289 last_run->firstOffset = next_offset;
6292 last_run->lastOffset = next_offset;
6294 /* Find the next block. */
6295 next_offset++;
6296 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
6297 if (FAILED(hr)) return hr;
6300 if (This->indexCacheLen)
6302 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
6303 This->numBlocks = last_run->lastOffset+1;
6305 else
6307 This->tailIndex = BLOCK_END_OF_CHAIN;
6308 This->numBlocks = 0;
6311 return S_OK;
6314 /* Locate the nth block in this stream. */
6315 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
6317 ULONG min_offset = 0, max_offset = This->numBlocks-1;
6318 ULONG min_run = 0, max_run = This->indexCacheLen-1;
6320 if (offset >= This->numBlocks)
6321 return BLOCK_END_OF_CHAIN;
6323 while (min_run < max_run)
6325 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
6326 if (offset < This->indexCache[run_to_check].firstOffset)
6328 max_offset = This->indexCache[run_to_check].firstOffset-1;
6329 max_run = run_to_check-1;
6331 else if (offset > This->indexCache[run_to_check].lastOffset)
6333 min_offset = This->indexCache[run_to_check].lastOffset+1;
6334 min_run = run_to_check+1;
6336 else
6337 /* Block is in this run. */
6338 min_run = max_run = run_to_check;
6341 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
6344 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
6345 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
6347 BlockChainBlock *result=NULL;
6348 int i;
6350 for (i=0; i<2; i++)
6351 if (This->cachedBlocks[i].index == index)
6353 *sector = This->cachedBlocks[i].sector;
6354 *block = &This->cachedBlocks[i];
6355 return S_OK;
6358 *sector = BlockChainStream_GetSectorOfOffset(This, index);
6359 if (*sector == BLOCK_END_OF_CHAIN)
6360 return STG_E_DOCFILECORRUPT;
6362 if (create)
6364 if (This->cachedBlocks[0].index == 0xffffffff)
6365 result = &This->cachedBlocks[0];
6366 else if (This->cachedBlocks[1].index == 0xffffffff)
6367 result = &This->cachedBlocks[1];
6368 else
6370 result = &This->cachedBlocks[This->blockToEvict++];
6371 if (This->blockToEvict == 2)
6372 This->blockToEvict = 0;
6375 if (result->dirty)
6377 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
6378 return STG_E_WRITEFAULT;
6379 result->dirty = FALSE;
6382 result->read = FALSE;
6383 result->index = index;
6384 result->sector = *sector;
6387 *block = result;
6388 return S_OK;
6391 BlockChainStream* BlockChainStream_Construct(
6392 StorageImpl* parentStorage,
6393 ULONG* headOfStreamPlaceHolder,
6394 DirRef dirEntry)
6396 BlockChainStream* newStream;
6398 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6400 newStream->parentStorage = parentStorage;
6401 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6402 newStream->ownerDirEntry = dirEntry;
6403 newStream->indexCache = NULL;
6404 newStream->indexCacheLen = 0;
6405 newStream->indexCacheSize = 0;
6406 newStream->cachedBlocks[0].index = 0xffffffff;
6407 newStream->cachedBlocks[0].dirty = FALSE;
6408 newStream->cachedBlocks[1].index = 0xffffffff;
6409 newStream->cachedBlocks[1].dirty = FALSE;
6410 newStream->blockToEvict = 0;
6412 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6414 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6415 HeapFree(GetProcessHeap(), 0, newStream);
6416 return NULL;
6419 return newStream;
6422 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6424 int i;
6425 if (!This) return S_OK;
6426 for (i=0; i<2; i++)
6428 if (This->cachedBlocks[i].dirty)
6430 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6431 This->cachedBlocks[i].dirty = FALSE;
6432 else
6433 return STG_E_WRITEFAULT;
6436 return S_OK;
6439 void BlockChainStream_Destroy(BlockChainStream* This)
6441 if (This)
6443 BlockChainStream_Flush(This);
6444 HeapFree(GetProcessHeap(), 0, This->indexCache);
6446 HeapFree(GetProcessHeap(), 0, This);
6449 /******************************************************************************
6450 * BlockChainStream_GetHeadOfChain
6452 * Returns the head of this stream chain.
6453 * Some special chains don't have directory entries, their heads are kept in
6454 * This->headOfStreamPlaceHolder.
6457 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6459 DirEntry chainEntry;
6460 HRESULT hr;
6462 if (This->headOfStreamPlaceHolder != 0)
6463 return *(This->headOfStreamPlaceHolder);
6465 if (This->ownerDirEntry != DIRENTRY_NULL)
6467 hr = StorageImpl_ReadDirEntry(
6468 This->parentStorage,
6469 This->ownerDirEntry,
6470 &chainEntry);
6472 if (SUCCEEDED(hr))
6474 return chainEntry.startingBlock;
6478 return BLOCK_END_OF_CHAIN;
6481 /******************************************************************************
6482 * BlockChainStream_GetCount
6484 * Returns the number of blocks that comprises this chain.
6485 * This is not the size of the stream as the last block may not be full!
6487 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6489 return This->numBlocks;
6492 /******************************************************************************
6493 * BlockChainStream_ReadAt
6495 * Reads a specified number of bytes from this chain at the specified offset.
6496 * bytesRead may be NULL.
6497 * Failure will be returned if the specified number of bytes has not been read.
6499 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6500 ULARGE_INTEGER offset,
6501 ULONG size,
6502 void* buffer,
6503 ULONG* bytesRead)
6505 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6506 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6507 ULONG bytesToReadInBuffer;
6508 ULONG blockIndex;
6509 BYTE* bufferWalker;
6510 ULARGE_INTEGER stream_size;
6511 HRESULT hr;
6512 BlockChainBlock *cachedBlock;
6514 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6517 * Find the first block in the stream that contains part of the buffer.
6519 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6521 *bytesRead = 0;
6523 stream_size = BlockChainStream_GetSize(This);
6524 if (stream_size.QuadPart > offset.QuadPart)
6525 size = min(stream_size.QuadPart - offset.QuadPart, size);
6526 else
6527 return S_OK;
6530 * Start reading the buffer.
6532 bufferWalker = buffer;
6534 while (size > 0)
6536 ULARGE_INTEGER ulOffset;
6537 DWORD bytesReadAt;
6540 * Calculate how many bytes we can copy from this big block.
6542 bytesToReadInBuffer =
6543 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6545 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6547 if (FAILED(hr))
6548 return hr;
6550 if (!cachedBlock)
6552 /* Not in cache, and we're going to read past the end of the block. */
6553 ulOffset.u.HighPart = 0;
6554 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6555 offsetInBlock;
6557 StorageImpl_ReadAt(This->parentStorage,
6558 ulOffset,
6559 bufferWalker,
6560 bytesToReadInBuffer,
6561 &bytesReadAt);
6563 else
6565 if (!cachedBlock->read)
6567 ULONG read;
6568 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
6569 return STG_E_READFAULT;
6571 cachedBlock->read = TRUE;
6574 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6575 bytesReadAt = bytesToReadInBuffer;
6578 blockNoInSequence++;
6579 bufferWalker += bytesReadAt;
6580 size -= bytesReadAt;
6581 *bytesRead += bytesReadAt;
6582 offsetInBlock = 0; /* There is no offset on the next block */
6584 if (bytesToReadInBuffer != bytesReadAt)
6585 break;
6588 return S_OK;
6591 /******************************************************************************
6592 * BlockChainStream_WriteAt
6594 * Writes the specified number of bytes to this chain at the specified offset.
6595 * Will fail if not all specified number of bytes have been written.
6597 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6598 ULARGE_INTEGER offset,
6599 ULONG size,
6600 const void* buffer,
6601 ULONG* bytesWritten)
6603 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6604 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6605 ULONG bytesToWrite;
6606 ULONG blockIndex;
6607 const BYTE* bufferWalker;
6608 HRESULT hr;
6609 BlockChainBlock *cachedBlock;
6611 *bytesWritten = 0;
6612 bufferWalker = buffer;
6614 while (size > 0)
6616 ULARGE_INTEGER ulOffset;
6617 DWORD bytesWrittenAt;
6620 * Calculate how many bytes we can copy to this big block.
6622 bytesToWrite =
6623 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6625 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6627 /* BlockChainStream_SetSize should have already been called to ensure we have
6628 * enough blocks in the chain to write into */
6629 if (FAILED(hr))
6631 ERR("not enough blocks in chain to write data\n");
6632 return hr;
6635 if (!cachedBlock)
6637 /* Not in cache, and we're going to write past the end of the block. */
6638 ulOffset.u.HighPart = 0;
6639 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6640 offsetInBlock;
6642 StorageImpl_WriteAt(This->parentStorage,
6643 ulOffset,
6644 bufferWalker,
6645 bytesToWrite,
6646 &bytesWrittenAt);
6648 else
6650 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6652 ULONG read;
6653 if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
6654 return STG_E_READFAULT;
6657 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6658 bytesWrittenAt = bytesToWrite;
6659 cachedBlock->read = TRUE;
6660 cachedBlock->dirty = TRUE;
6663 blockNoInSequence++;
6664 bufferWalker += bytesWrittenAt;
6665 size -= bytesWrittenAt;
6666 *bytesWritten += bytesWrittenAt;
6667 offsetInBlock = 0; /* There is no offset on the next block */
6669 if (bytesWrittenAt != bytesToWrite)
6670 break;
6673 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6676 /******************************************************************************
6677 * BlockChainStream_Shrink
6679 * Shrinks this chain in the big block depot.
6681 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6682 ULARGE_INTEGER newSize)
6684 ULONG blockIndex;
6685 ULONG numBlocks;
6686 int i;
6689 * Figure out how many blocks are needed to contain the new size
6691 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6693 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6694 numBlocks++;
6696 if (numBlocks)
6699 * Go to the new end of chain
6701 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6703 /* Mark the new end of chain */
6704 StorageImpl_SetNextBlockInChain(
6705 This->parentStorage,
6706 blockIndex,
6707 BLOCK_END_OF_CHAIN);
6709 This->tailIndex = blockIndex;
6711 else
6713 if (This->headOfStreamPlaceHolder != 0)
6715 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6717 else
6719 DirEntry chainEntry;
6720 assert(This->ownerDirEntry != DIRENTRY_NULL);
6722 StorageImpl_ReadDirEntry(
6723 This->parentStorage,
6724 This->ownerDirEntry,
6725 &chainEntry);
6727 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6729 StorageImpl_WriteDirEntry(
6730 This->parentStorage,
6731 This->ownerDirEntry,
6732 &chainEntry);
6735 This->tailIndex = BLOCK_END_OF_CHAIN;
6738 This->numBlocks = numBlocks;
6741 * Mark the extra blocks as free
6743 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6745 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6746 StorageImpl_FreeBigBlock(This->parentStorage,
6747 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6748 if (last_run->lastOffset == last_run->firstOffset)
6749 This->indexCacheLen--;
6750 else
6751 last_run->lastOffset--;
6755 * Reset the last accessed block cache.
6757 for (i=0; i<2; i++)
6759 if (This->cachedBlocks[i].index >= numBlocks)
6761 This->cachedBlocks[i].index = 0xffffffff;
6762 This->cachedBlocks[i].dirty = FALSE;
6766 return TRUE;
6769 /******************************************************************************
6770 * BlockChainStream_Enlarge
6772 * Grows this chain in the big block depot.
6774 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6775 ULARGE_INTEGER newSize)
6777 ULONG blockIndex, currentBlock;
6778 ULONG newNumBlocks;
6779 ULONG oldNumBlocks = 0;
6781 blockIndex = BlockChainStream_GetHeadOfChain(This);
6784 * Empty chain. Create the head.
6786 if (blockIndex == BLOCK_END_OF_CHAIN)
6788 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6789 StorageImpl_SetNextBlockInChain(This->parentStorage,
6790 blockIndex,
6791 BLOCK_END_OF_CHAIN);
6793 if (This->headOfStreamPlaceHolder != 0)
6795 *(This->headOfStreamPlaceHolder) = blockIndex;
6797 else
6799 DirEntry chainEntry;
6800 assert(This->ownerDirEntry != DIRENTRY_NULL);
6802 StorageImpl_ReadDirEntry(
6803 This->parentStorage,
6804 This->ownerDirEntry,
6805 &chainEntry);
6807 chainEntry.startingBlock = blockIndex;
6809 StorageImpl_WriteDirEntry(
6810 This->parentStorage,
6811 This->ownerDirEntry,
6812 &chainEntry);
6815 This->tailIndex = blockIndex;
6816 This->numBlocks = 1;
6820 * Figure out how many blocks are needed to contain this stream
6822 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6824 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6825 newNumBlocks++;
6828 * Go to the current end of chain
6830 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6832 currentBlock = blockIndex;
6834 while (blockIndex != BLOCK_END_OF_CHAIN)
6836 This->numBlocks++;
6837 currentBlock = blockIndex;
6839 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6840 &blockIndex)))
6841 return FALSE;
6844 This->tailIndex = currentBlock;
6847 currentBlock = This->tailIndex;
6848 oldNumBlocks = This->numBlocks;
6851 * Add new blocks to the chain
6853 if (oldNumBlocks < newNumBlocks)
6855 while (oldNumBlocks < newNumBlocks)
6857 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6859 StorageImpl_SetNextBlockInChain(
6860 This->parentStorage,
6861 currentBlock,
6862 blockIndex);
6864 StorageImpl_SetNextBlockInChain(
6865 This->parentStorage,
6866 blockIndex,
6867 BLOCK_END_OF_CHAIN);
6869 currentBlock = blockIndex;
6870 oldNumBlocks++;
6873 This->tailIndex = blockIndex;
6874 This->numBlocks = newNumBlocks;
6877 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6878 return FALSE;
6880 return TRUE;
6883 /******************************************************************************
6884 * BlockChainStream_SetSize
6886 * Sets the size of this stream. The big block depot will be updated.
6887 * The file will grow if we grow the chain.
6889 * TODO: Free the actual blocks in the file when we shrink the chain.
6890 * Currently, the blocks are still in the file. So the file size
6891 * doesn't shrink even if we shrink streams.
6893 BOOL BlockChainStream_SetSize(
6894 BlockChainStream* This,
6895 ULARGE_INTEGER newSize)
6897 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6899 if (newSize.u.LowPart == size.u.LowPart)
6900 return TRUE;
6902 if (newSize.u.LowPart < size.u.LowPart)
6904 BlockChainStream_Shrink(This, newSize);
6906 else
6908 BlockChainStream_Enlarge(This, newSize);
6911 return TRUE;
6914 /******************************************************************************
6915 * BlockChainStream_GetSize
6917 * Returns the size of this chain.
6918 * Will return the block count if this chain doesn't have a directory entry.
6920 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6922 DirEntry chainEntry;
6924 if(This->headOfStreamPlaceHolder == NULL)
6927 * This chain has a directory entry so use the size value from there.
6929 StorageImpl_ReadDirEntry(
6930 This->parentStorage,
6931 This->ownerDirEntry,
6932 &chainEntry);
6934 return chainEntry.size;
6936 else
6939 * this chain is a chain that does not have a directory entry, figure out the
6940 * size by making the product number of used blocks times the
6941 * size of them
6943 ULARGE_INTEGER result;
6944 result.u.HighPart = 0;
6946 result.u.LowPart =
6947 BlockChainStream_GetCount(This) *
6948 This->parentStorage->bigBlockSize;
6950 return result;
6954 /******************************************************************************
6955 ** SmallBlockChainStream implementation
6958 SmallBlockChainStream* SmallBlockChainStream_Construct(
6959 StorageImpl* parentStorage,
6960 ULONG* headOfStreamPlaceHolder,
6961 DirRef dirEntry)
6963 SmallBlockChainStream* newStream;
6965 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6967 newStream->parentStorage = parentStorage;
6968 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6969 newStream->ownerDirEntry = dirEntry;
6971 return newStream;
6974 void SmallBlockChainStream_Destroy(
6975 SmallBlockChainStream* This)
6977 HeapFree(GetProcessHeap(), 0, This);
6980 /******************************************************************************
6981 * SmallBlockChainStream_GetHeadOfChain
6983 * Returns the head of this chain of small blocks.
6985 static ULONG SmallBlockChainStream_GetHeadOfChain(
6986 SmallBlockChainStream* This)
6988 DirEntry chainEntry;
6989 HRESULT hr;
6991 if (This->headOfStreamPlaceHolder != NULL)
6992 return *(This->headOfStreamPlaceHolder);
6994 if (This->ownerDirEntry)
6996 hr = StorageImpl_ReadDirEntry(
6997 This->parentStorage,
6998 This->ownerDirEntry,
6999 &chainEntry);
7001 if (SUCCEEDED(hr))
7003 return chainEntry.startingBlock;
7008 return BLOCK_END_OF_CHAIN;
7011 /******************************************************************************
7012 * SmallBlockChainStream_GetNextBlockInChain
7014 * Returns the index of the next small block in this chain.
7016 * Return Values:
7017 * - BLOCK_END_OF_CHAIN: end of this chain
7018 * - BLOCK_UNUSED: small block 'blockIndex' is free
7020 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
7021 SmallBlockChainStream* This,
7022 ULONG blockIndex,
7023 ULONG* nextBlockInChain)
7025 ULARGE_INTEGER offsetOfBlockInDepot;
7026 DWORD buffer;
7027 ULONG bytesRead;
7028 HRESULT res;
7030 *nextBlockInChain = BLOCK_END_OF_CHAIN;
7032 offsetOfBlockInDepot.u.HighPart = 0;
7033 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
7036 * Read those bytes in the buffer from the small block file.
7038 res = BlockChainStream_ReadAt(
7039 This->parentStorage->smallBlockDepotChain,
7040 offsetOfBlockInDepot,
7041 sizeof(DWORD),
7042 &buffer,
7043 &bytesRead);
7045 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
7046 res = STG_E_READFAULT;
7048 if (SUCCEEDED(res))
7050 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
7051 return S_OK;
7054 return res;
7057 /******************************************************************************
7058 * SmallBlockChainStream_SetNextBlockInChain
7060 * Writes the index of the next block of the specified block in the small
7061 * block depot.
7062 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
7063 * To flag a block as free use BLOCK_UNUSED as nextBlock.
7065 static void SmallBlockChainStream_SetNextBlockInChain(
7066 SmallBlockChainStream* This,
7067 ULONG blockIndex,
7068 ULONG nextBlock)
7070 ULARGE_INTEGER offsetOfBlockInDepot;
7071 DWORD buffer;
7072 ULONG bytesWritten;
7074 offsetOfBlockInDepot.u.HighPart = 0;
7075 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
7077 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
7080 * Read those bytes in the buffer from the small block file.
7082 BlockChainStream_WriteAt(
7083 This->parentStorage->smallBlockDepotChain,
7084 offsetOfBlockInDepot,
7085 sizeof(DWORD),
7086 &buffer,
7087 &bytesWritten);
7090 /******************************************************************************
7091 * SmallBlockChainStream_FreeBlock
7093 * Flag small block 'blockIndex' as free in the small block depot.
7095 static void SmallBlockChainStream_FreeBlock(
7096 SmallBlockChainStream* This,
7097 ULONG blockIndex)
7099 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
7102 /******************************************************************************
7103 * SmallBlockChainStream_GetNextFreeBlock
7105 * Returns the index of a free small block. The small block depot will be
7106 * enlarged if necessary. The small block chain will also be enlarged if
7107 * necessary.
7109 static ULONG SmallBlockChainStream_GetNextFreeBlock(
7110 SmallBlockChainStream* This)
7112 ULARGE_INTEGER offsetOfBlockInDepot;
7113 DWORD buffer;
7114 ULONG bytesRead;
7115 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
7116 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
7117 HRESULT res = S_OK;
7118 ULONG smallBlocksPerBigBlock;
7119 DirEntry rootEntry;
7120 ULONG blocksRequired;
7121 ULARGE_INTEGER old_size, size_required;
7123 offsetOfBlockInDepot.u.HighPart = 0;
7126 * Scan the small block depot for a free block
7128 while (nextBlockIndex != BLOCK_UNUSED)
7130 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
7132 res = BlockChainStream_ReadAt(
7133 This->parentStorage->smallBlockDepotChain,
7134 offsetOfBlockInDepot,
7135 sizeof(DWORD),
7136 &buffer,
7137 &bytesRead);
7140 * If we run out of space for the small block depot, enlarge it
7142 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
7144 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
7146 if (nextBlockIndex != BLOCK_UNUSED)
7147 blockIndex++;
7149 else
7151 ULONG count =
7152 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
7154 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
7155 ULARGE_INTEGER newSize, offset;
7156 ULONG bytesWritten;
7158 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
7159 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
7162 * Initialize all the small blocks to free
7164 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
7165 offset.QuadPart = count * This->parentStorage->bigBlockSize;
7166 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
7167 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
7169 StorageImpl_SaveFileHeader(This->parentStorage);
7173 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
7175 smallBlocksPerBigBlock =
7176 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
7179 * Verify if we have to allocate big blocks to contain small blocks
7181 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
7183 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
7185 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
7187 if (size_required.QuadPart > old_size.QuadPart)
7189 BlockChainStream_SetSize(
7190 This->parentStorage->smallBlockRootChain,
7191 size_required);
7193 StorageImpl_ReadDirEntry(
7194 This->parentStorage,
7195 This->parentStorage->base.storageDirEntry,
7196 &rootEntry);
7198 rootEntry.size = size_required;
7200 StorageImpl_WriteDirEntry(
7201 This->parentStorage,
7202 This->parentStorage->base.storageDirEntry,
7203 &rootEntry);
7206 return blockIndex;
7209 /******************************************************************************
7210 * SmallBlockChainStream_ReadAt
7212 * Reads a specified number of bytes from this chain at the specified offset.
7213 * bytesRead may be NULL.
7214 * Failure will be returned if the specified number of bytes has not been read.
7216 HRESULT SmallBlockChainStream_ReadAt(
7217 SmallBlockChainStream* This,
7218 ULARGE_INTEGER offset,
7219 ULONG size,
7220 void* buffer,
7221 ULONG* bytesRead)
7223 HRESULT rc = S_OK;
7224 ULARGE_INTEGER offsetInBigBlockFile;
7225 ULONG blockNoInSequence =
7226 offset.u.LowPart / This->parentStorage->smallBlockSize;
7228 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7229 ULONG bytesToReadInBuffer;
7230 ULONG blockIndex;
7231 ULONG bytesReadFromBigBlockFile;
7232 BYTE* bufferWalker;
7233 ULARGE_INTEGER stream_size;
7236 * This should never happen on a small block file.
7238 assert(offset.u.HighPart==0);
7240 *bytesRead = 0;
7242 stream_size = SmallBlockChainStream_GetSize(This);
7243 if (stream_size.QuadPart > offset.QuadPart)
7244 size = min(stream_size.QuadPart - offset.QuadPart, size);
7245 else
7246 return S_OK;
7249 * Find the first block in the stream that contains part of the buffer.
7251 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7253 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7255 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7256 if(FAILED(rc))
7257 return rc;
7258 blockNoInSequence--;
7262 * Start reading the buffer.
7264 bufferWalker = buffer;
7266 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7269 * Calculate how many bytes we can copy from this small block.
7271 bytesToReadInBuffer =
7272 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7275 * Calculate the offset of the small block in the small block file.
7277 offsetInBigBlockFile.u.HighPart = 0;
7278 offsetInBigBlockFile.u.LowPart =
7279 blockIndex * This->parentStorage->smallBlockSize;
7281 offsetInBigBlockFile.u.LowPart += offsetInBlock;
7284 * Read those bytes in the buffer from the small block file.
7285 * The small block has already been identified so it shouldn't fail
7286 * unless the file is corrupt.
7288 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
7289 offsetInBigBlockFile,
7290 bytesToReadInBuffer,
7291 bufferWalker,
7292 &bytesReadFromBigBlockFile);
7294 if (FAILED(rc))
7295 return rc;
7297 if (!bytesReadFromBigBlockFile)
7298 return STG_E_DOCFILECORRUPT;
7301 * Step to the next big block.
7303 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
7304 if(FAILED(rc))
7305 return STG_E_DOCFILECORRUPT;
7307 bufferWalker += bytesReadFromBigBlockFile;
7308 size -= bytesReadFromBigBlockFile;
7309 *bytesRead += bytesReadFromBigBlockFile;
7310 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
7313 return S_OK;
7316 /******************************************************************************
7317 * SmallBlockChainStream_WriteAt
7319 * Writes the specified number of bytes to this chain at the specified offset.
7320 * Will fail if not all specified number of bytes have been written.
7322 HRESULT SmallBlockChainStream_WriteAt(
7323 SmallBlockChainStream* This,
7324 ULARGE_INTEGER offset,
7325 ULONG size,
7326 const void* buffer,
7327 ULONG* bytesWritten)
7329 ULARGE_INTEGER offsetInBigBlockFile;
7330 ULONG blockNoInSequence =
7331 offset.u.LowPart / This->parentStorage->smallBlockSize;
7333 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
7334 ULONG bytesToWriteInBuffer;
7335 ULONG blockIndex;
7336 ULONG bytesWrittenToBigBlockFile;
7337 const BYTE* bufferWalker;
7338 HRESULT res;
7341 * This should never happen on a small block file.
7343 assert(offset.u.HighPart==0);
7346 * Find the first block in the stream that contains part of the buffer.
7348 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7350 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
7352 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
7353 return STG_E_DOCFILECORRUPT;
7354 blockNoInSequence--;
7358 * Start writing the buffer.
7360 *bytesWritten = 0;
7361 bufferWalker = buffer;
7362 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
7365 * Calculate how many bytes we can copy to this small block.
7367 bytesToWriteInBuffer =
7368 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
7371 * Calculate the offset of the small block in the small block file.
7373 offsetInBigBlockFile.u.HighPart = 0;
7374 offsetInBigBlockFile.u.LowPart =
7375 blockIndex * This->parentStorage->smallBlockSize;
7377 offsetInBigBlockFile.u.LowPart += offsetInBlock;
7380 * Write those bytes in the buffer to the small block file.
7382 res = BlockChainStream_WriteAt(
7383 This->parentStorage->smallBlockRootChain,
7384 offsetInBigBlockFile,
7385 bytesToWriteInBuffer,
7386 bufferWalker,
7387 &bytesWrittenToBigBlockFile);
7388 if (FAILED(res))
7389 return res;
7392 * Step to the next big block.
7394 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7395 &blockIndex)))
7396 return FALSE;
7397 bufferWalker += bytesWrittenToBigBlockFile;
7398 size -= bytesWrittenToBigBlockFile;
7399 *bytesWritten += bytesWrittenToBigBlockFile;
7400 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7403 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7406 /******************************************************************************
7407 * SmallBlockChainStream_Shrink
7409 * Shrinks this chain in the small block depot.
7411 static BOOL SmallBlockChainStream_Shrink(
7412 SmallBlockChainStream* This,
7413 ULARGE_INTEGER newSize)
7415 ULONG blockIndex, extraBlock;
7416 ULONG numBlocks;
7417 ULONG count = 0;
7419 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7421 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7422 numBlocks++;
7424 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7427 * Go to the new end of chain
7429 while (count < numBlocks)
7431 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7432 &blockIndex)))
7433 return FALSE;
7434 count++;
7438 * If the count is 0, we have a special case, the head of the chain was
7439 * just freed.
7441 if (count == 0)
7443 DirEntry chainEntry;
7445 StorageImpl_ReadDirEntry(This->parentStorage,
7446 This->ownerDirEntry,
7447 &chainEntry);
7449 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7451 StorageImpl_WriteDirEntry(This->parentStorage,
7452 This->ownerDirEntry,
7453 &chainEntry);
7456 * We start freeing the chain at the head block.
7458 extraBlock = blockIndex;
7460 else
7462 /* Get the next block before marking the new end */
7463 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7464 &extraBlock)))
7465 return FALSE;
7467 /* Mark the new end of chain */
7468 SmallBlockChainStream_SetNextBlockInChain(
7469 This,
7470 blockIndex,
7471 BLOCK_END_OF_CHAIN);
7475 * Mark the extra blocks as free
7477 while (extraBlock != BLOCK_END_OF_CHAIN)
7479 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7480 &blockIndex)))
7481 return FALSE;
7482 SmallBlockChainStream_FreeBlock(This, extraBlock);
7483 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7484 extraBlock = blockIndex;
7487 return TRUE;
7490 /******************************************************************************
7491 * SmallBlockChainStream_Enlarge
7493 * Grows this chain in the small block depot.
7495 static BOOL SmallBlockChainStream_Enlarge(
7496 SmallBlockChainStream* This,
7497 ULARGE_INTEGER newSize)
7499 ULONG blockIndex, currentBlock;
7500 ULONG newNumBlocks;
7501 ULONG oldNumBlocks = 0;
7503 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7506 * Empty chain. Create the head.
7508 if (blockIndex == BLOCK_END_OF_CHAIN)
7510 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7511 SmallBlockChainStream_SetNextBlockInChain(
7512 This,
7513 blockIndex,
7514 BLOCK_END_OF_CHAIN);
7516 if (This->headOfStreamPlaceHolder != NULL)
7518 *(This->headOfStreamPlaceHolder) = blockIndex;
7520 else
7522 DirEntry chainEntry;
7524 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7525 &chainEntry);
7527 chainEntry.startingBlock = blockIndex;
7529 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7530 &chainEntry);
7534 currentBlock = blockIndex;
7537 * Figure out how many blocks are needed to contain this stream
7539 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7541 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7542 newNumBlocks++;
7545 * Go to the current end of chain
7547 while (blockIndex != BLOCK_END_OF_CHAIN)
7549 oldNumBlocks++;
7550 currentBlock = blockIndex;
7551 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7552 return FALSE;
7556 * Add new blocks to the chain
7558 while (oldNumBlocks < newNumBlocks)
7560 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7561 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7563 SmallBlockChainStream_SetNextBlockInChain(
7564 This,
7565 blockIndex,
7566 BLOCK_END_OF_CHAIN);
7568 currentBlock = blockIndex;
7569 oldNumBlocks++;
7572 return TRUE;
7575 /******************************************************************************
7576 * SmallBlockChainStream_SetSize
7578 * Sets the size of this stream.
7579 * The file will grow if we grow the chain.
7581 * TODO: Free the actual blocks in the file when we shrink the chain.
7582 * Currently, the blocks are still in the file. So the file size
7583 * doesn't shrink even if we shrink streams.
7585 BOOL SmallBlockChainStream_SetSize(
7586 SmallBlockChainStream* This,
7587 ULARGE_INTEGER newSize)
7589 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7591 if (newSize.u.LowPart == size.u.LowPart)
7592 return TRUE;
7594 if (newSize.u.LowPart < size.u.LowPart)
7596 SmallBlockChainStream_Shrink(This, newSize);
7598 else
7600 SmallBlockChainStream_Enlarge(This, newSize);
7603 return TRUE;
7606 /******************************************************************************
7607 * SmallBlockChainStream_GetCount
7609 * Returns the number of small blocks that comprises this chain.
7610 * This is not the size of the stream as the last block may not be full!
7613 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7615 ULONG blockIndex;
7616 ULONG count = 0;
7618 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7620 while(blockIndex != BLOCK_END_OF_CHAIN)
7622 count++;
7624 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7625 blockIndex, &blockIndex)))
7626 return 0;
7629 return count;
7632 /******************************************************************************
7633 * SmallBlockChainStream_GetSize
7635 * Returns the size of this chain.
7637 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7639 DirEntry chainEntry;
7641 if(This->headOfStreamPlaceHolder != NULL)
7643 ULARGE_INTEGER result;
7644 result.u.HighPart = 0;
7646 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7647 This->parentStorage->smallBlockSize;
7649 return result;
7652 StorageImpl_ReadDirEntry(
7653 This->parentStorage,
7654 This->ownerDirEntry,
7655 &chainEntry);
7657 return chainEntry.size;
7660 static HRESULT create_storagefile(
7661 LPCOLESTR pwcsName,
7662 DWORD grfMode,
7663 DWORD grfAttrs,
7664 STGOPTIONS* pStgOptions,
7665 REFIID riid,
7666 void** ppstgOpen)
7668 StorageBaseImpl* newStorage = 0;
7669 HANDLE hFile = INVALID_HANDLE_VALUE;
7670 HRESULT hr = STG_E_INVALIDFLAG;
7671 DWORD shareMode;
7672 DWORD accessMode;
7673 DWORD creationMode;
7674 DWORD fileAttributes;
7675 WCHAR tempFileName[MAX_PATH];
7677 if (ppstgOpen == 0)
7678 return STG_E_INVALIDPOINTER;
7680 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7681 return STG_E_INVALIDPARAMETER;
7683 /* if no share mode given then DENY_NONE is the default */
7684 if (STGM_SHARE_MODE(grfMode) == 0)
7685 grfMode |= STGM_SHARE_DENY_NONE;
7687 if ( FAILED( validateSTGM(grfMode) ))
7688 goto end;
7690 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7691 switch(STGM_ACCESS_MODE(grfMode))
7693 case STGM_WRITE:
7694 case STGM_READWRITE:
7695 break;
7696 default:
7697 goto end;
7700 /* in direct mode, can only use SHARE_EXCLUSIVE */
7701 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7702 goto end;
7704 /* but in transacted mode, any share mode is valid */
7707 * Generate a unique name.
7709 if (pwcsName == 0)
7711 WCHAR tempPath[MAX_PATH];
7712 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7714 memset(tempPath, 0, sizeof(tempPath));
7715 memset(tempFileName, 0, sizeof(tempFileName));
7717 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7718 tempPath[0] = '.';
7720 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7721 pwcsName = tempFileName;
7722 else
7724 hr = STG_E_INSUFFICIENTMEMORY;
7725 goto end;
7728 creationMode = TRUNCATE_EXISTING;
7730 else
7732 creationMode = GetCreationModeFromSTGM(grfMode);
7736 * Interpret the STGM value grfMode
7738 shareMode = GetShareModeFromSTGM(grfMode);
7739 accessMode = GetAccessModeFromSTGM(grfMode);
7741 if (grfMode & STGM_DELETEONRELEASE)
7742 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7743 else
7744 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7746 *ppstgOpen = 0;
7748 hFile = CreateFileW(pwcsName,
7749 accessMode,
7750 shareMode,
7751 NULL,
7752 creationMode,
7753 fileAttributes,
7756 if (hFile == INVALID_HANDLE_VALUE)
7758 if(GetLastError() == ERROR_FILE_EXISTS)
7759 hr = STG_E_FILEALREADYEXISTS;
7760 else
7761 hr = E_FAIL;
7762 goto end;
7766 * Allocate and initialize the new IStorage32object.
7768 hr = Storage_Construct(
7769 hFile,
7770 pwcsName,
7771 NULL,
7772 grfMode,
7773 TRUE,
7774 TRUE,
7775 pStgOptions->ulSectorSize,
7776 &newStorage);
7778 if (FAILED(hr))
7780 goto end;
7783 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
7784 IStorage_Release(&newStorage->IStorage_iface);
7786 end:
7787 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7789 return hr;
7792 /******************************************************************************
7793 * StgCreateDocfile [OLE32.@]
7794 * Creates a new compound file storage object
7796 * PARAMS
7797 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7798 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7799 * reserved [ ?] unused?, usually 0
7800 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7802 * RETURNS
7803 * S_OK if the file was successfully created
7804 * some STG_E_ value if error
7805 * NOTES
7806 * if pwcsName is NULL, create file with new unique name
7807 * the function can returns
7808 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7809 * (unrealized now)
7811 HRESULT WINAPI StgCreateDocfile(
7812 LPCOLESTR pwcsName,
7813 DWORD grfMode,
7814 DWORD reserved,
7815 IStorage **ppstgOpen)
7817 STGOPTIONS stgoptions = {1, 0, 512};
7819 TRACE("(%s, %x, %d, %p)\n",
7820 debugstr_w(pwcsName), grfMode,
7821 reserved, ppstgOpen);
7823 if (ppstgOpen == 0)
7824 return STG_E_INVALIDPOINTER;
7825 if (reserved != 0)
7826 return STG_E_INVALIDPARAMETER;
7828 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7831 /******************************************************************************
7832 * StgCreateStorageEx [OLE32.@]
7834 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7836 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7837 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7839 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7841 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7842 return STG_E_INVALIDPARAMETER;
7845 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7847 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7848 return STG_E_INVALIDPARAMETER;
7851 if (stgfmt == STGFMT_FILE)
7853 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7854 return STG_E_INVALIDPARAMETER;
7857 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7859 STGOPTIONS defaultOptions = {1, 0, 512};
7861 if (!pStgOptions) pStgOptions = &defaultOptions;
7862 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7866 ERR("Invalid stgfmt argument\n");
7867 return STG_E_INVALIDPARAMETER;
7870 /******************************************************************************
7871 * StgCreatePropSetStg [OLE32.@]
7873 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7874 IPropertySetStorage **propset)
7876 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
7877 if (reserved)
7878 return STG_E_INVALIDPARAMETER;
7880 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
7883 /******************************************************************************
7884 * StgOpenStorageEx [OLE32.@]
7886 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7888 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7889 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7891 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7893 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7894 return STG_E_INVALIDPARAMETER;
7897 switch (stgfmt)
7899 case STGFMT_FILE:
7900 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7901 return STG_E_INVALIDPARAMETER;
7903 case STGFMT_STORAGE:
7904 break;
7906 case STGFMT_DOCFILE:
7907 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7909 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7910 return STG_E_INVALIDPARAMETER;
7912 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7913 break;
7915 case STGFMT_ANY:
7916 WARN("STGFMT_ANY assuming storage\n");
7917 break;
7919 default:
7920 return STG_E_INVALIDPARAMETER;
7923 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7927 /******************************************************************************
7928 * StgOpenStorage [OLE32.@]
7930 HRESULT WINAPI StgOpenStorage(
7931 const OLECHAR *pwcsName,
7932 IStorage *pstgPriority,
7933 DWORD grfMode,
7934 SNB snbExclude,
7935 DWORD reserved,
7936 IStorage **ppstgOpen)
7938 StorageBaseImpl* newStorage = 0;
7939 HRESULT hr = S_OK;
7940 HANDLE hFile = 0;
7941 DWORD shareMode;
7942 DWORD accessMode;
7943 LPWSTR temp_name = NULL;
7945 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7946 debugstr_w(pwcsName), pstgPriority, grfMode,
7947 snbExclude, reserved, ppstgOpen);
7949 if (pstgPriority)
7951 /* FIXME: Copy ILockBytes instead? But currently for STGM_PRIORITY it'll be read-only. */
7952 hr = StorageBaseImpl_GetFilename((StorageBaseImpl*)pstgPriority, &temp_name);
7953 if (FAILED(hr)) goto end;
7954 pwcsName = temp_name;
7955 TRACE("using filename %s\n", debugstr_w(temp_name));
7958 if (pwcsName == 0)
7960 hr = STG_E_INVALIDNAME;
7961 goto end;
7964 if (ppstgOpen == 0)
7966 hr = STG_E_INVALIDPOINTER;
7967 goto end;
7970 if (reserved)
7972 hr = STG_E_INVALIDPARAMETER;
7973 goto end;
7976 if (grfMode & STGM_PRIORITY)
7978 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7979 return STG_E_INVALIDFLAG;
7980 if (grfMode & STGM_DELETEONRELEASE)
7981 return STG_E_INVALIDFUNCTION;
7982 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7983 return STG_E_INVALIDFLAG;
7984 grfMode &= ~0xf0; /* remove the existing sharing mode */
7985 grfMode |= STGM_SHARE_DENY_NONE;
7989 * Validate the sharing mode
7991 if (grfMode & STGM_DIRECT_SWMR)
7993 if ((STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_WRITE) &&
7994 (STGM_SHARE_MODE(grfMode) != STGM_SHARE_DENY_NONE))
7996 hr = STG_E_INVALIDFLAG;
7997 goto end;
8000 else if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
8001 switch(STGM_SHARE_MODE(grfMode))
8003 case STGM_SHARE_EXCLUSIVE:
8004 case STGM_SHARE_DENY_WRITE:
8005 break;
8006 default:
8007 hr = STG_E_INVALIDFLAG;
8008 goto end;
8011 if ( FAILED( validateSTGM(grfMode) ) ||
8012 (grfMode&STGM_CREATE))
8014 hr = STG_E_INVALIDFLAG;
8015 goto end;
8018 /* shared reading requires transacted or single writer mode */
8019 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
8020 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
8021 !(grfMode & STGM_TRANSACTED) && !(grfMode & STGM_DIRECT_SWMR))
8023 hr = STG_E_INVALIDFLAG;
8024 goto end;
8028 * Interpret the STGM value grfMode
8030 shareMode = GetShareModeFromSTGM(grfMode);
8031 accessMode = GetAccessModeFromSTGM(grfMode);
8033 *ppstgOpen = 0;
8035 hFile = CreateFileW( pwcsName,
8036 accessMode,
8037 shareMode,
8038 NULL,
8039 OPEN_EXISTING,
8040 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
8043 if (hFile==INVALID_HANDLE_VALUE)
8045 DWORD last_error = GetLastError();
8047 hr = E_FAIL;
8049 switch (last_error)
8051 case ERROR_FILE_NOT_FOUND:
8052 hr = STG_E_FILENOTFOUND;
8053 break;
8055 case ERROR_PATH_NOT_FOUND:
8056 hr = STG_E_PATHNOTFOUND;
8057 break;
8059 case ERROR_ACCESS_DENIED:
8060 case ERROR_WRITE_PROTECT:
8061 hr = STG_E_ACCESSDENIED;
8062 break;
8064 case ERROR_SHARING_VIOLATION:
8065 hr = STG_E_SHAREVIOLATION;
8066 break;
8068 default:
8069 hr = E_FAIL;
8072 goto end;
8076 * Refuse to open the file if it's too small to be a structured storage file
8077 * FIXME: verify the file when reading instead of here
8079 if (GetFileSize(hFile, NULL) < 0x100)
8081 CloseHandle(hFile);
8082 hr = STG_E_FILEALREADYEXISTS;
8083 goto end;
8087 * Allocate and initialize the new IStorage32object.
8089 hr = Storage_Construct(
8090 hFile,
8091 pwcsName,
8092 NULL,
8093 grfMode,
8094 TRUE,
8095 FALSE,
8096 512,
8097 &newStorage);
8099 if (FAILED(hr))
8102 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
8104 if(hr == STG_E_INVALIDHEADER)
8105 hr = STG_E_FILEALREADYEXISTS;
8106 goto end;
8109 *ppstgOpen = &newStorage->IStorage_iface;
8111 end:
8112 CoTaskMemFree(temp_name);
8113 if (pstgPriority) IStorage_Release(pstgPriority);
8114 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
8115 return hr;
8118 /******************************************************************************
8119 * StgCreateDocfileOnILockBytes [OLE32.@]
8121 HRESULT WINAPI StgCreateDocfileOnILockBytes(
8122 ILockBytes *plkbyt,
8123 DWORD grfMode,
8124 DWORD reserved,
8125 IStorage** ppstgOpen)
8127 StorageBaseImpl* newStorage = 0;
8128 HRESULT hr = S_OK;
8130 if ((ppstgOpen == 0) || (plkbyt == 0))
8131 return STG_E_INVALIDPOINTER;
8134 * Allocate and initialize the new IStorage object.
8136 hr = Storage_Construct(
8139 plkbyt,
8140 grfMode,
8141 FALSE,
8142 TRUE,
8143 512,
8144 &newStorage);
8146 if (FAILED(hr))
8148 return hr;
8151 *ppstgOpen = &newStorage->IStorage_iface;
8153 return hr;
8156 /******************************************************************************
8157 * StgOpenStorageOnILockBytes [OLE32.@]
8159 HRESULT WINAPI StgOpenStorageOnILockBytes(
8160 ILockBytes *plkbyt,
8161 IStorage *pstgPriority,
8162 DWORD grfMode,
8163 SNB snbExclude,
8164 DWORD reserved,
8165 IStorage **ppstgOpen)
8167 StorageBaseImpl* newStorage = 0;
8168 HRESULT hr = S_OK;
8170 if ((plkbyt == 0) || (ppstgOpen == 0))
8171 return STG_E_INVALIDPOINTER;
8173 if ( FAILED( validateSTGM(grfMode) ))
8174 return STG_E_INVALIDFLAG;
8176 *ppstgOpen = 0;
8179 * Allocate and initialize the new IStorage object.
8181 hr = Storage_Construct(
8184 plkbyt,
8185 grfMode,
8186 FALSE,
8187 FALSE,
8188 512,
8189 &newStorage);
8191 if (FAILED(hr))
8193 return hr;
8196 *ppstgOpen = &newStorage->IStorage_iface;
8198 return hr;
8201 /******************************************************************************
8202 * StgSetTimes [ole32.@]
8203 * StgSetTimes [OLE32.@]
8207 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
8208 FILETIME const *patime, FILETIME const *pmtime)
8210 IStorage *stg = NULL;
8211 HRESULT r;
8213 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
8215 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
8216 0, 0, &stg);
8217 if( SUCCEEDED(r) )
8219 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
8220 IStorage_Release(stg);
8223 return r;
8226 /******************************************************************************
8227 * StgIsStorageILockBytes [OLE32.@]
8229 * Determines if the ILockBytes contains a storage object.
8231 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
8233 BYTE sig[sizeof(STORAGE_magic)];
8234 ULARGE_INTEGER offset;
8235 ULONG read = 0;
8237 offset.u.HighPart = 0;
8238 offset.u.LowPart = 0;
8240 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
8242 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
8243 return S_OK;
8245 return S_FALSE;
8248 /******************************************************************************
8249 * WriteClassStg [OLE32.@]
8251 * This method will store the specified CLSID in the specified storage object
8253 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
8255 if(!pStg)
8256 return E_INVALIDARG;
8258 if(!rclsid)
8259 return STG_E_INVALIDPOINTER;
8261 return IStorage_SetClass(pStg, rclsid);
8264 /***********************************************************************
8265 * ReadClassStg (OLE32.@)
8267 * This method reads the CLSID previously written to a storage object with
8268 * the WriteClassStg.
8270 * PARAMS
8271 * pstg [I] IStorage pointer
8272 * pclsid [O] Pointer to where the CLSID is written
8274 * RETURNS
8275 * Success: S_OK.
8276 * Failure: HRESULT code.
8278 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
8280 STATSTG pstatstg;
8281 HRESULT hRes;
8283 TRACE("(%p, %p)\n", pstg, pclsid);
8285 if(!pstg || !pclsid)
8286 return E_INVALIDARG;
8289 * read a STATSTG structure (contains the clsid) from the storage
8291 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
8293 if(SUCCEEDED(hRes))
8294 *pclsid=pstatstg.clsid;
8296 return hRes;
8299 /***********************************************************************
8300 * OleLoadFromStream (OLE32.@)
8302 * This function loads an object from stream
8304 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
8306 CLSID clsid;
8307 HRESULT res;
8308 LPPERSISTSTREAM xstm;
8310 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
8312 res=ReadClassStm(pStm,&clsid);
8313 if (FAILED(res))
8314 return res;
8315 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
8316 if (FAILED(res))
8317 return res;
8318 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
8319 if (FAILED(res)) {
8320 IUnknown_Release((IUnknown*)*ppvObj);
8321 return res;
8323 res=IPersistStream_Load(xstm,pStm);
8324 IPersistStream_Release(xstm);
8325 /* FIXME: all refcounts ok at this point? I think they should be:
8326 * pStm : unchanged
8327 * ppvObj : 1
8328 * xstm : 0 (released)
8330 return res;
8333 /***********************************************************************
8334 * OleSaveToStream (OLE32.@)
8336 * This function saves an object with the IPersistStream interface on it
8337 * to the specified stream.
8339 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
8342 CLSID clsid;
8343 HRESULT res;
8345 TRACE("(%p,%p)\n",pPStm,pStm);
8347 res=IPersistStream_GetClassID(pPStm,&clsid);
8349 if (SUCCEEDED(res)){
8351 res=WriteClassStm(pStm,&clsid);
8353 if (SUCCEEDED(res))
8355 res=IPersistStream_Save(pPStm,pStm,TRUE);
8358 TRACE("Finished Save\n");
8359 return res;
8362 /****************************************************************************
8363 * This method validate a STGM parameter that can contain the values below
8365 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
8366 * The stgm values contained in 0xffff0000 are bitmasks.
8368 * STGM_DIRECT 0x00000000
8369 * STGM_TRANSACTED 0x00010000
8370 * STGM_SIMPLE 0x08000000
8372 * STGM_READ 0x00000000
8373 * STGM_WRITE 0x00000001
8374 * STGM_READWRITE 0x00000002
8376 * STGM_SHARE_DENY_NONE 0x00000040
8377 * STGM_SHARE_DENY_READ 0x00000030
8378 * STGM_SHARE_DENY_WRITE 0x00000020
8379 * STGM_SHARE_EXCLUSIVE 0x00000010
8381 * STGM_PRIORITY 0x00040000
8382 * STGM_DELETEONRELEASE 0x04000000
8384 * STGM_CREATE 0x00001000
8385 * STGM_CONVERT 0x00020000
8386 * STGM_FAILIFTHERE 0x00000000
8388 * STGM_NOSCRATCH 0x00100000
8389 * STGM_NOSNAPSHOT 0x00200000
8391 static HRESULT validateSTGM(DWORD stgm)
8393 DWORD access = STGM_ACCESS_MODE(stgm);
8394 DWORD share = STGM_SHARE_MODE(stgm);
8395 DWORD create = STGM_CREATE_MODE(stgm);
8397 if (stgm&~STGM_KNOWN_FLAGS)
8399 ERR("unknown flags %08x\n", stgm);
8400 return E_FAIL;
8403 switch (access)
8405 case STGM_READ:
8406 case STGM_WRITE:
8407 case STGM_READWRITE:
8408 break;
8409 default:
8410 return E_FAIL;
8413 switch (share)
8415 case STGM_SHARE_DENY_NONE:
8416 case STGM_SHARE_DENY_READ:
8417 case STGM_SHARE_DENY_WRITE:
8418 case STGM_SHARE_EXCLUSIVE:
8419 break;
8420 case 0:
8421 if (!(stgm & STGM_TRANSACTED))
8422 return E_FAIL;
8423 break;
8424 default:
8425 return E_FAIL;
8428 switch (create)
8430 case STGM_CREATE:
8431 case STGM_FAILIFTHERE:
8432 break;
8433 default:
8434 return E_FAIL;
8438 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8440 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8441 return E_FAIL;
8444 * STGM_CREATE | STGM_CONVERT
8445 * if both are false, STGM_FAILIFTHERE is set to TRUE
8447 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8448 return E_FAIL;
8451 * STGM_NOSCRATCH requires STGM_TRANSACTED
8453 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8454 return E_FAIL;
8457 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8458 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8460 if ( (stgm & STGM_NOSNAPSHOT) &&
8461 (!(stgm & STGM_TRANSACTED) ||
8462 share == STGM_SHARE_EXCLUSIVE ||
8463 share == STGM_SHARE_DENY_WRITE) )
8464 return E_FAIL;
8466 return S_OK;
8469 /****************************************************************************
8470 * GetShareModeFromSTGM
8472 * This method will return a share mode flag from a STGM value.
8473 * The STGM value is assumed valid.
8475 static DWORD GetShareModeFromSTGM(DWORD stgm)
8477 switch (STGM_SHARE_MODE(stgm))
8479 case 0:
8480 assert(stgm & STGM_TRANSACTED);
8481 /* fall-through */
8482 case STGM_SHARE_DENY_NONE:
8483 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8484 case STGM_SHARE_DENY_READ:
8485 return FILE_SHARE_WRITE;
8486 case STGM_SHARE_DENY_WRITE:
8487 case STGM_SHARE_EXCLUSIVE:
8488 return FILE_SHARE_READ;
8490 ERR("Invalid share mode!\n");
8491 assert(0);
8492 return 0;
8495 /****************************************************************************
8496 * GetAccessModeFromSTGM
8498 * This method will return an access mode flag from a STGM value.
8499 * The STGM value is assumed valid.
8501 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8503 switch (STGM_ACCESS_MODE(stgm))
8505 case STGM_READ:
8506 return GENERIC_READ;
8507 case STGM_WRITE:
8508 case STGM_READWRITE:
8509 return GENERIC_READ | GENERIC_WRITE;
8511 ERR("Invalid access mode!\n");
8512 assert(0);
8513 return 0;
8516 /****************************************************************************
8517 * GetCreationModeFromSTGM
8519 * This method will return a creation mode flag from a STGM value.
8520 * The STGM value is assumed valid.
8522 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8524 switch(STGM_CREATE_MODE(stgm))
8526 case STGM_CREATE:
8527 return CREATE_ALWAYS;
8528 case STGM_CONVERT:
8529 FIXME("STGM_CONVERT not implemented!\n");
8530 return CREATE_NEW;
8531 case STGM_FAILIFTHERE:
8532 return CREATE_NEW;
8534 ERR("Invalid create mode!\n");
8535 assert(0);
8536 return 0;
8540 /*************************************************************************
8541 * OLECONVERT_LoadOLE10 [Internal]
8543 * Loads the OLE10 STREAM to memory
8545 * PARAMS
8546 * pOleStream [I] The OLESTREAM
8547 * pData [I] Data Structure for the OLESTREAM Data
8549 * RETURNS
8550 * Success: S_OK
8551 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8552 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8554 * NOTES
8555 * This function is used by OleConvertOLESTREAMToIStorage only.
8557 * Memory allocated for pData must be freed by the caller
8559 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8561 DWORD dwSize;
8562 HRESULT hRes = S_OK;
8563 int nTryCnt=0;
8564 int max_try = 6;
8566 pData->pData = NULL;
8567 pData->pstrOleObjFileName = NULL;
8569 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8571 /* Get the OleID */
8572 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8573 if(dwSize != sizeof(pData->dwOleID))
8575 hRes = CONVERT10_E_OLESTREAM_GET;
8577 else if(pData->dwOleID != OLESTREAM_ID)
8579 hRes = CONVERT10_E_OLESTREAM_FMT;
8581 else
8583 hRes = S_OK;
8584 break;
8588 if(hRes == S_OK)
8590 /* Get the TypeID... more info needed for this field */
8591 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8592 if(dwSize != sizeof(pData->dwTypeID))
8594 hRes = CONVERT10_E_OLESTREAM_GET;
8597 if(hRes == S_OK)
8599 if(pData->dwTypeID != 0)
8601 /* Get the length of the OleTypeName */
8602 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8603 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8605 hRes = CONVERT10_E_OLESTREAM_GET;
8608 if(hRes == S_OK)
8610 if(pData->dwOleTypeNameLength > 0)
8612 /* Get the OleTypeName */
8613 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8614 if(dwSize != pData->dwOleTypeNameLength)
8616 hRes = CONVERT10_E_OLESTREAM_GET;
8620 if(bStrem1)
8622 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8623 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8625 hRes = CONVERT10_E_OLESTREAM_GET;
8627 if(hRes == S_OK)
8629 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8630 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8631 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8632 if(pData->pstrOleObjFileName)
8634 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8635 if(dwSize != pData->dwOleObjFileNameLength)
8637 hRes = CONVERT10_E_OLESTREAM_GET;
8640 else
8641 hRes = CONVERT10_E_OLESTREAM_GET;
8644 else
8646 /* Get the Width of the Metafile */
8647 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8648 if(dwSize != sizeof(pData->dwMetaFileWidth))
8650 hRes = CONVERT10_E_OLESTREAM_GET;
8652 if(hRes == S_OK)
8654 /* Get the Height of the Metafile */
8655 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8656 if(dwSize != sizeof(pData->dwMetaFileHeight))
8658 hRes = CONVERT10_E_OLESTREAM_GET;
8662 if(hRes == S_OK)
8664 /* Get the Length of the Data */
8665 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8666 if(dwSize != sizeof(pData->dwDataLength))
8668 hRes = CONVERT10_E_OLESTREAM_GET;
8672 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8674 if(!bStrem1) /* if it is a second OLE stream data */
8676 pData->dwDataLength -= 8;
8677 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8678 if(dwSize != sizeof(pData->strUnknown))
8680 hRes = CONVERT10_E_OLESTREAM_GET;
8684 if(hRes == S_OK)
8686 if(pData->dwDataLength > 0)
8688 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8690 /* Get Data (ex. IStorage, Metafile, or BMP) */
8691 if(pData->pData)
8693 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8694 if(dwSize != pData->dwDataLength)
8696 hRes = CONVERT10_E_OLESTREAM_GET;
8699 else
8701 hRes = CONVERT10_E_OLESTREAM_GET;
8707 return hRes;
8710 /*************************************************************************
8711 * OLECONVERT_SaveOLE10 [Internal]
8713 * Saves the OLE10 STREAM From memory
8715 * PARAMS
8716 * pData [I] Data Structure for the OLESTREAM Data
8717 * pOleStream [I] The OLESTREAM to save
8719 * RETURNS
8720 * Success: S_OK
8721 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8723 * NOTES
8724 * This function is used by OleConvertIStorageToOLESTREAM only.
8727 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8729 DWORD dwSize;
8730 HRESULT hRes = S_OK;
8733 /* Set the OleID */
8734 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8735 if(dwSize != sizeof(pData->dwOleID))
8737 hRes = CONVERT10_E_OLESTREAM_PUT;
8740 if(hRes == S_OK)
8742 /* Set the TypeID */
8743 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8744 if(dwSize != sizeof(pData->dwTypeID))
8746 hRes = CONVERT10_E_OLESTREAM_PUT;
8750 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8752 /* Set the Length of the OleTypeName */
8753 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8754 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8756 hRes = CONVERT10_E_OLESTREAM_PUT;
8759 if(hRes == S_OK)
8761 if(pData->dwOleTypeNameLength > 0)
8763 /* Set the OleTypeName */
8764 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8765 if(dwSize != pData->dwOleTypeNameLength)
8767 hRes = CONVERT10_E_OLESTREAM_PUT;
8772 if(hRes == S_OK)
8774 /* Set the width of the Metafile */
8775 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8776 if(dwSize != sizeof(pData->dwMetaFileWidth))
8778 hRes = CONVERT10_E_OLESTREAM_PUT;
8782 if(hRes == S_OK)
8784 /* Set the height of the Metafile */
8785 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8786 if(dwSize != sizeof(pData->dwMetaFileHeight))
8788 hRes = CONVERT10_E_OLESTREAM_PUT;
8792 if(hRes == S_OK)
8794 /* Set the length of the Data */
8795 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8796 if(dwSize != sizeof(pData->dwDataLength))
8798 hRes = CONVERT10_E_OLESTREAM_PUT;
8802 if(hRes == S_OK)
8804 if(pData->dwDataLength > 0)
8806 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8807 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8808 if(dwSize != pData->dwDataLength)
8810 hRes = CONVERT10_E_OLESTREAM_PUT;
8815 return hRes;
8818 /*************************************************************************
8819 * OLECONVERT_GetOLE20FromOLE10[Internal]
8821 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8822 * opens it, and copies the content to the dest IStorage for
8823 * OleConvertOLESTREAMToIStorage
8826 * PARAMS
8827 * pDestStorage [I] The IStorage to copy the data to
8828 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8829 * nBufferLength [I] The size of the buffer
8831 * RETURNS
8832 * Nothing
8834 * NOTES
8838 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8840 HRESULT hRes;
8841 HANDLE hFile;
8842 IStorage *pTempStorage;
8843 DWORD dwNumOfBytesWritten;
8844 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8845 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8847 /* Create a temp File */
8848 GetTempPathW(MAX_PATH, wstrTempDir);
8849 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8850 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8852 if(hFile != INVALID_HANDLE_VALUE)
8854 /* Write IStorage Data to File */
8855 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8856 CloseHandle(hFile);
8858 /* Open and copy temp storage to the Dest Storage */
8859 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8860 if(hRes == S_OK)
8862 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8863 IStorage_Release(pTempStorage);
8865 DeleteFileW(wstrTempFile);
8870 /*************************************************************************
8871 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8873 * Saves the OLE10 STREAM From memory
8875 * PARAMS
8876 * pStorage [I] The Src IStorage to copy
8877 * pData [I] The Dest Memory to write to.
8879 * RETURNS
8880 * The size in bytes allocated for pData
8882 * NOTES
8883 * Memory allocated for pData must be freed by the caller
8885 * Used by OleConvertIStorageToOLESTREAM only.
8888 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8890 HANDLE hFile;
8891 HRESULT hRes;
8892 DWORD nDataLength = 0;
8893 IStorage *pTempStorage;
8894 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8895 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8897 *pData = NULL;
8899 /* Create temp Storage */
8900 GetTempPathW(MAX_PATH, wstrTempDir);
8901 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8902 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8904 if(hRes == S_OK)
8906 /* Copy Src Storage to the Temp Storage */
8907 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8908 IStorage_Release(pTempStorage);
8910 /* Open Temp Storage as a file and copy to memory */
8911 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8912 if(hFile != INVALID_HANDLE_VALUE)
8914 nDataLength = GetFileSize(hFile, NULL);
8915 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8916 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8917 CloseHandle(hFile);
8919 DeleteFileW(wstrTempFile);
8921 return nDataLength;
8924 /*************************************************************************
8925 * STORAGE_CreateOleStream [Internal]
8927 * Creates the "\001OLE" stream in the IStorage if necessary.
8929 * PARAMS
8930 * storage [I] Dest storage to create the stream in
8931 * flags [I] flags to be set for newly created stream
8933 * RETURNS
8934 * HRESULT return value
8936 * NOTES
8938 * This stream is still unknown, MS Word seems to have extra data
8939 * but since the data is stored in the OLESTREAM there should be
8940 * no need to recreate the stream. If the stream is manually
8941 * deleted it will create it with this default data.
8944 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
8946 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
8947 static const DWORD version_magic = 0x02000001;
8948 IStream *stream;
8949 HRESULT hr;
8951 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
8952 if (hr == S_OK)
8954 struct empty_1ole_stream {
8955 DWORD version_magic;
8956 DWORD flags;
8957 DWORD update_options;
8958 DWORD reserved;
8959 DWORD mon_stream_size;
8961 struct empty_1ole_stream stream_data;
8963 stream_data.version_magic = version_magic;
8964 stream_data.flags = flags;
8965 stream_data.update_options = 0;
8966 stream_data.reserved = 0;
8967 stream_data.mon_stream_size = 0;
8969 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
8970 IStream_Release(stream);
8973 return hr;
8976 /* write a string to a stream, preceded by its length */
8977 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8979 HRESULT r;
8980 LPSTR str;
8981 DWORD len = 0;
8983 if( string )
8984 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8985 r = IStream_Write( stm, &len, sizeof(len), NULL);
8986 if( FAILED( r ) )
8987 return r;
8988 if(len == 0)
8989 return r;
8990 str = CoTaskMemAlloc( len );
8991 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8992 r = IStream_Write( stm, str, len, NULL);
8993 CoTaskMemFree( str );
8994 return r;
8997 /* read a string preceded by its length from a stream */
8998 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
9000 HRESULT r;
9001 DWORD len, count = 0;
9002 LPSTR str;
9003 LPWSTR wstr;
9005 r = IStream_Read( stm, &len, sizeof(len), &count );
9006 if( FAILED( r ) )
9007 return r;
9008 if( count != sizeof(len) )
9009 return E_OUTOFMEMORY;
9011 TRACE("%d bytes\n",len);
9013 str = CoTaskMemAlloc( len );
9014 if( !str )
9015 return E_OUTOFMEMORY;
9016 count = 0;
9017 r = IStream_Read( stm, str, len, &count );
9018 if( FAILED( r ) )
9019 return r;
9020 if( count != len )
9022 CoTaskMemFree( str );
9023 return E_OUTOFMEMORY;
9026 TRACE("Read string %s\n",debugstr_an(str,len));
9028 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
9029 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
9030 if( wstr )
9032 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
9033 wstr[len] = 0;
9035 CoTaskMemFree( str );
9037 *string = wstr;
9039 return r;
9043 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
9044 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
9046 IStream *pstm;
9047 HRESULT r = S_OK;
9048 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9050 static const BYTE unknown1[12] =
9051 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
9052 0xFF, 0xFF, 0xFF, 0xFF};
9053 static const BYTE unknown2[16] =
9054 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
9055 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
9057 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
9058 debugstr_w(lpszUserType), debugstr_w(szClipName),
9059 debugstr_w(szProgIDName));
9061 /* Create a CompObj stream */
9062 r = IStorage_CreateStream(pstg, szwStreamName,
9063 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
9064 if( FAILED (r) )
9065 return r;
9067 /* Write CompObj Structure to stream */
9068 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
9070 if( SUCCEEDED( r ) )
9071 r = WriteClassStm( pstm, clsid );
9073 if( SUCCEEDED( r ) )
9074 r = STREAM_WriteString( pstm, lpszUserType );
9075 if( SUCCEEDED( r ) )
9076 r = STREAM_WriteString( pstm, szClipName );
9077 if( SUCCEEDED( r ) )
9078 r = STREAM_WriteString( pstm, szProgIDName );
9079 if( SUCCEEDED( r ) )
9080 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
9082 IStream_Release( pstm );
9084 return r;
9087 /***********************************************************************
9088 * WriteFmtUserTypeStg (OLE32.@)
9090 HRESULT WINAPI WriteFmtUserTypeStg(
9091 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
9093 STATSTG stat;
9094 HRESULT r;
9095 WCHAR szwClipName[0x40];
9096 CLSID clsid;
9097 LPWSTR wstrProgID = NULL;
9098 DWORD n;
9100 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
9102 /* get the clipboard format name */
9103 if( cf )
9105 n = GetClipboardFormatNameW( cf, szwClipName,
9106 sizeof(szwClipName)/sizeof(szwClipName[0]) );
9107 szwClipName[n]=0;
9110 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
9112 r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
9113 if(SUCCEEDED(r))
9114 clsid = stat.clsid;
9115 else
9116 clsid = CLSID_NULL;
9118 ProgIDFromCLSID(&clsid, &wstrProgID);
9120 TRACE("progid is %s\n",debugstr_w(wstrProgID));
9122 r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
9123 cf ? szwClipName : NULL, wstrProgID );
9125 CoTaskMemFree(wstrProgID);
9127 return r;
9131 /******************************************************************************
9132 * ReadFmtUserTypeStg [OLE32.@]
9134 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
9136 HRESULT r;
9137 IStream *stm = 0;
9138 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
9139 unsigned char unknown1[12];
9140 unsigned char unknown2[16];
9141 DWORD count;
9142 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
9143 CLSID clsid;
9145 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
9147 r = IStorage_OpenStream( pstg, szCompObj, NULL,
9148 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
9149 if( FAILED ( r ) )
9151 WARN("Failed to open stream r = %08x\n", r);
9152 return r;
9155 /* read the various parts of the structure */
9156 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
9157 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
9158 goto end;
9159 r = ReadClassStm( stm, &clsid );
9160 if( FAILED( r ) )
9161 goto end;
9163 r = STREAM_ReadString( stm, &szCLSIDName );
9164 if( FAILED( r ) )
9165 goto end;
9167 r = STREAM_ReadString( stm, &szOleTypeName );
9168 if( FAILED( r ) )
9169 goto end;
9171 r = STREAM_ReadString( stm, &szProgIDName );
9172 if( FAILED( r ) )
9173 goto end;
9175 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
9176 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
9177 goto end;
9179 /* ok, success... now we just need to store what we found */
9180 if( pcf )
9181 *pcf = RegisterClipboardFormatW( szOleTypeName );
9183 if( lplpszUserType )
9185 *lplpszUserType = szCLSIDName;
9186 szCLSIDName = NULL;
9189 end:
9190 CoTaskMemFree( szCLSIDName );
9191 CoTaskMemFree( szOleTypeName );
9192 CoTaskMemFree( szProgIDName );
9193 IStream_Release( stm );
9195 return r;
9199 /*************************************************************************
9200 * OLECONVERT_CreateCompObjStream [Internal]
9202 * Creates a "\001CompObj" is the destination IStorage if necessary.
9204 * PARAMS
9205 * pStorage [I] The dest IStorage to create the CompObj Stream
9206 * if necessary.
9207 * strOleTypeName [I] The ProgID
9209 * RETURNS
9210 * Success: S_OK
9211 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9213 * NOTES
9214 * This function is used by OleConvertOLESTREAMToIStorage only.
9216 * The stream data is stored in the OLESTREAM and there should be
9217 * no need to recreate the stream. If the stream is manually
9218 * deleted it will attempt to create it by querying the registry.
9222 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
9224 IStream *pStream;
9225 HRESULT hStorageRes, hRes = S_OK;
9226 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
9227 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9228 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
9230 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
9231 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
9233 /* Initialize the CompObj structure */
9234 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
9235 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
9236 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
9239 /* Create a CompObj stream if it doesn't exist */
9240 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
9241 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9242 if(hStorageRes == S_OK)
9244 /* copy the OleTypeName to the compobj struct */
9245 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
9246 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
9248 /* copy the OleTypeName to the compobj struct */
9249 /* Note: in the test made, these were Identical */
9250 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
9251 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
9253 /* Get the CLSID */
9254 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
9255 bufferW, OLESTREAM_MAX_STR_LEN );
9256 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
9258 if(hRes == S_OK)
9260 HKEY hKey;
9261 LONG hErr;
9262 /* Get the CLSID Default Name from the Registry */
9263 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
9264 if(hErr == ERROR_SUCCESS)
9266 char strTemp[OLESTREAM_MAX_STR_LEN];
9267 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
9268 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
9269 if(hErr == ERROR_SUCCESS)
9271 strcpy(IStorageCompObj.strCLSIDName, strTemp);
9273 RegCloseKey(hKey);
9277 /* Write CompObj Structure to stream */
9278 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
9280 WriteClassStm(pStream,&(IStorageCompObj.clsid));
9282 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
9283 if(IStorageCompObj.dwCLSIDNameLength > 0)
9285 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
9287 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
9288 if(IStorageCompObj.dwOleTypeNameLength > 0)
9290 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
9292 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
9293 if(IStorageCompObj.dwProgIDNameLength > 0)
9295 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
9297 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
9298 IStream_Release(pStream);
9300 return hRes;
9304 /*************************************************************************
9305 * OLECONVERT_CreateOlePresStream[Internal]
9307 * Creates the "\002OlePres000" Stream with the Metafile data
9309 * PARAMS
9310 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
9311 * dwExtentX [I] Width of the Metafile
9312 * dwExtentY [I] Height of the Metafile
9313 * pData [I] Metafile data
9314 * dwDataLength [I] Size of the Metafile data
9316 * RETURNS
9317 * Success: S_OK
9318 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
9320 * NOTES
9321 * This function is used by OleConvertOLESTREAMToIStorage only.
9324 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
9326 HRESULT hRes;
9327 IStream *pStream;
9328 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9329 BYTE pOlePresStreamHeader [] =
9331 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
9332 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9333 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9334 0x00, 0x00, 0x00, 0x00
9337 BYTE pOlePresStreamHeaderEmpty [] =
9339 0x00, 0x00, 0x00, 0x00,
9340 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
9341 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
9342 0x00, 0x00, 0x00, 0x00
9345 /* Create the OlePres000 Stream */
9346 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9347 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9349 if(hRes == S_OK)
9351 DWORD nHeaderSize;
9352 OLECONVERT_ISTORAGE_OLEPRES OlePres;
9354 memset(&OlePres, 0, sizeof(OlePres));
9355 /* Do we have any metafile data to save */
9356 if(dwDataLength > 0)
9358 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
9359 nHeaderSize = sizeof(pOlePresStreamHeader);
9361 else
9363 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
9364 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
9366 /* Set width and height of the metafile */
9367 OlePres.dwExtentX = dwExtentX;
9368 OlePres.dwExtentY = -dwExtentY;
9370 /* Set Data and Length */
9371 if(dwDataLength > sizeof(METAFILEPICT16))
9373 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
9374 OlePres.pData = &(pData[8]);
9376 /* Save OlePres000 Data to Stream */
9377 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
9378 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
9379 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
9380 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
9381 if(OlePres.dwSize > 0)
9383 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
9385 IStream_Release(pStream);
9389 /*************************************************************************
9390 * OLECONVERT_CreateOle10NativeStream [Internal]
9392 * Creates the "\001Ole10Native" Stream (should contain a BMP)
9394 * PARAMS
9395 * pStorage [I] Dest storage to create the stream in
9396 * pData [I] Ole10 Native Data (ex. bmp)
9397 * dwDataLength [I] Size of the Ole10 Native Data
9399 * RETURNS
9400 * Nothing
9402 * NOTES
9403 * This function is used by OleConvertOLESTREAMToIStorage only.
9405 * Might need to verify the data and return appropriate error message
9408 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9410 HRESULT hRes;
9411 IStream *pStream;
9412 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9414 /* Create the Ole10Native Stream */
9415 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9416 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9418 if(hRes == S_OK)
9420 /* Write info to stream */
9421 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9422 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9423 IStream_Release(pStream);
9428 /*************************************************************************
9429 * OLECONVERT_GetOLE10ProgID [Internal]
9431 * Finds the ProgID (or OleTypeID) from the IStorage
9433 * PARAMS
9434 * pStorage [I] The Src IStorage to get the ProgID
9435 * strProgID [I] the ProgID string to get
9436 * dwSize [I] the size of the string
9438 * RETURNS
9439 * Success: S_OK
9440 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9442 * NOTES
9443 * This function is used by OleConvertIStorageToOLESTREAM only.
9447 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9449 HRESULT hRes;
9450 IStream *pStream;
9451 LARGE_INTEGER iSeekPos;
9452 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9453 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9455 /* Open the CompObj Stream */
9456 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9457 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9458 if(hRes == S_OK)
9461 /*Get the OleType from the CompObj Stream */
9462 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9463 iSeekPos.u.HighPart = 0;
9465 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9466 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9467 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9468 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9469 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9470 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9471 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9473 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9474 if(*dwSize > 0)
9476 IStream_Read(pStream, strProgID, *dwSize, NULL);
9478 IStream_Release(pStream);
9480 else
9482 STATSTG stat;
9483 LPOLESTR wstrProgID;
9485 /* Get the OleType from the registry */
9486 REFCLSID clsid = &(stat.clsid);
9487 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9488 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9489 if(hRes == S_OK)
9491 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9492 CoTaskMemFree(wstrProgID);
9496 return hRes;
9499 /*************************************************************************
9500 * OLECONVERT_GetOle10PresData [Internal]
9502 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9504 * PARAMS
9505 * pStorage [I] Src IStroage
9506 * pOleStream [I] Dest OleStream Mem Struct
9508 * RETURNS
9509 * Nothing
9511 * NOTES
9512 * This function is used by OleConvertIStorageToOLESTREAM only.
9514 * Memory allocated for pData must be freed by the caller
9518 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9521 HRESULT hRes;
9522 IStream *pStream;
9523 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9525 /* Initialize Default data for OLESTREAM */
9526 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9527 pOleStreamData[0].dwTypeID = 2;
9528 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9529 pOleStreamData[1].dwTypeID = 0;
9530 pOleStreamData[0].dwMetaFileWidth = 0;
9531 pOleStreamData[0].dwMetaFileHeight = 0;
9532 pOleStreamData[0].pData = NULL;
9533 pOleStreamData[1].pData = NULL;
9535 /* Open Ole10Native Stream */
9536 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9537 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9538 if(hRes == S_OK)
9541 /* Read Size and Data */
9542 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9543 if(pOleStreamData->dwDataLength > 0)
9545 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9546 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9548 IStream_Release(pStream);
9554 /*************************************************************************
9555 * OLECONVERT_GetOle20PresData[Internal]
9557 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9559 * PARAMS
9560 * pStorage [I] Src IStroage
9561 * pOleStreamData [I] Dest OleStream Mem Struct
9563 * RETURNS
9564 * Nothing
9566 * NOTES
9567 * This function is used by OleConvertIStorageToOLESTREAM only.
9569 * Memory allocated for pData must be freed by the caller
9571 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9573 HRESULT hRes;
9574 IStream *pStream;
9575 OLECONVERT_ISTORAGE_OLEPRES olePress;
9576 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9578 /* Initialize Default data for OLESTREAM */
9579 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9580 pOleStreamData[0].dwTypeID = 2;
9581 pOleStreamData[0].dwMetaFileWidth = 0;
9582 pOleStreamData[0].dwMetaFileHeight = 0;
9583 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9584 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9585 pOleStreamData[1].dwTypeID = 0;
9586 pOleStreamData[1].dwOleTypeNameLength = 0;
9587 pOleStreamData[1].strOleTypeName[0] = 0;
9588 pOleStreamData[1].dwMetaFileWidth = 0;
9589 pOleStreamData[1].dwMetaFileHeight = 0;
9590 pOleStreamData[1].pData = NULL;
9591 pOleStreamData[1].dwDataLength = 0;
9594 /* Open OlePress000 stream */
9595 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9596 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9597 if(hRes == S_OK)
9599 LARGE_INTEGER iSeekPos;
9600 METAFILEPICT16 MetaFilePict;
9601 static const char strMetafilePictName[] = "METAFILEPICT";
9603 /* Set the TypeID for a Metafile */
9604 pOleStreamData[1].dwTypeID = 5;
9606 /* Set the OleTypeName to Metafile */
9607 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9608 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9610 iSeekPos.u.HighPart = 0;
9611 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9613 /* Get Presentation Data */
9614 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9615 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9616 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9617 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9619 /*Set width and Height */
9620 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9621 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9622 if(olePress.dwSize > 0)
9624 /* Set Length */
9625 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9627 /* Set MetaFilePict struct */
9628 MetaFilePict.mm = 8;
9629 MetaFilePict.xExt = olePress.dwExtentX;
9630 MetaFilePict.yExt = olePress.dwExtentY;
9631 MetaFilePict.hMF = 0;
9633 /* Get Metafile Data */
9634 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9635 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9636 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9638 IStream_Release(pStream);
9642 /*************************************************************************
9643 * OleConvertOLESTREAMToIStorage [OLE32.@]
9645 * Read info on MSDN
9647 * TODO
9648 * DVTARGETDEVICE parameter is not handled
9649 * Still unsure of some mem fields for OLE 10 Stream
9650 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9651 * and "\001OLE" streams
9654 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9655 LPOLESTREAM pOleStream,
9656 LPSTORAGE pstg,
9657 const DVTARGETDEVICE* ptd)
9659 int i;
9660 HRESULT hRes=S_OK;
9661 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9663 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9665 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9667 if(ptd != NULL)
9669 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9672 if(pstg == NULL || pOleStream == NULL)
9674 hRes = E_INVALIDARG;
9677 if(hRes == S_OK)
9679 /* Load the OLESTREAM to Memory */
9680 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9683 if(hRes == S_OK)
9685 /* Load the OLESTREAM to Memory (part 2)*/
9686 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9689 if(hRes == S_OK)
9692 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9694 /* Do we have the IStorage Data in the OLESTREAM */
9695 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9697 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9698 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9700 else
9702 /* It must be an original OLE 1.0 source */
9703 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9706 else
9708 /* It must be an original OLE 1.0 source */
9709 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9712 /* Create CompObj Stream if necessary */
9713 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9714 if(hRes == S_OK)
9716 /*Create the Ole Stream if necessary */
9717 STORAGE_CreateOleStream(pstg, 0);
9722 /* Free allocated memory */
9723 for(i=0; i < 2; i++)
9725 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9726 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9727 pOleStreamData[i].pstrOleObjFileName = NULL;
9729 return hRes;
9732 /*************************************************************************
9733 * OleConvertIStorageToOLESTREAM [OLE32.@]
9735 * Read info on MSDN
9737 * Read info on MSDN
9739 * TODO
9740 * Still unsure of some mem fields for OLE 10 Stream
9741 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9742 * and "\001OLE" streams.
9745 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9746 LPSTORAGE pstg,
9747 LPOLESTREAM pOleStream)
9749 int i;
9750 HRESULT hRes = S_OK;
9751 IStream *pStream;
9752 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9753 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9755 TRACE("%p %p\n", pstg, pOleStream);
9757 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9759 if(pstg == NULL || pOleStream == NULL)
9761 hRes = E_INVALIDARG;
9763 if(hRes == S_OK)
9765 /* Get the ProgID */
9766 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9767 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9769 if(hRes == S_OK)
9771 /* Was it originally Ole10 */
9772 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9773 if(hRes == S_OK)
9775 IStream_Release(pStream);
9776 /* Get Presentation Data for Ole10Native */
9777 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9779 else
9781 /* Get Presentation Data (OLE20) */
9782 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9785 /* Save OLESTREAM */
9786 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9787 if(hRes == S_OK)
9789 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9794 /* Free allocated memory */
9795 for(i=0; i < 2; i++)
9797 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9800 return hRes;
9803 enum stream_1ole_flags {
9804 OleStream_LinkedObject = 0x00000001,
9805 OleStream_Convert = 0x00000004
9808 /***********************************************************************
9809 * GetConvertStg (OLE32.@)
9811 HRESULT WINAPI GetConvertStg(IStorage *stg)
9813 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9814 static const DWORD version_magic = 0x02000001;
9815 DWORD header[2];
9816 IStream *stream;
9817 HRESULT hr;
9819 TRACE("%p\n", stg);
9821 if (!stg) return E_INVALIDARG;
9823 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
9824 if (FAILED(hr)) return hr;
9826 hr = IStream_Read(stream, header, sizeof(header), NULL);
9827 IStream_Release(stream);
9828 if (FAILED(hr)) return hr;
9830 if (header[0] != version_magic)
9832 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
9833 return E_FAIL;
9836 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
9839 /***********************************************************************
9840 * SetConvertStg (OLE32.@)
9842 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
9844 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9845 DWORD flags = convert ? OleStream_Convert : 0;
9846 IStream *stream;
9847 DWORD header[2];
9848 HRESULT hr;
9850 TRACE("(%p, %d)\n", storage, convert);
9852 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
9853 if (FAILED(hr))
9855 if (hr != STG_E_FILENOTFOUND)
9856 return hr;
9858 return STORAGE_CreateOleStream(storage, flags);
9861 hr = IStream_Read(stream, header, sizeof(header), NULL);
9862 if (FAILED(hr))
9864 IStream_Release(stream);
9865 return hr;
9868 /* update flag if differs */
9869 if ((header[1] ^ flags) & OleStream_Convert)
9871 LARGE_INTEGER pos = {{0}};
9873 if (header[1] & OleStream_Convert)
9874 flags = header[1] & ~OleStream_Convert;
9875 else
9876 flags = header[1] | OleStream_Convert;
9878 pos.QuadPart = sizeof(DWORD);
9879 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
9880 if (FAILED(hr))
9882 IStream_Release(stream);
9883 return hr;
9886 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
9889 IStream_Release(stream);
9890 return hr;
9893 /******************************************************************************
9894 * StgIsStorageFile [OLE32.@]
9895 * Verify if the file contains a storage object
9897 * PARAMS
9898 * fn [ I] Filename
9900 * RETURNS
9901 * S_OK if file has magic bytes as a storage object
9902 * S_FALSE if file is not storage
9904 HRESULT WINAPI
9905 StgIsStorageFile(LPCOLESTR fn)
9907 HANDLE hf;
9908 BYTE magic[8];
9909 DWORD bytes_read;
9911 TRACE("%s\n", debugstr_w(fn));
9912 hf = CreateFileW(fn, GENERIC_READ,
9913 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9914 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9916 if (hf == INVALID_HANDLE_VALUE)
9917 return STG_E_FILENOTFOUND;
9919 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9921 WARN(" unable to read file\n");
9922 CloseHandle(hf);
9923 return S_FALSE;
9926 CloseHandle(hf);
9928 if (bytes_read != 8) {
9929 TRACE(" too short\n");
9930 return S_FALSE;
9933 if (!memcmp(magic,STORAGE_magic,8)) {
9934 TRACE(" -> YES\n");
9935 return S_OK;
9938 TRACE(" -> Invalid header.\n");
9939 return S_FALSE;
9942 /***********************************************************************
9943 * WriteClassStm (OLE32.@)
9945 * Writes a CLSID to a stream.
9947 * PARAMS
9948 * pStm [I] Stream to write to.
9949 * rclsid [I] CLSID to write.
9951 * RETURNS
9952 * Success: S_OK.
9953 * Failure: HRESULT code.
9955 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9957 TRACE("(%p,%p)\n",pStm,rclsid);
9959 if (!pStm || !rclsid)
9960 return E_INVALIDARG;
9962 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9965 /***********************************************************************
9966 * ReadClassStm (OLE32.@)
9968 * Reads a CLSID from a stream.
9970 * PARAMS
9971 * pStm [I] Stream to read from.
9972 * rclsid [O] CLSID to read.
9974 * RETURNS
9975 * Success: S_OK.
9976 * Failure: HRESULT code.
9978 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9980 ULONG nbByte;
9981 HRESULT res;
9983 TRACE("(%p,%p)\n",pStm,pclsid);
9985 if (!pStm || !pclsid)
9986 return E_INVALIDARG;
9988 /* clear the output args */
9989 *pclsid = CLSID_NULL;
9991 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9993 if (FAILED(res))
9994 return res;
9996 if (nbByte != sizeof(CLSID))
9997 return STG_E_READFAULT;
9998 else
9999 return S_OK;