msvcp60: Avoid FALSE:TRUE conditional expressions.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob70494a18ab28972c61202b068bb60136072db67a
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
70 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
73 /****************************************************************************
74 * Storage32InternalImpl definitions.
76 * Definition of the implementation structure for the IStorage32 interface.
77 * This one implements the IStorage32 interface for storage that are
78 * inside another storage.
80 struct StorageInternalImpl
82 struct StorageBaseImpl base;
85 * Entry in the parent's stream tracking list
87 struct list ParentListEntry;
89 StorageBaseImpl *parentStorage;
91 typedef struct StorageInternalImpl StorageInternalImpl;
93 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
94 static const IStorageVtbl Storage32InternalImpl_Vtbl;
96 /* Method definitions for the Storage32InternalImpl class. */
97 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
98 DWORD openFlags, DirRef storageDirEntry);
99 static void StorageImpl_Destroy(StorageBaseImpl* iface);
100 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
101 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
102 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
103 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
104 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
105 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
106 static void StorageImpl_SaveFileHeader(StorageImpl* This);
108 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
109 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
110 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
111 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
112 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
114 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
115 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
116 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
118 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
119 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
120 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
121 ULONG blockIndex, ULONG offset, DWORD value);
122 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
123 ULONG blockIndex, ULONG offset, DWORD* value);
125 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
126 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
128 typedef struct TransactedDirEntry
130 /* If applicable, a reference to the original DirEntry in the transacted
131 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
132 DirRef transactedParentEntry;
134 /* True if this entry is being used. */
135 int inuse;
137 /* True if data is up to date. */
138 int read;
140 /* True if this entry has been modified. */
141 int dirty;
143 /* True if this entry's stream has been modified. */
144 int stream_dirty;
146 /* True if this entry has been deleted in the transacted storage, but the
147 * delete has not yet been committed. */
148 int deleted;
150 /* If this entry's stream has been modified, a reference to where the stream
151 * is stored in the snapshot file. */
152 DirRef stream_entry;
154 /* This directory entry's data, including any changes that have been made. */
155 DirEntry data;
157 /* A reference to the parent of this node. This is only valid while we are
158 * committing changes. */
159 DirRef parent;
161 /* A reference to a newly-created entry in the transacted parent. This is
162 * always equal to transactedParentEntry except when committing changes. */
163 DirRef newTransactedParentEntry;
164 } TransactedDirEntry;
166 /****************************************************************************
167 * Transacted storage object.
169 typedef struct TransactedSnapshotImpl
171 struct StorageBaseImpl base;
174 * Modified streams are temporarily saved to the scratch file.
176 StorageBaseImpl *scratch;
178 /* The directory structure is kept here, so that we can track how these
179 * entries relate to those in the parent storage. */
180 TransactedDirEntry *entries;
181 ULONG entries_size;
182 ULONG firstFreeEntry;
185 * Changes are committed to the transacted parent.
187 StorageBaseImpl *transactedParent;
188 } TransactedSnapshotImpl;
190 /* Generic function to create a transacted wrapper for a direct storage object. */
191 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
193 /* OLESTREAM memory structure to use for Get and Put Routines */
194 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
195 typedef struct
197 DWORD dwOleID;
198 DWORD dwTypeID;
199 DWORD dwOleTypeNameLength;
200 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
201 CHAR *pstrOleObjFileName;
202 DWORD dwOleObjFileNameLength;
203 DWORD dwMetaFileWidth;
204 DWORD dwMetaFileHeight;
205 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
206 DWORD dwDataLength;
207 BYTE *pData;
208 }OLECONVERT_OLESTREAM_DATA;
210 /* CompObj Stream structure */
211 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
212 typedef struct
214 BYTE byUnknown1[12];
215 CLSID clsid;
216 DWORD dwCLSIDNameLength;
217 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
218 DWORD dwOleTypeNameLength;
219 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
220 DWORD dwProgIDNameLength;
221 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
222 BYTE byUnknown2[16];
223 }OLECONVERT_ISTORAGE_COMPOBJ;
226 /* Ole Presentation Stream structure */
227 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
228 typedef struct
230 BYTE byUnknown1[28];
231 DWORD dwExtentX;
232 DWORD dwExtentY;
233 DWORD dwSize;
234 BYTE *pData;
235 }OLECONVERT_ISTORAGE_OLEPRES;
239 /***********************************************************************
240 * Forward declaration of internal functions used by the method DestroyElement
242 static HRESULT deleteStorageContents(
243 StorageBaseImpl *parentStorage,
244 DirRef indexToDelete,
245 DirEntry entryDataToDelete);
247 static HRESULT deleteStreamContents(
248 StorageBaseImpl *parentStorage,
249 DirRef indexToDelete,
250 DirEntry entryDataToDelete);
252 static HRESULT removeFromTree(
253 StorageBaseImpl *This,
254 DirRef parentStorageIndex,
255 DirRef deletedIndex);
257 /***********************************************************************
258 * Declaration of the functions used to manipulate DirEntry
261 static HRESULT insertIntoTree(
262 StorageBaseImpl *This,
263 DirRef parentStorageIndex,
264 DirRef newEntryIndex);
266 static LONG entryNameCmp(
267 const OLECHAR *name1,
268 const OLECHAR *name2);
270 static DirRef findElement(
271 StorageBaseImpl *storage,
272 DirRef storageEntry,
273 const OLECHAR *name,
274 DirEntry *data);
276 static HRESULT findTreeParent(
277 StorageBaseImpl *storage,
278 DirRef storageEntry,
279 const OLECHAR *childName,
280 DirEntry *parentData,
281 DirRef *parentEntry,
282 ULONG *relation);
284 /***********************************************************************
285 * Declaration of miscellaneous functions...
287 static HRESULT validateSTGM(DWORD stgmValue);
289 static DWORD GetShareModeFromSTGM(DWORD stgm);
290 static DWORD GetAccessModeFromSTGM(DWORD stgm);
291 static DWORD GetCreationModeFromSTGM(DWORD stgm);
293 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
296 /****************************************************************************
297 * IEnumSTATSTGImpl definitions.
299 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
300 * This class allows iterating through the content of a storage and to find
301 * specific items inside it.
303 struct IEnumSTATSTGImpl
305 IEnumSTATSTG IEnumSTATSTG_iface;
307 LONG ref; /* Reference count */
308 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
309 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
311 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
314 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
316 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
320 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
321 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
323 /************************************************************************
324 ** Block Functions
327 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
329 return (index+1) * This->bigBlockSize;
332 /************************************************************************
333 ** Storage32BaseImpl implementation
335 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
336 ULARGE_INTEGER offset,
337 void* buffer,
338 ULONG size,
339 ULONG* bytesRead)
341 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
344 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
345 ULARGE_INTEGER offset,
346 const void* buffer,
347 const ULONG size,
348 ULONG* bytesWritten)
350 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
353 /************************************************************************
354 * Storage32BaseImpl_QueryInterface (IUnknown)
356 * This method implements the common QueryInterface for all IStorage32
357 * implementations contained in this file.
359 * See Windows documentation for more details on IUnknown methods.
361 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
362 IStorage* iface,
363 REFIID riid,
364 void** ppvObject)
366 StorageBaseImpl *This = impl_from_IStorage(iface);
368 if (!ppvObject)
369 return E_INVALIDARG;
371 *ppvObject = 0;
373 if (IsEqualGUID(&IID_IUnknown, riid) ||
374 IsEqualGUID(&IID_IStorage, riid))
376 *ppvObject = &This->IStorage_iface;
378 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
380 *ppvObject = &This->IPropertySetStorage_iface;
382 else
383 return E_NOINTERFACE;
385 IStorage_AddRef(iface);
387 return S_OK;
390 /************************************************************************
391 * Storage32BaseImpl_AddRef (IUnknown)
393 * This method implements the common AddRef for all IStorage32
394 * implementations contained in this file.
396 * See Windows documentation for more details on IUnknown methods.
398 static ULONG WINAPI StorageBaseImpl_AddRef(
399 IStorage* iface)
401 StorageBaseImpl *This = impl_from_IStorage(iface);
402 ULONG ref = InterlockedIncrement(&This->ref);
404 TRACE("(%p) AddRef to %d\n", This, ref);
406 return ref;
409 /************************************************************************
410 * Storage32BaseImpl_Release (IUnknown)
412 * This method implements the common Release for all IStorage32
413 * implementations contained in this file.
415 * See Windows documentation for more details on IUnknown methods.
417 static ULONG WINAPI StorageBaseImpl_Release(
418 IStorage* iface)
420 StorageBaseImpl *This = impl_from_IStorage(iface);
422 ULONG ref = InterlockedDecrement(&This->ref);
424 TRACE("(%p) ReleaseRef to %d\n", This, ref);
426 if (ref == 0)
429 * Since we are using a system of base-classes, we want to call the
430 * destructor of the appropriate derived class. To do this, we are
431 * using virtual functions to implement the destructor.
433 StorageBaseImpl_Destroy(This);
436 return ref;
439 /************************************************************************
440 * Storage32BaseImpl_OpenStream (IStorage)
442 * This method will open the specified stream object from the current storage.
444 * See Windows documentation for more details on IStorage methods.
446 static HRESULT WINAPI StorageBaseImpl_OpenStream(
447 IStorage* iface,
448 const OLECHAR* pwcsName, /* [string][in] */
449 void* reserved1, /* [unique][in] */
450 DWORD grfMode, /* [in] */
451 DWORD reserved2, /* [in] */
452 IStream** ppstm) /* [out] */
454 StorageBaseImpl *This = impl_from_IStorage(iface);
455 StgStreamImpl* newStream;
456 DirEntry currentEntry;
457 DirRef streamEntryRef;
458 HRESULT res = STG_E_UNKNOWN;
460 TRACE("(%p, %s, %p, %x, %d, %p)\n",
461 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
463 if ( (pwcsName==NULL) || (ppstm==0) )
465 res = E_INVALIDARG;
466 goto end;
469 *ppstm = NULL;
471 if ( FAILED( validateSTGM(grfMode) ) ||
472 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
474 res = STG_E_INVALIDFLAG;
475 goto end;
479 * As documented.
481 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
483 res = STG_E_INVALIDFUNCTION;
484 goto end;
487 if (This->reverted)
489 res = STG_E_REVERTED;
490 goto end;
494 * Check that we're compatible with the parent's storage mode, but
495 * only if we are not in transacted mode
497 if(!(This->openFlags & STGM_TRANSACTED)) {
498 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
500 res = STG_E_INVALIDFLAG;
501 goto end;
506 * Search for the element with the given name
508 streamEntryRef = findElement(
509 This,
510 This->storageDirEntry,
511 pwcsName,
512 &currentEntry);
515 * If it was found, construct the stream object and return a pointer to it.
517 if ( (streamEntryRef!=DIRENTRY_NULL) &&
518 (currentEntry.stgType==STGTY_STREAM) )
520 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
522 /* A single stream cannot be opened a second time. */
523 res = STG_E_ACCESSDENIED;
524 goto end;
527 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
529 if (newStream)
531 newStream->grfMode = grfMode;
532 *ppstm = &newStream->IStream_iface;
534 IStream_AddRef(*ppstm);
536 res = S_OK;
537 goto end;
540 res = E_OUTOFMEMORY;
541 goto end;
544 res = STG_E_FILENOTFOUND;
546 end:
547 if (res == S_OK)
548 TRACE("<-- IStream %p\n", *ppstm);
549 TRACE("<-- %08x\n", res);
550 return res;
553 /************************************************************************
554 * Storage32BaseImpl_OpenStorage (IStorage)
556 * This method will open a new storage object from the current storage.
558 * See Windows documentation for more details on IStorage methods.
560 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
561 IStorage* iface,
562 const OLECHAR* pwcsName, /* [string][unique][in] */
563 IStorage* pstgPriority, /* [unique][in] */
564 DWORD grfMode, /* [in] */
565 SNB snbExclude, /* [unique][in] */
566 DWORD reserved, /* [in] */
567 IStorage** ppstg) /* [out] */
569 StorageBaseImpl *This = impl_from_IStorage(iface);
570 StorageInternalImpl* newStorage;
571 StorageBaseImpl* newTransactedStorage;
572 DirEntry currentEntry;
573 DirRef storageEntryRef;
574 HRESULT res = STG_E_UNKNOWN;
576 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
577 iface, debugstr_w(pwcsName), pstgPriority,
578 grfMode, snbExclude, reserved, ppstg);
580 if ((pwcsName==NULL) || (ppstg==0) )
582 res = E_INVALIDARG;
583 goto end;
586 if (This->openFlags & STGM_SIMPLE)
588 res = STG_E_INVALIDFUNCTION;
589 goto end;
592 /* as documented */
593 if (snbExclude != NULL)
595 res = STG_E_INVALIDPARAMETER;
596 goto end;
599 if ( FAILED( validateSTGM(grfMode) ))
601 res = STG_E_INVALIDFLAG;
602 goto end;
606 * As documented.
608 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
609 (grfMode & STGM_DELETEONRELEASE) ||
610 (grfMode & STGM_PRIORITY) )
612 res = STG_E_INVALIDFUNCTION;
613 goto end;
616 if (This->reverted)
617 return STG_E_REVERTED;
620 * Check that we're compatible with the parent's storage mode,
621 * but only if we are not transacted
623 if(!(This->openFlags & STGM_TRANSACTED)) {
624 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
626 res = STG_E_ACCESSDENIED;
627 goto end;
631 *ppstg = NULL;
633 storageEntryRef = findElement(
634 This,
635 This->storageDirEntry,
636 pwcsName,
637 &currentEntry);
639 if ( (storageEntryRef!=DIRENTRY_NULL) &&
640 (currentEntry.stgType==STGTY_STORAGE) )
642 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
644 /* A single storage cannot be opened a second time. */
645 res = STG_E_ACCESSDENIED;
646 goto end;
649 newStorage = StorageInternalImpl_Construct(
650 This,
651 grfMode,
652 storageEntryRef);
654 if (newStorage != 0)
656 if (grfMode & STGM_TRANSACTED)
658 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
660 if (FAILED(res))
662 HeapFree(GetProcessHeap(), 0, newStorage);
663 goto end;
666 *ppstg = &newTransactedStorage->IStorage_iface;
668 else
670 *ppstg = &newStorage->base.IStorage_iface;
673 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
675 res = S_OK;
676 goto end;
679 res = STG_E_INSUFFICIENTMEMORY;
680 goto end;
683 res = STG_E_FILENOTFOUND;
685 end:
686 TRACE("<-- %08x\n", res);
687 return res;
690 /************************************************************************
691 * Storage32BaseImpl_EnumElements (IStorage)
693 * This method will create an enumerator object that can be used to
694 * retrieve information about all the elements in the storage object.
696 * See Windows documentation for more details on IStorage methods.
698 static HRESULT WINAPI StorageBaseImpl_EnumElements(
699 IStorage* iface,
700 DWORD reserved1, /* [in] */
701 void* reserved2, /* [size_is][unique][in] */
702 DWORD reserved3, /* [in] */
703 IEnumSTATSTG** ppenum) /* [out] */
705 StorageBaseImpl *This = impl_from_IStorage(iface);
706 IEnumSTATSTGImpl* newEnum;
708 TRACE("(%p, %d, %p, %d, %p)\n",
709 iface, reserved1, reserved2, reserved3, ppenum);
711 if (!ppenum)
712 return E_INVALIDARG;
714 if (This->reverted)
715 return STG_E_REVERTED;
717 newEnum = IEnumSTATSTGImpl_Construct(
718 This,
719 This->storageDirEntry);
721 if (newEnum)
723 *ppenum = &newEnum->IEnumSTATSTG_iface;
725 IEnumSTATSTG_AddRef(*ppenum);
727 return S_OK;
730 return E_OUTOFMEMORY;
733 /************************************************************************
734 * Storage32BaseImpl_Stat (IStorage)
736 * This method will retrieve information about this storage object.
738 * See Windows documentation for more details on IStorage methods.
740 static HRESULT WINAPI StorageBaseImpl_Stat(
741 IStorage* iface,
742 STATSTG* pstatstg, /* [out] */
743 DWORD grfStatFlag) /* [in] */
745 StorageBaseImpl *This = impl_from_IStorage(iface);
746 DirEntry currentEntry;
747 HRESULT res = STG_E_UNKNOWN;
749 TRACE("(%p, %p, %x)\n",
750 iface, pstatstg, grfStatFlag);
752 if (!pstatstg)
754 res = E_INVALIDARG;
755 goto end;
758 if (This->reverted)
760 res = STG_E_REVERTED;
761 goto end;
764 res = StorageBaseImpl_ReadDirEntry(
765 This,
766 This->storageDirEntry,
767 &currentEntry);
769 if (SUCCEEDED(res))
771 StorageUtl_CopyDirEntryToSTATSTG(
772 This,
773 pstatstg,
774 &currentEntry,
775 grfStatFlag);
777 pstatstg->grfMode = This->openFlags;
778 pstatstg->grfStateBits = This->stateBits;
781 end:
782 if (res == S_OK)
784 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);
786 TRACE("<-- %08x\n", res);
787 return res;
790 /************************************************************************
791 * Storage32BaseImpl_RenameElement (IStorage)
793 * This method will rename the specified element.
795 * See Windows documentation for more details on IStorage methods.
797 static HRESULT WINAPI StorageBaseImpl_RenameElement(
798 IStorage* iface,
799 const OLECHAR* pwcsOldName, /* [in] */
800 const OLECHAR* pwcsNewName) /* [in] */
802 StorageBaseImpl *This = impl_from_IStorage(iface);
803 DirEntry currentEntry;
804 DirRef currentEntryRef;
806 TRACE("(%p, %s, %s)\n",
807 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
809 if (This->reverted)
810 return STG_E_REVERTED;
812 currentEntryRef = findElement(This,
813 This->storageDirEntry,
814 pwcsNewName,
815 &currentEntry);
817 if (currentEntryRef != DIRENTRY_NULL)
820 * There is already an element with the new name
822 return STG_E_FILEALREADYEXISTS;
826 * Search for the old element name
828 currentEntryRef = findElement(This,
829 This->storageDirEntry,
830 pwcsOldName,
831 &currentEntry);
833 if (currentEntryRef != DIRENTRY_NULL)
835 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
836 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
838 WARN("Element is already open; cannot rename.\n");
839 return STG_E_ACCESSDENIED;
842 /* Remove the element from its current position in the tree */
843 removeFromTree(This, This->storageDirEntry,
844 currentEntryRef);
846 /* Change the name of the element */
847 strcpyW(currentEntry.name, pwcsNewName);
849 /* Delete any sibling links */
850 currentEntry.leftChild = DIRENTRY_NULL;
851 currentEntry.rightChild = DIRENTRY_NULL;
853 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
854 &currentEntry);
856 /* Insert the element in a new position in the tree */
857 insertIntoTree(This, This->storageDirEntry,
858 currentEntryRef);
860 else
863 * There is no element with the old name
865 return STG_E_FILENOTFOUND;
868 return StorageBaseImpl_Flush(This);
871 /************************************************************************
872 * Storage32BaseImpl_CreateStream (IStorage)
874 * This method will create a stream object within this storage
876 * See Windows documentation for more details on IStorage methods.
878 static HRESULT WINAPI StorageBaseImpl_CreateStream(
879 IStorage* iface,
880 const OLECHAR* pwcsName, /* [string][in] */
881 DWORD grfMode, /* [in] */
882 DWORD reserved1, /* [in] */
883 DWORD reserved2, /* [in] */
884 IStream** ppstm) /* [out] */
886 StorageBaseImpl *This = impl_from_IStorage(iface);
887 StgStreamImpl* newStream;
888 DirEntry currentEntry, newStreamEntry;
889 DirRef currentEntryRef, newStreamEntryRef;
890 HRESULT hr;
892 TRACE("(%p, %s, %x, %d, %d, %p)\n",
893 iface, debugstr_w(pwcsName), grfMode,
894 reserved1, reserved2, ppstm);
896 if (ppstm == 0)
897 return STG_E_INVALIDPOINTER;
899 if (pwcsName == 0)
900 return STG_E_INVALIDNAME;
902 if (reserved1 || reserved2)
903 return STG_E_INVALIDPARAMETER;
905 if ( FAILED( validateSTGM(grfMode) ))
906 return STG_E_INVALIDFLAG;
908 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
909 return STG_E_INVALIDFLAG;
911 if (This->reverted)
912 return STG_E_REVERTED;
915 * As documented.
917 if ((grfMode & STGM_DELETEONRELEASE) ||
918 (grfMode & STGM_TRANSACTED))
919 return STG_E_INVALIDFUNCTION;
922 * Don't worry about permissions in transacted mode, as we can always write
923 * changes; we just can't always commit them.
925 if(!(This->openFlags & STGM_TRANSACTED)) {
926 /* Can't create a stream on read-only storage */
927 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
928 return STG_E_ACCESSDENIED;
930 /* Can't create a stream with greater access than the parent. */
931 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
932 return STG_E_ACCESSDENIED;
935 if(This->openFlags & STGM_SIMPLE)
936 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
938 *ppstm = 0;
940 currentEntryRef = findElement(This,
941 This->storageDirEntry,
942 pwcsName,
943 &currentEntry);
945 if (currentEntryRef != DIRENTRY_NULL)
948 * An element with this name already exists
950 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
952 IStorage_DestroyElement(iface, pwcsName);
954 else
955 return STG_E_FILEALREADYEXISTS;
959 * memset the empty entry
961 memset(&newStreamEntry, 0, sizeof(DirEntry));
963 newStreamEntry.sizeOfNameString =
964 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
966 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
967 return STG_E_INVALIDNAME;
969 strcpyW(newStreamEntry.name, pwcsName);
971 newStreamEntry.stgType = STGTY_STREAM;
972 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
973 newStreamEntry.size.u.LowPart = 0;
974 newStreamEntry.size.u.HighPart = 0;
976 newStreamEntry.leftChild = DIRENTRY_NULL;
977 newStreamEntry.rightChild = DIRENTRY_NULL;
978 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
980 /* call CoFileTime to get the current time
981 newStreamEntry.ctime
982 newStreamEntry.mtime
985 /* newStreamEntry.clsid */
988 * Create an entry with the new data
990 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
991 if (FAILED(hr))
992 return hr;
995 * Insert the new entry in the parent storage's tree.
997 hr = insertIntoTree(
998 This,
999 This->storageDirEntry,
1000 newStreamEntryRef);
1001 if (FAILED(hr))
1003 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1004 return hr;
1008 * Open the stream to return it.
1010 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1012 if (newStream)
1014 *ppstm = &newStream->IStream_iface;
1015 IStream_AddRef(*ppstm);
1017 else
1019 return STG_E_INSUFFICIENTMEMORY;
1022 return StorageBaseImpl_Flush(This);
1025 /************************************************************************
1026 * Storage32BaseImpl_SetClass (IStorage)
1028 * This method will write the specified CLSID in the directory entry of this
1029 * storage.
1031 * See Windows documentation for more details on IStorage methods.
1033 static HRESULT WINAPI StorageBaseImpl_SetClass(
1034 IStorage* iface,
1035 REFCLSID clsid) /* [in] */
1037 StorageBaseImpl *This = impl_from_IStorage(iface);
1038 HRESULT hRes;
1039 DirEntry currentEntry;
1041 TRACE("(%p, %p)\n", iface, clsid);
1043 if (This->reverted)
1044 return STG_E_REVERTED;
1046 hRes = StorageBaseImpl_ReadDirEntry(This,
1047 This->storageDirEntry,
1048 &currentEntry);
1049 if (SUCCEEDED(hRes))
1051 currentEntry.clsid = *clsid;
1053 hRes = StorageBaseImpl_WriteDirEntry(This,
1054 This->storageDirEntry,
1055 &currentEntry);
1058 if (SUCCEEDED(hRes))
1059 hRes = StorageBaseImpl_Flush(This);
1061 return hRes;
1064 /************************************************************************
1065 ** Storage32Impl implementation
1068 /************************************************************************
1069 * Storage32BaseImpl_CreateStorage (IStorage)
1071 * This method will create the storage object within the provided storage.
1073 * See Windows documentation for more details on IStorage methods.
1075 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1076 IStorage* iface,
1077 const OLECHAR *pwcsName, /* [string][in] */
1078 DWORD grfMode, /* [in] */
1079 DWORD reserved1, /* [in] */
1080 DWORD reserved2, /* [in] */
1081 IStorage **ppstg) /* [out] */
1083 StorageBaseImpl* This = impl_from_IStorage(iface);
1085 DirEntry currentEntry;
1086 DirEntry newEntry;
1087 DirRef currentEntryRef;
1088 DirRef newEntryRef;
1089 HRESULT hr;
1091 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1092 iface, debugstr_w(pwcsName), grfMode,
1093 reserved1, reserved2, ppstg);
1095 if (ppstg == 0)
1096 return STG_E_INVALIDPOINTER;
1098 if (This->openFlags & STGM_SIMPLE)
1100 return STG_E_INVALIDFUNCTION;
1103 if (pwcsName == 0)
1104 return STG_E_INVALIDNAME;
1106 *ppstg = NULL;
1108 if ( FAILED( validateSTGM(grfMode) ) ||
1109 (grfMode & STGM_DELETEONRELEASE) )
1111 WARN("bad grfMode: 0x%x\n", grfMode);
1112 return STG_E_INVALIDFLAG;
1115 if (This->reverted)
1116 return STG_E_REVERTED;
1119 * Check that we're compatible with the parent's storage mode
1121 if ( !(This->openFlags & STGM_TRANSACTED) &&
1122 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1124 WARN("access denied\n");
1125 return STG_E_ACCESSDENIED;
1128 currentEntryRef = findElement(This,
1129 This->storageDirEntry,
1130 pwcsName,
1131 &currentEntry);
1133 if (currentEntryRef != DIRENTRY_NULL)
1136 * An element with this name already exists
1138 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1139 ((This->openFlags & STGM_TRANSACTED) ||
1140 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1142 hr = IStorage_DestroyElement(iface, pwcsName);
1143 if (FAILED(hr))
1144 return hr;
1146 else
1148 WARN("file already exists\n");
1149 return STG_E_FILEALREADYEXISTS;
1152 else if (!(This->openFlags & STGM_TRANSACTED) &&
1153 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1155 WARN("read-only storage\n");
1156 return STG_E_ACCESSDENIED;
1159 memset(&newEntry, 0, sizeof(DirEntry));
1161 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1163 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1165 FIXME("name too long\n");
1166 return STG_E_INVALIDNAME;
1169 strcpyW(newEntry.name, pwcsName);
1171 newEntry.stgType = STGTY_STORAGE;
1172 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1173 newEntry.size.u.LowPart = 0;
1174 newEntry.size.u.HighPart = 0;
1176 newEntry.leftChild = DIRENTRY_NULL;
1177 newEntry.rightChild = DIRENTRY_NULL;
1178 newEntry.dirRootEntry = DIRENTRY_NULL;
1180 /* call CoFileTime to get the current time
1181 newEntry.ctime
1182 newEntry.mtime
1185 /* newEntry.clsid */
1188 * Create a new directory entry for the storage
1190 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1191 if (FAILED(hr))
1192 return hr;
1195 * Insert the new directory entry into the parent storage's tree
1197 hr = insertIntoTree(
1198 This,
1199 This->storageDirEntry,
1200 newEntryRef);
1201 if (FAILED(hr))
1203 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1204 return hr;
1208 * Open it to get a pointer to return.
1210 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1212 if( (hr != S_OK) || (*ppstg == NULL))
1214 return hr;
1217 if (SUCCEEDED(hr))
1218 hr = StorageBaseImpl_Flush(This);
1220 return S_OK;
1224 /***************************************************************************
1226 * Internal Method
1228 * Reserve a directory entry in the file and initialize it.
1230 static HRESULT StorageImpl_CreateDirEntry(
1231 StorageBaseImpl *base,
1232 const DirEntry *newData,
1233 DirRef *index)
1235 StorageImpl *storage = (StorageImpl*)base;
1236 ULONG currentEntryIndex = 0;
1237 ULONG newEntryIndex = DIRENTRY_NULL;
1238 HRESULT hr = S_OK;
1239 BYTE currentData[RAW_DIRENTRY_SIZE];
1240 WORD sizeOfNameString;
1244 hr = StorageImpl_ReadRawDirEntry(storage,
1245 currentEntryIndex,
1246 currentData);
1248 if (SUCCEEDED(hr))
1250 StorageUtl_ReadWord(
1251 currentData,
1252 OFFSET_PS_NAMELENGTH,
1253 &sizeOfNameString);
1255 if (sizeOfNameString == 0)
1258 * The entry exists and is available, we found it.
1260 newEntryIndex = currentEntryIndex;
1263 else
1266 * We exhausted the directory entries, we will create more space below
1268 newEntryIndex = currentEntryIndex;
1270 currentEntryIndex++;
1272 } while (newEntryIndex == DIRENTRY_NULL);
1275 * grow the directory stream
1277 if (FAILED(hr))
1279 BYTE emptyData[RAW_DIRENTRY_SIZE];
1280 ULARGE_INTEGER newSize;
1281 ULONG entryIndex;
1282 ULONG lastEntry = 0;
1283 ULONG blockCount = 0;
1286 * obtain the new count of blocks in the directory stream
1288 blockCount = BlockChainStream_GetCount(
1289 storage->rootBlockChain)+1;
1292 * initialize the size used by the directory stream
1294 newSize.u.HighPart = 0;
1295 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1298 * add a block to the directory stream
1300 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1303 * memset the empty entry in order to initialize the unused newly
1304 * created entries
1306 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1309 * initialize them
1311 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1313 for(
1314 entryIndex = newEntryIndex + 1;
1315 entryIndex < lastEntry;
1316 entryIndex++)
1318 StorageImpl_WriteRawDirEntry(
1319 storage,
1320 entryIndex,
1321 emptyData);
1324 StorageImpl_SaveFileHeader(storage);
1327 UpdateRawDirEntry(currentData, newData);
1329 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1331 if (SUCCEEDED(hr))
1332 *index = newEntryIndex;
1334 return hr;
1337 /***************************************************************************
1339 * Internal Method
1341 * Mark a directory entry in the file as free.
1343 static HRESULT StorageImpl_DestroyDirEntry(
1344 StorageBaseImpl *base,
1345 DirRef index)
1347 BYTE emptyData[RAW_DIRENTRY_SIZE];
1348 StorageImpl *storage = (StorageImpl*)base;
1350 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1352 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1356 /****************************************************************************
1358 * Internal Method
1360 * Case insensitive comparison of DirEntry.name by first considering
1361 * their size.
1363 * Returns <0 when name1 < name2
1364 * >0 when name1 > name2
1365 * 0 when name1 == name2
1367 static LONG entryNameCmp(
1368 const OLECHAR *name1,
1369 const OLECHAR *name2)
1371 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1373 while (diff == 0 && *name1 != 0)
1376 * We compare the string themselves only when they are of the same length
1378 diff = toupperW(*name1++) - toupperW(*name2++);
1381 return diff;
1384 /****************************************************************************
1386 * Internal Method
1388 * Add a directory entry to a storage
1390 static HRESULT insertIntoTree(
1391 StorageBaseImpl *This,
1392 DirRef parentStorageIndex,
1393 DirRef newEntryIndex)
1395 DirEntry currentEntry;
1396 DirEntry newEntry;
1399 * Read the inserted entry
1401 StorageBaseImpl_ReadDirEntry(This,
1402 newEntryIndex,
1403 &newEntry);
1406 * Read the storage entry
1408 StorageBaseImpl_ReadDirEntry(This,
1409 parentStorageIndex,
1410 &currentEntry);
1412 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1415 * The root storage contains some element, therefore, start the research
1416 * for the appropriate location.
1418 BOOL found = 0;
1419 DirRef current, next, previous, currentEntryId;
1422 * Keep a reference to the root of the storage's element tree
1424 currentEntryId = currentEntry.dirRootEntry;
1427 * Read
1429 StorageBaseImpl_ReadDirEntry(This,
1430 currentEntry.dirRootEntry,
1431 &currentEntry);
1433 previous = currentEntry.leftChild;
1434 next = currentEntry.rightChild;
1435 current = currentEntryId;
1437 while (found == 0)
1439 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1441 if (diff < 0)
1443 if (previous != DIRENTRY_NULL)
1445 StorageBaseImpl_ReadDirEntry(This,
1446 previous,
1447 &currentEntry);
1448 current = previous;
1450 else
1452 currentEntry.leftChild = newEntryIndex;
1453 StorageBaseImpl_WriteDirEntry(This,
1454 current,
1455 &currentEntry);
1456 found = 1;
1459 else if (diff > 0)
1461 if (next != DIRENTRY_NULL)
1463 StorageBaseImpl_ReadDirEntry(This,
1464 next,
1465 &currentEntry);
1466 current = next;
1468 else
1470 currentEntry.rightChild = newEntryIndex;
1471 StorageBaseImpl_WriteDirEntry(This,
1472 current,
1473 &currentEntry);
1474 found = 1;
1477 else
1480 * Trying to insert an item with the same name in the
1481 * subtree structure.
1483 return STG_E_FILEALREADYEXISTS;
1486 previous = currentEntry.leftChild;
1487 next = currentEntry.rightChild;
1490 else
1493 * The storage is empty, make the new entry the root of its element tree
1495 currentEntry.dirRootEntry = newEntryIndex;
1496 StorageBaseImpl_WriteDirEntry(This,
1497 parentStorageIndex,
1498 &currentEntry);
1501 return S_OK;
1504 /****************************************************************************
1506 * Internal Method
1508 * Find and read the element of a storage with the given name.
1510 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1511 const OLECHAR *name, DirEntry *data)
1513 DirRef currentEntry;
1515 /* Read the storage entry to find the root of the tree. */
1516 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1518 currentEntry = data->dirRootEntry;
1520 while (currentEntry != DIRENTRY_NULL)
1522 LONG cmp;
1524 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1526 cmp = entryNameCmp(name, data->name);
1528 if (cmp == 0)
1529 /* found it */
1530 break;
1532 else if (cmp < 0)
1533 currentEntry = data->leftChild;
1535 else if (cmp > 0)
1536 currentEntry = data->rightChild;
1539 return currentEntry;
1542 /****************************************************************************
1544 * Internal Method
1546 * Find and read the binary tree parent of the element with the given name.
1548 * If there is no such element, find a place where it could be inserted and
1549 * return STG_E_FILENOTFOUND.
1551 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1552 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1553 ULONG *relation)
1555 DirRef childEntry;
1556 DirEntry childData;
1558 /* Read the storage entry to find the root of the tree. */
1559 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1561 *parentEntry = storageEntry;
1562 *relation = DIRENTRY_RELATION_DIR;
1564 childEntry = parentData->dirRootEntry;
1566 while (childEntry != DIRENTRY_NULL)
1568 LONG cmp;
1570 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1572 cmp = entryNameCmp(childName, childData.name);
1574 if (cmp == 0)
1575 /* found it */
1576 break;
1578 else if (cmp < 0)
1580 *parentData = childData;
1581 *parentEntry = childEntry;
1582 *relation = DIRENTRY_RELATION_PREVIOUS;
1584 childEntry = parentData->leftChild;
1587 else if (cmp > 0)
1589 *parentData = childData;
1590 *parentEntry = childEntry;
1591 *relation = DIRENTRY_RELATION_NEXT;
1593 childEntry = parentData->rightChild;
1597 if (childEntry == DIRENTRY_NULL)
1598 return STG_E_FILENOTFOUND;
1599 else
1600 return S_OK;
1604 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1605 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1606 SNB snbExclude, IStorage *pstgDest);
1608 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1609 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1610 SNB snbExclude, IStorage *pstgDest)
1612 DirEntry data;
1613 HRESULT hr;
1614 BOOL skip = FALSE;
1615 IStorage *pstgTmp;
1616 IStream *pstrChild, *pstrTmp;
1617 STATSTG strStat;
1619 if (srcEntry == DIRENTRY_NULL)
1620 return S_OK;
1622 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1624 if (FAILED(hr))
1625 return hr;
1627 if ( snbExclude )
1629 WCHAR **snb = snbExclude;
1631 while ( *snb != NULL && !skip )
1633 if ( lstrcmpW(data.name, *snb) == 0 )
1634 skip = TRUE;
1635 ++snb;
1639 if (!skip)
1641 if (data.stgType == STGTY_STORAGE && !skip_storage)
1644 * create a new storage in destination storage
1646 hr = IStorage_CreateStorage( pstgDest, data.name,
1647 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1648 0, 0,
1649 &pstgTmp );
1652 * if it already exist, don't create a new one use this one
1654 if (hr == STG_E_FILEALREADYEXISTS)
1656 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1657 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1658 NULL, 0, &pstgTmp );
1661 if (SUCCEEDED(hr))
1663 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1664 skip_stream, NULL, pstgTmp );
1666 IStorage_Release(pstgTmp);
1669 else if (data.stgType == STGTY_STREAM && !skip_stream)
1672 * create a new stream in destination storage. If the stream already
1673 * exist, it will be deleted and a new one will be created.
1675 hr = IStorage_CreateStream( pstgDest, data.name,
1676 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1677 0, 0, &pstrTmp );
1680 * open child stream storage. This operation must succeed even if the
1681 * stream is already open, so we use internal functions to do it.
1683 if (hr == S_OK)
1685 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1687 if (streamimpl)
1689 pstrChild = &streamimpl->IStream_iface;
1690 if (pstrChild)
1691 IStream_AddRef(pstrChild);
1693 else
1695 pstrChild = NULL;
1696 hr = E_OUTOFMEMORY;
1700 if (hr == S_OK)
1703 * Get the size of the source stream
1705 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1708 * Set the size of the destination stream.
1710 IStream_SetSize(pstrTmp, strStat.cbSize);
1713 * do the copy
1715 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1716 NULL, NULL );
1718 IStream_Release( pstrChild );
1721 IStream_Release( pstrTmp );
1725 /* copy siblings */
1726 if (SUCCEEDED(hr))
1727 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1728 skip_stream, snbExclude, pstgDest );
1730 if (SUCCEEDED(hr))
1731 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1732 skip_stream, snbExclude, pstgDest );
1734 return hr;
1737 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1738 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1739 SNB snbExclude, IStorage *pstgDest)
1741 DirEntry data;
1742 HRESULT hr;
1744 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1746 if (SUCCEEDED(hr))
1747 hr = IStorage_SetClass( pstgDest, &data.clsid );
1749 if (SUCCEEDED(hr))
1750 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1751 skip_stream, snbExclude, pstgDest );
1753 return hr;
1756 /*************************************************************************
1757 * CopyTo (IStorage)
1759 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1760 IStorage* iface,
1761 DWORD ciidExclude, /* [in] */
1762 const IID* rgiidExclude, /* [size_is][unique][in] */
1763 SNB snbExclude, /* [unique][in] */
1764 IStorage* pstgDest) /* [unique][in] */
1766 StorageBaseImpl *This = impl_from_IStorage(iface);
1768 BOOL skip_storage = FALSE, skip_stream = FALSE;
1769 int i;
1771 TRACE("(%p, %d, %p, %p, %p)\n",
1772 iface, ciidExclude, rgiidExclude,
1773 snbExclude, pstgDest);
1775 if ( pstgDest == 0 )
1776 return STG_E_INVALIDPOINTER;
1778 for(i = 0; i < ciidExclude; ++i)
1780 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1781 skip_storage = TRUE;
1782 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1783 skip_stream = TRUE;
1784 else
1785 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1788 if (!skip_storage)
1790 /* Give up early if it looks like this would be infinitely recursive.
1791 * Oddly enough, this includes some cases that aren't really recursive, like
1792 * copying to a transacted child. */
1793 IStorage *pstgDestAncestor = pstgDest;
1794 IStorage *pstgDestAncestorChild = NULL;
1796 /* Go up the chain from the destination until we find the source storage. */
1797 while (pstgDestAncestor != iface) {
1798 pstgDestAncestorChild = pstgDest;
1800 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1802 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
1804 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
1806 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1808 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
1810 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
1812 else
1813 break;
1816 if (pstgDestAncestor == iface)
1818 BOOL fail = TRUE;
1820 if (pstgDestAncestorChild && snbExclude)
1822 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1823 DirEntry data;
1824 WCHAR **snb = snbExclude;
1826 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1828 while ( *snb != NULL && fail )
1830 if ( lstrcmpW(data.name, *snb) == 0 )
1831 fail = FALSE;
1832 ++snb;
1836 if (fail)
1837 return STG_E_ACCESSDENIED;
1841 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1842 skip_storage, skip_stream, snbExclude, pstgDest );
1845 /*************************************************************************
1846 * MoveElementTo (IStorage)
1848 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1849 IStorage* iface,
1850 const OLECHAR *pwcsName, /* [string][in] */
1851 IStorage *pstgDest, /* [unique][in] */
1852 const OLECHAR *pwcsNewName,/* [string][in] */
1853 DWORD grfFlags) /* [in] */
1855 FIXME("(%p %s %p %s %u): stub\n", iface,
1856 debugstr_w(pwcsName), pstgDest,
1857 debugstr_w(pwcsNewName), grfFlags);
1858 return E_NOTIMPL;
1861 /*************************************************************************
1862 * Commit (IStorage)
1864 * Ensures that any changes made to a storage object open in transacted mode
1865 * are reflected in the parent storage
1867 * In a non-transacted mode, this ensures all cached writes are completed.
1869 static HRESULT WINAPI StorageImpl_Commit(
1870 IStorage* iface,
1871 DWORD grfCommitFlags)/* [in] */
1873 StorageBaseImpl* This = impl_from_IStorage(iface);
1874 TRACE("(%p %d)\n", iface, grfCommitFlags);
1875 return StorageBaseImpl_Flush(This);
1878 /*************************************************************************
1879 * Revert (IStorage)
1881 * Discard all changes that have been made since the last commit operation
1883 static HRESULT WINAPI StorageImpl_Revert(
1884 IStorage* iface)
1886 TRACE("(%p)\n", iface);
1887 return S_OK;
1890 /*************************************************************************
1891 * DestroyElement (IStorage)
1893 * Strategy: This implementation is built this way for simplicity not for speed.
1894 * I always delete the topmost element of the enumeration and adjust
1895 * the deleted element pointer all the time. This takes longer to
1896 * do but allow to reinvoke DestroyElement whenever we encounter a
1897 * storage object. The optimisation resides in the usage of another
1898 * enumeration strategy that would give all the leaves of a storage
1899 * first. (postfix order)
1901 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1902 IStorage* iface,
1903 const OLECHAR *pwcsName)/* [string][in] */
1905 StorageBaseImpl *This = impl_from_IStorage(iface);
1907 HRESULT hr = S_OK;
1908 DirEntry entryToDelete;
1909 DirRef entryToDeleteRef;
1911 TRACE("(%p, %s)\n",
1912 iface, debugstr_w(pwcsName));
1914 if (pwcsName==NULL)
1915 return STG_E_INVALIDPOINTER;
1917 if (This->reverted)
1918 return STG_E_REVERTED;
1920 if ( !(This->openFlags & STGM_TRANSACTED) &&
1921 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1922 return STG_E_ACCESSDENIED;
1924 entryToDeleteRef = findElement(
1925 This,
1926 This->storageDirEntry,
1927 pwcsName,
1928 &entryToDelete);
1930 if ( entryToDeleteRef == DIRENTRY_NULL )
1932 return STG_E_FILENOTFOUND;
1935 if ( entryToDelete.stgType == STGTY_STORAGE )
1937 hr = deleteStorageContents(
1938 This,
1939 entryToDeleteRef,
1940 entryToDelete);
1942 else if ( entryToDelete.stgType == STGTY_STREAM )
1944 hr = deleteStreamContents(
1945 This,
1946 entryToDeleteRef,
1947 entryToDelete);
1950 if (hr!=S_OK)
1951 return hr;
1954 * Remove the entry from its parent storage
1956 hr = removeFromTree(
1957 This,
1958 This->storageDirEntry,
1959 entryToDeleteRef);
1962 * Invalidate the entry
1964 if (SUCCEEDED(hr))
1965 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1967 if (SUCCEEDED(hr))
1968 hr = StorageBaseImpl_Flush(This);
1970 return hr;
1974 /******************************************************************************
1975 * Internal stream list handlers
1978 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1980 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1981 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1984 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1986 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1987 list_remove(&(strm->StrmListEntry));
1990 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1992 StgStreamImpl *strm;
1994 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1996 if (strm->dirEntry == streamEntry)
1998 return TRUE;
2002 return FALSE;
2005 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2007 StorageInternalImpl *childstg;
2009 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2011 if (childstg->base.storageDirEntry == storageEntry)
2013 return TRUE;
2017 return FALSE;
2020 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2022 struct list *cur, *cur2;
2023 StgStreamImpl *strm=NULL;
2024 StorageInternalImpl *childstg=NULL;
2026 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2027 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2028 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2029 strm->parentStorage = NULL;
2030 list_remove(cur);
2033 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2034 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2035 StorageBaseImpl_Invalidate( &childstg->base );
2038 if (stg->transactedChild)
2040 StorageBaseImpl_Invalidate(stg->transactedChild);
2042 stg->transactedChild = NULL;
2047 /*********************************************************************
2049 * Internal Method
2051 * Delete the contents of a storage entry.
2054 static HRESULT deleteStorageContents(
2055 StorageBaseImpl *parentStorage,
2056 DirRef indexToDelete,
2057 DirEntry entryDataToDelete)
2059 IEnumSTATSTG *elements = 0;
2060 IStorage *childStorage = 0;
2061 STATSTG currentElement;
2062 HRESULT hr;
2063 HRESULT destroyHr = S_OK;
2064 StorageInternalImpl *stg, *stg2;
2066 /* Invalidate any open storage objects. */
2067 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2069 if (stg->base.storageDirEntry == indexToDelete)
2071 StorageBaseImpl_Invalidate(&stg->base);
2076 * Open the storage and enumerate it
2078 hr = IStorage_OpenStorage(
2079 &parentStorage->IStorage_iface,
2080 entryDataToDelete.name,
2082 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2085 &childStorage);
2087 if (hr != S_OK)
2089 return hr;
2093 * Enumerate the elements
2095 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2100 * Obtain the next element
2102 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2103 if (hr==S_OK)
2105 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2107 CoTaskMemFree(currentElement.pwcsName);
2111 * We need to Reset the enumeration every time because we delete elements
2112 * and the enumeration could be invalid
2114 IEnumSTATSTG_Reset(elements);
2116 } while ((hr == S_OK) && (destroyHr == S_OK));
2118 IStorage_Release(childStorage);
2119 IEnumSTATSTG_Release(elements);
2121 return destroyHr;
2124 /*********************************************************************
2126 * Internal Method
2128 * Perform the deletion of a stream's data
2131 static HRESULT deleteStreamContents(
2132 StorageBaseImpl *parentStorage,
2133 DirRef indexToDelete,
2134 DirEntry entryDataToDelete)
2136 IStream *pis;
2137 HRESULT hr;
2138 ULARGE_INTEGER size;
2139 StgStreamImpl *strm, *strm2;
2141 /* Invalidate any open stream objects. */
2142 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2144 if (strm->dirEntry == indexToDelete)
2146 TRACE("Stream deleted %p\n", strm);
2147 strm->parentStorage = NULL;
2148 list_remove(&strm->StrmListEntry);
2152 size.u.HighPart = 0;
2153 size.u.LowPart = 0;
2155 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2156 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2158 if (hr!=S_OK)
2160 return(hr);
2164 * Zap the stream
2166 hr = IStream_SetSize(pis, size);
2168 if(hr != S_OK)
2170 return hr;
2174 * Release the stream object.
2176 IStream_Release(pis);
2178 return S_OK;
2181 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2183 switch (relation)
2185 case DIRENTRY_RELATION_PREVIOUS:
2186 entry->leftChild = new_target;
2187 break;
2188 case DIRENTRY_RELATION_NEXT:
2189 entry->rightChild = new_target;
2190 break;
2191 case DIRENTRY_RELATION_DIR:
2192 entry->dirRootEntry = new_target;
2193 break;
2194 default:
2195 assert(0);
2199 /*************************************************************************
2201 * Internal Method
2203 * This method removes a directory entry from its parent storage tree without
2204 * freeing any resources attached to it.
2206 static HRESULT removeFromTree(
2207 StorageBaseImpl *This,
2208 DirRef parentStorageIndex,
2209 DirRef deletedIndex)
2211 DirEntry entryToDelete;
2212 DirEntry parentEntry;
2213 DirRef parentEntryRef;
2214 ULONG typeOfRelation;
2215 HRESULT hr;
2217 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2219 if (hr != S_OK)
2220 return hr;
2223 * Find the element that links to the one we want to delete.
2225 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2226 &parentEntry, &parentEntryRef, &typeOfRelation);
2228 if (hr != S_OK)
2229 return hr;
2231 if (entryToDelete.leftChild != DIRENTRY_NULL)
2234 * Replace the deleted entry with its left child
2236 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2238 hr = StorageBaseImpl_WriteDirEntry(
2239 This,
2240 parentEntryRef,
2241 &parentEntry);
2242 if(FAILED(hr))
2244 return hr;
2247 if (entryToDelete.rightChild != DIRENTRY_NULL)
2250 * We need to reinsert the right child somewhere. We already know it and
2251 * its children are greater than everything in the left tree, so we
2252 * insert it at the rightmost point in the left tree.
2254 DirRef newRightChildParent = entryToDelete.leftChild;
2255 DirEntry newRightChildParentEntry;
2259 hr = StorageBaseImpl_ReadDirEntry(
2260 This,
2261 newRightChildParent,
2262 &newRightChildParentEntry);
2263 if (FAILED(hr))
2265 return hr;
2268 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2269 newRightChildParent = newRightChildParentEntry.rightChild;
2270 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2272 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2274 hr = StorageBaseImpl_WriteDirEntry(
2275 This,
2276 newRightChildParent,
2277 &newRightChildParentEntry);
2278 if (FAILED(hr))
2280 return hr;
2284 else
2287 * Replace the deleted entry with its right child
2289 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2291 hr = StorageBaseImpl_WriteDirEntry(
2292 This,
2293 parentEntryRef,
2294 &parentEntry);
2295 if(FAILED(hr))
2297 return hr;
2301 return hr;
2305 /******************************************************************************
2306 * SetElementTimes (IStorage)
2308 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2309 IStorage* iface,
2310 const OLECHAR *pwcsName,/* [string][in] */
2311 const FILETIME *pctime, /* [in] */
2312 const FILETIME *patime, /* [in] */
2313 const FILETIME *pmtime) /* [in] */
2315 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2316 return S_OK;
2319 /******************************************************************************
2320 * SetStateBits (IStorage)
2322 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2323 IStorage* iface,
2324 DWORD grfStateBits,/* [in] */
2325 DWORD grfMask) /* [in] */
2327 StorageBaseImpl *This = impl_from_IStorage(iface);
2329 if (This->reverted)
2330 return STG_E_REVERTED;
2332 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2333 return S_OK;
2336 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2337 DirRef index, const DirEntry *data)
2339 StorageImpl *This = (StorageImpl*)base;
2340 return StorageImpl_WriteDirEntry(This, index, data);
2343 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2344 DirRef index, DirEntry *data)
2346 StorageImpl *This = (StorageImpl*)base;
2347 return StorageImpl_ReadDirEntry(This, index, data);
2350 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2352 int i;
2354 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2356 if (!This->blockChainCache[i])
2358 return &This->blockChainCache[i];
2362 i = This->blockChainToEvict;
2364 BlockChainStream_Destroy(This->blockChainCache[i]);
2365 This->blockChainCache[i] = NULL;
2367 This->blockChainToEvict++;
2368 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2369 This->blockChainToEvict = 0;
2371 return &This->blockChainCache[i];
2374 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2375 DirRef index)
2377 int i, free_index=-1;
2379 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2381 if (!This->blockChainCache[i])
2383 if (free_index == -1) free_index = i;
2385 else if (This->blockChainCache[i]->ownerDirEntry == index)
2387 return &This->blockChainCache[i];
2391 if (free_index == -1)
2393 free_index = This->blockChainToEvict;
2395 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2396 This->blockChainCache[free_index] = NULL;
2398 This->blockChainToEvict++;
2399 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2400 This->blockChainToEvict = 0;
2403 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2404 return &This->blockChainCache[free_index];
2407 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2409 int i;
2411 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2413 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2415 BlockChainStream_Destroy(This->blockChainCache[i]);
2416 This->blockChainCache[i] = NULL;
2417 return;
2422 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2423 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2425 StorageImpl *This = (StorageImpl*)base;
2426 DirEntry data;
2427 HRESULT hr;
2428 ULONG bytesToRead;
2430 hr = StorageImpl_ReadDirEntry(This, index, &data);
2431 if (FAILED(hr)) return hr;
2433 if (data.size.QuadPart == 0)
2435 *bytesRead = 0;
2436 return S_OK;
2439 if (offset.QuadPart + size > data.size.QuadPart)
2441 bytesToRead = data.size.QuadPart - offset.QuadPart;
2443 else
2445 bytesToRead = size;
2448 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2450 SmallBlockChainStream *stream;
2452 stream = SmallBlockChainStream_Construct(This, NULL, index);
2453 if (!stream) return E_OUTOFMEMORY;
2455 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2457 SmallBlockChainStream_Destroy(stream);
2459 return hr;
2461 else
2463 BlockChainStream *stream = NULL;
2465 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2466 if (!stream) return E_OUTOFMEMORY;
2468 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2470 return hr;
2474 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2475 ULARGE_INTEGER newsize)
2477 StorageImpl *This = (StorageImpl*)base;
2478 DirEntry data;
2479 HRESULT hr;
2480 SmallBlockChainStream *smallblock=NULL;
2481 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2483 hr = StorageImpl_ReadDirEntry(This, index, &data);
2484 if (FAILED(hr)) return hr;
2486 /* In simple mode keep the stream size above the small block limit */
2487 if (This->base.openFlags & STGM_SIMPLE)
2488 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2490 if (data.size.QuadPart == newsize.QuadPart)
2491 return S_OK;
2493 /* Create a block chain object of the appropriate type */
2494 if (data.size.QuadPart == 0)
2496 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2498 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2499 if (!smallblock) return E_OUTOFMEMORY;
2501 else
2503 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2504 bigblock = *pbigblock;
2505 if (!bigblock) return E_OUTOFMEMORY;
2508 else if (data.size.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 /* Change the block chain type if necessary. */
2521 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2523 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2524 if (!bigblock)
2526 SmallBlockChainStream_Destroy(smallblock);
2527 return E_FAIL;
2530 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2531 *pbigblock = bigblock;
2533 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2535 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2536 if (!smallblock)
2537 return E_FAIL;
2540 /* Set the size of the block chain. */
2541 if (smallblock)
2543 SmallBlockChainStream_SetSize(smallblock, newsize);
2544 SmallBlockChainStream_Destroy(smallblock);
2546 else
2548 BlockChainStream_SetSize(bigblock, newsize);
2551 /* Set the size in the directory entry. */
2552 hr = StorageImpl_ReadDirEntry(This, index, &data);
2553 if (SUCCEEDED(hr))
2555 data.size = newsize;
2557 hr = StorageImpl_WriteDirEntry(This, index, &data);
2559 return hr;
2562 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2563 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2565 StorageImpl *This = (StorageImpl*)base;
2566 DirEntry data;
2567 HRESULT hr;
2568 ULARGE_INTEGER newSize;
2570 hr = StorageImpl_ReadDirEntry(This, index, &data);
2571 if (FAILED(hr)) return hr;
2573 /* Grow the stream if necessary */
2574 newSize.QuadPart = 0;
2575 newSize.QuadPart = offset.QuadPart + size;
2577 if (newSize.QuadPart > data.size.QuadPart)
2579 hr = StorageImpl_StreamSetSize(base, index, newSize);
2580 if (FAILED(hr))
2581 return hr;
2583 hr = StorageImpl_ReadDirEntry(This, index, &data);
2584 if (FAILED(hr)) return hr;
2587 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2589 SmallBlockChainStream *stream;
2591 stream = SmallBlockChainStream_Construct(This, NULL, index);
2592 if (!stream) return E_OUTOFMEMORY;
2594 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2596 SmallBlockChainStream_Destroy(stream);
2598 return hr;
2600 else
2602 BlockChainStream *stream;
2604 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2605 if (!stream) return E_OUTOFMEMORY;
2607 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2609 return hr;
2613 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2614 DirRef src)
2616 StorageImpl *This = (StorageImpl*)base;
2617 DirEntry dst_data, src_data;
2618 HRESULT hr;
2620 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2622 if (SUCCEEDED(hr))
2623 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2625 if (SUCCEEDED(hr))
2627 StorageImpl_DeleteCachedBlockChainStream(This, src);
2628 dst_data.startingBlock = src_data.startingBlock;
2629 dst_data.size = src_data.size;
2631 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2634 return hr;
2637 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2639 StorageImpl *This = (StorageImpl*) iface;
2640 STATSTG statstg;
2641 HRESULT hr;
2643 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2645 *result = statstg.pwcsName;
2647 return hr;
2651 * Virtual function table for the IStorage32Impl class.
2653 static const IStorageVtbl Storage32Impl_Vtbl =
2655 StorageBaseImpl_QueryInterface,
2656 StorageBaseImpl_AddRef,
2657 StorageBaseImpl_Release,
2658 StorageBaseImpl_CreateStream,
2659 StorageBaseImpl_OpenStream,
2660 StorageBaseImpl_CreateStorage,
2661 StorageBaseImpl_OpenStorage,
2662 StorageBaseImpl_CopyTo,
2663 StorageBaseImpl_MoveElementTo,
2664 StorageImpl_Commit,
2665 StorageImpl_Revert,
2666 StorageBaseImpl_EnumElements,
2667 StorageBaseImpl_DestroyElement,
2668 StorageBaseImpl_RenameElement,
2669 StorageBaseImpl_SetElementTimes,
2670 StorageBaseImpl_SetClass,
2671 StorageBaseImpl_SetStateBits,
2672 StorageBaseImpl_Stat
2675 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2677 StorageImpl_Destroy,
2678 StorageImpl_Invalidate,
2679 StorageImpl_Flush,
2680 StorageImpl_GetFilename,
2681 StorageImpl_CreateDirEntry,
2682 StorageImpl_BaseWriteDirEntry,
2683 StorageImpl_BaseReadDirEntry,
2684 StorageImpl_DestroyDirEntry,
2685 StorageImpl_StreamReadAt,
2686 StorageImpl_StreamWriteAt,
2687 StorageImpl_StreamSetSize,
2688 StorageImpl_StreamLink
2691 static HRESULT StorageImpl_Construct(
2692 HANDLE hFile,
2693 LPCOLESTR pwcsName,
2694 ILockBytes* pLkbyt,
2695 DWORD openFlags,
2696 BOOL fileBased,
2697 BOOL create,
2698 ULONG sector_size,
2699 StorageImpl** result)
2701 StorageImpl* This;
2702 HRESULT hr = S_OK;
2703 DirEntry currentEntry;
2704 DirRef currentEntryRef;
2706 if ( FAILED( validateSTGM(openFlags) ))
2707 return STG_E_INVALIDFLAG;
2709 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2710 if (!This)
2711 return E_OUTOFMEMORY;
2713 memset(This, 0, sizeof(StorageImpl));
2715 list_init(&This->base.strmHead);
2717 list_init(&This->base.storageHead);
2719 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
2720 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
2721 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2722 This->base.openFlags = (openFlags & ~STGM_CREATE);
2723 This->base.ref = 1;
2724 This->base.create = create;
2726 This->base.reverted = 0;
2729 * Initialize the big block cache.
2731 This->bigBlockSize = sector_size;
2732 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2733 if (hFile)
2734 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2735 else
2737 This->lockBytes = pLkbyt;
2738 ILockBytes_AddRef(pLkbyt);
2741 if (FAILED(hr))
2742 goto end;
2744 if (create)
2746 ULARGE_INTEGER size;
2747 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2749 /* Discard any existing data. */
2750 size.QuadPart = 0;
2751 ILockBytes_SetSize(This->lockBytes, size);
2754 * Initialize all header variables:
2755 * - The big block depot consists of one block and it is at block 0
2756 * - The directory table starts at block 1
2757 * - There is no small block depot
2759 memset( This->bigBlockDepotStart,
2760 BLOCK_UNUSED,
2761 sizeof(This->bigBlockDepotStart));
2763 This->bigBlockDepotCount = 1;
2764 This->bigBlockDepotStart[0] = 0;
2765 This->rootStartBlock = 1;
2766 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2767 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2768 if (sector_size == 4096)
2769 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2770 else
2771 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2772 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2773 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2774 This->extBigBlockDepotCount = 0;
2776 StorageImpl_SaveFileHeader(This);
2779 * Add one block for the big block depot and one block for the directory table
2781 size.u.HighPart = 0;
2782 size.u.LowPart = This->bigBlockSize * 3;
2783 ILockBytes_SetSize(This->lockBytes, size);
2786 * Initialize the big block depot
2788 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2789 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2790 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2791 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2793 else
2796 * Load the header for the file.
2798 hr = StorageImpl_LoadFileHeader(This);
2800 if (FAILED(hr))
2802 goto end;
2807 * There is no block depot cached yet.
2809 This->indexBlockDepotCached = 0xFFFFFFFF;
2810 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2813 * Start searching for free blocks with block 0.
2815 This->prevFreeBlock = 0;
2817 This->firstFreeSmallBlock = 0;
2819 /* Read the extended big block depot locations. */
2820 if (This->extBigBlockDepotCount != 0)
2822 ULONG current_block = This->extBigBlockDepotStart;
2823 ULONG cache_size = This->extBigBlockDepotCount * 2;
2824 int i;
2826 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2827 if (!This->extBigBlockDepotLocations)
2829 hr = E_OUTOFMEMORY;
2830 goto end;
2833 This->extBigBlockDepotLocationsSize = cache_size;
2835 for (i=0; i<This->extBigBlockDepotCount; i++)
2837 if (current_block == BLOCK_END_OF_CHAIN)
2839 WARN("File has too few extended big block depot blocks.\n");
2840 hr = STG_E_DOCFILECORRUPT;
2841 goto end;
2843 This->extBigBlockDepotLocations[i] = current_block;
2844 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2847 else
2849 This->extBigBlockDepotLocations = NULL;
2850 This->extBigBlockDepotLocationsSize = 0;
2854 * Create the block chain abstractions.
2856 if(!(This->rootBlockChain =
2857 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2859 hr = STG_E_READFAULT;
2860 goto end;
2863 if(!(This->smallBlockDepotChain =
2864 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2865 DIRENTRY_NULL)))
2867 hr = STG_E_READFAULT;
2868 goto end;
2872 * Write the root storage entry (memory only)
2874 if (create)
2876 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
2877 DirEntry rootEntry;
2879 * Initialize the directory table
2881 memset(&rootEntry, 0, sizeof(rootEntry));
2882 strcpyW(rootEntry.name, rootentryW);
2883 rootEntry.sizeOfNameString = sizeof(rootentryW);
2884 rootEntry.stgType = STGTY_ROOT;
2885 rootEntry.leftChild = DIRENTRY_NULL;
2886 rootEntry.rightChild = DIRENTRY_NULL;
2887 rootEntry.dirRootEntry = DIRENTRY_NULL;
2888 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2889 rootEntry.size.u.HighPart = 0;
2890 rootEntry.size.u.LowPart = 0;
2892 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2896 * Find the ID of the root storage.
2898 currentEntryRef = 0;
2902 hr = StorageImpl_ReadDirEntry(
2903 This,
2904 currentEntryRef,
2905 &currentEntry);
2907 if (SUCCEEDED(hr))
2909 if ( (currentEntry.sizeOfNameString != 0 ) &&
2910 (currentEntry.stgType == STGTY_ROOT) )
2912 This->base.storageDirEntry = currentEntryRef;
2916 currentEntryRef++;
2918 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2920 if (FAILED(hr))
2922 hr = STG_E_READFAULT;
2923 goto end;
2927 * Create the block chain abstraction for the small block root chain.
2929 if(!(This->smallBlockRootChain =
2930 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2932 hr = STG_E_READFAULT;
2935 end:
2936 if (FAILED(hr))
2938 IStorage_Release(&This->base.IStorage_iface);
2939 *result = NULL;
2941 else
2943 StorageImpl_Flush((StorageBaseImpl*)This);
2944 *result = This;
2947 return hr;
2950 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2952 StorageImpl *This = (StorageImpl*) iface;
2954 StorageBaseImpl_DeleteAll(&This->base);
2956 This->base.reverted = 1;
2959 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2961 StorageImpl *This = (StorageImpl*) iface;
2962 int i;
2963 TRACE("(%p)\n", This);
2965 StorageImpl_Flush(iface);
2967 StorageImpl_Invalidate(iface);
2969 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
2971 BlockChainStream_Destroy(This->smallBlockRootChain);
2972 BlockChainStream_Destroy(This->rootBlockChain);
2973 BlockChainStream_Destroy(This->smallBlockDepotChain);
2975 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2976 BlockChainStream_Destroy(This->blockChainCache[i]);
2978 if (This->lockBytes)
2979 ILockBytes_Release(This->lockBytes);
2980 HeapFree(GetProcessHeap(), 0, This);
2983 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2985 StorageImpl *This = (StorageImpl*) iface;
2986 int i;
2987 HRESULT hr;
2988 TRACE("(%p)\n", This);
2990 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2992 if (SUCCEEDED(hr))
2993 hr = BlockChainStream_Flush(This->rootBlockChain);
2995 if (SUCCEEDED(hr))
2996 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2998 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
2999 if (This->blockChainCache[i])
3000 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3002 if (SUCCEEDED(hr))
3003 hr = ILockBytes_Flush(This->lockBytes);
3005 return hr;
3008 /******************************************************************************
3009 * Storage32Impl_GetNextFreeBigBlock
3011 * Returns the index of the next free big block.
3012 * If the big block depot is filled, this method will enlarge it.
3015 static ULONG StorageImpl_GetNextFreeBigBlock(
3016 StorageImpl* This)
3018 ULONG depotBlockIndexPos;
3019 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3020 BOOL success;
3021 ULONG depotBlockOffset;
3022 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3023 ULONG nextBlockIndex = BLOCK_SPECIAL;
3024 int depotIndex = 0;
3025 ULONG freeBlock = BLOCK_UNUSED;
3026 ULARGE_INTEGER neededSize;
3027 STATSTG statstg;
3029 depotIndex = This->prevFreeBlock / blocksPerDepot;
3030 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3033 * Scan the entire big block depot until we find a block marked free
3035 while (nextBlockIndex != BLOCK_UNUSED)
3037 if (depotIndex < COUNT_BBDEPOTINHEADER)
3039 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3042 * Grow the primary depot.
3044 if (depotBlockIndexPos == BLOCK_UNUSED)
3046 depotBlockIndexPos = depotIndex*blocksPerDepot;
3049 * Add a block depot.
3051 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3052 This->bigBlockDepotCount++;
3053 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3056 * Flag it as a block depot.
3058 StorageImpl_SetNextBlockInChain(This,
3059 depotBlockIndexPos,
3060 BLOCK_SPECIAL);
3062 /* Save new header information.
3064 StorageImpl_SaveFileHeader(This);
3067 else
3069 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3071 if (depotBlockIndexPos == BLOCK_UNUSED)
3074 * Grow the extended depot.
3076 ULONG extIndex = BLOCK_UNUSED;
3077 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3078 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3080 if (extBlockOffset == 0)
3082 /* We need an extended block.
3084 extIndex = Storage32Impl_AddExtBlockDepot(This);
3085 This->extBigBlockDepotCount++;
3086 depotBlockIndexPos = extIndex + 1;
3088 else
3089 depotBlockIndexPos = depotIndex * blocksPerDepot;
3092 * Add a block depot and mark it in the extended block.
3094 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3095 This->bigBlockDepotCount++;
3096 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3098 /* Flag the block depot.
3100 StorageImpl_SetNextBlockInChain(This,
3101 depotBlockIndexPos,
3102 BLOCK_SPECIAL);
3104 /* If necessary, flag the extended depot block.
3106 if (extIndex != BLOCK_UNUSED)
3107 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3109 /* Save header information.
3111 StorageImpl_SaveFileHeader(This);
3115 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3117 if (success)
3119 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3120 ( nextBlockIndex != BLOCK_UNUSED))
3122 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3124 if (nextBlockIndex == BLOCK_UNUSED)
3126 freeBlock = (depotIndex * blocksPerDepot) +
3127 (depotBlockOffset/sizeof(ULONG));
3130 depotBlockOffset += sizeof(ULONG);
3134 depotIndex++;
3135 depotBlockOffset = 0;
3139 * make sure that the block physically exists before using it
3141 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3143 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3145 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3146 ILockBytes_SetSize(This->lockBytes, neededSize);
3148 This->prevFreeBlock = freeBlock;
3150 return freeBlock;
3153 /******************************************************************************
3154 * Storage32Impl_AddBlockDepot
3156 * This will create a depot block, essentially it is a block initialized
3157 * to BLOCK_UNUSEDs.
3159 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3161 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3164 * Initialize blocks as free
3166 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3167 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3170 /******************************************************************************
3171 * Storage32Impl_GetExtDepotBlock
3173 * Returns the index of the block that corresponds to the specified depot
3174 * index. This method is only for depot indexes equal or greater than
3175 * COUNT_BBDEPOTINHEADER.
3177 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3179 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3180 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3181 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3182 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3183 ULONG blockIndex = BLOCK_UNUSED;
3184 ULONG extBlockIndex;
3185 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3186 int index, num_blocks;
3188 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3190 if (extBlockCount >= This->extBigBlockDepotCount)
3191 return BLOCK_UNUSED;
3193 if (This->indexExtBlockDepotCached != extBlockCount)
3195 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3197 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);
3199 num_blocks = This->bigBlockSize / 4;
3201 for (index = 0; index < num_blocks; index++)
3203 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3204 This->extBlockDepotCached[index] = blockIndex;
3207 This->indexExtBlockDepotCached = extBlockCount;
3210 blockIndex = This->extBlockDepotCached[extBlockOffset];
3212 return blockIndex;
3215 /******************************************************************************
3216 * Storage32Impl_SetExtDepotBlock
3218 * Associates the specified block index to the specified depot index.
3219 * This method is only for depot indexes equal or greater than
3220 * COUNT_BBDEPOTINHEADER.
3222 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3224 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3225 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3226 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3227 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3228 ULONG extBlockIndex;
3230 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3232 assert(extBlockCount < This->extBigBlockDepotCount);
3234 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3236 if (extBlockIndex != BLOCK_UNUSED)
3238 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3239 extBlockOffset * sizeof(ULONG),
3240 blockIndex);
3243 if (This->indexExtBlockDepotCached == extBlockCount)
3245 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3249 /******************************************************************************
3250 * Storage32Impl_AddExtBlockDepot
3252 * Creates an extended depot block.
3254 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3256 ULONG numExtBlocks = This->extBigBlockDepotCount;
3257 ULONG nextExtBlock = This->extBigBlockDepotStart;
3258 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3259 ULONG index = BLOCK_UNUSED;
3260 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3261 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3262 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3264 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3265 blocksPerDepotBlock;
3267 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3270 * The first extended block.
3272 This->extBigBlockDepotStart = index;
3274 else
3277 * Find the last existing extended block.
3279 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3282 * Add the new extended block to the chain.
3284 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3285 index);
3289 * Initialize this block.
3291 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3292 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3294 /* Add the block to our cache. */
3295 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3297 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3298 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3300 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3301 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3303 This->extBigBlockDepotLocations = new_cache;
3304 This->extBigBlockDepotLocationsSize = new_cache_size;
3306 This->extBigBlockDepotLocations[numExtBlocks] = index;
3308 return index;
3311 /******************************************************************************
3312 * Storage32Impl_FreeBigBlock
3314 * This method will flag the specified block as free in the big block depot.
3316 static void StorageImpl_FreeBigBlock(
3317 StorageImpl* This,
3318 ULONG blockIndex)
3320 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3322 if (blockIndex < This->prevFreeBlock)
3323 This->prevFreeBlock = blockIndex;
3326 /************************************************************************
3327 * Storage32Impl_GetNextBlockInChain
3329 * This method will retrieve the block index of the next big block in
3330 * in the chain.
3332 * Params: This - Pointer to the Storage object.
3333 * blockIndex - Index of the block to retrieve the chain
3334 * for.
3335 * nextBlockIndex - receives the return value.
3337 * Returns: This method returns the index of the next block in the chain.
3338 * It will return the constants:
3339 * BLOCK_SPECIAL - If the block given was not part of a
3340 * chain.
3341 * BLOCK_END_OF_CHAIN - If the block given was the last in
3342 * a chain.
3343 * BLOCK_UNUSED - If the block given was not past of a chain
3344 * and is available.
3345 * BLOCK_EXTBBDEPOT - This block is part of the extended
3346 * big block depot.
3348 * See Windows documentation for more details on IStorage methods.
3350 static HRESULT StorageImpl_GetNextBlockInChain(
3351 StorageImpl* This,
3352 ULONG blockIndex,
3353 ULONG* nextBlockIndex)
3355 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3356 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3357 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3358 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3359 BOOL success;
3360 ULONG depotBlockIndexPos;
3361 int index, num_blocks;
3363 *nextBlockIndex = BLOCK_SPECIAL;
3365 if(depotBlockCount >= This->bigBlockDepotCount)
3367 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3368 This->bigBlockDepotCount);
3369 return STG_E_READFAULT;
3373 * Cache the currently accessed depot block.
3375 if (depotBlockCount != This->indexBlockDepotCached)
3377 This->indexBlockDepotCached = depotBlockCount;
3379 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3381 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3383 else
3386 * We have to look in the extended depot.
3388 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3391 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3393 if (!success)
3394 return STG_E_READFAULT;
3396 num_blocks = This->bigBlockSize / 4;
3398 for (index = 0; index < num_blocks; index++)
3400 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3401 This->blockDepotCached[index] = *nextBlockIndex;
3405 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3407 return S_OK;
3410 /******************************************************************************
3411 * Storage32Impl_GetNextExtendedBlock
3413 * Given an extended block this method will return the next extended block.
3415 * NOTES:
3416 * The last ULONG of an extended block is the block index of the next
3417 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3418 * depot.
3420 * Return values:
3421 * - The index of the next extended block
3422 * - BLOCK_UNUSED: there is no next extended block.
3423 * - Any other return values denotes failure.
3425 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3427 ULONG nextBlockIndex = BLOCK_SPECIAL;
3428 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3430 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3431 &nextBlockIndex);
3433 return nextBlockIndex;
3436 /******************************************************************************
3437 * Storage32Impl_SetNextBlockInChain
3439 * This method will write the index of the specified block's next block
3440 * in the big block depot.
3442 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3443 * do the following
3445 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3446 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3447 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3450 static void StorageImpl_SetNextBlockInChain(
3451 StorageImpl* This,
3452 ULONG blockIndex,
3453 ULONG nextBlock)
3455 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3456 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3457 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3458 ULONG depotBlockIndexPos;
3460 assert(depotBlockCount < This->bigBlockDepotCount);
3461 assert(blockIndex != nextBlock);
3463 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3465 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3467 else
3470 * We have to look in the extended depot.
3472 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3475 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3476 nextBlock);
3478 * Update the cached block depot, if necessary.
3480 if (depotBlockCount == This->indexBlockDepotCached)
3482 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3486 /******************************************************************************
3487 * Storage32Impl_LoadFileHeader
3489 * This method will read in the file header
3491 static HRESULT StorageImpl_LoadFileHeader(
3492 StorageImpl* This)
3494 HRESULT hr;
3495 BYTE headerBigBlock[HEADER_SIZE];
3496 int index;
3497 ULARGE_INTEGER offset;
3498 DWORD bytes_read;
3500 TRACE("\n");
3502 * Get a pointer to the big block of data containing the header.
3504 offset.u.HighPart = 0;
3505 offset.u.LowPart = 0;
3506 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3507 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3508 hr = STG_E_FILENOTFOUND;
3511 * Extract the information from the header.
3513 if (SUCCEEDED(hr))
3516 * Check for the "magic number" signature and return an error if it is not
3517 * found.
3519 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3521 return STG_E_OLDFORMAT;
3524 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3526 return STG_E_INVALIDHEADER;
3529 StorageUtl_ReadWord(
3530 headerBigBlock,
3531 OFFSET_BIGBLOCKSIZEBITS,
3532 &This->bigBlockSizeBits);
3534 StorageUtl_ReadWord(
3535 headerBigBlock,
3536 OFFSET_SMALLBLOCKSIZEBITS,
3537 &This->smallBlockSizeBits);
3539 StorageUtl_ReadDWord(
3540 headerBigBlock,
3541 OFFSET_BBDEPOTCOUNT,
3542 &This->bigBlockDepotCount);
3544 StorageUtl_ReadDWord(
3545 headerBigBlock,
3546 OFFSET_ROOTSTARTBLOCK,
3547 &This->rootStartBlock);
3549 StorageUtl_ReadDWord(
3550 headerBigBlock,
3551 OFFSET_SMALLBLOCKLIMIT,
3552 &This->smallBlockLimit);
3554 StorageUtl_ReadDWord(
3555 headerBigBlock,
3556 OFFSET_SBDEPOTSTART,
3557 &This->smallBlockDepotStart);
3559 StorageUtl_ReadDWord(
3560 headerBigBlock,
3561 OFFSET_EXTBBDEPOTSTART,
3562 &This->extBigBlockDepotStart);
3564 StorageUtl_ReadDWord(
3565 headerBigBlock,
3566 OFFSET_EXTBBDEPOTCOUNT,
3567 &This->extBigBlockDepotCount);
3569 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3571 StorageUtl_ReadDWord(
3572 headerBigBlock,
3573 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3574 &(This->bigBlockDepotStart[index]));
3578 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3580 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3581 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3584 * Right now, the code is making some assumptions about the size of the
3585 * blocks, just make sure they are what we're expecting.
3587 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3588 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3589 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3591 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3592 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3593 hr = STG_E_INVALIDHEADER;
3595 else
3596 hr = S_OK;
3599 return hr;
3602 /******************************************************************************
3603 * Storage32Impl_SaveFileHeader
3605 * This method will save to the file the header
3607 static void StorageImpl_SaveFileHeader(
3608 StorageImpl* This)
3610 BYTE headerBigBlock[HEADER_SIZE];
3611 int index;
3612 HRESULT hr;
3613 ULARGE_INTEGER offset;
3614 DWORD bytes_read, bytes_written;
3615 DWORD major_version, dirsectorcount;
3618 * Get a pointer to the big block of data containing the header.
3620 offset.u.HighPart = 0;
3621 offset.u.LowPart = 0;
3622 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3623 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3624 hr = STG_E_FILENOTFOUND;
3626 if (This->bigBlockSizeBits == 0x9)
3627 major_version = 3;
3628 else if (This->bigBlockSizeBits == 0xc)
3629 major_version = 4;
3630 else
3632 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3633 major_version = 4;
3637 * If the block read failed, the file is probably new.
3639 if (FAILED(hr))
3642 * Initialize for all unknown fields.
3644 memset(headerBigBlock, 0, HEADER_SIZE);
3647 * Initialize the magic number.
3649 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3653 * Write the information to the header.
3655 StorageUtl_WriteWord(
3656 headerBigBlock,
3657 OFFSET_MINORVERSION,
3658 0x3e);
3660 StorageUtl_WriteWord(
3661 headerBigBlock,
3662 OFFSET_MAJORVERSION,
3663 major_version);
3665 StorageUtl_WriteWord(
3666 headerBigBlock,
3667 OFFSET_BYTEORDERMARKER,
3668 (WORD)-2);
3670 StorageUtl_WriteWord(
3671 headerBigBlock,
3672 OFFSET_BIGBLOCKSIZEBITS,
3673 This->bigBlockSizeBits);
3675 StorageUtl_WriteWord(
3676 headerBigBlock,
3677 OFFSET_SMALLBLOCKSIZEBITS,
3678 This->smallBlockSizeBits);
3680 if (major_version >= 4)
3682 if (This->rootBlockChain)
3683 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3684 else
3685 /* This file is being created, and it will start out with one block. */
3686 dirsectorcount = 1;
3688 else
3689 /* This field must be 0 in versions older than 4 */
3690 dirsectorcount = 0;
3692 StorageUtl_WriteDWord(
3693 headerBigBlock,
3694 OFFSET_DIRSECTORCOUNT,
3695 dirsectorcount);
3697 StorageUtl_WriteDWord(
3698 headerBigBlock,
3699 OFFSET_BBDEPOTCOUNT,
3700 This->bigBlockDepotCount);
3702 StorageUtl_WriteDWord(
3703 headerBigBlock,
3704 OFFSET_ROOTSTARTBLOCK,
3705 This->rootStartBlock);
3707 StorageUtl_WriteDWord(
3708 headerBigBlock,
3709 OFFSET_SMALLBLOCKLIMIT,
3710 This->smallBlockLimit);
3712 StorageUtl_WriteDWord(
3713 headerBigBlock,
3714 OFFSET_SBDEPOTSTART,
3715 This->smallBlockDepotStart);
3717 StorageUtl_WriteDWord(
3718 headerBigBlock,
3719 OFFSET_SBDEPOTCOUNT,
3720 This->smallBlockDepotChain ?
3721 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3723 StorageUtl_WriteDWord(
3724 headerBigBlock,
3725 OFFSET_EXTBBDEPOTSTART,
3726 This->extBigBlockDepotStart);
3728 StorageUtl_WriteDWord(
3729 headerBigBlock,
3730 OFFSET_EXTBBDEPOTCOUNT,
3731 This->extBigBlockDepotCount);
3733 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3735 StorageUtl_WriteDWord(
3736 headerBigBlock,
3737 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3738 (This->bigBlockDepotStart[index]));
3742 * Write the big block back to the file.
3744 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3747 /******************************************************************************
3748 * StorageImpl_ReadRawDirEntry
3750 * This method will read the raw data from a directory entry in the file.
3752 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3754 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3756 ULARGE_INTEGER offset;
3757 HRESULT hr;
3758 ULONG bytesRead;
3760 offset.u.HighPart = 0;
3761 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3763 hr = BlockChainStream_ReadAt(
3764 This->rootBlockChain,
3765 offset,
3766 RAW_DIRENTRY_SIZE,
3767 buffer,
3768 &bytesRead);
3770 if (bytesRead != RAW_DIRENTRY_SIZE)
3771 return STG_E_READFAULT;
3773 return hr;
3776 /******************************************************************************
3777 * StorageImpl_WriteRawDirEntry
3779 * This method will write the raw data from a directory entry in the file.
3781 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3783 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3785 ULARGE_INTEGER offset;
3786 HRESULT hr;
3787 ULONG bytesRead;
3789 offset.u.HighPart = 0;
3790 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3792 hr = BlockChainStream_WriteAt(
3793 This->rootBlockChain,
3794 offset,
3795 RAW_DIRENTRY_SIZE,
3796 buffer,
3797 &bytesRead);
3799 return hr;
3802 /******************************************************************************
3803 * UpdateRawDirEntry
3805 * Update raw directory entry data from the fields in newData.
3807 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3809 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3811 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3813 memcpy(
3814 buffer + OFFSET_PS_NAME,
3815 newData->name,
3816 DIRENTRY_NAME_BUFFER_LEN );
3818 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3820 StorageUtl_WriteWord(
3821 buffer,
3822 OFFSET_PS_NAMELENGTH,
3823 newData->sizeOfNameString);
3825 StorageUtl_WriteDWord(
3826 buffer,
3827 OFFSET_PS_LEFTCHILD,
3828 newData->leftChild);
3830 StorageUtl_WriteDWord(
3831 buffer,
3832 OFFSET_PS_RIGHTCHILD,
3833 newData->rightChild);
3835 StorageUtl_WriteDWord(
3836 buffer,
3837 OFFSET_PS_DIRROOT,
3838 newData->dirRootEntry);
3840 StorageUtl_WriteGUID(
3841 buffer,
3842 OFFSET_PS_GUID,
3843 &newData->clsid);
3845 StorageUtl_WriteDWord(
3846 buffer,
3847 OFFSET_PS_CTIMELOW,
3848 newData->ctime.dwLowDateTime);
3850 StorageUtl_WriteDWord(
3851 buffer,
3852 OFFSET_PS_CTIMEHIGH,
3853 newData->ctime.dwHighDateTime);
3855 StorageUtl_WriteDWord(
3856 buffer,
3857 OFFSET_PS_MTIMELOW,
3858 newData->mtime.dwLowDateTime);
3860 StorageUtl_WriteDWord(
3861 buffer,
3862 OFFSET_PS_MTIMEHIGH,
3863 newData->ctime.dwHighDateTime);
3865 StorageUtl_WriteDWord(
3866 buffer,
3867 OFFSET_PS_STARTBLOCK,
3868 newData->startingBlock);
3870 StorageUtl_WriteDWord(
3871 buffer,
3872 OFFSET_PS_SIZE,
3873 newData->size.u.LowPart);
3876 /******************************************************************************
3877 * Storage32Impl_ReadDirEntry
3879 * This method will read the specified directory entry.
3881 HRESULT StorageImpl_ReadDirEntry(
3882 StorageImpl* This,
3883 DirRef index,
3884 DirEntry* buffer)
3886 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3887 HRESULT readRes;
3889 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3891 if (SUCCEEDED(readRes))
3893 memset(buffer->name, 0, sizeof(buffer->name));
3894 memcpy(
3895 buffer->name,
3896 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3897 DIRENTRY_NAME_BUFFER_LEN );
3898 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3900 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3902 StorageUtl_ReadWord(
3903 currentEntry,
3904 OFFSET_PS_NAMELENGTH,
3905 &buffer->sizeOfNameString);
3907 StorageUtl_ReadDWord(
3908 currentEntry,
3909 OFFSET_PS_LEFTCHILD,
3910 &buffer->leftChild);
3912 StorageUtl_ReadDWord(
3913 currentEntry,
3914 OFFSET_PS_RIGHTCHILD,
3915 &buffer->rightChild);
3917 StorageUtl_ReadDWord(
3918 currentEntry,
3919 OFFSET_PS_DIRROOT,
3920 &buffer->dirRootEntry);
3922 StorageUtl_ReadGUID(
3923 currentEntry,
3924 OFFSET_PS_GUID,
3925 &buffer->clsid);
3927 StorageUtl_ReadDWord(
3928 currentEntry,
3929 OFFSET_PS_CTIMELOW,
3930 &buffer->ctime.dwLowDateTime);
3932 StorageUtl_ReadDWord(
3933 currentEntry,
3934 OFFSET_PS_CTIMEHIGH,
3935 &buffer->ctime.dwHighDateTime);
3937 StorageUtl_ReadDWord(
3938 currentEntry,
3939 OFFSET_PS_MTIMELOW,
3940 &buffer->mtime.dwLowDateTime);
3942 StorageUtl_ReadDWord(
3943 currentEntry,
3944 OFFSET_PS_MTIMEHIGH,
3945 &buffer->mtime.dwHighDateTime);
3947 StorageUtl_ReadDWord(
3948 currentEntry,
3949 OFFSET_PS_STARTBLOCK,
3950 &buffer->startingBlock);
3952 StorageUtl_ReadDWord(
3953 currentEntry,
3954 OFFSET_PS_SIZE,
3955 &buffer->size.u.LowPart);
3957 buffer->size.u.HighPart = 0;
3960 return readRes;
3963 /*********************************************************************
3964 * Write the specified directory entry to the file
3966 HRESULT StorageImpl_WriteDirEntry(
3967 StorageImpl* This,
3968 DirRef index,
3969 const DirEntry* buffer)
3971 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3972 HRESULT writeRes;
3974 UpdateRawDirEntry(currentEntry, buffer);
3976 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3977 return writeRes;
3980 static BOOL StorageImpl_ReadBigBlock(
3981 StorageImpl* This,
3982 ULONG blockIndex,
3983 void* buffer)
3985 ULARGE_INTEGER ulOffset;
3986 DWORD read=0;
3988 ulOffset.u.HighPart = 0;
3989 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3991 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3993 if (read && read < This->bigBlockSize)
3995 /* File ends during this block; fill the rest with 0's. */
3996 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3999 return (read != 0);
4002 static BOOL StorageImpl_ReadDWordFromBigBlock(
4003 StorageImpl* This,
4004 ULONG blockIndex,
4005 ULONG offset,
4006 DWORD* value)
4008 ULARGE_INTEGER ulOffset;
4009 DWORD read;
4010 DWORD tmp;
4012 ulOffset.u.HighPart = 0;
4013 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4014 ulOffset.u.LowPart += offset;
4016 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4017 *value = lendian32toh(tmp);
4018 return (read == sizeof(DWORD));
4021 static BOOL StorageImpl_WriteBigBlock(
4022 StorageImpl* This,
4023 ULONG blockIndex,
4024 const void* buffer)
4026 ULARGE_INTEGER ulOffset;
4027 DWORD wrote;
4029 ulOffset.u.HighPart = 0;
4030 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4032 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4033 return (wrote == This->bigBlockSize);
4036 static BOOL StorageImpl_WriteDWordToBigBlock(
4037 StorageImpl* This,
4038 ULONG blockIndex,
4039 ULONG offset,
4040 DWORD value)
4042 ULARGE_INTEGER ulOffset;
4043 DWORD wrote;
4045 ulOffset.u.HighPart = 0;
4046 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4047 ulOffset.u.LowPart += offset;
4049 value = htole32(value);
4050 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4051 return (wrote == sizeof(DWORD));
4054 /******************************************************************************
4055 * Storage32Impl_SmallBlocksToBigBlocks
4057 * This method will convert a small block chain to a big block chain.
4058 * The small block chain will be destroyed.
4060 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4061 StorageImpl* This,
4062 SmallBlockChainStream** ppsbChain)
4064 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4065 ULARGE_INTEGER size, offset;
4066 ULONG cbRead, cbWritten;
4067 ULARGE_INTEGER cbTotalRead;
4068 DirRef streamEntryRef;
4069 HRESULT resWrite = S_OK;
4070 HRESULT resRead;
4071 DirEntry streamEntry;
4072 BYTE *buffer;
4073 BlockChainStream *bbTempChain = NULL;
4074 BlockChainStream *bigBlockChain = NULL;
4077 * Create a temporary big block chain that doesn't have
4078 * an associated directory entry. This temporary chain will be
4079 * used to copy data from small blocks to big blocks.
4081 bbTempChain = BlockChainStream_Construct(This,
4082 &bbHeadOfChain,
4083 DIRENTRY_NULL);
4084 if(!bbTempChain) return NULL;
4086 * Grow the big block chain.
4088 size = SmallBlockChainStream_GetSize(*ppsbChain);
4089 BlockChainStream_SetSize(bbTempChain, size);
4092 * Copy the contents of the small block chain to the big block chain
4093 * by small block size increments.
4095 offset.u.LowPart = 0;
4096 offset.u.HighPart = 0;
4097 cbTotalRead.QuadPart = 0;
4099 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4102 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4103 offset,
4104 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4105 buffer,
4106 &cbRead);
4107 if (FAILED(resRead))
4108 break;
4110 if (cbRead > 0)
4112 cbTotalRead.QuadPart += cbRead;
4114 resWrite = BlockChainStream_WriteAt(bbTempChain,
4115 offset,
4116 cbRead,
4117 buffer,
4118 &cbWritten);
4120 if (FAILED(resWrite))
4121 break;
4123 offset.u.LowPart += cbRead;
4125 else
4127 resRead = STG_E_READFAULT;
4128 break;
4130 } while (cbTotalRead.QuadPart < size.QuadPart);
4131 HeapFree(GetProcessHeap(),0,buffer);
4133 size.u.HighPart = 0;
4134 size.u.LowPart = 0;
4136 if (FAILED(resRead) || FAILED(resWrite))
4138 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4139 BlockChainStream_SetSize(bbTempChain, size);
4140 BlockChainStream_Destroy(bbTempChain);
4141 return NULL;
4145 * Destroy the small block chain.
4147 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4148 SmallBlockChainStream_SetSize(*ppsbChain, size);
4149 SmallBlockChainStream_Destroy(*ppsbChain);
4150 *ppsbChain = 0;
4153 * Change the directory entry. This chain is now a big block chain
4154 * and it doesn't reside in the small blocks chain anymore.
4156 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4158 streamEntry.startingBlock = bbHeadOfChain;
4160 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4163 * Destroy the temporary entryless big block chain.
4164 * Create a new big block chain associated with this entry.
4166 BlockChainStream_Destroy(bbTempChain);
4167 bigBlockChain = BlockChainStream_Construct(This,
4168 NULL,
4169 streamEntryRef);
4171 return bigBlockChain;
4174 /******************************************************************************
4175 * Storage32Impl_BigBlocksToSmallBlocks
4177 * This method will convert a big block chain to a small block chain.
4178 * The big block chain will be destroyed on success.
4180 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4181 StorageImpl* This,
4182 BlockChainStream** ppbbChain,
4183 ULARGE_INTEGER newSize)
4185 ULARGE_INTEGER size, offset, cbTotalRead;
4186 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4187 DirRef streamEntryRef;
4188 HRESULT resWrite = S_OK, resRead = S_OK;
4189 DirEntry streamEntry;
4190 BYTE* buffer;
4191 SmallBlockChainStream* sbTempChain;
4193 TRACE("%p %p\n", This, ppbbChain);
4195 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4196 DIRENTRY_NULL);
4198 if(!sbTempChain)
4199 return NULL;
4201 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4202 size = BlockChainStream_GetSize(*ppbbChain);
4203 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4205 offset.u.HighPart = 0;
4206 offset.u.LowPart = 0;
4207 cbTotalRead.QuadPart = 0;
4208 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4209 while(cbTotalRead.QuadPart < size.QuadPart)
4211 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4212 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4213 buffer, &cbRead);
4215 if(FAILED(resRead))
4216 break;
4218 if(cbRead > 0)
4220 cbTotalRead.QuadPart += cbRead;
4222 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4223 cbRead, buffer, &cbWritten);
4225 if(FAILED(resWrite))
4226 break;
4228 offset.u.LowPart += cbRead;
4230 else
4232 resRead = STG_E_READFAULT;
4233 break;
4236 HeapFree(GetProcessHeap(), 0, buffer);
4238 size.u.HighPart = 0;
4239 size.u.LowPart = 0;
4241 if(FAILED(resRead) || FAILED(resWrite))
4243 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4244 SmallBlockChainStream_SetSize(sbTempChain, size);
4245 SmallBlockChainStream_Destroy(sbTempChain);
4246 return NULL;
4249 /* destroy the original big block chain */
4250 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4251 BlockChainStream_SetSize(*ppbbChain, size);
4252 BlockChainStream_Destroy(*ppbbChain);
4253 *ppbbChain = NULL;
4255 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4256 streamEntry.startingBlock = sbHeadOfChain;
4257 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4259 SmallBlockChainStream_Destroy(sbTempChain);
4260 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4263 static HRESULT StorageBaseImpl_CopyStream(
4264 StorageBaseImpl *dst, DirRef dst_entry,
4265 StorageBaseImpl *src, DirRef src_entry)
4267 HRESULT hr;
4268 BYTE data[4096];
4269 DirEntry srcdata;
4270 ULARGE_INTEGER bytes_copied;
4271 ULONG bytestocopy, bytesread, byteswritten;
4273 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4275 if (SUCCEEDED(hr))
4277 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4279 bytes_copied.QuadPart = 0;
4280 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4282 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4284 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4285 data, &bytesread);
4286 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4288 if (SUCCEEDED(hr))
4289 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4290 data, &byteswritten);
4291 if (SUCCEEDED(hr))
4293 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4294 bytes_copied.QuadPart += byteswritten;
4299 return hr;
4302 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4304 DirRef result=This->firstFreeEntry;
4306 while (result < This->entries_size && This->entries[result].inuse)
4307 result++;
4309 if (result == This->entries_size)
4311 ULONG new_size = This->entries_size * 2;
4312 TransactedDirEntry *new_entries;
4314 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4315 if (!new_entries) return DIRENTRY_NULL;
4317 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4318 HeapFree(GetProcessHeap(), 0, This->entries);
4320 This->entries = new_entries;
4321 This->entries_size = new_size;
4324 This->entries[result].inuse = 1;
4326 This->firstFreeEntry = result+1;
4328 return result;
4331 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4332 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4334 DirRef stubEntryRef;
4335 TransactedDirEntry *entry;
4337 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4339 if (stubEntryRef != DIRENTRY_NULL)
4341 entry = &This->entries[stubEntryRef];
4343 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4345 entry->read = 0;
4348 return stubEntryRef;
4351 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4352 TransactedSnapshotImpl *This, DirRef entry)
4354 HRESULT hr=S_OK;
4355 DirEntry data;
4357 if (!This->entries[entry].read)
4359 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4360 This->entries[entry].transactedParentEntry,
4361 &data);
4363 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4365 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4367 if (data.leftChild == DIRENTRY_NULL)
4368 hr = E_OUTOFMEMORY;
4371 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4373 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4375 if (data.rightChild == DIRENTRY_NULL)
4376 hr = E_OUTOFMEMORY;
4379 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4381 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4383 if (data.dirRootEntry == DIRENTRY_NULL)
4384 hr = E_OUTOFMEMORY;
4387 if (SUCCEEDED(hr))
4389 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4390 This->entries[entry].read = 1;
4394 return hr;
4397 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4398 TransactedSnapshotImpl *This, DirRef entry)
4400 HRESULT hr = S_OK;
4402 if (!This->entries[entry].stream_dirty)
4404 DirEntry new_entrydata;
4406 memset(&new_entrydata, 0, sizeof(DirEntry));
4407 new_entrydata.name[0] = 'S';
4408 new_entrydata.sizeOfNameString = 1;
4409 new_entrydata.stgType = STGTY_STREAM;
4410 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4411 new_entrydata.leftChild = DIRENTRY_NULL;
4412 new_entrydata.rightChild = DIRENTRY_NULL;
4413 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4415 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4416 &This->entries[entry].stream_entry);
4418 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4420 hr = StorageBaseImpl_CopyStream(
4421 This->scratch, This->entries[entry].stream_entry,
4422 This->transactedParent, This->entries[entry].transactedParentEntry);
4424 if (FAILED(hr))
4425 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4428 if (SUCCEEDED(hr))
4429 This->entries[entry].stream_dirty = 1;
4431 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4433 /* Since this entry is modified, and we aren't using its stream data, we
4434 * no longer care about the original entry. */
4435 DirRef delete_ref;
4436 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4438 if (delete_ref != DIRENTRY_NULL)
4439 This->entries[delete_ref].deleted = 1;
4441 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4445 return hr;
4448 /* Find the first entry in a depth-first traversal. */
4449 static DirRef TransactedSnapshotImpl_FindFirstChild(
4450 TransactedSnapshotImpl* This, DirRef parent)
4452 DirRef cursor, prev;
4453 TransactedDirEntry *entry;
4455 cursor = parent;
4456 entry = &This->entries[cursor];
4457 while (entry->read)
4459 if (entry->data.leftChild != DIRENTRY_NULL)
4461 prev = cursor;
4462 cursor = entry->data.leftChild;
4463 entry = &This->entries[cursor];
4464 entry->parent = prev;
4466 else if (entry->data.rightChild != DIRENTRY_NULL)
4468 prev = cursor;
4469 cursor = entry->data.rightChild;
4470 entry = &This->entries[cursor];
4471 entry->parent = prev;
4473 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4475 prev = cursor;
4476 cursor = entry->data.dirRootEntry;
4477 entry = &This->entries[cursor];
4478 entry->parent = prev;
4480 else
4481 break;
4484 return cursor;
4487 /* Find the next entry in a depth-first traversal. */
4488 static DirRef TransactedSnapshotImpl_FindNextChild(
4489 TransactedSnapshotImpl* This, DirRef current)
4491 DirRef parent;
4492 TransactedDirEntry *parent_entry;
4494 parent = This->entries[current].parent;
4495 parent_entry = &This->entries[parent];
4497 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4499 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4501 This->entries[parent_entry->data.rightChild].parent = parent;
4502 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4505 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4507 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4508 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4512 return parent;
4515 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4516 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4517 TransactedSnapshotImpl* This, DirRef entry)
4519 return entry != DIRENTRY_NULL &&
4520 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4523 /* Destroy the entries created by CopyTree. */
4524 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4525 TransactedSnapshotImpl* This, DirRef stop)
4527 DirRef cursor;
4528 TransactedDirEntry *entry;
4529 ULARGE_INTEGER zero;
4531 zero.QuadPart = 0;
4533 if (!This->entries[This->base.storageDirEntry].read)
4534 return;
4536 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4538 if (cursor == DIRENTRY_NULL)
4539 return;
4541 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4543 while (cursor != DIRENTRY_NULL && cursor != stop)
4545 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4547 entry = &This->entries[cursor];
4549 if (entry->stream_dirty)
4550 StorageBaseImpl_StreamSetSize(This->transactedParent,
4551 entry->newTransactedParentEntry, zero);
4553 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4554 entry->newTransactedParentEntry);
4556 entry->newTransactedParentEntry = entry->transactedParentEntry;
4559 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4563 /* Make a copy of our edited tree that we can use in the parent. */
4564 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4566 DirRef cursor;
4567 TransactedDirEntry *entry;
4568 HRESULT hr = S_OK;
4570 cursor = This->base.storageDirEntry;
4571 entry = &This->entries[cursor];
4572 entry->parent = DIRENTRY_NULL;
4573 entry->newTransactedParentEntry = entry->transactedParentEntry;
4575 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4576 return S_OK;
4578 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4580 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4581 entry = &This->entries[cursor];
4583 while (cursor != DIRENTRY_NULL)
4585 /* Make a copy of this entry in the transacted parent. */
4586 if (!entry->read ||
4587 (!entry->dirty && !entry->stream_dirty &&
4588 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4589 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4590 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4591 entry->newTransactedParentEntry = entry->transactedParentEntry;
4592 else
4594 DirEntry newData;
4596 memcpy(&newData, &entry->data, sizeof(DirEntry));
4598 newData.size.QuadPart = 0;
4599 newData.startingBlock = BLOCK_END_OF_CHAIN;
4601 if (newData.leftChild != DIRENTRY_NULL)
4602 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4604 if (newData.rightChild != DIRENTRY_NULL)
4605 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4607 if (newData.dirRootEntry != DIRENTRY_NULL)
4608 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4610 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4611 &entry->newTransactedParentEntry);
4612 if (FAILED(hr))
4614 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4615 return hr;
4618 if (entry->stream_dirty)
4620 hr = StorageBaseImpl_CopyStream(
4621 This->transactedParent, entry->newTransactedParentEntry,
4622 This->scratch, entry->stream_entry);
4624 else if (entry->data.size.QuadPart)
4626 hr = StorageBaseImpl_StreamLink(
4627 This->transactedParent, entry->newTransactedParentEntry,
4628 entry->transactedParentEntry);
4631 if (FAILED(hr))
4633 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4634 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4635 return hr;
4639 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4640 entry = &This->entries[cursor];
4643 return hr;
4646 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4647 IStorage* iface,
4648 DWORD grfCommitFlags) /* [in] */
4650 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4651 TransactedDirEntry *root_entry;
4652 DirRef i, dir_root_ref;
4653 DirEntry data;
4654 ULARGE_INTEGER zero;
4655 HRESULT hr;
4657 zero.QuadPart = 0;
4659 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4661 /* Cannot commit a read-only transacted storage */
4662 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4663 return STG_E_ACCESSDENIED;
4665 /* To prevent data loss, we create the new structure in the file before we
4666 * delete the old one, so that in case of errors the old data is intact. We
4667 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4668 * needed in the rare situation where we have just enough free disk space to
4669 * overwrite the existing data. */
4671 root_entry = &This->entries[This->base.storageDirEntry];
4673 if (!root_entry->read)
4674 return S_OK;
4676 hr = TransactedSnapshotImpl_CopyTree(This);
4677 if (FAILED(hr)) return hr;
4679 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4680 dir_root_ref = DIRENTRY_NULL;
4681 else
4682 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4684 hr = StorageBaseImpl_Flush(This->transactedParent);
4686 /* Update the storage to use the new data in one step. */
4687 if (SUCCEEDED(hr))
4688 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4689 root_entry->transactedParentEntry, &data);
4691 if (SUCCEEDED(hr))
4693 data.dirRootEntry = dir_root_ref;
4694 data.clsid = root_entry->data.clsid;
4695 data.ctime = root_entry->data.ctime;
4696 data.mtime = root_entry->data.mtime;
4698 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4699 root_entry->transactedParentEntry, &data);
4702 /* Try to flush after updating the root storage, but if the flush fails, keep
4703 * going, on the theory that it'll either succeed later or the subsequent
4704 * writes will fail. */
4705 StorageBaseImpl_Flush(This->transactedParent);
4707 if (SUCCEEDED(hr))
4709 /* Destroy the old now-orphaned data. */
4710 for (i=0; i<This->entries_size; i++)
4712 TransactedDirEntry *entry = &This->entries[i];
4713 if (entry->inuse)
4715 if (entry->deleted)
4717 StorageBaseImpl_StreamSetSize(This->transactedParent,
4718 entry->transactedParentEntry, zero);
4719 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4720 entry->transactedParentEntry);
4721 memset(entry, 0, sizeof(TransactedDirEntry));
4722 This->firstFreeEntry = min(i, This->firstFreeEntry);
4724 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4726 if (entry->transactedParentEntry != DIRENTRY_NULL)
4727 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4728 entry->transactedParentEntry);
4729 if (entry->stream_dirty)
4731 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4732 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4733 entry->stream_dirty = 0;
4735 entry->dirty = 0;
4736 entry->transactedParentEntry = entry->newTransactedParentEntry;
4741 else
4743 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4746 if (SUCCEEDED(hr))
4747 hr = StorageBaseImpl_Flush(This->transactedParent);
4749 return hr;
4752 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4753 IStorage* iface)
4755 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4756 ULARGE_INTEGER zero;
4757 ULONG i;
4759 TRACE("(%p)\n", iface);
4761 /* Destroy the open objects. */
4762 StorageBaseImpl_DeleteAll(&This->base);
4764 /* Clear out the scratch file. */
4765 zero.QuadPart = 0;
4766 for (i=0; i<This->entries_size; i++)
4768 if (This->entries[i].stream_dirty)
4770 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4771 zero);
4773 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4777 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4779 This->firstFreeEntry = 0;
4780 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4782 return S_OK;
4785 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4787 if (!This->reverted)
4789 TRACE("Storage invalidated (stg=%p)\n", This);
4791 This->reverted = 1;
4793 StorageBaseImpl_DeleteAll(This);
4797 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4799 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4801 IStorage_Revert(&This->base.IStorage_iface);
4802 IStorage_Release(&This->transactedParent->IStorage_iface);
4803 IStorage_Release(&This->scratch->IStorage_iface);
4804 HeapFree(GetProcessHeap(), 0, This->entries);
4805 HeapFree(GetProcessHeap(), 0, This);
4808 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4810 /* We only need to flush when committing. */
4811 return S_OK;
4814 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4816 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4818 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4821 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4822 const DirEntry *newData, DirRef *index)
4824 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4825 DirRef new_ref;
4826 TransactedDirEntry *new_entry;
4828 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4829 if (new_ref == DIRENTRY_NULL)
4830 return E_OUTOFMEMORY;
4832 new_entry = &This->entries[new_ref];
4834 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4835 new_entry->read = 1;
4836 new_entry->dirty = 1;
4837 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4839 *index = new_ref;
4841 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4843 return S_OK;
4846 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4847 DirRef index, const DirEntry *data)
4849 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4850 HRESULT hr;
4852 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4854 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4855 if (FAILED(hr)) return hr;
4857 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4859 if (index != This->base.storageDirEntry)
4861 This->entries[index].dirty = 1;
4863 if (data->size.QuadPart == 0 &&
4864 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4866 /* Since this entry is modified, and we aren't using its stream data, we
4867 * no longer care about the original entry. */
4868 DirRef delete_ref;
4869 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4871 if (delete_ref != DIRENTRY_NULL)
4872 This->entries[delete_ref].deleted = 1;
4874 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4878 return S_OK;
4881 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4882 DirRef index, DirEntry *data)
4884 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4885 HRESULT hr;
4887 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4888 if (FAILED(hr)) return hr;
4890 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4892 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4894 return S_OK;
4897 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4898 DirRef index)
4900 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4902 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4903 This->entries[index].data.size.QuadPart != 0)
4905 /* If we deleted this entry while it has stream data. We must have left the
4906 * data because some other entry is using it, and we need to leave the
4907 * original entry alone. */
4908 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4909 This->firstFreeEntry = min(index, This->firstFreeEntry);
4911 else
4913 This->entries[index].deleted = 1;
4916 return S_OK;
4919 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4920 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4922 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4924 if (This->entries[index].stream_dirty)
4926 return StorageBaseImpl_StreamReadAt(This->scratch,
4927 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4929 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4931 /* This stream doesn't live in the parent, and we haven't allocated storage
4932 * for it yet */
4933 *bytesRead = 0;
4934 return S_OK;
4936 else
4938 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4939 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4943 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4944 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4946 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4947 HRESULT hr;
4949 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4950 if (FAILED(hr)) return hr;
4952 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4953 if (FAILED(hr)) return hr;
4955 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4956 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4958 if (SUCCEEDED(hr) && size != 0)
4959 This->entries[index].data.size.QuadPart = max(
4960 This->entries[index].data.size.QuadPart,
4961 offset.QuadPart + size);
4963 return hr;
4966 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4967 DirRef index, ULARGE_INTEGER newsize)
4969 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4970 HRESULT hr;
4972 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4973 if (FAILED(hr)) return hr;
4975 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4976 return S_OK;
4978 if (newsize.QuadPart == 0)
4980 /* Destroy any parent references or entries in the scratch file. */
4981 if (This->entries[index].stream_dirty)
4983 ULARGE_INTEGER zero;
4984 zero.QuadPart = 0;
4985 StorageBaseImpl_StreamSetSize(This->scratch,
4986 This->entries[index].stream_entry, zero);
4987 StorageBaseImpl_DestroyDirEntry(This->scratch,
4988 This->entries[index].stream_entry);
4989 This->entries[index].stream_dirty = 0;
4991 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4993 DirRef delete_ref;
4994 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4996 if (delete_ref != DIRENTRY_NULL)
4997 This->entries[delete_ref].deleted = 1;
4999 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5002 else
5004 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5005 if (FAILED(hr)) return hr;
5007 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5008 This->entries[index].stream_entry, newsize);
5011 if (SUCCEEDED(hr))
5012 This->entries[index].data.size = newsize;
5014 return hr;
5017 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5018 DirRef dst, DirRef src)
5020 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5021 HRESULT hr;
5022 TransactedDirEntry *dst_entry, *src_entry;
5024 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5025 if (FAILED(hr)) return hr;
5027 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5028 if (FAILED(hr)) return hr;
5030 dst_entry = &This->entries[dst];
5031 src_entry = &This->entries[src];
5033 dst_entry->stream_dirty = src_entry->stream_dirty;
5034 dst_entry->stream_entry = src_entry->stream_entry;
5035 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5036 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5037 dst_entry->data.size = src_entry->data.size;
5039 return S_OK;
5042 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5044 StorageBaseImpl_QueryInterface,
5045 StorageBaseImpl_AddRef,
5046 StorageBaseImpl_Release,
5047 StorageBaseImpl_CreateStream,
5048 StorageBaseImpl_OpenStream,
5049 StorageBaseImpl_CreateStorage,
5050 StorageBaseImpl_OpenStorage,
5051 StorageBaseImpl_CopyTo,
5052 StorageBaseImpl_MoveElementTo,
5053 TransactedSnapshotImpl_Commit,
5054 TransactedSnapshotImpl_Revert,
5055 StorageBaseImpl_EnumElements,
5056 StorageBaseImpl_DestroyElement,
5057 StorageBaseImpl_RenameElement,
5058 StorageBaseImpl_SetElementTimes,
5059 StorageBaseImpl_SetClass,
5060 StorageBaseImpl_SetStateBits,
5061 StorageBaseImpl_Stat
5064 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5066 TransactedSnapshotImpl_Destroy,
5067 TransactedSnapshotImpl_Invalidate,
5068 TransactedSnapshotImpl_Flush,
5069 TransactedSnapshotImpl_GetFilename,
5070 TransactedSnapshotImpl_CreateDirEntry,
5071 TransactedSnapshotImpl_WriteDirEntry,
5072 TransactedSnapshotImpl_ReadDirEntry,
5073 TransactedSnapshotImpl_DestroyDirEntry,
5074 TransactedSnapshotImpl_StreamReadAt,
5075 TransactedSnapshotImpl_StreamWriteAt,
5076 TransactedSnapshotImpl_StreamSetSize,
5077 TransactedSnapshotImpl_StreamLink
5080 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5081 TransactedSnapshotImpl** result)
5083 HRESULT hr;
5085 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5086 if (*result)
5088 IStorage *scratch;
5090 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5092 /* This is OK because the property set storage functions use the IStorage functions. */
5093 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5094 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5096 list_init(&(*result)->base.strmHead);
5098 list_init(&(*result)->base.storageHead);
5100 (*result)->base.ref = 1;
5102 (*result)->base.openFlags = parentStorage->openFlags;
5104 /* Create a new temporary storage to act as the scratch file. */
5105 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5106 0, &scratch);
5107 (*result)->scratch = impl_from_IStorage(scratch);
5109 if (SUCCEEDED(hr))
5111 ULONG num_entries = 20;
5113 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5114 (*result)->entries_size = num_entries;
5115 (*result)->firstFreeEntry = 0;
5117 if ((*result)->entries)
5119 /* parentStorage already has 1 reference, which we take over here. */
5120 (*result)->transactedParent = parentStorage;
5122 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5124 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5126 else
5128 IStorage_Release(scratch);
5130 hr = E_OUTOFMEMORY;
5134 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5136 return hr;
5138 else
5139 return E_OUTOFMEMORY;
5142 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5143 StorageBaseImpl** result)
5145 static int fixme=0;
5147 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5149 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5152 return TransactedSnapshotImpl_Construct(parentStorage,
5153 (TransactedSnapshotImpl**)result);
5156 static HRESULT Storage_Construct(
5157 HANDLE hFile,
5158 LPCOLESTR pwcsName,
5159 ILockBytes* pLkbyt,
5160 DWORD openFlags,
5161 BOOL fileBased,
5162 BOOL create,
5163 ULONG sector_size,
5164 StorageBaseImpl** result)
5166 StorageImpl *newStorage;
5167 StorageBaseImpl *newTransactedStorage;
5168 HRESULT hr;
5170 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5171 if (FAILED(hr)) goto end;
5173 if (openFlags & STGM_TRANSACTED)
5175 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5176 if (FAILED(hr))
5177 IStorage_Release(&newStorage->base.IStorage_iface);
5178 else
5179 *result = newTransactedStorage;
5181 else
5182 *result = &newStorage->base;
5184 end:
5185 return hr;
5188 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5190 StorageInternalImpl* This = (StorageInternalImpl*) base;
5192 if (!This->base.reverted)
5194 TRACE("Storage invalidated (stg=%p)\n", This);
5196 This->base.reverted = 1;
5198 This->parentStorage = NULL;
5200 StorageBaseImpl_DeleteAll(&This->base);
5202 list_remove(&This->ParentListEntry);
5206 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5208 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5210 StorageInternalImpl_Invalidate(&This->base);
5212 HeapFree(GetProcessHeap(), 0, This);
5215 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5217 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5219 return StorageBaseImpl_Flush(This->parentStorage);
5222 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5224 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5226 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5229 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5230 const DirEntry *newData, DirRef *index)
5232 StorageInternalImpl* This = (StorageInternalImpl*) base;
5234 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5235 newData, index);
5238 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5239 DirRef index, const DirEntry *data)
5241 StorageInternalImpl* This = (StorageInternalImpl*) base;
5243 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5244 index, data);
5247 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5248 DirRef index, DirEntry *data)
5250 StorageInternalImpl* This = (StorageInternalImpl*) base;
5252 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5253 index, data);
5256 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5257 DirRef index)
5259 StorageInternalImpl* This = (StorageInternalImpl*) base;
5261 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5262 index);
5265 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5266 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5268 StorageInternalImpl* This = (StorageInternalImpl*) base;
5270 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5271 index, offset, size, buffer, bytesRead);
5274 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5275 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5277 StorageInternalImpl* This = (StorageInternalImpl*) base;
5279 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5280 index, offset, size, buffer, bytesWritten);
5283 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5284 DirRef index, ULARGE_INTEGER newsize)
5286 StorageInternalImpl* This = (StorageInternalImpl*) base;
5288 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5289 index, newsize);
5292 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5293 DirRef dst, DirRef src)
5295 StorageInternalImpl* This = (StorageInternalImpl*) base;
5297 return StorageBaseImpl_StreamLink(This->parentStorage,
5298 dst, src);
5301 /******************************************************************************
5303 ** Storage32InternalImpl_Commit
5306 static HRESULT WINAPI StorageInternalImpl_Commit(
5307 IStorage* iface,
5308 DWORD grfCommitFlags) /* [in] */
5310 StorageBaseImpl* This = impl_from_IStorage(iface);
5311 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5312 return StorageBaseImpl_Flush(This);
5315 /******************************************************************************
5317 ** Storage32InternalImpl_Revert
5320 static HRESULT WINAPI StorageInternalImpl_Revert(
5321 IStorage* iface)
5323 FIXME("(%p): stub\n", iface);
5324 return S_OK;
5327 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5329 IStorage_Release(&This->parentStorage->IStorage_iface);
5330 HeapFree(GetProcessHeap(), 0, This);
5333 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5334 IEnumSTATSTG* iface,
5335 REFIID riid,
5336 void** ppvObject)
5338 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5340 if (ppvObject==0)
5341 return E_INVALIDARG;
5343 *ppvObject = 0;
5345 if (IsEqualGUID(&IID_IUnknown, riid) ||
5346 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5348 *ppvObject = This;
5349 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5350 return S_OK;
5353 return E_NOINTERFACE;
5356 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5357 IEnumSTATSTG* iface)
5359 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5360 return InterlockedIncrement(&This->ref);
5363 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5364 IEnumSTATSTG* iface)
5366 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5368 ULONG newRef;
5370 newRef = InterlockedDecrement(&This->ref);
5372 if (newRef==0)
5374 IEnumSTATSTGImpl_Destroy(This);
5377 return newRef;
5380 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5381 IEnumSTATSTGImpl* This,
5382 DirRef *ref)
5384 DirRef result = DIRENTRY_NULL;
5385 DirRef searchNode;
5386 DirEntry entry;
5387 HRESULT hr;
5388 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5390 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5391 This->parentStorage->storageDirEntry, &entry);
5392 searchNode = entry.dirRootEntry;
5394 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5396 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5398 if (SUCCEEDED(hr))
5400 LONG diff = entryNameCmp( entry.name, This->name);
5402 if (diff <= 0)
5404 searchNode = entry.rightChild;
5406 else
5408 result = searchNode;
5409 memcpy(result_name, entry.name, sizeof(result_name));
5410 searchNode = entry.leftChild;
5415 if (SUCCEEDED(hr))
5417 *ref = result;
5418 if (result != DIRENTRY_NULL)
5419 memcpy(This->name, result_name, sizeof(result_name));
5422 return hr;
5425 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5426 IEnumSTATSTG* iface,
5427 ULONG celt,
5428 STATSTG* rgelt,
5429 ULONG* pceltFetched)
5431 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5433 DirEntry currentEntry;
5434 STATSTG* currentReturnStruct = rgelt;
5435 ULONG objectFetched = 0;
5436 DirRef currentSearchNode;
5437 HRESULT hr=S_OK;
5439 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5440 return E_INVALIDARG;
5442 if (This->parentStorage->reverted)
5443 return STG_E_REVERTED;
5446 * To avoid the special case, get another pointer to a ULONG value if
5447 * the caller didn't supply one.
5449 if (pceltFetched==0)
5450 pceltFetched = &objectFetched;
5453 * Start the iteration, we will iterate until we hit the end of the
5454 * linked list or until we hit the number of items to iterate through
5456 *pceltFetched = 0;
5458 while ( *pceltFetched < celt )
5460 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5462 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5463 break;
5466 * Read the entry from the storage.
5468 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5469 currentSearchNode,
5470 &currentEntry);
5473 * Copy the information to the return buffer.
5475 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5476 currentReturnStruct,
5477 &currentEntry,
5478 STATFLAG_DEFAULT);
5481 * Step to the next item in the iteration
5483 (*pceltFetched)++;
5484 currentReturnStruct++;
5487 if (SUCCEEDED(hr) && *pceltFetched != celt)
5488 hr = S_FALSE;
5490 return hr;
5494 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5495 IEnumSTATSTG* iface,
5496 ULONG celt)
5498 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5500 ULONG objectFetched = 0;
5501 DirRef currentSearchNode;
5502 HRESULT hr=S_OK;
5504 if (This->parentStorage->reverted)
5505 return STG_E_REVERTED;
5507 while ( (objectFetched < celt) )
5509 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5511 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5512 break;
5514 objectFetched++;
5517 if (SUCCEEDED(hr) && objectFetched != celt)
5518 return S_FALSE;
5520 return hr;
5523 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5524 IEnumSTATSTG* iface)
5526 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5528 if (This->parentStorage->reverted)
5529 return STG_E_REVERTED;
5531 This->name[0] = 0;
5533 return S_OK;
5536 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5537 IEnumSTATSTG* iface,
5538 IEnumSTATSTG** ppenum)
5540 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5542 IEnumSTATSTGImpl* newClone;
5544 if (This->parentStorage->reverted)
5545 return STG_E_REVERTED;
5548 * Perform a sanity check on the parameters.
5550 if (ppenum==0)
5551 return E_INVALIDARG;
5553 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5554 This->storageDirEntry);
5558 * The new clone enumeration must point to the same current node as
5559 * the ole one.
5561 memcpy(newClone->name, This->name, sizeof(newClone->name));
5563 *ppenum = &newClone->IEnumSTATSTG_iface;
5566 * Don't forget to nail down a reference to the clone before
5567 * returning it.
5569 IEnumSTATSTGImpl_AddRef(*ppenum);
5571 return S_OK;
5575 * Virtual function table for the IEnumSTATSTGImpl class.
5577 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5579 IEnumSTATSTGImpl_QueryInterface,
5580 IEnumSTATSTGImpl_AddRef,
5581 IEnumSTATSTGImpl_Release,
5582 IEnumSTATSTGImpl_Next,
5583 IEnumSTATSTGImpl_Skip,
5584 IEnumSTATSTGImpl_Reset,
5585 IEnumSTATSTGImpl_Clone
5588 /******************************************************************************
5589 ** IEnumSTATSTGImpl implementation
5592 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5593 StorageBaseImpl* parentStorage,
5594 DirRef storageDirEntry)
5596 IEnumSTATSTGImpl* newEnumeration;
5598 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5600 if (newEnumeration!=0)
5603 * Set-up the virtual function table and reference count.
5605 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5606 newEnumeration->ref = 0;
5609 * We want to nail-down the reference to the storage in case the
5610 * enumeration out-lives the storage in the client application.
5612 newEnumeration->parentStorage = parentStorage;
5613 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
5615 newEnumeration->storageDirEntry = storageDirEntry;
5618 * Make sure the current node of the iterator is the first one.
5620 IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface);
5623 return newEnumeration;
5627 * Virtual function table for the Storage32InternalImpl class.
5629 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5631 StorageBaseImpl_QueryInterface,
5632 StorageBaseImpl_AddRef,
5633 StorageBaseImpl_Release,
5634 StorageBaseImpl_CreateStream,
5635 StorageBaseImpl_OpenStream,
5636 StorageBaseImpl_CreateStorage,
5637 StorageBaseImpl_OpenStorage,
5638 StorageBaseImpl_CopyTo,
5639 StorageBaseImpl_MoveElementTo,
5640 StorageInternalImpl_Commit,
5641 StorageInternalImpl_Revert,
5642 StorageBaseImpl_EnumElements,
5643 StorageBaseImpl_DestroyElement,
5644 StorageBaseImpl_RenameElement,
5645 StorageBaseImpl_SetElementTimes,
5646 StorageBaseImpl_SetClass,
5647 StorageBaseImpl_SetStateBits,
5648 StorageBaseImpl_Stat
5651 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5653 StorageInternalImpl_Destroy,
5654 StorageInternalImpl_Invalidate,
5655 StorageInternalImpl_Flush,
5656 StorageInternalImpl_GetFilename,
5657 StorageInternalImpl_CreateDirEntry,
5658 StorageInternalImpl_WriteDirEntry,
5659 StorageInternalImpl_ReadDirEntry,
5660 StorageInternalImpl_DestroyDirEntry,
5661 StorageInternalImpl_StreamReadAt,
5662 StorageInternalImpl_StreamWriteAt,
5663 StorageInternalImpl_StreamSetSize,
5664 StorageInternalImpl_StreamLink
5667 /******************************************************************************
5668 ** Storage32InternalImpl implementation
5671 static StorageInternalImpl* StorageInternalImpl_Construct(
5672 StorageBaseImpl* parentStorage,
5673 DWORD openFlags,
5674 DirRef storageDirEntry)
5676 StorageInternalImpl* newStorage;
5678 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5680 if (newStorage!=0)
5682 list_init(&newStorage->base.strmHead);
5684 list_init(&newStorage->base.storageHead);
5687 * Initialize the virtual function table.
5689 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
5690 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5691 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5692 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5694 newStorage->base.reverted = 0;
5696 newStorage->base.ref = 1;
5698 newStorage->parentStorage = parentStorage;
5701 * Keep a reference to the directory entry of this storage
5703 newStorage->base.storageDirEntry = storageDirEntry;
5705 newStorage->base.create = 0;
5707 return newStorage;
5710 return 0;
5713 /******************************************************************************
5714 ** StorageUtl implementation
5717 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5719 WORD tmp;
5721 memcpy(&tmp, buffer+offset, sizeof(WORD));
5722 *value = lendian16toh(tmp);
5725 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5727 value = htole16(value);
5728 memcpy(buffer+offset, &value, sizeof(WORD));
5731 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5733 DWORD tmp;
5735 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5736 *value = lendian32toh(tmp);
5739 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5741 value = htole32(value);
5742 memcpy(buffer+offset, &value, sizeof(DWORD));
5745 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5746 ULARGE_INTEGER* value)
5748 #ifdef WORDS_BIGENDIAN
5749 ULARGE_INTEGER tmp;
5751 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5752 value->u.LowPart = htole32(tmp.u.HighPart);
5753 value->u.HighPart = htole32(tmp.u.LowPart);
5754 #else
5755 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5756 #endif
5759 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5760 const ULARGE_INTEGER *value)
5762 #ifdef WORDS_BIGENDIAN
5763 ULARGE_INTEGER tmp;
5765 tmp.u.LowPart = htole32(value->u.HighPart);
5766 tmp.u.HighPart = htole32(value->u.LowPart);
5767 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5768 #else
5769 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5770 #endif
5773 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5775 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5776 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5777 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5779 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5782 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5784 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5785 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5786 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5788 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5791 void StorageUtl_CopyDirEntryToSTATSTG(
5792 StorageBaseImpl* storage,
5793 STATSTG* destination,
5794 const DirEntry* source,
5795 int statFlags)
5798 * The copy of the string occurs only when the flag is not set
5800 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5802 /* Use the filename for the root storage. */
5803 destination->pwcsName = 0;
5804 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5806 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5807 (source->name[0] == 0) )
5809 destination->pwcsName = 0;
5811 else
5813 destination->pwcsName =
5814 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5816 strcpyW(destination->pwcsName, source->name);
5819 switch (source->stgType)
5821 case STGTY_STORAGE:
5822 case STGTY_ROOT:
5823 destination->type = STGTY_STORAGE;
5824 break;
5825 case STGTY_STREAM:
5826 destination->type = STGTY_STREAM;
5827 break;
5828 default:
5829 destination->type = STGTY_STREAM;
5830 break;
5833 destination->cbSize = source->size;
5835 currentReturnStruct->mtime = {0}; TODO
5836 currentReturnStruct->ctime = {0};
5837 currentReturnStruct->atime = {0};
5839 destination->grfMode = 0;
5840 destination->grfLocksSupported = 0;
5841 destination->clsid = source->clsid;
5842 destination->grfStateBits = 0;
5843 destination->reserved = 0;
5846 /******************************************************************************
5847 ** BlockChainStream implementation
5850 /* Read and save the index of all blocks in this stream. */
5851 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5853 ULONG next_sector, next_offset;
5854 HRESULT hr;
5855 struct BlockChainRun *last_run;
5857 if (This->indexCacheLen == 0)
5859 last_run = NULL;
5860 next_offset = 0;
5861 next_sector = BlockChainStream_GetHeadOfChain(This);
5863 else
5865 last_run = &This->indexCache[This->indexCacheLen-1];
5866 next_offset = last_run->lastOffset+1;
5867 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5868 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5869 &next_sector);
5870 if (FAILED(hr)) return hr;
5873 while (next_sector != BLOCK_END_OF_CHAIN)
5875 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5877 /* Add the current block to the cache. */
5878 if (This->indexCacheSize == 0)
5880 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5881 if (!This->indexCache) return E_OUTOFMEMORY;
5882 This->indexCacheSize = 16;
5884 else if (This->indexCacheSize == This->indexCacheLen)
5886 struct BlockChainRun *new_cache;
5887 ULONG new_size;
5889 new_size = This->indexCacheSize * 2;
5890 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5891 if (!new_cache) return E_OUTOFMEMORY;
5892 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5894 HeapFree(GetProcessHeap(), 0, This->indexCache);
5895 This->indexCache = new_cache;
5896 This->indexCacheSize = new_size;
5899 This->indexCacheLen++;
5900 last_run = &This->indexCache[This->indexCacheLen-1];
5901 last_run->firstSector = next_sector;
5902 last_run->firstOffset = next_offset;
5905 last_run->lastOffset = next_offset;
5907 /* Find the next block. */
5908 next_offset++;
5909 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5910 if (FAILED(hr)) return hr;
5913 if (This->indexCacheLen)
5915 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5916 This->numBlocks = last_run->lastOffset+1;
5918 else
5920 This->tailIndex = BLOCK_END_OF_CHAIN;
5921 This->numBlocks = 0;
5924 return S_OK;
5927 /* Locate the nth block in this stream. */
5928 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5930 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5931 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5933 if (offset >= This->numBlocks)
5934 return BLOCK_END_OF_CHAIN;
5936 while (min_run < max_run)
5938 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5939 if (offset < This->indexCache[run_to_check].firstOffset)
5941 max_offset = This->indexCache[run_to_check].firstOffset-1;
5942 max_run = run_to_check-1;
5944 else if (offset > This->indexCache[run_to_check].lastOffset)
5946 min_offset = This->indexCache[run_to_check].lastOffset+1;
5947 min_run = run_to_check+1;
5949 else
5950 /* Block is in this run. */
5951 min_run = max_run = run_to_check;
5954 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5957 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5958 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5960 BlockChainBlock *result=NULL;
5961 int i;
5963 for (i=0; i<2; i++)
5964 if (This->cachedBlocks[i].index == index)
5966 *sector = This->cachedBlocks[i].sector;
5967 *block = &This->cachedBlocks[i];
5968 return S_OK;
5971 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5972 if (*sector == BLOCK_END_OF_CHAIN)
5973 return STG_E_DOCFILECORRUPT;
5975 if (create)
5977 if (This->cachedBlocks[0].index == 0xffffffff)
5978 result = &This->cachedBlocks[0];
5979 else if (This->cachedBlocks[1].index == 0xffffffff)
5980 result = &This->cachedBlocks[1];
5981 else
5983 result = &This->cachedBlocks[This->blockToEvict++];
5984 if (This->blockToEvict == 2)
5985 This->blockToEvict = 0;
5988 if (result->dirty)
5990 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5991 return STG_E_WRITEFAULT;
5992 result->dirty = 0;
5995 result->read = 0;
5996 result->index = index;
5997 result->sector = *sector;
6000 *block = result;
6001 return S_OK;
6004 BlockChainStream* BlockChainStream_Construct(
6005 StorageImpl* parentStorage,
6006 ULONG* headOfStreamPlaceHolder,
6007 DirRef dirEntry)
6009 BlockChainStream* newStream;
6011 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6013 newStream->parentStorage = parentStorage;
6014 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6015 newStream->ownerDirEntry = dirEntry;
6016 newStream->indexCache = NULL;
6017 newStream->indexCacheLen = 0;
6018 newStream->indexCacheSize = 0;
6019 newStream->cachedBlocks[0].index = 0xffffffff;
6020 newStream->cachedBlocks[0].dirty = 0;
6021 newStream->cachedBlocks[1].index = 0xffffffff;
6022 newStream->cachedBlocks[1].dirty = 0;
6023 newStream->blockToEvict = 0;
6025 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6027 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6028 HeapFree(GetProcessHeap(), 0, newStream);
6029 return NULL;
6032 return newStream;
6035 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6037 int i;
6038 if (!This) return S_OK;
6039 for (i=0; i<2; i++)
6041 if (This->cachedBlocks[i].dirty)
6043 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6044 This->cachedBlocks[i].dirty = 0;
6045 else
6046 return STG_E_WRITEFAULT;
6049 return S_OK;
6052 void BlockChainStream_Destroy(BlockChainStream* This)
6054 if (This)
6056 BlockChainStream_Flush(This);
6057 HeapFree(GetProcessHeap(), 0, This->indexCache);
6059 HeapFree(GetProcessHeap(), 0, This);
6062 /******************************************************************************
6063 * BlockChainStream_GetHeadOfChain
6065 * Returns the head of this stream chain.
6066 * Some special chains don't have directory entries, their heads are kept in
6067 * This->headOfStreamPlaceHolder.
6070 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6072 DirEntry chainEntry;
6073 HRESULT hr;
6075 if (This->headOfStreamPlaceHolder != 0)
6076 return *(This->headOfStreamPlaceHolder);
6078 if (This->ownerDirEntry != DIRENTRY_NULL)
6080 hr = StorageImpl_ReadDirEntry(
6081 This->parentStorage,
6082 This->ownerDirEntry,
6083 &chainEntry);
6085 if (SUCCEEDED(hr))
6087 return chainEntry.startingBlock;
6091 return BLOCK_END_OF_CHAIN;
6094 /******************************************************************************
6095 * BlockChainStream_GetCount
6097 * Returns the number of blocks that comprises this chain.
6098 * This is not the size of the stream as the last block may not be full!
6100 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6102 return This->numBlocks;
6105 /******************************************************************************
6106 * BlockChainStream_ReadAt
6108 * Reads a specified number of bytes from this chain at the specified offset.
6109 * bytesRead may be NULL.
6110 * Failure will be returned if the specified number of bytes has not been read.
6112 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6113 ULARGE_INTEGER offset,
6114 ULONG size,
6115 void* buffer,
6116 ULONG* bytesRead)
6118 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6119 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6120 ULONG bytesToReadInBuffer;
6121 ULONG blockIndex;
6122 BYTE* bufferWalker;
6123 ULARGE_INTEGER stream_size;
6124 HRESULT hr;
6125 BlockChainBlock *cachedBlock;
6127 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6130 * Find the first block in the stream that contains part of the buffer.
6132 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6134 *bytesRead = 0;
6136 stream_size = BlockChainStream_GetSize(This);
6137 if (stream_size.QuadPart > offset.QuadPart)
6138 size = min(stream_size.QuadPart - offset.QuadPart, size);
6139 else
6140 return S_OK;
6143 * Start reading the buffer.
6145 bufferWalker = buffer;
6147 while (size > 0)
6149 ULARGE_INTEGER ulOffset;
6150 DWORD bytesReadAt;
6153 * Calculate how many bytes we can copy from this big block.
6155 bytesToReadInBuffer =
6156 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6158 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6160 if (FAILED(hr))
6161 return hr;
6163 if (!cachedBlock)
6165 /* Not in cache, and we're going to read past the end of the block. */
6166 ulOffset.u.HighPart = 0;
6167 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6168 offsetInBlock;
6170 StorageImpl_ReadAt(This->parentStorage,
6171 ulOffset,
6172 bufferWalker,
6173 bytesToReadInBuffer,
6174 &bytesReadAt);
6176 else
6178 if (!cachedBlock->read)
6180 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6181 return STG_E_READFAULT;
6183 cachedBlock->read = 1;
6186 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6187 bytesReadAt = bytesToReadInBuffer;
6190 blockNoInSequence++;
6191 bufferWalker += bytesReadAt;
6192 size -= bytesReadAt;
6193 *bytesRead += bytesReadAt;
6194 offsetInBlock = 0; /* There is no offset on the next block */
6196 if (bytesToReadInBuffer != bytesReadAt)
6197 break;
6200 return S_OK;
6203 /******************************************************************************
6204 * BlockChainStream_WriteAt
6206 * Writes the specified number of bytes to this chain at the specified offset.
6207 * Will fail if not all specified number of bytes have been written.
6209 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6210 ULARGE_INTEGER offset,
6211 ULONG size,
6212 const void* buffer,
6213 ULONG* bytesWritten)
6215 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6216 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6217 ULONG bytesToWrite;
6218 ULONG blockIndex;
6219 const BYTE* bufferWalker;
6220 HRESULT hr;
6221 BlockChainBlock *cachedBlock;
6223 *bytesWritten = 0;
6224 bufferWalker = buffer;
6226 while (size > 0)
6228 ULARGE_INTEGER ulOffset;
6229 DWORD bytesWrittenAt;
6232 * Calculate how many bytes we can copy to this big block.
6234 bytesToWrite =
6235 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6237 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6239 /* BlockChainStream_SetSize should have already been called to ensure we have
6240 * enough blocks in the chain to write into */
6241 if (FAILED(hr))
6243 ERR("not enough blocks in chain to write data\n");
6244 return hr;
6247 if (!cachedBlock)
6249 /* Not in cache, and we're going to write past the end of the block. */
6250 ulOffset.u.HighPart = 0;
6251 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6252 offsetInBlock;
6254 StorageImpl_WriteAt(This->parentStorage,
6255 ulOffset,
6256 bufferWalker,
6257 bytesToWrite,
6258 &bytesWrittenAt);
6260 else
6262 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6264 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6265 return STG_E_READFAULT;
6268 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6269 bytesWrittenAt = bytesToWrite;
6270 cachedBlock->read = 1;
6271 cachedBlock->dirty = 1;
6274 blockNoInSequence++;
6275 bufferWalker += bytesWrittenAt;
6276 size -= bytesWrittenAt;
6277 *bytesWritten += bytesWrittenAt;
6278 offsetInBlock = 0; /* There is no offset on the next block */
6280 if (bytesWrittenAt != bytesToWrite)
6281 break;
6284 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6287 /******************************************************************************
6288 * BlockChainStream_Shrink
6290 * Shrinks this chain in the big block depot.
6292 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6293 ULARGE_INTEGER newSize)
6295 ULONG blockIndex;
6296 ULONG numBlocks;
6297 int i;
6300 * Figure out how many blocks are needed to contain the new size
6302 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6304 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6305 numBlocks++;
6307 if (numBlocks)
6310 * Go to the new end of chain
6312 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6314 /* Mark the new end of chain */
6315 StorageImpl_SetNextBlockInChain(
6316 This->parentStorage,
6317 blockIndex,
6318 BLOCK_END_OF_CHAIN);
6320 This->tailIndex = blockIndex;
6322 else
6324 if (This->headOfStreamPlaceHolder != 0)
6326 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6328 else
6330 DirEntry chainEntry;
6331 assert(This->ownerDirEntry != DIRENTRY_NULL);
6333 StorageImpl_ReadDirEntry(
6334 This->parentStorage,
6335 This->ownerDirEntry,
6336 &chainEntry);
6338 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6340 StorageImpl_WriteDirEntry(
6341 This->parentStorage,
6342 This->ownerDirEntry,
6343 &chainEntry);
6346 This->tailIndex = BLOCK_END_OF_CHAIN;
6349 This->numBlocks = numBlocks;
6352 * Mark the extra blocks as free
6354 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6356 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6357 StorageImpl_FreeBigBlock(This->parentStorage,
6358 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6359 if (last_run->lastOffset == last_run->firstOffset)
6360 This->indexCacheLen--;
6361 else
6362 last_run->lastOffset--;
6366 * Reset the last accessed block cache.
6368 for (i=0; i<2; i++)
6370 if (This->cachedBlocks[i].index >= numBlocks)
6372 This->cachedBlocks[i].index = 0xffffffff;
6373 This->cachedBlocks[i].dirty = 0;
6377 return TRUE;
6380 /******************************************************************************
6381 * BlockChainStream_Enlarge
6383 * Grows this chain in the big block depot.
6385 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6386 ULARGE_INTEGER newSize)
6388 ULONG blockIndex, currentBlock;
6389 ULONG newNumBlocks;
6390 ULONG oldNumBlocks = 0;
6392 blockIndex = BlockChainStream_GetHeadOfChain(This);
6395 * Empty chain. Create the head.
6397 if (blockIndex == BLOCK_END_OF_CHAIN)
6399 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6400 StorageImpl_SetNextBlockInChain(This->parentStorage,
6401 blockIndex,
6402 BLOCK_END_OF_CHAIN);
6404 if (This->headOfStreamPlaceHolder != 0)
6406 *(This->headOfStreamPlaceHolder) = blockIndex;
6408 else
6410 DirEntry chainEntry;
6411 assert(This->ownerDirEntry != DIRENTRY_NULL);
6413 StorageImpl_ReadDirEntry(
6414 This->parentStorage,
6415 This->ownerDirEntry,
6416 &chainEntry);
6418 chainEntry.startingBlock = blockIndex;
6420 StorageImpl_WriteDirEntry(
6421 This->parentStorage,
6422 This->ownerDirEntry,
6423 &chainEntry);
6426 This->tailIndex = blockIndex;
6427 This->numBlocks = 1;
6431 * Figure out how many blocks are needed to contain this stream
6433 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6435 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6436 newNumBlocks++;
6439 * Go to the current end of chain
6441 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6443 currentBlock = blockIndex;
6445 while (blockIndex != BLOCK_END_OF_CHAIN)
6447 This->numBlocks++;
6448 currentBlock = blockIndex;
6450 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6451 &blockIndex)))
6452 return FALSE;
6455 This->tailIndex = currentBlock;
6458 currentBlock = This->tailIndex;
6459 oldNumBlocks = This->numBlocks;
6462 * Add new blocks to the chain
6464 if (oldNumBlocks < newNumBlocks)
6466 while (oldNumBlocks < newNumBlocks)
6468 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6470 StorageImpl_SetNextBlockInChain(
6471 This->parentStorage,
6472 currentBlock,
6473 blockIndex);
6475 StorageImpl_SetNextBlockInChain(
6476 This->parentStorage,
6477 blockIndex,
6478 BLOCK_END_OF_CHAIN);
6480 currentBlock = blockIndex;
6481 oldNumBlocks++;
6484 This->tailIndex = blockIndex;
6485 This->numBlocks = newNumBlocks;
6488 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6489 return FALSE;
6491 return TRUE;
6494 /******************************************************************************
6495 * BlockChainStream_SetSize
6497 * Sets the size of this stream. The big block depot will be updated.
6498 * The file will grow if we grow the chain.
6500 * TODO: Free the actual blocks in the file when we shrink the chain.
6501 * Currently, the blocks are still in the file. So the file size
6502 * doesn't shrink even if we shrink streams.
6504 BOOL BlockChainStream_SetSize(
6505 BlockChainStream* This,
6506 ULARGE_INTEGER newSize)
6508 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6510 if (newSize.u.LowPart == size.u.LowPart)
6511 return TRUE;
6513 if (newSize.u.LowPart < size.u.LowPart)
6515 BlockChainStream_Shrink(This, newSize);
6517 else
6519 BlockChainStream_Enlarge(This, newSize);
6522 return TRUE;
6525 /******************************************************************************
6526 * BlockChainStream_GetSize
6528 * Returns the size of this chain.
6529 * Will return the block count if this chain doesn't have a directory entry.
6531 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6533 DirEntry chainEntry;
6535 if(This->headOfStreamPlaceHolder == NULL)
6538 * This chain has a directory entry so use the size value from there.
6540 StorageImpl_ReadDirEntry(
6541 This->parentStorage,
6542 This->ownerDirEntry,
6543 &chainEntry);
6545 return chainEntry.size;
6547 else
6550 * this chain is a chain that does not have a directory entry, figure out the
6551 * size by making the product number of used blocks times the
6552 * size of them
6554 ULARGE_INTEGER result;
6555 result.u.HighPart = 0;
6557 result.u.LowPart =
6558 BlockChainStream_GetCount(This) *
6559 This->parentStorage->bigBlockSize;
6561 return result;
6565 /******************************************************************************
6566 ** SmallBlockChainStream implementation
6569 SmallBlockChainStream* SmallBlockChainStream_Construct(
6570 StorageImpl* parentStorage,
6571 ULONG* headOfStreamPlaceHolder,
6572 DirRef dirEntry)
6574 SmallBlockChainStream* newStream;
6576 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6578 newStream->parentStorage = parentStorage;
6579 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6580 newStream->ownerDirEntry = dirEntry;
6582 return newStream;
6585 void SmallBlockChainStream_Destroy(
6586 SmallBlockChainStream* This)
6588 HeapFree(GetProcessHeap(), 0, This);
6591 /******************************************************************************
6592 * SmallBlockChainStream_GetHeadOfChain
6594 * Returns the head of this chain of small blocks.
6596 static ULONG SmallBlockChainStream_GetHeadOfChain(
6597 SmallBlockChainStream* This)
6599 DirEntry chainEntry;
6600 HRESULT hr;
6602 if (This->headOfStreamPlaceHolder != NULL)
6603 return *(This->headOfStreamPlaceHolder);
6605 if (This->ownerDirEntry)
6607 hr = StorageImpl_ReadDirEntry(
6608 This->parentStorage,
6609 This->ownerDirEntry,
6610 &chainEntry);
6612 if (SUCCEEDED(hr))
6614 return chainEntry.startingBlock;
6619 return BLOCK_END_OF_CHAIN;
6622 /******************************************************************************
6623 * SmallBlockChainStream_GetNextBlockInChain
6625 * Returns the index of the next small block in this chain.
6627 * Return Values:
6628 * - BLOCK_END_OF_CHAIN: end of this chain
6629 * - BLOCK_UNUSED: small block 'blockIndex' is free
6631 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6632 SmallBlockChainStream* This,
6633 ULONG blockIndex,
6634 ULONG* nextBlockInChain)
6636 ULARGE_INTEGER offsetOfBlockInDepot;
6637 DWORD buffer;
6638 ULONG bytesRead;
6639 HRESULT res;
6641 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6643 offsetOfBlockInDepot.u.HighPart = 0;
6644 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6647 * Read those bytes in the buffer from the small block file.
6649 res = BlockChainStream_ReadAt(
6650 This->parentStorage->smallBlockDepotChain,
6651 offsetOfBlockInDepot,
6652 sizeof(DWORD),
6653 &buffer,
6654 &bytesRead);
6656 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6657 res = STG_E_READFAULT;
6659 if (SUCCEEDED(res))
6661 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6662 return S_OK;
6665 return res;
6668 /******************************************************************************
6669 * SmallBlockChainStream_SetNextBlockInChain
6671 * Writes the index of the next block of the specified block in the small
6672 * block depot.
6673 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6674 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6676 static void SmallBlockChainStream_SetNextBlockInChain(
6677 SmallBlockChainStream* This,
6678 ULONG blockIndex,
6679 ULONG nextBlock)
6681 ULARGE_INTEGER offsetOfBlockInDepot;
6682 DWORD buffer;
6683 ULONG bytesWritten;
6685 offsetOfBlockInDepot.u.HighPart = 0;
6686 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6688 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6691 * Read those bytes in the buffer from the small block file.
6693 BlockChainStream_WriteAt(
6694 This->parentStorage->smallBlockDepotChain,
6695 offsetOfBlockInDepot,
6696 sizeof(DWORD),
6697 &buffer,
6698 &bytesWritten);
6701 /******************************************************************************
6702 * SmallBlockChainStream_FreeBlock
6704 * Flag small block 'blockIndex' as free in the small block depot.
6706 static void SmallBlockChainStream_FreeBlock(
6707 SmallBlockChainStream* This,
6708 ULONG blockIndex)
6710 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6713 /******************************************************************************
6714 * SmallBlockChainStream_GetNextFreeBlock
6716 * Returns the index of a free small block. The small block depot will be
6717 * enlarged if necessary. The small block chain will also be enlarged if
6718 * necessary.
6720 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6721 SmallBlockChainStream* This)
6723 ULARGE_INTEGER offsetOfBlockInDepot;
6724 DWORD buffer;
6725 ULONG bytesRead;
6726 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6727 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6728 HRESULT res = S_OK;
6729 ULONG smallBlocksPerBigBlock;
6730 DirEntry rootEntry;
6731 ULONG blocksRequired;
6732 ULARGE_INTEGER old_size, size_required;
6734 offsetOfBlockInDepot.u.HighPart = 0;
6737 * Scan the small block depot for a free block
6739 while (nextBlockIndex != BLOCK_UNUSED)
6741 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6743 res = BlockChainStream_ReadAt(
6744 This->parentStorage->smallBlockDepotChain,
6745 offsetOfBlockInDepot,
6746 sizeof(DWORD),
6747 &buffer,
6748 &bytesRead);
6751 * If we run out of space for the small block depot, enlarge it
6753 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6755 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6757 if (nextBlockIndex != BLOCK_UNUSED)
6758 blockIndex++;
6760 else
6762 ULONG count =
6763 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6765 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6766 ULARGE_INTEGER newSize, offset;
6767 ULONG bytesWritten;
6769 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6770 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6773 * Initialize all the small blocks to free
6775 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6776 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6777 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6778 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6780 StorageImpl_SaveFileHeader(This->parentStorage);
6784 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6786 smallBlocksPerBigBlock =
6787 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6790 * Verify if we have to allocate big blocks to contain small blocks
6792 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6794 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6796 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6798 if (size_required.QuadPart > old_size.QuadPart)
6800 BlockChainStream_SetSize(
6801 This->parentStorage->smallBlockRootChain,
6802 size_required);
6804 StorageImpl_ReadDirEntry(
6805 This->parentStorage,
6806 This->parentStorage->base.storageDirEntry,
6807 &rootEntry);
6809 rootEntry.size = size_required;
6811 StorageImpl_WriteDirEntry(
6812 This->parentStorage,
6813 This->parentStorage->base.storageDirEntry,
6814 &rootEntry);
6817 return blockIndex;
6820 /******************************************************************************
6821 * SmallBlockChainStream_ReadAt
6823 * Reads a specified number of bytes from this chain at the specified offset.
6824 * bytesRead may be NULL.
6825 * Failure will be returned if the specified number of bytes has not been read.
6827 HRESULT SmallBlockChainStream_ReadAt(
6828 SmallBlockChainStream* This,
6829 ULARGE_INTEGER offset,
6830 ULONG size,
6831 void* buffer,
6832 ULONG* bytesRead)
6834 HRESULT rc = S_OK;
6835 ULARGE_INTEGER offsetInBigBlockFile;
6836 ULONG blockNoInSequence =
6837 offset.u.LowPart / This->parentStorage->smallBlockSize;
6839 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6840 ULONG bytesToReadInBuffer;
6841 ULONG blockIndex;
6842 ULONG bytesReadFromBigBlockFile;
6843 BYTE* bufferWalker;
6844 ULARGE_INTEGER stream_size;
6847 * This should never happen on a small block file.
6849 assert(offset.u.HighPart==0);
6851 *bytesRead = 0;
6853 stream_size = SmallBlockChainStream_GetSize(This);
6854 if (stream_size.QuadPart > offset.QuadPart)
6855 size = min(stream_size.QuadPart - offset.QuadPart, size);
6856 else
6857 return S_OK;
6860 * Find the first block in the stream that contains part of the buffer.
6862 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6864 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6866 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6867 if(FAILED(rc))
6868 return rc;
6869 blockNoInSequence--;
6873 * Start reading the buffer.
6875 bufferWalker = buffer;
6877 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6880 * Calculate how many bytes we can copy from this small block.
6882 bytesToReadInBuffer =
6883 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6886 * Calculate the offset of the small block in the small block file.
6888 offsetInBigBlockFile.u.HighPart = 0;
6889 offsetInBigBlockFile.u.LowPart =
6890 blockIndex * This->parentStorage->smallBlockSize;
6892 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6895 * Read those bytes in the buffer from the small block file.
6896 * The small block has already been identified so it shouldn't fail
6897 * unless the file is corrupt.
6899 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6900 offsetInBigBlockFile,
6901 bytesToReadInBuffer,
6902 bufferWalker,
6903 &bytesReadFromBigBlockFile);
6905 if (FAILED(rc))
6906 return rc;
6908 if (!bytesReadFromBigBlockFile)
6909 return STG_E_DOCFILECORRUPT;
6912 * Step to the next big block.
6914 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6915 if(FAILED(rc))
6916 return STG_E_DOCFILECORRUPT;
6918 bufferWalker += bytesReadFromBigBlockFile;
6919 size -= bytesReadFromBigBlockFile;
6920 *bytesRead += bytesReadFromBigBlockFile;
6921 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6924 return S_OK;
6927 /******************************************************************************
6928 * SmallBlockChainStream_WriteAt
6930 * Writes the specified number of bytes to this chain at the specified offset.
6931 * Will fail if not all specified number of bytes have been written.
6933 HRESULT SmallBlockChainStream_WriteAt(
6934 SmallBlockChainStream* This,
6935 ULARGE_INTEGER offset,
6936 ULONG size,
6937 const void* buffer,
6938 ULONG* bytesWritten)
6940 ULARGE_INTEGER offsetInBigBlockFile;
6941 ULONG blockNoInSequence =
6942 offset.u.LowPart / This->parentStorage->smallBlockSize;
6944 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6945 ULONG bytesToWriteInBuffer;
6946 ULONG blockIndex;
6947 ULONG bytesWrittenToBigBlockFile;
6948 const BYTE* bufferWalker;
6949 HRESULT res;
6952 * This should never happen on a small block file.
6954 assert(offset.u.HighPart==0);
6957 * Find the first block in the stream that contains part of the buffer.
6959 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6961 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6963 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6964 return STG_E_DOCFILECORRUPT;
6965 blockNoInSequence--;
6969 * Start writing the buffer.
6971 *bytesWritten = 0;
6972 bufferWalker = buffer;
6973 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6976 * Calculate how many bytes we can copy to this small block.
6978 bytesToWriteInBuffer =
6979 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6982 * Calculate the offset of the small block in the small block file.
6984 offsetInBigBlockFile.u.HighPart = 0;
6985 offsetInBigBlockFile.u.LowPart =
6986 blockIndex * This->parentStorage->smallBlockSize;
6988 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6991 * Write those bytes in the buffer to the small block file.
6993 res = BlockChainStream_WriteAt(
6994 This->parentStorage->smallBlockRootChain,
6995 offsetInBigBlockFile,
6996 bytesToWriteInBuffer,
6997 bufferWalker,
6998 &bytesWrittenToBigBlockFile);
6999 if (FAILED(res))
7000 return res;
7003 * Step to the next big block.
7005 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7006 &blockIndex)))
7007 return FALSE;
7008 bufferWalker += bytesWrittenToBigBlockFile;
7009 size -= bytesWrittenToBigBlockFile;
7010 *bytesWritten += bytesWrittenToBigBlockFile;
7011 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7014 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7017 /******************************************************************************
7018 * SmallBlockChainStream_Shrink
7020 * Shrinks this chain in the small block depot.
7022 static BOOL SmallBlockChainStream_Shrink(
7023 SmallBlockChainStream* This,
7024 ULARGE_INTEGER newSize)
7026 ULONG blockIndex, extraBlock;
7027 ULONG numBlocks;
7028 ULONG count = 0;
7030 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7032 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7033 numBlocks++;
7035 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7038 * Go to the new end of chain
7040 while (count < numBlocks)
7042 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7043 &blockIndex)))
7044 return FALSE;
7045 count++;
7049 * If the count is 0, we have a special case, the head of the chain was
7050 * just freed.
7052 if (count == 0)
7054 DirEntry chainEntry;
7056 StorageImpl_ReadDirEntry(This->parentStorage,
7057 This->ownerDirEntry,
7058 &chainEntry);
7060 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7062 StorageImpl_WriteDirEntry(This->parentStorage,
7063 This->ownerDirEntry,
7064 &chainEntry);
7067 * We start freeing the chain at the head block.
7069 extraBlock = blockIndex;
7071 else
7073 /* Get the next block before marking the new end */
7074 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7075 &extraBlock)))
7076 return FALSE;
7078 /* Mark the new end of chain */
7079 SmallBlockChainStream_SetNextBlockInChain(
7080 This,
7081 blockIndex,
7082 BLOCK_END_OF_CHAIN);
7086 * Mark the extra blocks as free
7088 while (extraBlock != BLOCK_END_OF_CHAIN)
7090 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7091 &blockIndex)))
7092 return FALSE;
7093 SmallBlockChainStream_FreeBlock(This, extraBlock);
7094 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7095 extraBlock = blockIndex;
7098 return TRUE;
7101 /******************************************************************************
7102 * SmallBlockChainStream_Enlarge
7104 * Grows this chain in the small block depot.
7106 static BOOL SmallBlockChainStream_Enlarge(
7107 SmallBlockChainStream* This,
7108 ULARGE_INTEGER newSize)
7110 ULONG blockIndex, currentBlock;
7111 ULONG newNumBlocks;
7112 ULONG oldNumBlocks = 0;
7114 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7117 * Empty chain. Create the head.
7119 if (blockIndex == BLOCK_END_OF_CHAIN)
7121 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7122 SmallBlockChainStream_SetNextBlockInChain(
7123 This,
7124 blockIndex,
7125 BLOCK_END_OF_CHAIN);
7127 if (This->headOfStreamPlaceHolder != NULL)
7129 *(This->headOfStreamPlaceHolder) = blockIndex;
7131 else
7133 DirEntry chainEntry;
7135 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7136 &chainEntry);
7138 chainEntry.startingBlock = blockIndex;
7140 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7141 &chainEntry);
7145 currentBlock = blockIndex;
7148 * Figure out how many blocks are needed to contain this stream
7150 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7152 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7153 newNumBlocks++;
7156 * Go to the current end of chain
7158 while (blockIndex != BLOCK_END_OF_CHAIN)
7160 oldNumBlocks++;
7161 currentBlock = blockIndex;
7162 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7163 return FALSE;
7167 * Add new blocks to the chain
7169 while (oldNumBlocks < newNumBlocks)
7171 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7172 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7174 SmallBlockChainStream_SetNextBlockInChain(
7175 This,
7176 blockIndex,
7177 BLOCK_END_OF_CHAIN);
7179 currentBlock = blockIndex;
7180 oldNumBlocks++;
7183 return TRUE;
7186 /******************************************************************************
7187 * SmallBlockChainStream_SetSize
7189 * Sets the size of this stream.
7190 * The file will grow if we grow the chain.
7192 * TODO: Free the actual blocks in the file when we shrink the chain.
7193 * Currently, the blocks are still in the file. So the file size
7194 * doesn't shrink even if we shrink streams.
7196 BOOL SmallBlockChainStream_SetSize(
7197 SmallBlockChainStream* This,
7198 ULARGE_INTEGER newSize)
7200 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7202 if (newSize.u.LowPart == size.u.LowPart)
7203 return TRUE;
7205 if (newSize.u.LowPart < size.u.LowPart)
7207 SmallBlockChainStream_Shrink(This, newSize);
7209 else
7211 SmallBlockChainStream_Enlarge(This, newSize);
7214 return TRUE;
7217 /******************************************************************************
7218 * SmallBlockChainStream_GetCount
7220 * Returns the number of small blocks that comprises this chain.
7221 * This is not the size of the stream as the last block may not be full!
7224 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7226 ULONG blockIndex;
7227 ULONG count = 0;
7229 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7231 while(blockIndex != BLOCK_END_OF_CHAIN)
7233 count++;
7235 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7236 blockIndex, &blockIndex)))
7237 return 0;
7240 return count;
7243 /******************************************************************************
7244 * SmallBlockChainStream_GetSize
7246 * Returns the size of this chain.
7248 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7250 DirEntry chainEntry;
7252 if(This->headOfStreamPlaceHolder != NULL)
7254 ULARGE_INTEGER result;
7255 result.u.HighPart = 0;
7257 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7258 This->parentStorage->smallBlockSize;
7260 return result;
7263 StorageImpl_ReadDirEntry(
7264 This->parentStorage,
7265 This->ownerDirEntry,
7266 &chainEntry);
7268 return chainEntry.size;
7271 static HRESULT create_storagefile(
7272 LPCOLESTR pwcsName,
7273 DWORD grfMode,
7274 DWORD grfAttrs,
7275 STGOPTIONS* pStgOptions,
7276 REFIID riid,
7277 void** ppstgOpen)
7279 StorageBaseImpl* newStorage = 0;
7280 HANDLE hFile = INVALID_HANDLE_VALUE;
7281 HRESULT hr = STG_E_INVALIDFLAG;
7282 DWORD shareMode;
7283 DWORD accessMode;
7284 DWORD creationMode;
7285 DWORD fileAttributes;
7286 WCHAR tempFileName[MAX_PATH];
7288 if (ppstgOpen == 0)
7289 return STG_E_INVALIDPOINTER;
7291 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7292 return STG_E_INVALIDPARAMETER;
7294 /* if no share mode given then DENY_NONE is the default */
7295 if (STGM_SHARE_MODE(grfMode) == 0)
7296 grfMode |= STGM_SHARE_DENY_NONE;
7298 if ( FAILED( validateSTGM(grfMode) ))
7299 goto end;
7301 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7302 switch(STGM_ACCESS_MODE(grfMode))
7304 case STGM_WRITE:
7305 case STGM_READWRITE:
7306 break;
7307 default:
7308 goto end;
7311 /* in direct mode, can only use SHARE_EXCLUSIVE */
7312 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7313 goto end;
7315 /* but in transacted mode, any share mode is valid */
7318 * Generate a unique name.
7320 if (pwcsName == 0)
7322 WCHAR tempPath[MAX_PATH];
7323 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7325 memset(tempPath, 0, sizeof(tempPath));
7326 memset(tempFileName, 0, sizeof(tempFileName));
7328 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7329 tempPath[0] = '.';
7331 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7332 pwcsName = tempFileName;
7333 else
7335 hr = STG_E_INSUFFICIENTMEMORY;
7336 goto end;
7339 creationMode = TRUNCATE_EXISTING;
7341 else
7343 creationMode = GetCreationModeFromSTGM(grfMode);
7347 * Interpret the STGM value grfMode
7349 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7350 accessMode = GetAccessModeFromSTGM(grfMode);
7352 if (grfMode & STGM_DELETEONRELEASE)
7353 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7354 else
7355 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7357 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7359 static int fixme;
7360 if (!fixme++)
7361 FIXME("Storage share mode not implemented.\n");
7364 *ppstgOpen = 0;
7366 hFile = CreateFileW(pwcsName,
7367 accessMode,
7368 shareMode,
7369 NULL,
7370 creationMode,
7371 fileAttributes,
7374 if (hFile == INVALID_HANDLE_VALUE)
7376 if(GetLastError() == ERROR_FILE_EXISTS)
7377 hr = STG_E_FILEALREADYEXISTS;
7378 else
7379 hr = E_FAIL;
7380 goto end;
7384 * Allocate and initialize the new IStorage32object.
7386 hr = Storage_Construct(
7387 hFile,
7388 pwcsName,
7389 NULL,
7390 grfMode,
7391 TRUE,
7392 TRUE,
7393 pStgOptions->ulSectorSize,
7394 &newStorage);
7396 if (FAILED(hr))
7398 goto end;
7401 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
7402 IStorage_Release(&newStorage->IStorage_iface);
7404 end:
7405 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7407 return hr;
7410 /******************************************************************************
7411 * StgCreateDocfile [OLE32.@]
7412 * Creates a new compound file storage object
7414 * PARAMS
7415 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7416 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7417 * reserved [ ?] unused?, usually 0
7418 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7420 * RETURNS
7421 * S_OK if the file was successfully created
7422 * some STG_E_ value if error
7423 * NOTES
7424 * if pwcsName is NULL, create file with new unique name
7425 * the function can returns
7426 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7427 * (unrealized now)
7429 HRESULT WINAPI StgCreateDocfile(
7430 LPCOLESTR pwcsName,
7431 DWORD grfMode,
7432 DWORD reserved,
7433 IStorage **ppstgOpen)
7435 STGOPTIONS stgoptions = {1, 0, 512};
7437 TRACE("(%s, %x, %d, %p)\n",
7438 debugstr_w(pwcsName), grfMode,
7439 reserved, ppstgOpen);
7441 if (ppstgOpen == 0)
7442 return STG_E_INVALIDPOINTER;
7443 if (reserved != 0)
7444 return STG_E_INVALIDPARAMETER;
7446 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7449 /******************************************************************************
7450 * StgCreateStorageEx [OLE32.@]
7452 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7454 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7455 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7457 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7459 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7460 return STG_E_INVALIDPARAMETER;
7463 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7465 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7466 return STG_E_INVALIDPARAMETER;
7469 if (stgfmt == STGFMT_FILE)
7471 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7472 return STG_E_INVALIDPARAMETER;
7475 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7477 STGOPTIONS defaultOptions = {1, 0, 512};
7479 if (!pStgOptions) pStgOptions = &defaultOptions;
7480 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7484 ERR("Invalid stgfmt argument\n");
7485 return STG_E_INVALIDPARAMETER;
7488 /******************************************************************************
7489 * StgCreatePropSetStg [OLE32.@]
7491 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7492 IPropertySetStorage **propset)
7494 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
7495 if (reserved)
7496 return STG_E_INVALIDPARAMETER;
7498 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
7501 /******************************************************************************
7502 * StgOpenStorageEx [OLE32.@]
7504 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7506 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7507 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7509 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7511 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7512 return STG_E_INVALIDPARAMETER;
7515 switch (stgfmt)
7517 case STGFMT_FILE:
7518 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7519 return STG_E_INVALIDPARAMETER;
7521 case STGFMT_STORAGE:
7522 break;
7524 case STGFMT_DOCFILE:
7525 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7527 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7528 return STG_E_INVALIDPARAMETER;
7530 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7531 break;
7533 case STGFMT_ANY:
7534 WARN("STGFMT_ANY assuming storage\n");
7535 break;
7537 default:
7538 return STG_E_INVALIDPARAMETER;
7541 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7545 /******************************************************************************
7546 * StgOpenStorage [OLE32.@]
7548 HRESULT WINAPI StgOpenStorage(
7549 const OLECHAR *pwcsName,
7550 IStorage *pstgPriority,
7551 DWORD grfMode,
7552 SNB snbExclude,
7553 DWORD reserved,
7554 IStorage **ppstgOpen)
7556 StorageBaseImpl* newStorage = 0;
7557 HRESULT hr = S_OK;
7558 HANDLE hFile = 0;
7559 DWORD shareMode;
7560 DWORD accessMode;
7562 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7563 debugstr_w(pwcsName), pstgPriority, grfMode,
7564 snbExclude, reserved, ppstgOpen);
7566 if (pwcsName == 0)
7568 hr = STG_E_INVALIDNAME;
7569 goto end;
7572 if (ppstgOpen == 0)
7574 hr = STG_E_INVALIDPOINTER;
7575 goto end;
7578 if (reserved)
7580 hr = STG_E_INVALIDPARAMETER;
7581 goto end;
7584 if (grfMode & STGM_PRIORITY)
7586 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7587 return STG_E_INVALIDFLAG;
7588 if (grfMode & STGM_DELETEONRELEASE)
7589 return STG_E_INVALIDFUNCTION;
7590 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7591 return STG_E_INVALIDFLAG;
7592 grfMode &= ~0xf0; /* remove the existing sharing mode */
7593 grfMode |= STGM_SHARE_DENY_NONE;
7595 /* STGM_PRIORITY stops other IStorage objects on the same file from
7596 * committing until the STGM_PRIORITY IStorage is closed. it also
7597 * stops non-transacted mode StgOpenStorage calls with write access from
7598 * succeeding. obviously, both of these cannot be achieved through just
7599 * file share flags */
7600 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7604 * Validate the sharing mode
7606 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7607 switch(STGM_SHARE_MODE(grfMode))
7609 case STGM_SHARE_EXCLUSIVE:
7610 case STGM_SHARE_DENY_WRITE:
7611 break;
7612 default:
7613 hr = STG_E_INVALIDFLAG;
7614 goto end;
7617 if ( FAILED( validateSTGM(grfMode) ) ||
7618 (grfMode&STGM_CREATE))
7620 hr = STG_E_INVALIDFLAG;
7621 goto end;
7624 /* shared reading requires transacted mode */
7625 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7626 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7627 !(grfMode&STGM_TRANSACTED) )
7629 hr = STG_E_INVALIDFLAG;
7630 goto end;
7634 * Interpret the STGM value grfMode
7636 shareMode = GetShareModeFromSTGM(grfMode);
7637 accessMode = GetAccessModeFromSTGM(grfMode);
7639 *ppstgOpen = 0;
7641 hFile = CreateFileW( pwcsName,
7642 accessMode,
7643 shareMode,
7644 NULL,
7645 OPEN_EXISTING,
7646 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7649 if (hFile==INVALID_HANDLE_VALUE)
7651 DWORD last_error = GetLastError();
7653 hr = E_FAIL;
7655 switch (last_error)
7657 case ERROR_FILE_NOT_FOUND:
7658 hr = STG_E_FILENOTFOUND;
7659 break;
7661 case ERROR_PATH_NOT_FOUND:
7662 hr = STG_E_PATHNOTFOUND;
7663 break;
7665 case ERROR_ACCESS_DENIED:
7666 case ERROR_WRITE_PROTECT:
7667 hr = STG_E_ACCESSDENIED;
7668 break;
7670 case ERROR_SHARING_VIOLATION:
7671 hr = STG_E_SHAREVIOLATION;
7672 break;
7674 default:
7675 hr = E_FAIL;
7678 goto end;
7682 * Refuse to open the file if it's too small to be a structured storage file
7683 * FIXME: verify the file when reading instead of here
7685 if (GetFileSize(hFile, NULL) < 0x100)
7687 CloseHandle(hFile);
7688 hr = STG_E_FILEALREADYEXISTS;
7689 goto end;
7693 * Allocate and initialize the new IStorage32object.
7695 hr = Storage_Construct(
7696 hFile,
7697 pwcsName,
7698 NULL,
7699 grfMode,
7700 TRUE,
7701 FALSE,
7702 512,
7703 &newStorage);
7705 if (FAILED(hr))
7708 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7710 if(hr == STG_E_INVALIDHEADER)
7711 hr = STG_E_FILEALREADYEXISTS;
7712 goto end;
7715 *ppstgOpen = &newStorage->IStorage_iface;
7717 end:
7718 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7719 return hr;
7722 /******************************************************************************
7723 * StgCreateDocfileOnILockBytes [OLE32.@]
7725 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7726 ILockBytes *plkbyt,
7727 DWORD grfMode,
7728 DWORD reserved,
7729 IStorage** ppstgOpen)
7731 StorageBaseImpl* newStorage = 0;
7732 HRESULT hr = S_OK;
7734 if ((ppstgOpen == 0) || (plkbyt == 0))
7735 return STG_E_INVALIDPOINTER;
7738 * Allocate and initialize the new IStorage object.
7740 hr = Storage_Construct(
7743 plkbyt,
7744 grfMode,
7745 FALSE,
7746 TRUE,
7747 512,
7748 &newStorage);
7750 if (FAILED(hr))
7752 return hr;
7755 *ppstgOpen = &newStorage->IStorage_iface;
7757 return hr;
7760 /******************************************************************************
7761 * StgOpenStorageOnILockBytes [OLE32.@]
7763 HRESULT WINAPI StgOpenStorageOnILockBytes(
7764 ILockBytes *plkbyt,
7765 IStorage *pstgPriority,
7766 DWORD grfMode,
7767 SNB snbExclude,
7768 DWORD reserved,
7769 IStorage **ppstgOpen)
7771 StorageBaseImpl* newStorage = 0;
7772 HRESULT hr = S_OK;
7774 if ((plkbyt == 0) || (ppstgOpen == 0))
7775 return STG_E_INVALIDPOINTER;
7777 if ( FAILED( validateSTGM(grfMode) ))
7778 return STG_E_INVALIDFLAG;
7780 *ppstgOpen = 0;
7783 * Allocate and initialize the new IStorage object.
7785 hr = Storage_Construct(
7788 plkbyt,
7789 grfMode,
7790 FALSE,
7791 FALSE,
7792 512,
7793 &newStorage);
7795 if (FAILED(hr))
7797 return hr;
7800 *ppstgOpen = &newStorage->IStorage_iface;
7802 return hr;
7805 /******************************************************************************
7806 * StgSetTimes [ole32.@]
7807 * StgSetTimes [OLE32.@]
7811 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7812 FILETIME const *patime, FILETIME const *pmtime)
7814 IStorage *stg = NULL;
7815 HRESULT r;
7817 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7819 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7820 0, 0, &stg);
7821 if( SUCCEEDED(r) )
7823 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7824 IStorage_Release(stg);
7827 return r;
7830 /******************************************************************************
7831 * StgIsStorageILockBytes [OLE32.@]
7833 * Determines if the ILockBytes contains a storage object.
7835 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7837 BYTE sig[sizeof(STORAGE_magic)];
7838 ULARGE_INTEGER offset;
7839 ULONG read = 0;
7841 offset.u.HighPart = 0;
7842 offset.u.LowPart = 0;
7844 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
7846 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
7847 return S_OK;
7849 return S_FALSE;
7852 /******************************************************************************
7853 * WriteClassStg [OLE32.@]
7855 * This method will store the specified CLSID in the specified storage object
7857 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7859 if(!pStg)
7860 return E_INVALIDARG;
7862 if(!rclsid)
7863 return STG_E_INVALIDPOINTER;
7865 return IStorage_SetClass(pStg, rclsid);
7868 /***********************************************************************
7869 * ReadClassStg (OLE32.@)
7871 * This method reads the CLSID previously written to a storage object with
7872 * the WriteClassStg.
7874 * PARAMS
7875 * pstg [I] IStorage pointer
7876 * pclsid [O] Pointer to where the CLSID is written
7878 * RETURNS
7879 * Success: S_OK.
7880 * Failure: HRESULT code.
7882 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7884 STATSTG pstatstg;
7885 HRESULT hRes;
7887 TRACE("(%p, %p)\n", pstg, pclsid);
7889 if(!pstg || !pclsid)
7890 return E_INVALIDARG;
7893 * read a STATSTG structure (contains the clsid) from the storage
7895 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7897 if(SUCCEEDED(hRes))
7898 *pclsid=pstatstg.clsid;
7900 return hRes;
7903 /***********************************************************************
7904 * OleLoadFromStream (OLE32.@)
7906 * This function loads an object from stream
7908 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7910 CLSID clsid;
7911 HRESULT res;
7912 LPPERSISTSTREAM xstm;
7914 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7916 res=ReadClassStm(pStm,&clsid);
7917 if (FAILED(res))
7918 return res;
7919 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7920 if (FAILED(res))
7921 return res;
7922 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7923 if (FAILED(res)) {
7924 IUnknown_Release((IUnknown*)*ppvObj);
7925 return res;
7927 res=IPersistStream_Load(xstm,pStm);
7928 IPersistStream_Release(xstm);
7929 /* FIXME: all refcounts ok at this point? I think they should be:
7930 * pStm : unchanged
7931 * ppvObj : 1
7932 * xstm : 0 (released)
7934 return res;
7937 /***********************************************************************
7938 * OleSaveToStream (OLE32.@)
7940 * This function saves an object with the IPersistStream interface on it
7941 * to the specified stream.
7943 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7946 CLSID clsid;
7947 HRESULT res;
7949 TRACE("(%p,%p)\n",pPStm,pStm);
7951 res=IPersistStream_GetClassID(pPStm,&clsid);
7953 if (SUCCEEDED(res)){
7955 res=WriteClassStm(pStm,&clsid);
7957 if (SUCCEEDED(res))
7959 res=IPersistStream_Save(pPStm,pStm,TRUE);
7962 TRACE("Finished Save\n");
7963 return res;
7966 /****************************************************************************
7967 * This method validate a STGM parameter that can contain the values below
7969 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7970 * The stgm values contained in 0xffff0000 are bitmasks.
7972 * STGM_DIRECT 0x00000000
7973 * STGM_TRANSACTED 0x00010000
7974 * STGM_SIMPLE 0x08000000
7976 * STGM_READ 0x00000000
7977 * STGM_WRITE 0x00000001
7978 * STGM_READWRITE 0x00000002
7980 * STGM_SHARE_DENY_NONE 0x00000040
7981 * STGM_SHARE_DENY_READ 0x00000030
7982 * STGM_SHARE_DENY_WRITE 0x00000020
7983 * STGM_SHARE_EXCLUSIVE 0x00000010
7985 * STGM_PRIORITY 0x00040000
7986 * STGM_DELETEONRELEASE 0x04000000
7988 * STGM_CREATE 0x00001000
7989 * STGM_CONVERT 0x00020000
7990 * STGM_FAILIFTHERE 0x00000000
7992 * STGM_NOSCRATCH 0x00100000
7993 * STGM_NOSNAPSHOT 0x00200000
7995 static HRESULT validateSTGM(DWORD stgm)
7997 DWORD access = STGM_ACCESS_MODE(stgm);
7998 DWORD share = STGM_SHARE_MODE(stgm);
7999 DWORD create = STGM_CREATE_MODE(stgm);
8001 if (stgm&~STGM_KNOWN_FLAGS)
8003 ERR("unknown flags %08x\n", stgm);
8004 return E_FAIL;
8007 switch (access)
8009 case STGM_READ:
8010 case STGM_WRITE:
8011 case STGM_READWRITE:
8012 break;
8013 default:
8014 return E_FAIL;
8017 switch (share)
8019 case STGM_SHARE_DENY_NONE:
8020 case STGM_SHARE_DENY_READ:
8021 case STGM_SHARE_DENY_WRITE:
8022 case STGM_SHARE_EXCLUSIVE:
8023 break;
8024 default:
8025 return E_FAIL;
8028 switch (create)
8030 case STGM_CREATE:
8031 case STGM_FAILIFTHERE:
8032 break;
8033 default:
8034 return E_FAIL;
8038 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8040 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8041 return E_FAIL;
8044 * STGM_CREATE | STGM_CONVERT
8045 * if both are false, STGM_FAILIFTHERE is set to TRUE
8047 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8048 return E_FAIL;
8051 * STGM_NOSCRATCH requires STGM_TRANSACTED
8053 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8054 return E_FAIL;
8057 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8058 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8060 if ( (stgm & STGM_NOSNAPSHOT) &&
8061 (!(stgm & STGM_TRANSACTED) ||
8062 share == STGM_SHARE_EXCLUSIVE ||
8063 share == STGM_SHARE_DENY_WRITE) )
8064 return E_FAIL;
8066 return S_OK;
8069 /****************************************************************************
8070 * GetShareModeFromSTGM
8072 * This method will return a share mode flag from a STGM value.
8073 * The STGM value is assumed valid.
8075 static DWORD GetShareModeFromSTGM(DWORD stgm)
8077 switch (STGM_SHARE_MODE(stgm))
8079 case STGM_SHARE_DENY_NONE:
8080 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8081 case STGM_SHARE_DENY_READ:
8082 return FILE_SHARE_WRITE;
8083 case STGM_SHARE_DENY_WRITE:
8084 return FILE_SHARE_READ;
8085 case STGM_SHARE_EXCLUSIVE:
8086 return 0;
8088 ERR("Invalid share mode!\n");
8089 assert(0);
8090 return 0;
8093 /****************************************************************************
8094 * GetAccessModeFromSTGM
8096 * This method will return an access mode flag from a STGM value.
8097 * The STGM value is assumed valid.
8099 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8101 switch (STGM_ACCESS_MODE(stgm))
8103 case STGM_READ:
8104 return GENERIC_READ;
8105 case STGM_WRITE:
8106 case STGM_READWRITE:
8107 return GENERIC_READ | GENERIC_WRITE;
8109 ERR("Invalid access mode!\n");
8110 assert(0);
8111 return 0;
8114 /****************************************************************************
8115 * GetCreationModeFromSTGM
8117 * This method will return a creation mode flag from a STGM value.
8118 * The STGM value is assumed valid.
8120 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8122 switch(STGM_CREATE_MODE(stgm))
8124 case STGM_CREATE:
8125 return CREATE_ALWAYS;
8126 case STGM_CONVERT:
8127 FIXME("STGM_CONVERT not implemented!\n");
8128 return CREATE_NEW;
8129 case STGM_FAILIFTHERE:
8130 return CREATE_NEW;
8132 ERR("Invalid create mode!\n");
8133 assert(0);
8134 return 0;
8138 /*************************************************************************
8139 * OLECONVERT_LoadOLE10 [Internal]
8141 * Loads the OLE10 STREAM to memory
8143 * PARAMS
8144 * pOleStream [I] The OLESTREAM
8145 * pData [I] Data Structure for the OLESTREAM Data
8147 * RETURNS
8148 * Success: S_OK
8149 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8150 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8152 * NOTES
8153 * This function is used by OleConvertOLESTREAMToIStorage only.
8155 * Memory allocated for pData must be freed by the caller
8157 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8159 DWORD dwSize;
8160 HRESULT hRes = S_OK;
8161 int nTryCnt=0;
8162 int max_try = 6;
8164 pData->pData = NULL;
8165 pData->pstrOleObjFileName = NULL;
8167 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8169 /* Get the OleID */
8170 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8171 if(dwSize != sizeof(pData->dwOleID))
8173 hRes = CONVERT10_E_OLESTREAM_GET;
8175 else if(pData->dwOleID != OLESTREAM_ID)
8177 hRes = CONVERT10_E_OLESTREAM_FMT;
8179 else
8181 hRes = S_OK;
8182 break;
8186 if(hRes == S_OK)
8188 /* Get the TypeID... more info needed for this field */
8189 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8190 if(dwSize != sizeof(pData->dwTypeID))
8192 hRes = CONVERT10_E_OLESTREAM_GET;
8195 if(hRes == S_OK)
8197 if(pData->dwTypeID != 0)
8199 /* Get the length of the OleTypeName */
8200 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8201 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8203 hRes = CONVERT10_E_OLESTREAM_GET;
8206 if(hRes == S_OK)
8208 if(pData->dwOleTypeNameLength > 0)
8210 /* Get the OleTypeName */
8211 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8212 if(dwSize != pData->dwOleTypeNameLength)
8214 hRes = CONVERT10_E_OLESTREAM_GET;
8218 if(bStrem1)
8220 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8221 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8223 hRes = CONVERT10_E_OLESTREAM_GET;
8225 if(hRes == S_OK)
8227 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8228 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8229 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8230 if(pData->pstrOleObjFileName)
8232 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8233 if(dwSize != pData->dwOleObjFileNameLength)
8235 hRes = CONVERT10_E_OLESTREAM_GET;
8238 else
8239 hRes = CONVERT10_E_OLESTREAM_GET;
8242 else
8244 /* Get the Width of the Metafile */
8245 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8246 if(dwSize != sizeof(pData->dwMetaFileWidth))
8248 hRes = CONVERT10_E_OLESTREAM_GET;
8250 if(hRes == S_OK)
8252 /* Get the Height of the Metafile */
8253 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8254 if(dwSize != sizeof(pData->dwMetaFileHeight))
8256 hRes = CONVERT10_E_OLESTREAM_GET;
8260 if(hRes == S_OK)
8262 /* Get the Length of the Data */
8263 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8264 if(dwSize != sizeof(pData->dwDataLength))
8266 hRes = CONVERT10_E_OLESTREAM_GET;
8270 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8272 if(!bStrem1) /* if it is a second OLE stream data */
8274 pData->dwDataLength -= 8;
8275 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8276 if(dwSize != sizeof(pData->strUnknown))
8278 hRes = CONVERT10_E_OLESTREAM_GET;
8282 if(hRes == S_OK)
8284 if(pData->dwDataLength > 0)
8286 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8288 /* Get Data (ex. IStorage, Metafile, or BMP) */
8289 if(pData->pData)
8291 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8292 if(dwSize != pData->dwDataLength)
8294 hRes = CONVERT10_E_OLESTREAM_GET;
8297 else
8299 hRes = CONVERT10_E_OLESTREAM_GET;
8305 return hRes;
8308 /*************************************************************************
8309 * OLECONVERT_SaveOLE10 [Internal]
8311 * Saves the OLE10 STREAM From memory
8313 * PARAMS
8314 * pData [I] Data Structure for the OLESTREAM Data
8315 * pOleStream [I] The OLESTREAM to save
8317 * RETURNS
8318 * Success: S_OK
8319 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8321 * NOTES
8322 * This function is used by OleConvertIStorageToOLESTREAM only.
8325 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8327 DWORD dwSize;
8328 HRESULT hRes = S_OK;
8331 /* Set the OleID */
8332 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8333 if(dwSize != sizeof(pData->dwOleID))
8335 hRes = CONVERT10_E_OLESTREAM_PUT;
8338 if(hRes == S_OK)
8340 /* Set the TypeID */
8341 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8342 if(dwSize != sizeof(pData->dwTypeID))
8344 hRes = CONVERT10_E_OLESTREAM_PUT;
8348 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8350 /* Set the Length of the OleTypeName */
8351 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8352 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8354 hRes = CONVERT10_E_OLESTREAM_PUT;
8357 if(hRes == S_OK)
8359 if(pData->dwOleTypeNameLength > 0)
8361 /* Set the OleTypeName */
8362 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8363 if(dwSize != pData->dwOleTypeNameLength)
8365 hRes = CONVERT10_E_OLESTREAM_PUT;
8370 if(hRes == S_OK)
8372 /* Set the width of the Metafile */
8373 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8374 if(dwSize != sizeof(pData->dwMetaFileWidth))
8376 hRes = CONVERT10_E_OLESTREAM_PUT;
8380 if(hRes == S_OK)
8382 /* Set the height of the Metafile */
8383 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8384 if(dwSize != sizeof(pData->dwMetaFileHeight))
8386 hRes = CONVERT10_E_OLESTREAM_PUT;
8390 if(hRes == S_OK)
8392 /* Set the length of the Data */
8393 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8394 if(dwSize != sizeof(pData->dwDataLength))
8396 hRes = CONVERT10_E_OLESTREAM_PUT;
8400 if(hRes == S_OK)
8402 if(pData->dwDataLength > 0)
8404 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8405 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8406 if(dwSize != pData->dwDataLength)
8408 hRes = CONVERT10_E_OLESTREAM_PUT;
8413 return hRes;
8416 /*************************************************************************
8417 * OLECONVERT_GetOLE20FromOLE10[Internal]
8419 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8420 * opens it, and copies the content to the dest IStorage for
8421 * OleConvertOLESTREAMToIStorage
8424 * PARAMS
8425 * pDestStorage [I] The IStorage to copy the data to
8426 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8427 * nBufferLength [I] The size of the buffer
8429 * RETURNS
8430 * Nothing
8432 * NOTES
8436 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8438 HRESULT hRes;
8439 HANDLE hFile;
8440 IStorage *pTempStorage;
8441 DWORD dwNumOfBytesWritten;
8442 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8443 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8445 /* Create a temp File */
8446 GetTempPathW(MAX_PATH, wstrTempDir);
8447 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8448 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8450 if(hFile != INVALID_HANDLE_VALUE)
8452 /* Write IStorage Data to File */
8453 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8454 CloseHandle(hFile);
8456 /* Open and copy temp storage to the Dest Storage */
8457 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8458 if(hRes == S_OK)
8460 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8461 IStorage_Release(pTempStorage);
8463 DeleteFileW(wstrTempFile);
8468 /*************************************************************************
8469 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8471 * Saves the OLE10 STREAM From memory
8473 * PARAMS
8474 * pStorage [I] The Src IStorage to copy
8475 * pData [I] The Dest Memory to write to.
8477 * RETURNS
8478 * The size in bytes allocated for pData
8480 * NOTES
8481 * Memory allocated for pData must be freed by the caller
8483 * Used by OleConvertIStorageToOLESTREAM only.
8486 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8488 HANDLE hFile;
8489 HRESULT hRes;
8490 DWORD nDataLength = 0;
8491 IStorage *pTempStorage;
8492 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8493 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8495 *pData = NULL;
8497 /* Create temp Storage */
8498 GetTempPathW(MAX_PATH, wstrTempDir);
8499 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8500 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8502 if(hRes == S_OK)
8504 /* Copy Src Storage to the Temp Storage */
8505 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8506 IStorage_Release(pTempStorage);
8508 /* Open Temp Storage as a file and copy to memory */
8509 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8510 if(hFile != INVALID_HANDLE_VALUE)
8512 nDataLength = GetFileSize(hFile, NULL);
8513 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8514 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8515 CloseHandle(hFile);
8517 DeleteFileW(wstrTempFile);
8519 return nDataLength;
8522 /*************************************************************************
8523 * STORAGE_CreateOleStream [Internal]
8525 * Creates the "\001OLE" stream in the IStorage if necessary.
8527 * PARAMS
8528 * storage [I] Dest storage to create the stream in
8529 * flags [I] flags to be set for newly created stream
8531 * RETURNS
8532 * HRESULT return value
8534 * NOTES
8536 * This stream is still unknown, MS Word seems to have extra data
8537 * but since the data is stored in the OLESTREAM there should be
8538 * no need to recreate the stream. If the stream is manually
8539 * deleted it will create it with this default data.
8542 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
8544 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
8545 static const DWORD version_magic = 0x02000001;
8546 IStream *stream;
8547 HRESULT hr;
8549 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
8550 if (hr == S_OK)
8552 struct empty_1ole_stream {
8553 DWORD version_magic;
8554 DWORD flags;
8555 DWORD update_options;
8556 DWORD reserved;
8557 DWORD mon_stream_size;
8559 struct empty_1ole_stream stream_data;
8561 stream_data.version_magic = version_magic;
8562 stream_data.flags = flags;
8563 stream_data.update_options = 0;
8564 stream_data.reserved = 0;
8565 stream_data.mon_stream_size = 0;
8567 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
8568 IStream_Release(stream);
8571 return hr;
8574 /* write a string to a stream, preceded by its length */
8575 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8577 HRESULT r;
8578 LPSTR str;
8579 DWORD len = 0;
8581 if( string )
8582 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8583 r = IStream_Write( stm, &len, sizeof(len), NULL);
8584 if( FAILED( r ) )
8585 return r;
8586 if(len == 0)
8587 return r;
8588 str = CoTaskMemAlloc( len );
8589 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8590 r = IStream_Write( stm, str, len, NULL);
8591 CoTaskMemFree( str );
8592 return r;
8595 /* read a string preceded by its length from a stream */
8596 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8598 HRESULT r;
8599 DWORD len, count = 0;
8600 LPSTR str;
8601 LPWSTR wstr;
8603 r = IStream_Read( stm, &len, sizeof(len), &count );
8604 if( FAILED( r ) )
8605 return r;
8606 if( count != sizeof(len) )
8607 return E_OUTOFMEMORY;
8609 TRACE("%d bytes\n",len);
8611 str = CoTaskMemAlloc( len );
8612 if( !str )
8613 return E_OUTOFMEMORY;
8614 count = 0;
8615 r = IStream_Read( stm, str, len, &count );
8616 if( FAILED( r ) )
8617 return r;
8618 if( count != len )
8620 CoTaskMemFree( str );
8621 return E_OUTOFMEMORY;
8624 TRACE("Read string %s\n",debugstr_an(str,len));
8626 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8627 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8628 if( wstr )
8629 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8630 CoTaskMemFree( str );
8632 *string = wstr;
8634 return r;
8638 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8639 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8641 IStream *pstm;
8642 HRESULT r = S_OK;
8643 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8645 static const BYTE unknown1[12] =
8646 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8647 0xFF, 0xFF, 0xFF, 0xFF};
8648 static const BYTE unknown2[16] =
8649 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8650 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8652 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8653 debugstr_w(lpszUserType), debugstr_w(szClipName),
8654 debugstr_w(szProgIDName));
8656 /* Create a CompObj stream */
8657 r = IStorage_CreateStream(pstg, szwStreamName,
8658 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8659 if( FAILED (r) )
8660 return r;
8662 /* Write CompObj Structure to stream */
8663 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8665 if( SUCCEEDED( r ) )
8666 r = WriteClassStm( pstm, clsid );
8668 if( SUCCEEDED( r ) )
8669 r = STREAM_WriteString( pstm, lpszUserType );
8670 if( SUCCEEDED( r ) )
8671 r = STREAM_WriteString( pstm, szClipName );
8672 if( SUCCEEDED( r ) )
8673 r = STREAM_WriteString( pstm, szProgIDName );
8674 if( SUCCEEDED( r ) )
8675 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8677 IStream_Release( pstm );
8679 return r;
8682 /***********************************************************************
8683 * WriteFmtUserTypeStg (OLE32.@)
8685 HRESULT WINAPI WriteFmtUserTypeStg(
8686 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8688 HRESULT r;
8689 WCHAR szwClipName[0x40];
8690 CLSID clsid = CLSID_NULL;
8691 LPWSTR wstrProgID = NULL;
8692 DWORD n;
8694 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8696 /* get the clipboard format name */
8697 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8698 szwClipName[n]=0;
8700 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8702 /* FIXME: There's room to save a CLSID and its ProgID, but
8703 the CLSID is not looked up in the registry and in all the
8704 tests I wrote it was CLSID_NULL. Where does it come from?
8707 /* get the real program ID. This may fail, but that's fine */
8708 ProgIDFromCLSID(&clsid, &wstrProgID);
8710 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8712 r = STORAGE_WriteCompObj( pstg, &clsid,
8713 lpszUserType, szwClipName, wstrProgID );
8715 CoTaskMemFree(wstrProgID);
8717 return r;
8721 /******************************************************************************
8722 * ReadFmtUserTypeStg [OLE32.@]
8724 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8726 HRESULT r;
8727 IStream *stm = 0;
8728 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8729 unsigned char unknown1[12];
8730 unsigned char unknown2[16];
8731 DWORD count;
8732 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8733 CLSID clsid;
8735 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8737 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8738 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8739 if( FAILED ( r ) )
8741 WARN("Failed to open stream r = %08x\n", r);
8742 return r;
8745 /* read the various parts of the structure */
8746 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8747 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8748 goto end;
8749 r = ReadClassStm( stm, &clsid );
8750 if( FAILED( r ) )
8751 goto end;
8753 r = STREAM_ReadString( stm, &szCLSIDName );
8754 if( FAILED( r ) )
8755 goto end;
8757 r = STREAM_ReadString( stm, &szOleTypeName );
8758 if( FAILED( r ) )
8759 goto end;
8761 r = STREAM_ReadString( stm, &szProgIDName );
8762 if( FAILED( r ) )
8763 goto end;
8765 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8766 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8767 goto end;
8769 /* ok, success... now we just need to store what we found */
8770 if( pcf )
8771 *pcf = RegisterClipboardFormatW( szOleTypeName );
8772 CoTaskMemFree( szOleTypeName );
8774 if( lplpszUserType )
8775 *lplpszUserType = szCLSIDName;
8776 CoTaskMemFree( szProgIDName );
8778 end:
8779 IStream_Release( stm );
8781 return r;
8785 /*************************************************************************
8786 * OLECONVERT_CreateCompObjStream [Internal]
8788 * Creates a "\001CompObj" is the destination IStorage if necessary.
8790 * PARAMS
8791 * pStorage [I] The dest IStorage to create the CompObj Stream
8792 * if necessary.
8793 * strOleTypeName [I] The ProgID
8795 * RETURNS
8796 * Success: S_OK
8797 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8799 * NOTES
8800 * This function is used by OleConvertOLESTREAMToIStorage only.
8802 * The stream data is stored in the OLESTREAM and there should be
8803 * no need to recreate the stream. If the stream is manually
8804 * deleted it will attempt to create it by querying the registry.
8808 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8810 IStream *pStream;
8811 HRESULT hStorageRes, hRes = S_OK;
8812 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8813 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8814 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8816 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8817 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8819 /* Initialize the CompObj structure */
8820 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8821 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8822 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8825 /* Create a CompObj stream if it doesn't exist */
8826 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8827 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8828 if(hStorageRes == S_OK)
8830 /* copy the OleTypeName to the compobj struct */
8831 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8832 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8834 /* copy the OleTypeName to the compobj struct */
8835 /* Note: in the test made, these were Identical */
8836 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8837 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8839 /* Get the CLSID */
8840 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8841 bufferW, OLESTREAM_MAX_STR_LEN );
8842 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8844 if(hRes == S_OK)
8846 HKEY hKey;
8847 LONG hErr;
8848 /* Get the CLSID Default Name from the Registry */
8849 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8850 if(hErr == ERROR_SUCCESS)
8852 char strTemp[OLESTREAM_MAX_STR_LEN];
8853 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8854 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8855 if(hErr == ERROR_SUCCESS)
8857 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8859 RegCloseKey(hKey);
8863 /* Write CompObj Structure to stream */
8864 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8866 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8868 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8869 if(IStorageCompObj.dwCLSIDNameLength > 0)
8871 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8873 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8874 if(IStorageCompObj.dwOleTypeNameLength > 0)
8876 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8878 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8879 if(IStorageCompObj.dwProgIDNameLength > 0)
8881 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8883 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8884 IStream_Release(pStream);
8886 return hRes;
8890 /*************************************************************************
8891 * OLECONVERT_CreateOlePresStream[Internal]
8893 * Creates the "\002OlePres000" Stream with the Metafile data
8895 * PARAMS
8896 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8897 * dwExtentX [I] Width of the Metafile
8898 * dwExtentY [I] Height of the Metafile
8899 * pData [I] Metafile data
8900 * dwDataLength [I] Size of the Metafile data
8902 * RETURNS
8903 * Success: S_OK
8904 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8906 * NOTES
8907 * This function is used by OleConvertOLESTREAMToIStorage only.
8910 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8912 HRESULT hRes;
8913 IStream *pStream;
8914 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8915 BYTE pOlePresStreamHeader [] =
8917 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8918 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8919 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8920 0x00, 0x00, 0x00, 0x00
8923 BYTE pOlePresStreamHeaderEmpty [] =
8925 0x00, 0x00, 0x00, 0x00,
8926 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8927 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8928 0x00, 0x00, 0x00, 0x00
8931 /* Create the OlePres000 Stream */
8932 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8933 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8935 if(hRes == S_OK)
8937 DWORD nHeaderSize;
8938 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8940 memset(&OlePres, 0, sizeof(OlePres));
8941 /* Do we have any metafile data to save */
8942 if(dwDataLength > 0)
8944 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8945 nHeaderSize = sizeof(pOlePresStreamHeader);
8947 else
8949 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8950 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8952 /* Set width and height of the metafile */
8953 OlePres.dwExtentX = dwExtentX;
8954 OlePres.dwExtentY = -dwExtentY;
8956 /* Set Data and Length */
8957 if(dwDataLength > sizeof(METAFILEPICT16))
8959 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8960 OlePres.pData = &(pData[8]);
8962 /* Save OlePres000 Data to Stream */
8963 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8964 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8965 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8966 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8967 if(OlePres.dwSize > 0)
8969 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8971 IStream_Release(pStream);
8975 /*************************************************************************
8976 * OLECONVERT_CreateOle10NativeStream [Internal]
8978 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8980 * PARAMS
8981 * pStorage [I] Dest storage to create the stream in
8982 * pData [I] Ole10 Native Data (ex. bmp)
8983 * dwDataLength [I] Size of the Ole10 Native Data
8985 * RETURNS
8986 * Nothing
8988 * NOTES
8989 * This function is used by OleConvertOLESTREAMToIStorage only.
8991 * Might need to verify the data and return appropriate error message
8994 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8996 HRESULT hRes;
8997 IStream *pStream;
8998 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9000 /* Create the Ole10Native Stream */
9001 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9002 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9004 if(hRes == S_OK)
9006 /* Write info to stream */
9007 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9008 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9009 IStream_Release(pStream);
9014 /*************************************************************************
9015 * OLECONVERT_GetOLE10ProgID [Internal]
9017 * Finds the ProgID (or OleTypeID) from the IStorage
9019 * PARAMS
9020 * pStorage [I] The Src IStorage to get the ProgID
9021 * strProgID [I] the ProgID string to get
9022 * dwSize [I] the size of the string
9024 * RETURNS
9025 * Success: S_OK
9026 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9028 * NOTES
9029 * This function is used by OleConvertIStorageToOLESTREAM only.
9033 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9035 HRESULT hRes;
9036 IStream *pStream;
9037 LARGE_INTEGER iSeekPos;
9038 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9039 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9041 /* Open the CompObj Stream */
9042 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9043 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9044 if(hRes == S_OK)
9047 /*Get the OleType from the CompObj Stream */
9048 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9049 iSeekPos.u.HighPart = 0;
9051 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9052 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9053 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9054 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9055 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9056 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9057 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9059 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9060 if(*dwSize > 0)
9062 IStream_Read(pStream, strProgID, *dwSize, NULL);
9064 IStream_Release(pStream);
9066 else
9068 STATSTG stat;
9069 LPOLESTR wstrProgID;
9071 /* Get the OleType from the registry */
9072 REFCLSID clsid = &(stat.clsid);
9073 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9074 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9075 if(hRes == S_OK)
9077 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9081 return hRes;
9084 /*************************************************************************
9085 * OLECONVERT_GetOle10PresData [Internal]
9087 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9089 * PARAMS
9090 * pStorage [I] Src IStroage
9091 * pOleStream [I] Dest OleStream Mem Struct
9093 * RETURNS
9094 * Nothing
9096 * NOTES
9097 * This function is used by OleConvertIStorageToOLESTREAM only.
9099 * Memory allocated for pData must be freed by the caller
9103 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9106 HRESULT hRes;
9107 IStream *pStream;
9108 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9110 /* Initialize Default data for OLESTREAM */
9111 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9112 pOleStreamData[0].dwTypeID = 2;
9113 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9114 pOleStreamData[1].dwTypeID = 0;
9115 pOleStreamData[0].dwMetaFileWidth = 0;
9116 pOleStreamData[0].dwMetaFileHeight = 0;
9117 pOleStreamData[0].pData = NULL;
9118 pOleStreamData[1].pData = NULL;
9120 /* Open Ole10Native Stream */
9121 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9122 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9123 if(hRes == S_OK)
9126 /* Read Size and Data */
9127 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9128 if(pOleStreamData->dwDataLength > 0)
9130 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9131 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9133 IStream_Release(pStream);
9139 /*************************************************************************
9140 * OLECONVERT_GetOle20PresData[Internal]
9142 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9144 * PARAMS
9145 * pStorage [I] Src IStroage
9146 * pOleStreamData [I] Dest OleStream Mem Struct
9148 * RETURNS
9149 * Nothing
9151 * NOTES
9152 * This function is used by OleConvertIStorageToOLESTREAM only.
9154 * Memory allocated for pData must be freed by the caller
9156 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9158 HRESULT hRes;
9159 IStream *pStream;
9160 OLECONVERT_ISTORAGE_OLEPRES olePress;
9161 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9163 /* Initialize Default data for OLESTREAM */
9164 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9165 pOleStreamData[0].dwTypeID = 2;
9166 pOleStreamData[0].dwMetaFileWidth = 0;
9167 pOleStreamData[0].dwMetaFileHeight = 0;
9168 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9169 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9170 pOleStreamData[1].dwTypeID = 0;
9171 pOleStreamData[1].dwOleTypeNameLength = 0;
9172 pOleStreamData[1].strOleTypeName[0] = 0;
9173 pOleStreamData[1].dwMetaFileWidth = 0;
9174 pOleStreamData[1].dwMetaFileHeight = 0;
9175 pOleStreamData[1].pData = NULL;
9176 pOleStreamData[1].dwDataLength = 0;
9179 /* Open OlePress000 stream */
9180 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9181 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9182 if(hRes == S_OK)
9184 LARGE_INTEGER iSeekPos;
9185 METAFILEPICT16 MetaFilePict;
9186 static const char strMetafilePictName[] = "METAFILEPICT";
9188 /* Set the TypeID for a Metafile */
9189 pOleStreamData[1].dwTypeID = 5;
9191 /* Set the OleTypeName to Metafile */
9192 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9193 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9195 iSeekPos.u.HighPart = 0;
9196 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9198 /* Get Presentation Data */
9199 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9200 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9201 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9202 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9204 /*Set width and Height */
9205 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9206 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9207 if(olePress.dwSize > 0)
9209 /* Set Length */
9210 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9212 /* Set MetaFilePict struct */
9213 MetaFilePict.mm = 8;
9214 MetaFilePict.xExt = olePress.dwExtentX;
9215 MetaFilePict.yExt = olePress.dwExtentY;
9216 MetaFilePict.hMF = 0;
9218 /* Get Metafile Data */
9219 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9220 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9221 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9223 IStream_Release(pStream);
9227 /*************************************************************************
9228 * OleConvertOLESTREAMToIStorage [OLE32.@]
9230 * Read info on MSDN
9232 * TODO
9233 * DVTARGETDEVICE parameter is not handled
9234 * Still unsure of some mem fields for OLE 10 Stream
9235 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9236 * and "\001OLE" streams
9239 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9240 LPOLESTREAM pOleStream,
9241 LPSTORAGE pstg,
9242 const DVTARGETDEVICE* ptd)
9244 int i;
9245 HRESULT hRes=S_OK;
9246 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9248 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9250 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9252 if(ptd != NULL)
9254 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9257 if(pstg == NULL || pOleStream == NULL)
9259 hRes = E_INVALIDARG;
9262 if(hRes == S_OK)
9264 /* Load the OLESTREAM to Memory */
9265 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9268 if(hRes == S_OK)
9270 /* Load the OLESTREAM to Memory (part 2)*/
9271 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9274 if(hRes == S_OK)
9277 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9279 /* Do we have the IStorage Data in the OLESTREAM */
9280 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9282 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9283 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9285 else
9287 /* It must be an original OLE 1.0 source */
9288 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9291 else
9293 /* It must be an original OLE 1.0 source */
9294 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9297 /* Create CompObj Stream if necessary */
9298 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9299 if(hRes == S_OK)
9301 /*Create the Ole Stream if necessary */
9302 STORAGE_CreateOleStream(pstg, 0);
9307 /* Free allocated memory */
9308 for(i=0; i < 2; i++)
9310 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9311 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9312 pOleStreamData[i].pstrOleObjFileName = NULL;
9314 return hRes;
9317 /*************************************************************************
9318 * OleConvertIStorageToOLESTREAM [OLE32.@]
9320 * Read info on MSDN
9322 * Read info on MSDN
9324 * TODO
9325 * Still unsure of some mem fields for OLE 10 Stream
9326 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9327 * and "\001OLE" streams.
9330 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9331 LPSTORAGE pstg,
9332 LPOLESTREAM pOleStream)
9334 int i;
9335 HRESULT hRes = S_OK;
9336 IStream *pStream;
9337 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9338 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9340 TRACE("%p %p\n", pstg, pOleStream);
9342 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9344 if(pstg == NULL || pOleStream == NULL)
9346 hRes = E_INVALIDARG;
9348 if(hRes == S_OK)
9350 /* Get the ProgID */
9351 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9352 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9354 if(hRes == S_OK)
9356 /* Was it originally Ole10 */
9357 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9358 if(hRes == S_OK)
9360 IStream_Release(pStream);
9361 /* Get Presentation Data for Ole10Native */
9362 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9364 else
9366 /* Get Presentation Data (OLE20) */
9367 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9370 /* Save OLESTREAM */
9371 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9372 if(hRes == S_OK)
9374 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9379 /* Free allocated memory */
9380 for(i=0; i < 2; i++)
9382 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9385 return hRes;
9388 enum stream_1ole_flags {
9389 OleStream_LinkedObject = 0x00000001,
9390 OleStream_Convert = 0x00000004
9393 /***********************************************************************
9394 * GetConvertStg (OLE32.@)
9396 HRESULT WINAPI GetConvertStg(IStorage *stg)
9398 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9399 static const DWORD version_magic = 0x02000001;
9400 DWORD header[2];
9401 IStream *stream;
9402 HRESULT hr;
9404 TRACE("%p\n", stg);
9406 if (!stg) return E_INVALIDARG;
9408 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
9409 if (FAILED(hr)) return hr;
9411 hr = IStream_Read(stream, header, sizeof(header), NULL);
9412 IStream_Release(stream);
9413 if (FAILED(hr)) return hr;
9415 if (header[0] != version_magic)
9417 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
9418 return E_FAIL;
9421 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
9424 /***********************************************************************
9425 * SetConvertStg (OLE32.@)
9427 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
9429 DWORD flags = convert ? OleStream_Convert : 0;
9430 HRESULT hr;
9432 TRACE("(%p, %d)\n", storage, convert);
9434 hr = STORAGE_CreateOleStream(storage, flags);
9435 if (hr == STG_E_FILEALREADYEXISTS)
9437 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9438 IStream *stream;
9439 DWORD header[2];
9441 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
9442 if (FAILED(hr)) return hr;
9444 hr = IStream_Read(stream, header, sizeof(header), NULL);
9445 if (FAILED(hr))
9447 IStream_Release(stream);
9448 return hr;
9451 /* update flag if differs */
9452 if ((header[1] ^ flags) & OleStream_Convert)
9454 LARGE_INTEGER pos;
9456 if (header[1] & OleStream_Convert)
9457 flags = header[1] & ~OleStream_Convert;
9458 else
9459 flags = header[1] | OleStream_Convert;
9461 pos.QuadPart = sizeof(DWORD);
9462 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
9463 if (FAILED(hr))
9465 IStream_Release(stream);
9466 return hr;
9469 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
9471 IStream_Release(stream);
9474 return hr;
9477 /******************************************************************************
9478 * StgIsStorageFile [OLE32.@]
9479 * Verify if the file contains a storage object
9481 * PARAMS
9482 * fn [ I] Filename
9484 * RETURNS
9485 * S_OK if file has magic bytes as a storage object
9486 * S_FALSE if file is not storage
9488 HRESULT WINAPI
9489 StgIsStorageFile(LPCOLESTR fn)
9491 HANDLE hf;
9492 BYTE magic[8];
9493 DWORD bytes_read;
9495 TRACE("%s\n", debugstr_w(fn));
9496 hf = CreateFileW(fn, GENERIC_READ,
9497 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9498 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9500 if (hf == INVALID_HANDLE_VALUE)
9501 return STG_E_FILENOTFOUND;
9503 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9505 WARN(" unable to read file\n");
9506 CloseHandle(hf);
9507 return S_FALSE;
9510 CloseHandle(hf);
9512 if (bytes_read != 8) {
9513 TRACE(" too short\n");
9514 return S_FALSE;
9517 if (!memcmp(magic,STORAGE_magic,8)) {
9518 TRACE(" -> YES\n");
9519 return S_OK;
9522 TRACE(" -> Invalid header.\n");
9523 return S_FALSE;
9526 /***********************************************************************
9527 * WriteClassStm (OLE32.@)
9529 * Writes a CLSID to a stream.
9531 * PARAMS
9532 * pStm [I] Stream to write to.
9533 * rclsid [I] CLSID to write.
9535 * RETURNS
9536 * Success: S_OK.
9537 * Failure: HRESULT code.
9539 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9541 TRACE("(%p,%p)\n",pStm,rclsid);
9543 if (!pStm || !rclsid)
9544 return E_INVALIDARG;
9546 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9549 /***********************************************************************
9550 * ReadClassStm (OLE32.@)
9552 * Reads a CLSID from a stream.
9554 * PARAMS
9555 * pStm [I] Stream to read from.
9556 * rclsid [O] CLSID to read.
9558 * RETURNS
9559 * Success: S_OK.
9560 * Failure: HRESULT code.
9562 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9564 ULONG nbByte;
9565 HRESULT res;
9567 TRACE("(%p,%p)\n",pStm,pclsid);
9569 if (!pStm || !pclsid)
9570 return E_INVALIDARG;
9572 /* clear the output args */
9573 *pclsid = CLSID_NULL;
9575 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9577 if (FAILED(res))
9578 return res;
9580 if (nbByte != sizeof(CLSID))
9581 return STG_E_READFAULT;
9582 else
9583 return S_OK;