ole32: No need to test for null implementation pointer in methods.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blobc04675d3ca1a45786663de0a52222930f5567bf0
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 = StorageBaseImpl_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 TransactedSnapshotImpl_Revert(&This->base.IStorage_iface);
4803 IStorage_Release(&This->transactedParent->IStorage_iface);
4805 IStorage_Release(&This->scratch->IStorage_iface);
4807 HeapFree(GetProcessHeap(), 0, This->entries);
4809 HeapFree(GetProcessHeap(), 0, This);
4812 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4814 /* We only need to flush when committing. */
4815 return S_OK;
4818 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4820 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4822 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4825 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4826 const DirEntry *newData, DirRef *index)
4828 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4829 DirRef new_ref;
4830 TransactedDirEntry *new_entry;
4832 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4833 if (new_ref == DIRENTRY_NULL)
4834 return E_OUTOFMEMORY;
4836 new_entry = &This->entries[new_ref];
4838 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4839 new_entry->read = 1;
4840 new_entry->dirty = 1;
4841 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4843 *index = new_ref;
4845 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4847 return S_OK;
4850 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4851 DirRef index, const DirEntry *data)
4853 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4854 HRESULT hr;
4856 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4858 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4859 if (FAILED(hr)) return hr;
4861 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4863 if (index != This->base.storageDirEntry)
4865 This->entries[index].dirty = 1;
4867 if (data->size.QuadPart == 0 &&
4868 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4870 /* Since this entry is modified, and we aren't using its stream data, we
4871 * no longer care about the original entry. */
4872 DirRef delete_ref;
4873 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4875 if (delete_ref != DIRENTRY_NULL)
4876 This->entries[delete_ref].deleted = 1;
4878 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4882 return S_OK;
4885 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4886 DirRef index, DirEntry *data)
4888 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4889 HRESULT hr;
4891 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4892 if (FAILED(hr)) return hr;
4894 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4896 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4898 return S_OK;
4901 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4902 DirRef index)
4904 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4906 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4907 This->entries[index].data.size.QuadPart != 0)
4909 /* If we deleted this entry while it has stream data. We must have left the
4910 * data because some other entry is using it, and we need to leave the
4911 * original entry alone. */
4912 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4913 This->firstFreeEntry = min(index, This->firstFreeEntry);
4915 else
4917 This->entries[index].deleted = 1;
4920 return S_OK;
4923 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4924 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4926 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4928 if (This->entries[index].stream_dirty)
4930 return StorageBaseImpl_StreamReadAt(This->scratch,
4931 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4933 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4935 /* This stream doesn't live in the parent, and we haven't allocated storage
4936 * for it yet */
4937 *bytesRead = 0;
4938 return S_OK;
4940 else
4942 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4943 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4947 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4948 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4950 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4951 HRESULT hr;
4953 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4954 if (FAILED(hr)) return hr;
4956 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4957 if (FAILED(hr)) return hr;
4959 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4960 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4962 if (SUCCEEDED(hr) && size != 0)
4963 This->entries[index].data.size.QuadPart = max(
4964 This->entries[index].data.size.QuadPart,
4965 offset.QuadPart + size);
4967 return hr;
4970 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4971 DirRef index, ULARGE_INTEGER newsize)
4973 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4974 HRESULT hr;
4976 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4977 if (FAILED(hr)) return hr;
4979 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4980 return S_OK;
4982 if (newsize.QuadPart == 0)
4984 /* Destroy any parent references or entries in the scratch file. */
4985 if (This->entries[index].stream_dirty)
4987 ULARGE_INTEGER zero;
4988 zero.QuadPart = 0;
4989 StorageBaseImpl_StreamSetSize(This->scratch,
4990 This->entries[index].stream_entry, zero);
4991 StorageBaseImpl_DestroyDirEntry(This->scratch,
4992 This->entries[index].stream_entry);
4993 This->entries[index].stream_dirty = 0;
4995 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4997 DirRef delete_ref;
4998 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
5000 if (delete_ref != DIRENTRY_NULL)
5001 This->entries[delete_ref].deleted = 1;
5003 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5006 else
5008 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5009 if (FAILED(hr)) return hr;
5011 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5012 This->entries[index].stream_entry, newsize);
5015 if (SUCCEEDED(hr))
5016 This->entries[index].data.size = newsize;
5018 return hr;
5021 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5022 DirRef dst, DirRef src)
5024 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5025 HRESULT hr;
5026 TransactedDirEntry *dst_entry, *src_entry;
5028 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5029 if (FAILED(hr)) return hr;
5031 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5032 if (FAILED(hr)) return hr;
5034 dst_entry = &This->entries[dst];
5035 src_entry = &This->entries[src];
5037 dst_entry->stream_dirty = src_entry->stream_dirty;
5038 dst_entry->stream_entry = src_entry->stream_entry;
5039 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5040 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5041 dst_entry->data.size = src_entry->data.size;
5043 return S_OK;
5046 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5048 StorageBaseImpl_QueryInterface,
5049 StorageBaseImpl_AddRef,
5050 StorageBaseImpl_Release,
5051 StorageBaseImpl_CreateStream,
5052 StorageBaseImpl_OpenStream,
5053 StorageBaseImpl_CreateStorage,
5054 StorageBaseImpl_OpenStorage,
5055 StorageBaseImpl_CopyTo,
5056 StorageBaseImpl_MoveElementTo,
5057 TransactedSnapshotImpl_Commit,
5058 TransactedSnapshotImpl_Revert,
5059 StorageBaseImpl_EnumElements,
5060 StorageBaseImpl_DestroyElement,
5061 StorageBaseImpl_RenameElement,
5062 StorageBaseImpl_SetElementTimes,
5063 StorageBaseImpl_SetClass,
5064 StorageBaseImpl_SetStateBits,
5065 StorageBaseImpl_Stat
5068 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5070 TransactedSnapshotImpl_Destroy,
5071 TransactedSnapshotImpl_Invalidate,
5072 TransactedSnapshotImpl_Flush,
5073 TransactedSnapshotImpl_GetFilename,
5074 TransactedSnapshotImpl_CreateDirEntry,
5075 TransactedSnapshotImpl_WriteDirEntry,
5076 TransactedSnapshotImpl_ReadDirEntry,
5077 TransactedSnapshotImpl_DestroyDirEntry,
5078 TransactedSnapshotImpl_StreamReadAt,
5079 TransactedSnapshotImpl_StreamWriteAt,
5080 TransactedSnapshotImpl_StreamSetSize,
5081 TransactedSnapshotImpl_StreamLink
5084 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5085 TransactedSnapshotImpl** result)
5087 HRESULT hr;
5089 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5090 if (*result)
5092 IStorage *scratch;
5094 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5096 /* This is OK because the property set storage functions use the IStorage functions. */
5097 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5098 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5100 list_init(&(*result)->base.strmHead);
5102 list_init(&(*result)->base.storageHead);
5104 (*result)->base.ref = 1;
5106 (*result)->base.openFlags = parentStorage->openFlags;
5108 /* Create a new temporary storage to act as the scratch file. */
5109 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5110 0, &scratch);
5111 (*result)->scratch = impl_from_IStorage(scratch);
5113 if (SUCCEEDED(hr))
5115 ULONG num_entries = 20;
5117 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5118 (*result)->entries_size = num_entries;
5119 (*result)->firstFreeEntry = 0;
5121 if ((*result)->entries)
5123 /* parentStorage already has 1 reference, which we take over here. */
5124 (*result)->transactedParent = parentStorage;
5126 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5128 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5130 else
5132 IStorage_Release(scratch);
5134 hr = E_OUTOFMEMORY;
5138 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5140 return hr;
5142 else
5143 return E_OUTOFMEMORY;
5146 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5147 StorageBaseImpl** result)
5149 static int fixme=0;
5151 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5153 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5156 return TransactedSnapshotImpl_Construct(parentStorage,
5157 (TransactedSnapshotImpl**)result);
5160 static HRESULT Storage_Construct(
5161 HANDLE hFile,
5162 LPCOLESTR pwcsName,
5163 ILockBytes* pLkbyt,
5164 DWORD openFlags,
5165 BOOL fileBased,
5166 BOOL create,
5167 ULONG sector_size,
5168 StorageBaseImpl** result)
5170 StorageImpl *newStorage;
5171 StorageBaseImpl *newTransactedStorage;
5172 HRESULT hr;
5174 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5175 if (FAILED(hr)) goto end;
5177 if (openFlags & STGM_TRANSACTED)
5179 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5180 if (FAILED(hr))
5181 IStorage_Release(&newStorage->base.IStorage_iface);
5182 else
5183 *result = newTransactedStorage;
5185 else
5186 *result = &newStorage->base;
5188 end:
5189 return hr;
5192 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5194 StorageInternalImpl* This = (StorageInternalImpl*) base;
5196 if (!This->base.reverted)
5198 TRACE("Storage invalidated (stg=%p)\n", This);
5200 This->base.reverted = 1;
5202 This->parentStorage = NULL;
5204 StorageBaseImpl_DeleteAll(&This->base);
5206 list_remove(&This->ParentListEntry);
5210 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5212 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5214 StorageInternalImpl_Invalidate(&This->base);
5216 HeapFree(GetProcessHeap(), 0, This);
5219 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5221 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5223 return StorageBaseImpl_Flush(This->parentStorage);
5226 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5228 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5230 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5233 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5234 const DirEntry *newData, DirRef *index)
5236 StorageInternalImpl* This = (StorageInternalImpl*) base;
5238 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5239 newData, index);
5242 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5243 DirRef index, const DirEntry *data)
5245 StorageInternalImpl* This = (StorageInternalImpl*) base;
5247 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5248 index, data);
5251 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5252 DirRef index, DirEntry *data)
5254 StorageInternalImpl* This = (StorageInternalImpl*) base;
5256 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5257 index, data);
5260 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5261 DirRef index)
5263 StorageInternalImpl* This = (StorageInternalImpl*) base;
5265 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5266 index);
5269 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5270 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5272 StorageInternalImpl* This = (StorageInternalImpl*) base;
5274 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5275 index, offset, size, buffer, bytesRead);
5278 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5279 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5281 StorageInternalImpl* This = (StorageInternalImpl*) base;
5283 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5284 index, offset, size, buffer, bytesWritten);
5287 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5288 DirRef index, ULARGE_INTEGER newsize)
5290 StorageInternalImpl* This = (StorageInternalImpl*) base;
5292 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5293 index, newsize);
5296 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5297 DirRef dst, DirRef src)
5299 StorageInternalImpl* This = (StorageInternalImpl*) base;
5301 return StorageBaseImpl_StreamLink(This->parentStorage,
5302 dst, src);
5305 /******************************************************************************
5307 ** Storage32InternalImpl_Commit
5310 static HRESULT WINAPI StorageInternalImpl_Commit(
5311 IStorage* iface,
5312 DWORD grfCommitFlags) /* [in] */
5314 StorageBaseImpl* This = impl_from_IStorage(iface);
5315 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5316 return StorageBaseImpl_Flush(This);
5319 /******************************************************************************
5321 ** Storage32InternalImpl_Revert
5324 static HRESULT WINAPI StorageInternalImpl_Revert(
5325 IStorage* iface)
5327 FIXME("(%p): stub\n", iface);
5328 return S_OK;
5331 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5333 IStorage_Release(&This->parentStorage->IStorage_iface);
5334 HeapFree(GetProcessHeap(), 0, This);
5337 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5338 IEnumSTATSTG* iface,
5339 REFIID riid,
5340 void** ppvObject)
5342 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5344 if (ppvObject==0)
5345 return E_INVALIDARG;
5347 *ppvObject = 0;
5349 if (IsEqualGUID(&IID_IUnknown, riid) ||
5350 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5352 *ppvObject = This;
5353 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5354 return S_OK;
5357 return E_NOINTERFACE;
5360 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5361 IEnumSTATSTG* iface)
5363 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5364 return InterlockedIncrement(&This->ref);
5367 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5368 IEnumSTATSTG* iface)
5370 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5372 ULONG newRef;
5374 newRef = InterlockedDecrement(&This->ref);
5376 if (newRef==0)
5378 IEnumSTATSTGImpl_Destroy(This);
5381 return newRef;
5384 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5385 IEnumSTATSTGImpl* This,
5386 DirRef *ref)
5388 DirRef result = DIRENTRY_NULL;
5389 DirRef searchNode;
5390 DirEntry entry;
5391 HRESULT hr;
5392 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5394 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5395 This->parentStorage->storageDirEntry, &entry);
5396 searchNode = entry.dirRootEntry;
5398 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5400 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5402 if (SUCCEEDED(hr))
5404 LONG diff = entryNameCmp( entry.name, This->name);
5406 if (diff <= 0)
5408 searchNode = entry.rightChild;
5410 else
5412 result = searchNode;
5413 memcpy(result_name, entry.name, sizeof(result_name));
5414 searchNode = entry.leftChild;
5419 if (SUCCEEDED(hr))
5421 *ref = result;
5422 if (result != DIRENTRY_NULL)
5423 memcpy(This->name, result_name, sizeof(result_name));
5426 return hr;
5429 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5430 IEnumSTATSTG* iface,
5431 ULONG celt,
5432 STATSTG* rgelt,
5433 ULONG* pceltFetched)
5435 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5437 DirEntry currentEntry;
5438 STATSTG* currentReturnStruct = rgelt;
5439 ULONG objectFetched = 0;
5440 DirRef currentSearchNode;
5441 HRESULT hr=S_OK;
5443 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5444 return E_INVALIDARG;
5446 if (This->parentStorage->reverted)
5447 return STG_E_REVERTED;
5450 * To avoid the special case, get another pointer to a ULONG value if
5451 * the caller didn't supply one.
5453 if (pceltFetched==0)
5454 pceltFetched = &objectFetched;
5457 * Start the iteration, we will iterate until we hit the end of the
5458 * linked list or until we hit the number of items to iterate through
5460 *pceltFetched = 0;
5462 while ( *pceltFetched < celt )
5464 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5466 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5467 break;
5470 * Read the entry from the storage.
5472 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5473 currentSearchNode,
5474 &currentEntry);
5477 * Copy the information to the return buffer.
5479 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5480 currentReturnStruct,
5481 &currentEntry,
5482 STATFLAG_DEFAULT);
5485 * Step to the next item in the iteration
5487 (*pceltFetched)++;
5488 currentReturnStruct++;
5491 if (SUCCEEDED(hr) && *pceltFetched != celt)
5492 hr = S_FALSE;
5494 return hr;
5498 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5499 IEnumSTATSTG* iface,
5500 ULONG celt)
5502 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5504 ULONG objectFetched = 0;
5505 DirRef currentSearchNode;
5506 HRESULT hr=S_OK;
5508 if (This->parentStorage->reverted)
5509 return STG_E_REVERTED;
5511 while ( (objectFetched < celt) )
5513 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5515 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5516 break;
5518 objectFetched++;
5521 if (SUCCEEDED(hr) && objectFetched != celt)
5522 return S_FALSE;
5524 return hr;
5527 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5528 IEnumSTATSTG* iface)
5530 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5532 if (This->parentStorage->reverted)
5533 return STG_E_REVERTED;
5535 This->name[0] = 0;
5537 return S_OK;
5540 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5541 IEnumSTATSTG* iface,
5542 IEnumSTATSTG** ppenum)
5544 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5546 IEnumSTATSTGImpl* newClone;
5548 if (This->parentStorage->reverted)
5549 return STG_E_REVERTED;
5552 * Perform a sanity check on the parameters.
5554 if (ppenum==0)
5555 return E_INVALIDARG;
5557 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5558 This->storageDirEntry);
5562 * The new clone enumeration must point to the same current node as
5563 * the ole one.
5565 memcpy(newClone->name, This->name, sizeof(newClone->name));
5567 *ppenum = &newClone->IEnumSTATSTG_iface;
5570 * Don't forget to nail down a reference to the clone before
5571 * returning it.
5573 IEnumSTATSTGImpl_AddRef(*ppenum);
5575 return S_OK;
5579 * Virtual function table for the IEnumSTATSTGImpl class.
5581 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5583 IEnumSTATSTGImpl_QueryInterface,
5584 IEnumSTATSTGImpl_AddRef,
5585 IEnumSTATSTGImpl_Release,
5586 IEnumSTATSTGImpl_Next,
5587 IEnumSTATSTGImpl_Skip,
5588 IEnumSTATSTGImpl_Reset,
5589 IEnumSTATSTGImpl_Clone
5592 /******************************************************************************
5593 ** IEnumSTATSTGImpl implementation
5596 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5597 StorageBaseImpl* parentStorage,
5598 DirRef storageDirEntry)
5600 IEnumSTATSTGImpl* newEnumeration;
5602 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5604 if (newEnumeration!=0)
5607 * Set-up the virtual function table and reference count.
5609 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5610 newEnumeration->ref = 0;
5613 * We want to nail-down the reference to the storage in case the
5614 * enumeration out-lives the storage in the client application.
5616 newEnumeration->parentStorage = parentStorage;
5617 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
5619 newEnumeration->storageDirEntry = storageDirEntry;
5622 * Make sure the current node of the iterator is the first one.
5624 IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface);
5627 return newEnumeration;
5631 * Virtual function table for the Storage32InternalImpl class.
5633 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5635 StorageBaseImpl_QueryInterface,
5636 StorageBaseImpl_AddRef,
5637 StorageBaseImpl_Release,
5638 StorageBaseImpl_CreateStream,
5639 StorageBaseImpl_OpenStream,
5640 StorageBaseImpl_CreateStorage,
5641 StorageBaseImpl_OpenStorage,
5642 StorageBaseImpl_CopyTo,
5643 StorageBaseImpl_MoveElementTo,
5644 StorageInternalImpl_Commit,
5645 StorageInternalImpl_Revert,
5646 StorageBaseImpl_EnumElements,
5647 StorageBaseImpl_DestroyElement,
5648 StorageBaseImpl_RenameElement,
5649 StorageBaseImpl_SetElementTimes,
5650 StorageBaseImpl_SetClass,
5651 StorageBaseImpl_SetStateBits,
5652 StorageBaseImpl_Stat
5655 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5657 StorageInternalImpl_Destroy,
5658 StorageInternalImpl_Invalidate,
5659 StorageInternalImpl_Flush,
5660 StorageInternalImpl_GetFilename,
5661 StorageInternalImpl_CreateDirEntry,
5662 StorageInternalImpl_WriteDirEntry,
5663 StorageInternalImpl_ReadDirEntry,
5664 StorageInternalImpl_DestroyDirEntry,
5665 StorageInternalImpl_StreamReadAt,
5666 StorageInternalImpl_StreamWriteAt,
5667 StorageInternalImpl_StreamSetSize,
5668 StorageInternalImpl_StreamLink
5671 /******************************************************************************
5672 ** Storage32InternalImpl implementation
5675 static StorageInternalImpl* StorageInternalImpl_Construct(
5676 StorageBaseImpl* parentStorage,
5677 DWORD openFlags,
5678 DirRef storageDirEntry)
5680 StorageInternalImpl* newStorage;
5682 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5684 if (newStorage!=0)
5686 list_init(&newStorage->base.strmHead);
5688 list_init(&newStorage->base.storageHead);
5691 * Initialize the virtual function table.
5693 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
5694 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5695 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5696 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5698 newStorage->base.reverted = 0;
5700 newStorage->base.ref = 1;
5702 newStorage->parentStorage = parentStorage;
5705 * Keep a reference to the directory entry of this storage
5707 newStorage->base.storageDirEntry = storageDirEntry;
5709 newStorage->base.create = 0;
5711 return newStorage;
5714 return 0;
5717 /******************************************************************************
5718 ** StorageUtl implementation
5721 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5723 WORD tmp;
5725 memcpy(&tmp, buffer+offset, sizeof(WORD));
5726 *value = lendian16toh(tmp);
5729 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5731 value = htole16(value);
5732 memcpy(buffer+offset, &value, sizeof(WORD));
5735 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5737 DWORD tmp;
5739 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5740 *value = lendian32toh(tmp);
5743 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5745 value = htole32(value);
5746 memcpy(buffer+offset, &value, sizeof(DWORD));
5749 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5750 ULARGE_INTEGER* value)
5752 #ifdef WORDS_BIGENDIAN
5753 ULARGE_INTEGER tmp;
5755 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5756 value->u.LowPart = htole32(tmp.u.HighPart);
5757 value->u.HighPart = htole32(tmp.u.LowPart);
5758 #else
5759 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5760 #endif
5763 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5764 const ULARGE_INTEGER *value)
5766 #ifdef WORDS_BIGENDIAN
5767 ULARGE_INTEGER tmp;
5769 tmp.u.LowPart = htole32(value->u.HighPart);
5770 tmp.u.HighPart = htole32(value->u.LowPart);
5771 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5772 #else
5773 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5774 #endif
5777 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5779 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5780 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5781 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5783 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5786 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5788 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5789 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5790 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5792 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5795 void StorageUtl_CopyDirEntryToSTATSTG(
5796 StorageBaseImpl* storage,
5797 STATSTG* destination,
5798 const DirEntry* source,
5799 int statFlags)
5802 * The copy of the string occurs only when the flag is not set
5804 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5806 /* Use the filename for the root storage. */
5807 destination->pwcsName = 0;
5808 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5810 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5811 (source->name[0] == 0) )
5813 destination->pwcsName = 0;
5815 else
5817 destination->pwcsName =
5818 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5820 strcpyW(destination->pwcsName, source->name);
5823 switch (source->stgType)
5825 case STGTY_STORAGE:
5826 case STGTY_ROOT:
5827 destination->type = STGTY_STORAGE;
5828 break;
5829 case STGTY_STREAM:
5830 destination->type = STGTY_STREAM;
5831 break;
5832 default:
5833 destination->type = STGTY_STREAM;
5834 break;
5837 destination->cbSize = source->size;
5839 currentReturnStruct->mtime = {0}; TODO
5840 currentReturnStruct->ctime = {0};
5841 currentReturnStruct->atime = {0};
5843 destination->grfMode = 0;
5844 destination->grfLocksSupported = 0;
5845 destination->clsid = source->clsid;
5846 destination->grfStateBits = 0;
5847 destination->reserved = 0;
5850 /******************************************************************************
5851 ** BlockChainStream implementation
5854 /* Read and save the index of all blocks in this stream. */
5855 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5857 ULONG next_sector, next_offset;
5858 HRESULT hr;
5859 struct BlockChainRun *last_run;
5861 if (This->indexCacheLen == 0)
5863 last_run = NULL;
5864 next_offset = 0;
5865 next_sector = BlockChainStream_GetHeadOfChain(This);
5867 else
5869 last_run = &This->indexCache[This->indexCacheLen-1];
5870 next_offset = last_run->lastOffset+1;
5871 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5872 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5873 &next_sector);
5874 if (FAILED(hr)) return hr;
5877 while (next_sector != BLOCK_END_OF_CHAIN)
5879 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5881 /* Add the current block to the cache. */
5882 if (This->indexCacheSize == 0)
5884 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5885 if (!This->indexCache) return E_OUTOFMEMORY;
5886 This->indexCacheSize = 16;
5888 else if (This->indexCacheSize == This->indexCacheLen)
5890 struct BlockChainRun *new_cache;
5891 ULONG new_size;
5893 new_size = This->indexCacheSize * 2;
5894 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5895 if (!new_cache) return E_OUTOFMEMORY;
5896 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5898 HeapFree(GetProcessHeap(), 0, This->indexCache);
5899 This->indexCache = new_cache;
5900 This->indexCacheSize = new_size;
5903 This->indexCacheLen++;
5904 last_run = &This->indexCache[This->indexCacheLen-1];
5905 last_run->firstSector = next_sector;
5906 last_run->firstOffset = next_offset;
5909 last_run->lastOffset = next_offset;
5911 /* Find the next block. */
5912 next_offset++;
5913 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5914 if (FAILED(hr)) return hr;
5917 if (This->indexCacheLen)
5919 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5920 This->numBlocks = last_run->lastOffset+1;
5922 else
5924 This->tailIndex = BLOCK_END_OF_CHAIN;
5925 This->numBlocks = 0;
5928 return S_OK;
5931 /* Locate the nth block in this stream. */
5932 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5934 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5935 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5937 if (offset >= This->numBlocks)
5938 return BLOCK_END_OF_CHAIN;
5940 while (min_run < max_run)
5942 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5943 if (offset < This->indexCache[run_to_check].firstOffset)
5945 max_offset = This->indexCache[run_to_check].firstOffset-1;
5946 max_run = run_to_check-1;
5948 else if (offset > This->indexCache[run_to_check].lastOffset)
5950 min_offset = This->indexCache[run_to_check].lastOffset+1;
5951 min_run = run_to_check+1;
5953 else
5954 /* Block is in this run. */
5955 min_run = max_run = run_to_check;
5958 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5961 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5962 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5964 BlockChainBlock *result=NULL;
5965 int i;
5967 for (i=0; i<2; i++)
5968 if (This->cachedBlocks[i].index == index)
5970 *sector = This->cachedBlocks[i].sector;
5971 *block = &This->cachedBlocks[i];
5972 return S_OK;
5975 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5976 if (*sector == BLOCK_END_OF_CHAIN)
5977 return STG_E_DOCFILECORRUPT;
5979 if (create)
5981 if (This->cachedBlocks[0].index == 0xffffffff)
5982 result = &This->cachedBlocks[0];
5983 else if (This->cachedBlocks[1].index == 0xffffffff)
5984 result = &This->cachedBlocks[1];
5985 else
5987 result = &This->cachedBlocks[This->blockToEvict++];
5988 if (This->blockToEvict == 2)
5989 This->blockToEvict = 0;
5992 if (result->dirty)
5994 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5995 return STG_E_WRITEFAULT;
5996 result->dirty = 0;
5999 result->read = 0;
6000 result->index = index;
6001 result->sector = *sector;
6004 *block = result;
6005 return S_OK;
6008 BlockChainStream* BlockChainStream_Construct(
6009 StorageImpl* parentStorage,
6010 ULONG* headOfStreamPlaceHolder,
6011 DirRef dirEntry)
6013 BlockChainStream* newStream;
6015 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6017 newStream->parentStorage = parentStorage;
6018 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6019 newStream->ownerDirEntry = dirEntry;
6020 newStream->indexCache = NULL;
6021 newStream->indexCacheLen = 0;
6022 newStream->indexCacheSize = 0;
6023 newStream->cachedBlocks[0].index = 0xffffffff;
6024 newStream->cachedBlocks[0].dirty = 0;
6025 newStream->cachedBlocks[1].index = 0xffffffff;
6026 newStream->cachedBlocks[1].dirty = 0;
6027 newStream->blockToEvict = 0;
6029 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6031 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6032 HeapFree(GetProcessHeap(), 0, newStream);
6033 return NULL;
6036 return newStream;
6039 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6041 int i;
6042 if (!This) return S_OK;
6043 for (i=0; i<2; i++)
6045 if (This->cachedBlocks[i].dirty)
6047 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6048 This->cachedBlocks[i].dirty = 0;
6049 else
6050 return STG_E_WRITEFAULT;
6053 return S_OK;
6056 void BlockChainStream_Destroy(BlockChainStream* This)
6058 if (This)
6060 BlockChainStream_Flush(This);
6061 HeapFree(GetProcessHeap(), 0, This->indexCache);
6063 HeapFree(GetProcessHeap(), 0, This);
6066 /******************************************************************************
6067 * BlockChainStream_GetHeadOfChain
6069 * Returns the head of this stream chain.
6070 * Some special chains don't have directory entries, their heads are kept in
6071 * This->headOfStreamPlaceHolder.
6074 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6076 DirEntry chainEntry;
6077 HRESULT hr;
6079 if (This->headOfStreamPlaceHolder != 0)
6080 return *(This->headOfStreamPlaceHolder);
6082 if (This->ownerDirEntry != DIRENTRY_NULL)
6084 hr = StorageImpl_ReadDirEntry(
6085 This->parentStorage,
6086 This->ownerDirEntry,
6087 &chainEntry);
6089 if (SUCCEEDED(hr))
6091 return chainEntry.startingBlock;
6095 return BLOCK_END_OF_CHAIN;
6098 /******************************************************************************
6099 * BlockChainStream_GetCount
6101 * Returns the number of blocks that comprises this chain.
6102 * This is not the size of the stream as the last block may not be full!
6104 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6106 return This->numBlocks;
6109 /******************************************************************************
6110 * BlockChainStream_ReadAt
6112 * Reads a specified number of bytes from this chain at the specified offset.
6113 * bytesRead may be NULL.
6114 * Failure will be returned if the specified number of bytes has not been read.
6116 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6117 ULARGE_INTEGER offset,
6118 ULONG size,
6119 void* buffer,
6120 ULONG* bytesRead)
6122 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6123 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6124 ULONG bytesToReadInBuffer;
6125 ULONG blockIndex;
6126 BYTE* bufferWalker;
6127 ULARGE_INTEGER stream_size;
6128 HRESULT hr;
6129 BlockChainBlock *cachedBlock;
6131 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6134 * Find the first block in the stream that contains part of the buffer.
6136 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6138 *bytesRead = 0;
6140 stream_size = BlockChainStream_GetSize(This);
6141 if (stream_size.QuadPart > offset.QuadPart)
6142 size = min(stream_size.QuadPart - offset.QuadPart, size);
6143 else
6144 return S_OK;
6147 * Start reading the buffer.
6149 bufferWalker = buffer;
6151 while (size > 0)
6153 ULARGE_INTEGER ulOffset;
6154 DWORD bytesReadAt;
6157 * Calculate how many bytes we can copy from this big block.
6159 bytesToReadInBuffer =
6160 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6162 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6164 if (FAILED(hr))
6165 return hr;
6167 if (!cachedBlock)
6169 /* Not in cache, and we're going to read past the end of the block. */
6170 ulOffset.u.HighPart = 0;
6171 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6172 offsetInBlock;
6174 StorageImpl_ReadAt(This->parentStorage,
6175 ulOffset,
6176 bufferWalker,
6177 bytesToReadInBuffer,
6178 &bytesReadAt);
6180 else
6182 if (!cachedBlock->read)
6184 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6185 return STG_E_READFAULT;
6187 cachedBlock->read = 1;
6190 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6191 bytesReadAt = bytesToReadInBuffer;
6194 blockNoInSequence++;
6195 bufferWalker += bytesReadAt;
6196 size -= bytesReadAt;
6197 *bytesRead += bytesReadAt;
6198 offsetInBlock = 0; /* There is no offset on the next block */
6200 if (bytesToReadInBuffer != bytesReadAt)
6201 break;
6204 return S_OK;
6207 /******************************************************************************
6208 * BlockChainStream_WriteAt
6210 * Writes the specified number of bytes to this chain at the specified offset.
6211 * Will fail if not all specified number of bytes have been written.
6213 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6214 ULARGE_INTEGER offset,
6215 ULONG size,
6216 const void* buffer,
6217 ULONG* bytesWritten)
6219 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6220 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6221 ULONG bytesToWrite;
6222 ULONG blockIndex;
6223 const BYTE* bufferWalker;
6224 HRESULT hr;
6225 BlockChainBlock *cachedBlock;
6227 *bytesWritten = 0;
6228 bufferWalker = buffer;
6230 while (size > 0)
6232 ULARGE_INTEGER ulOffset;
6233 DWORD bytesWrittenAt;
6236 * Calculate how many bytes we can copy to this big block.
6238 bytesToWrite =
6239 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6241 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6243 /* BlockChainStream_SetSize should have already been called to ensure we have
6244 * enough blocks in the chain to write into */
6245 if (FAILED(hr))
6247 ERR("not enough blocks in chain to write data\n");
6248 return hr;
6251 if (!cachedBlock)
6253 /* Not in cache, and we're going to write past the end of the block. */
6254 ulOffset.u.HighPart = 0;
6255 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6256 offsetInBlock;
6258 StorageImpl_WriteAt(This->parentStorage,
6259 ulOffset,
6260 bufferWalker,
6261 bytesToWrite,
6262 &bytesWrittenAt);
6264 else
6266 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6268 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6269 return STG_E_READFAULT;
6272 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6273 bytesWrittenAt = bytesToWrite;
6274 cachedBlock->read = 1;
6275 cachedBlock->dirty = 1;
6278 blockNoInSequence++;
6279 bufferWalker += bytesWrittenAt;
6280 size -= bytesWrittenAt;
6281 *bytesWritten += bytesWrittenAt;
6282 offsetInBlock = 0; /* There is no offset on the next block */
6284 if (bytesWrittenAt != bytesToWrite)
6285 break;
6288 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6291 /******************************************************************************
6292 * BlockChainStream_Shrink
6294 * Shrinks this chain in the big block depot.
6296 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6297 ULARGE_INTEGER newSize)
6299 ULONG blockIndex;
6300 ULONG numBlocks;
6301 int i;
6304 * Figure out how many blocks are needed to contain the new size
6306 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6308 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6309 numBlocks++;
6311 if (numBlocks)
6314 * Go to the new end of chain
6316 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6318 /* Mark the new end of chain */
6319 StorageImpl_SetNextBlockInChain(
6320 This->parentStorage,
6321 blockIndex,
6322 BLOCK_END_OF_CHAIN);
6324 This->tailIndex = blockIndex;
6326 else
6328 if (This->headOfStreamPlaceHolder != 0)
6330 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6332 else
6334 DirEntry chainEntry;
6335 assert(This->ownerDirEntry != DIRENTRY_NULL);
6337 StorageImpl_ReadDirEntry(
6338 This->parentStorage,
6339 This->ownerDirEntry,
6340 &chainEntry);
6342 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6344 StorageImpl_WriteDirEntry(
6345 This->parentStorage,
6346 This->ownerDirEntry,
6347 &chainEntry);
6350 This->tailIndex = BLOCK_END_OF_CHAIN;
6353 This->numBlocks = numBlocks;
6356 * Mark the extra blocks as free
6358 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6360 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6361 StorageImpl_FreeBigBlock(This->parentStorage,
6362 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6363 if (last_run->lastOffset == last_run->firstOffset)
6364 This->indexCacheLen--;
6365 else
6366 last_run->lastOffset--;
6370 * Reset the last accessed block cache.
6372 for (i=0; i<2; i++)
6374 if (This->cachedBlocks[i].index >= numBlocks)
6376 This->cachedBlocks[i].index = 0xffffffff;
6377 This->cachedBlocks[i].dirty = 0;
6381 return TRUE;
6384 /******************************************************************************
6385 * BlockChainStream_Enlarge
6387 * Grows this chain in the big block depot.
6389 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6390 ULARGE_INTEGER newSize)
6392 ULONG blockIndex, currentBlock;
6393 ULONG newNumBlocks;
6394 ULONG oldNumBlocks = 0;
6396 blockIndex = BlockChainStream_GetHeadOfChain(This);
6399 * Empty chain. Create the head.
6401 if (blockIndex == BLOCK_END_OF_CHAIN)
6403 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6404 StorageImpl_SetNextBlockInChain(This->parentStorage,
6405 blockIndex,
6406 BLOCK_END_OF_CHAIN);
6408 if (This->headOfStreamPlaceHolder != 0)
6410 *(This->headOfStreamPlaceHolder) = blockIndex;
6412 else
6414 DirEntry chainEntry;
6415 assert(This->ownerDirEntry != DIRENTRY_NULL);
6417 StorageImpl_ReadDirEntry(
6418 This->parentStorage,
6419 This->ownerDirEntry,
6420 &chainEntry);
6422 chainEntry.startingBlock = blockIndex;
6424 StorageImpl_WriteDirEntry(
6425 This->parentStorage,
6426 This->ownerDirEntry,
6427 &chainEntry);
6430 This->tailIndex = blockIndex;
6431 This->numBlocks = 1;
6435 * Figure out how many blocks are needed to contain this stream
6437 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6439 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6440 newNumBlocks++;
6443 * Go to the current end of chain
6445 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6447 currentBlock = blockIndex;
6449 while (blockIndex != BLOCK_END_OF_CHAIN)
6451 This->numBlocks++;
6452 currentBlock = blockIndex;
6454 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6455 &blockIndex)))
6456 return FALSE;
6459 This->tailIndex = currentBlock;
6462 currentBlock = This->tailIndex;
6463 oldNumBlocks = This->numBlocks;
6466 * Add new blocks to the chain
6468 if (oldNumBlocks < newNumBlocks)
6470 while (oldNumBlocks < newNumBlocks)
6472 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6474 StorageImpl_SetNextBlockInChain(
6475 This->parentStorage,
6476 currentBlock,
6477 blockIndex);
6479 StorageImpl_SetNextBlockInChain(
6480 This->parentStorage,
6481 blockIndex,
6482 BLOCK_END_OF_CHAIN);
6484 currentBlock = blockIndex;
6485 oldNumBlocks++;
6488 This->tailIndex = blockIndex;
6489 This->numBlocks = newNumBlocks;
6492 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6493 return FALSE;
6495 return TRUE;
6498 /******************************************************************************
6499 * BlockChainStream_SetSize
6501 * Sets the size of this stream. The big block depot will be updated.
6502 * The file will grow if we grow the chain.
6504 * TODO: Free the actual blocks in the file when we shrink the chain.
6505 * Currently, the blocks are still in the file. So the file size
6506 * doesn't shrink even if we shrink streams.
6508 BOOL BlockChainStream_SetSize(
6509 BlockChainStream* This,
6510 ULARGE_INTEGER newSize)
6512 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6514 if (newSize.u.LowPart == size.u.LowPart)
6515 return TRUE;
6517 if (newSize.u.LowPart < size.u.LowPart)
6519 BlockChainStream_Shrink(This, newSize);
6521 else
6523 BlockChainStream_Enlarge(This, newSize);
6526 return TRUE;
6529 /******************************************************************************
6530 * BlockChainStream_GetSize
6532 * Returns the size of this chain.
6533 * Will return the block count if this chain doesn't have a directory entry.
6535 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6537 DirEntry chainEntry;
6539 if(This->headOfStreamPlaceHolder == NULL)
6542 * This chain has a directory entry so use the size value from there.
6544 StorageImpl_ReadDirEntry(
6545 This->parentStorage,
6546 This->ownerDirEntry,
6547 &chainEntry);
6549 return chainEntry.size;
6551 else
6554 * this chain is a chain that does not have a directory entry, figure out the
6555 * size by making the product number of used blocks times the
6556 * size of them
6558 ULARGE_INTEGER result;
6559 result.u.HighPart = 0;
6561 result.u.LowPart =
6562 BlockChainStream_GetCount(This) *
6563 This->parentStorage->bigBlockSize;
6565 return result;
6569 /******************************************************************************
6570 ** SmallBlockChainStream implementation
6573 SmallBlockChainStream* SmallBlockChainStream_Construct(
6574 StorageImpl* parentStorage,
6575 ULONG* headOfStreamPlaceHolder,
6576 DirRef dirEntry)
6578 SmallBlockChainStream* newStream;
6580 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6582 newStream->parentStorage = parentStorage;
6583 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6584 newStream->ownerDirEntry = dirEntry;
6586 return newStream;
6589 void SmallBlockChainStream_Destroy(
6590 SmallBlockChainStream* This)
6592 HeapFree(GetProcessHeap(), 0, This);
6595 /******************************************************************************
6596 * SmallBlockChainStream_GetHeadOfChain
6598 * Returns the head of this chain of small blocks.
6600 static ULONG SmallBlockChainStream_GetHeadOfChain(
6601 SmallBlockChainStream* This)
6603 DirEntry chainEntry;
6604 HRESULT hr;
6606 if (This->headOfStreamPlaceHolder != NULL)
6607 return *(This->headOfStreamPlaceHolder);
6609 if (This->ownerDirEntry)
6611 hr = StorageImpl_ReadDirEntry(
6612 This->parentStorage,
6613 This->ownerDirEntry,
6614 &chainEntry);
6616 if (SUCCEEDED(hr))
6618 return chainEntry.startingBlock;
6623 return BLOCK_END_OF_CHAIN;
6626 /******************************************************************************
6627 * SmallBlockChainStream_GetNextBlockInChain
6629 * Returns the index of the next small block in this chain.
6631 * Return Values:
6632 * - BLOCK_END_OF_CHAIN: end of this chain
6633 * - BLOCK_UNUSED: small block 'blockIndex' is free
6635 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6636 SmallBlockChainStream* This,
6637 ULONG blockIndex,
6638 ULONG* nextBlockInChain)
6640 ULARGE_INTEGER offsetOfBlockInDepot;
6641 DWORD buffer;
6642 ULONG bytesRead;
6643 HRESULT res;
6645 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6647 offsetOfBlockInDepot.u.HighPart = 0;
6648 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6651 * Read those bytes in the buffer from the small block file.
6653 res = BlockChainStream_ReadAt(
6654 This->parentStorage->smallBlockDepotChain,
6655 offsetOfBlockInDepot,
6656 sizeof(DWORD),
6657 &buffer,
6658 &bytesRead);
6660 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6661 res = STG_E_READFAULT;
6663 if (SUCCEEDED(res))
6665 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6666 return S_OK;
6669 return res;
6672 /******************************************************************************
6673 * SmallBlockChainStream_SetNextBlockInChain
6675 * Writes the index of the next block of the specified block in the small
6676 * block depot.
6677 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6678 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6680 static void SmallBlockChainStream_SetNextBlockInChain(
6681 SmallBlockChainStream* This,
6682 ULONG blockIndex,
6683 ULONG nextBlock)
6685 ULARGE_INTEGER offsetOfBlockInDepot;
6686 DWORD buffer;
6687 ULONG bytesWritten;
6689 offsetOfBlockInDepot.u.HighPart = 0;
6690 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6692 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6695 * Read those bytes in the buffer from the small block file.
6697 BlockChainStream_WriteAt(
6698 This->parentStorage->smallBlockDepotChain,
6699 offsetOfBlockInDepot,
6700 sizeof(DWORD),
6701 &buffer,
6702 &bytesWritten);
6705 /******************************************************************************
6706 * SmallBlockChainStream_FreeBlock
6708 * Flag small block 'blockIndex' as free in the small block depot.
6710 static void SmallBlockChainStream_FreeBlock(
6711 SmallBlockChainStream* This,
6712 ULONG blockIndex)
6714 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6717 /******************************************************************************
6718 * SmallBlockChainStream_GetNextFreeBlock
6720 * Returns the index of a free small block. The small block depot will be
6721 * enlarged if necessary. The small block chain will also be enlarged if
6722 * necessary.
6724 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6725 SmallBlockChainStream* This)
6727 ULARGE_INTEGER offsetOfBlockInDepot;
6728 DWORD buffer;
6729 ULONG bytesRead;
6730 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6731 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6732 HRESULT res = S_OK;
6733 ULONG smallBlocksPerBigBlock;
6734 DirEntry rootEntry;
6735 ULONG blocksRequired;
6736 ULARGE_INTEGER old_size, size_required;
6738 offsetOfBlockInDepot.u.HighPart = 0;
6741 * Scan the small block depot for a free block
6743 while (nextBlockIndex != BLOCK_UNUSED)
6745 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6747 res = BlockChainStream_ReadAt(
6748 This->parentStorage->smallBlockDepotChain,
6749 offsetOfBlockInDepot,
6750 sizeof(DWORD),
6751 &buffer,
6752 &bytesRead);
6755 * If we run out of space for the small block depot, enlarge it
6757 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6759 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6761 if (nextBlockIndex != BLOCK_UNUSED)
6762 blockIndex++;
6764 else
6766 ULONG count =
6767 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6769 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6770 ULARGE_INTEGER newSize, offset;
6771 ULONG bytesWritten;
6773 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6774 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6777 * Initialize all the small blocks to free
6779 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6780 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6781 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6782 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6784 StorageImpl_SaveFileHeader(This->parentStorage);
6788 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6790 smallBlocksPerBigBlock =
6791 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6794 * Verify if we have to allocate big blocks to contain small blocks
6796 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6798 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6800 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6802 if (size_required.QuadPart > old_size.QuadPart)
6804 BlockChainStream_SetSize(
6805 This->parentStorage->smallBlockRootChain,
6806 size_required);
6808 StorageImpl_ReadDirEntry(
6809 This->parentStorage,
6810 This->parentStorage->base.storageDirEntry,
6811 &rootEntry);
6813 rootEntry.size = size_required;
6815 StorageImpl_WriteDirEntry(
6816 This->parentStorage,
6817 This->parentStorage->base.storageDirEntry,
6818 &rootEntry);
6821 return blockIndex;
6824 /******************************************************************************
6825 * SmallBlockChainStream_ReadAt
6827 * Reads a specified number of bytes from this chain at the specified offset.
6828 * bytesRead may be NULL.
6829 * Failure will be returned if the specified number of bytes has not been read.
6831 HRESULT SmallBlockChainStream_ReadAt(
6832 SmallBlockChainStream* This,
6833 ULARGE_INTEGER offset,
6834 ULONG size,
6835 void* buffer,
6836 ULONG* bytesRead)
6838 HRESULT rc = S_OK;
6839 ULARGE_INTEGER offsetInBigBlockFile;
6840 ULONG blockNoInSequence =
6841 offset.u.LowPart / This->parentStorage->smallBlockSize;
6843 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6844 ULONG bytesToReadInBuffer;
6845 ULONG blockIndex;
6846 ULONG bytesReadFromBigBlockFile;
6847 BYTE* bufferWalker;
6848 ULARGE_INTEGER stream_size;
6851 * This should never happen on a small block file.
6853 assert(offset.u.HighPart==0);
6855 *bytesRead = 0;
6857 stream_size = SmallBlockChainStream_GetSize(This);
6858 if (stream_size.QuadPart > offset.QuadPart)
6859 size = min(stream_size.QuadPart - offset.QuadPart, size);
6860 else
6861 return S_OK;
6864 * Find the first block in the stream that contains part of the buffer.
6866 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6868 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6870 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6871 if(FAILED(rc))
6872 return rc;
6873 blockNoInSequence--;
6877 * Start reading the buffer.
6879 bufferWalker = buffer;
6881 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6884 * Calculate how many bytes we can copy from this small block.
6886 bytesToReadInBuffer =
6887 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6890 * Calculate the offset of the small block in the small block file.
6892 offsetInBigBlockFile.u.HighPart = 0;
6893 offsetInBigBlockFile.u.LowPart =
6894 blockIndex * This->parentStorage->smallBlockSize;
6896 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6899 * Read those bytes in the buffer from the small block file.
6900 * The small block has already been identified so it shouldn't fail
6901 * unless the file is corrupt.
6903 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6904 offsetInBigBlockFile,
6905 bytesToReadInBuffer,
6906 bufferWalker,
6907 &bytesReadFromBigBlockFile);
6909 if (FAILED(rc))
6910 return rc;
6912 if (!bytesReadFromBigBlockFile)
6913 return STG_E_DOCFILECORRUPT;
6916 * Step to the next big block.
6918 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6919 if(FAILED(rc))
6920 return STG_E_DOCFILECORRUPT;
6922 bufferWalker += bytesReadFromBigBlockFile;
6923 size -= bytesReadFromBigBlockFile;
6924 *bytesRead += bytesReadFromBigBlockFile;
6925 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6928 return S_OK;
6931 /******************************************************************************
6932 * SmallBlockChainStream_WriteAt
6934 * Writes the specified number of bytes to this chain at the specified offset.
6935 * Will fail if not all specified number of bytes have been written.
6937 HRESULT SmallBlockChainStream_WriteAt(
6938 SmallBlockChainStream* This,
6939 ULARGE_INTEGER offset,
6940 ULONG size,
6941 const void* buffer,
6942 ULONG* bytesWritten)
6944 ULARGE_INTEGER offsetInBigBlockFile;
6945 ULONG blockNoInSequence =
6946 offset.u.LowPart / This->parentStorage->smallBlockSize;
6948 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6949 ULONG bytesToWriteInBuffer;
6950 ULONG blockIndex;
6951 ULONG bytesWrittenToBigBlockFile;
6952 const BYTE* bufferWalker;
6953 HRESULT res;
6956 * This should never happen on a small block file.
6958 assert(offset.u.HighPart==0);
6961 * Find the first block in the stream that contains part of the buffer.
6963 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6965 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6967 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6968 return STG_E_DOCFILECORRUPT;
6969 blockNoInSequence--;
6973 * Start writing the buffer.
6975 *bytesWritten = 0;
6976 bufferWalker = buffer;
6977 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6980 * Calculate how many bytes we can copy to this small block.
6982 bytesToWriteInBuffer =
6983 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6986 * Calculate the offset of the small block in the small block file.
6988 offsetInBigBlockFile.u.HighPart = 0;
6989 offsetInBigBlockFile.u.LowPart =
6990 blockIndex * This->parentStorage->smallBlockSize;
6992 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6995 * Write those bytes in the buffer to the small block file.
6997 res = BlockChainStream_WriteAt(
6998 This->parentStorage->smallBlockRootChain,
6999 offsetInBigBlockFile,
7000 bytesToWriteInBuffer,
7001 bufferWalker,
7002 &bytesWrittenToBigBlockFile);
7003 if (FAILED(res))
7004 return res;
7007 * Step to the next big block.
7009 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7010 &blockIndex)))
7011 return FALSE;
7012 bufferWalker += bytesWrittenToBigBlockFile;
7013 size -= bytesWrittenToBigBlockFile;
7014 *bytesWritten += bytesWrittenToBigBlockFile;
7015 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7018 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7021 /******************************************************************************
7022 * SmallBlockChainStream_Shrink
7024 * Shrinks this chain in the small block depot.
7026 static BOOL SmallBlockChainStream_Shrink(
7027 SmallBlockChainStream* This,
7028 ULARGE_INTEGER newSize)
7030 ULONG blockIndex, extraBlock;
7031 ULONG numBlocks;
7032 ULONG count = 0;
7034 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7036 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7037 numBlocks++;
7039 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7042 * Go to the new end of chain
7044 while (count < numBlocks)
7046 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7047 &blockIndex)))
7048 return FALSE;
7049 count++;
7053 * If the count is 0, we have a special case, the head of the chain was
7054 * just freed.
7056 if (count == 0)
7058 DirEntry chainEntry;
7060 StorageImpl_ReadDirEntry(This->parentStorage,
7061 This->ownerDirEntry,
7062 &chainEntry);
7064 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7066 StorageImpl_WriteDirEntry(This->parentStorage,
7067 This->ownerDirEntry,
7068 &chainEntry);
7071 * We start freeing the chain at the head block.
7073 extraBlock = blockIndex;
7075 else
7077 /* Get the next block before marking the new end */
7078 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7079 &extraBlock)))
7080 return FALSE;
7082 /* Mark the new end of chain */
7083 SmallBlockChainStream_SetNextBlockInChain(
7084 This,
7085 blockIndex,
7086 BLOCK_END_OF_CHAIN);
7090 * Mark the extra blocks as free
7092 while (extraBlock != BLOCK_END_OF_CHAIN)
7094 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7095 &blockIndex)))
7096 return FALSE;
7097 SmallBlockChainStream_FreeBlock(This, extraBlock);
7098 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7099 extraBlock = blockIndex;
7102 return TRUE;
7105 /******************************************************************************
7106 * SmallBlockChainStream_Enlarge
7108 * Grows this chain in the small block depot.
7110 static BOOL SmallBlockChainStream_Enlarge(
7111 SmallBlockChainStream* This,
7112 ULARGE_INTEGER newSize)
7114 ULONG blockIndex, currentBlock;
7115 ULONG newNumBlocks;
7116 ULONG oldNumBlocks = 0;
7118 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7121 * Empty chain. Create the head.
7123 if (blockIndex == BLOCK_END_OF_CHAIN)
7125 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7126 SmallBlockChainStream_SetNextBlockInChain(
7127 This,
7128 blockIndex,
7129 BLOCK_END_OF_CHAIN);
7131 if (This->headOfStreamPlaceHolder != NULL)
7133 *(This->headOfStreamPlaceHolder) = blockIndex;
7135 else
7137 DirEntry chainEntry;
7139 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7140 &chainEntry);
7142 chainEntry.startingBlock = blockIndex;
7144 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7145 &chainEntry);
7149 currentBlock = blockIndex;
7152 * Figure out how many blocks are needed to contain this stream
7154 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7156 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7157 newNumBlocks++;
7160 * Go to the current end of chain
7162 while (blockIndex != BLOCK_END_OF_CHAIN)
7164 oldNumBlocks++;
7165 currentBlock = blockIndex;
7166 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7167 return FALSE;
7171 * Add new blocks to the chain
7173 while (oldNumBlocks < newNumBlocks)
7175 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7176 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7178 SmallBlockChainStream_SetNextBlockInChain(
7179 This,
7180 blockIndex,
7181 BLOCK_END_OF_CHAIN);
7183 currentBlock = blockIndex;
7184 oldNumBlocks++;
7187 return TRUE;
7190 /******************************************************************************
7191 * SmallBlockChainStream_SetSize
7193 * Sets the size of this stream.
7194 * The file will grow if we grow the chain.
7196 * TODO: Free the actual blocks in the file when we shrink the chain.
7197 * Currently, the blocks are still in the file. So the file size
7198 * doesn't shrink even if we shrink streams.
7200 BOOL SmallBlockChainStream_SetSize(
7201 SmallBlockChainStream* This,
7202 ULARGE_INTEGER newSize)
7204 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7206 if (newSize.u.LowPart == size.u.LowPart)
7207 return TRUE;
7209 if (newSize.u.LowPart < size.u.LowPart)
7211 SmallBlockChainStream_Shrink(This, newSize);
7213 else
7215 SmallBlockChainStream_Enlarge(This, newSize);
7218 return TRUE;
7221 /******************************************************************************
7222 * SmallBlockChainStream_GetCount
7224 * Returns the number of small blocks that comprises this chain.
7225 * This is not the size of the stream as the last block may not be full!
7228 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7230 ULONG blockIndex;
7231 ULONG count = 0;
7233 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7235 while(blockIndex != BLOCK_END_OF_CHAIN)
7237 count++;
7239 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7240 blockIndex, &blockIndex)))
7241 return 0;
7244 return count;
7247 /******************************************************************************
7248 * SmallBlockChainStream_GetSize
7250 * Returns the size of this chain.
7252 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7254 DirEntry chainEntry;
7256 if(This->headOfStreamPlaceHolder != NULL)
7258 ULARGE_INTEGER result;
7259 result.u.HighPart = 0;
7261 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7262 This->parentStorage->smallBlockSize;
7264 return result;
7267 StorageImpl_ReadDirEntry(
7268 This->parentStorage,
7269 This->ownerDirEntry,
7270 &chainEntry);
7272 return chainEntry.size;
7275 static HRESULT create_storagefile(
7276 LPCOLESTR pwcsName,
7277 DWORD grfMode,
7278 DWORD grfAttrs,
7279 STGOPTIONS* pStgOptions,
7280 REFIID riid,
7281 void** ppstgOpen)
7283 StorageBaseImpl* newStorage = 0;
7284 HANDLE hFile = INVALID_HANDLE_VALUE;
7285 HRESULT hr = STG_E_INVALIDFLAG;
7286 DWORD shareMode;
7287 DWORD accessMode;
7288 DWORD creationMode;
7289 DWORD fileAttributes;
7290 WCHAR tempFileName[MAX_PATH];
7292 if (ppstgOpen == 0)
7293 return STG_E_INVALIDPOINTER;
7295 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7296 return STG_E_INVALIDPARAMETER;
7298 /* if no share mode given then DENY_NONE is the default */
7299 if (STGM_SHARE_MODE(grfMode) == 0)
7300 grfMode |= STGM_SHARE_DENY_NONE;
7302 if ( FAILED( validateSTGM(grfMode) ))
7303 goto end;
7305 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7306 switch(STGM_ACCESS_MODE(grfMode))
7308 case STGM_WRITE:
7309 case STGM_READWRITE:
7310 break;
7311 default:
7312 goto end;
7315 /* in direct mode, can only use SHARE_EXCLUSIVE */
7316 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7317 goto end;
7319 /* but in transacted mode, any share mode is valid */
7322 * Generate a unique name.
7324 if (pwcsName == 0)
7326 WCHAR tempPath[MAX_PATH];
7327 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7329 memset(tempPath, 0, sizeof(tempPath));
7330 memset(tempFileName, 0, sizeof(tempFileName));
7332 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7333 tempPath[0] = '.';
7335 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7336 pwcsName = tempFileName;
7337 else
7339 hr = STG_E_INSUFFICIENTMEMORY;
7340 goto end;
7343 creationMode = TRUNCATE_EXISTING;
7345 else
7347 creationMode = GetCreationModeFromSTGM(grfMode);
7351 * Interpret the STGM value grfMode
7353 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7354 accessMode = GetAccessModeFromSTGM(grfMode);
7356 if (grfMode & STGM_DELETEONRELEASE)
7357 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7358 else
7359 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7361 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7363 static int fixme;
7364 if (!fixme++)
7365 FIXME("Storage share mode not implemented.\n");
7368 *ppstgOpen = 0;
7370 hFile = CreateFileW(pwcsName,
7371 accessMode,
7372 shareMode,
7373 NULL,
7374 creationMode,
7375 fileAttributes,
7378 if (hFile == INVALID_HANDLE_VALUE)
7380 if(GetLastError() == ERROR_FILE_EXISTS)
7381 hr = STG_E_FILEALREADYEXISTS;
7382 else
7383 hr = E_FAIL;
7384 goto end;
7388 * Allocate and initialize the new IStorage32object.
7390 hr = Storage_Construct(
7391 hFile,
7392 pwcsName,
7393 NULL,
7394 grfMode,
7395 TRUE,
7396 TRUE,
7397 pStgOptions->ulSectorSize,
7398 &newStorage);
7400 if (FAILED(hr))
7402 goto end;
7405 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
7406 IStorage_Release(&newStorage->IStorage_iface);
7408 end:
7409 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7411 return hr;
7414 /******************************************************************************
7415 * StgCreateDocfile [OLE32.@]
7416 * Creates a new compound file storage object
7418 * PARAMS
7419 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7420 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7421 * reserved [ ?] unused?, usually 0
7422 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7424 * RETURNS
7425 * S_OK if the file was successfully created
7426 * some STG_E_ value if error
7427 * NOTES
7428 * if pwcsName is NULL, create file with new unique name
7429 * the function can returns
7430 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7431 * (unrealized now)
7433 HRESULT WINAPI StgCreateDocfile(
7434 LPCOLESTR pwcsName,
7435 DWORD grfMode,
7436 DWORD reserved,
7437 IStorage **ppstgOpen)
7439 STGOPTIONS stgoptions = {1, 0, 512};
7441 TRACE("(%s, %x, %d, %p)\n",
7442 debugstr_w(pwcsName), grfMode,
7443 reserved, ppstgOpen);
7445 if (ppstgOpen == 0)
7446 return STG_E_INVALIDPOINTER;
7447 if (reserved != 0)
7448 return STG_E_INVALIDPARAMETER;
7450 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7453 /******************************************************************************
7454 * StgCreateStorageEx [OLE32.@]
7456 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7458 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7459 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7461 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7463 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7464 return STG_E_INVALIDPARAMETER;
7467 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7469 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7470 return STG_E_INVALIDPARAMETER;
7473 if (stgfmt == STGFMT_FILE)
7475 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7476 return STG_E_INVALIDPARAMETER;
7479 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7481 STGOPTIONS defaultOptions = {1, 0, 512};
7483 if (!pStgOptions) pStgOptions = &defaultOptions;
7484 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7488 ERR("Invalid stgfmt argument\n");
7489 return STG_E_INVALIDPARAMETER;
7492 /******************************************************************************
7493 * StgCreatePropSetStg [OLE32.@]
7495 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7496 IPropertySetStorage **propset)
7498 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
7499 if (reserved)
7500 return STG_E_INVALIDPARAMETER;
7502 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
7505 /******************************************************************************
7506 * StgOpenStorageEx [OLE32.@]
7508 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7510 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7511 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7513 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7515 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7516 return STG_E_INVALIDPARAMETER;
7519 switch (stgfmt)
7521 case STGFMT_FILE:
7522 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7523 return STG_E_INVALIDPARAMETER;
7525 case STGFMT_STORAGE:
7526 break;
7528 case STGFMT_DOCFILE:
7529 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7531 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7532 return STG_E_INVALIDPARAMETER;
7534 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7535 break;
7537 case STGFMT_ANY:
7538 WARN("STGFMT_ANY assuming storage\n");
7539 break;
7541 default:
7542 return STG_E_INVALIDPARAMETER;
7545 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7549 /******************************************************************************
7550 * StgOpenStorage [OLE32.@]
7552 HRESULT WINAPI StgOpenStorage(
7553 const OLECHAR *pwcsName,
7554 IStorage *pstgPriority,
7555 DWORD grfMode,
7556 SNB snbExclude,
7557 DWORD reserved,
7558 IStorage **ppstgOpen)
7560 StorageBaseImpl* newStorage = 0;
7561 HRESULT hr = S_OK;
7562 HANDLE hFile = 0;
7563 DWORD shareMode;
7564 DWORD accessMode;
7566 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7567 debugstr_w(pwcsName), pstgPriority, grfMode,
7568 snbExclude, reserved, ppstgOpen);
7570 if (pwcsName == 0)
7572 hr = STG_E_INVALIDNAME;
7573 goto end;
7576 if (ppstgOpen == 0)
7578 hr = STG_E_INVALIDPOINTER;
7579 goto end;
7582 if (reserved)
7584 hr = STG_E_INVALIDPARAMETER;
7585 goto end;
7588 if (grfMode & STGM_PRIORITY)
7590 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7591 return STG_E_INVALIDFLAG;
7592 if (grfMode & STGM_DELETEONRELEASE)
7593 return STG_E_INVALIDFUNCTION;
7594 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7595 return STG_E_INVALIDFLAG;
7596 grfMode &= ~0xf0; /* remove the existing sharing mode */
7597 grfMode |= STGM_SHARE_DENY_NONE;
7599 /* STGM_PRIORITY stops other IStorage objects on the same file from
7600 * committing until the STGM_PRIORITY IStorage is closed. it also
7601 * stops non-transacted mode StgOpenStorage calls with write access from
7602 * succeeding. obviously, both of these cannot be achieved through just
7603 * file share flags */
7604 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7608 * Validate the sharing mode
7610 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7611 switch(STGM_SHARE_MODE(grfMode))
7613 case STGM_SHARE_EXCLUSIVE:
7614 case STGM_SHARE_DENY_WRITE:
7615 break;
7616 default:
7617 hr = STG_E_INVALIDFLAG;
7618 goto end;
7621 if ( FAILED( validateSTGM(grfMode) ) ||
7622 (grfMode&STGM_CREATE))
7624 hr = STG_E_INVALIDFLAG;
7625 goto end;
7628 /* shared reading requires transacted mode */
7629 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7630 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7631 !(grfMode&STGM_TRANSACTED) )
7633 hr = STG_E_INVALIDFLAG;
7634 goto end;
7638 * Interpret the STGM value grfMode
7640 shareMode = GetShareModeFromSTGM(grfMode);
7641 accessMode = GetAccessModeFromSTGM(grfMode);
7643 *ppstgOpen = 0;
7645 hFile = CreateFileW( pwcsName,
7646 accessMode,
7647 shareMode,
7648 NULL,
7649 OPEN_EXISTING,
7650 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7653 if (hFile==INVALID_HANDLE_VALUE)
7655 DWORD last_error = GetLastError();
7657 hr = E_FAIL;
7659 switch (last_error)
7661 case ERROR_FILE_NOT_FOUND:
7662 hr = STG_E_FILENOTFOUND;
7663 break;
7665 case ERROR_PATH_NOT_FOUND:
7666 hr = STG_E_PATHNOTFOUND;
7667 break;
7669 case ERROR_ACCESS_DENIED:
7670 case ERROR_WRITE_PROTECT:
7671 hr = STG_E_ACCESSDENIED;
7672 break;
7674 case ERROR_SHARING_VIOLATION:
7675 hr = STG_E_SHAREVIOLATION;
7676 break;
7678 default:
7679 hr = E_FAIL;
7682 goto end;
7686 * Refuse to open the file if it's too small to be a structured storage file
7687 * FIXME: verify the file when reading instead of here
7689 if (GetFileSize(hFile, NULL) < 0x100)
7691 CloseHandle(hFile);
7692 hr = STG_E_FILEALREADYEXISTS;
7693 goto end;
7697 * Allocate and initialize the new IStorage32object.
7699 hr = Storage_Construct(
7700 hFile,
7701 pwcsName,
7702 NULL,
7703 grfMode,
7704 TRUE,
7705 FALSE,
7706 512,
7707 &newStorage);
7709 if (FAILED(hr))
7712 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7714 if(hr == STG_E_INVALIDHEADER)
7715 hr = STG_E_FILEALREADYEXISTS;
7716 goto end;
7719 *ppstgOpen = &newStorage->IStorage_iface;
7721 end:
7722 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7723 return hr;
7726 /******************************************************************************
7727 * StgCreateDocfileOnILockBytes [OLE32.@]
7729 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7730 ILockBytes *plkbyt,
7731 DWORD grfMode,
7732 DWORD reserved,
7733 IStorage** ppstgOpen)
7735 StorageBaseImpl* newStorage = 0;
7736 HRESULT hr = S_OK;
7738 if ((ppstgOpen == 0) || (plkbyt == 0))
7739 return STG_E_INVALIDPOINTER;
7742 * Allocate and initialize the new IStorage object.
7744 hr = Storage_Construct(
7747 plkbyt,
7748 grfMode,
7749 FALSE,
7750 TRUE,
7751 512,
7752 &newStorage);
7754 if (FAILED(hr))
7756 return hr;
7759 *ppstgOpen = &newStorage->IStorage_iface;
7761 return hr;
7764 /******************************************************************************
7765 * StgOpenStorageOnILockBytes [OLE32.@]
7767 HRESULT WINAPI StgOpenStorageOnILockBytes(
7768 ILockBytes *plkbyt,
7769 IStorage *pstgPriority,
7770 DWORD grfMode,
7771 SNB snbExclude,
7772 DWORD reserved,
7773 IStorage **ppstgOpen)
7775 StorageBaseImpl* newStorage = 0;
7776 HRESULT hr = S_OK;
7778 if ((plkbyt == 0) || (ppstgOpen == 0))
7779 return STG_E_INVALIDPOINTER;
7781 if ( FAILED( validateSTGM(grfMode) ))
7782 return STG_E_INVALIDFLAG;
7784 *ppstgOpen = 0;
7787 * Allocate and initialize the new IStorage object.
7789 hr = Storage_Construct(
7792 plkbyt,
7793 grfMode,
7794 FALSE,
7795 FALSE,
7796 512,
7797 &newStorage);
7799 if (FAILED(hr))
7801 return hr;
7804 *ppstgOpen = &newStorage->IStorage_iface;
7806 return hr;
7809 /******************************************************************************
7810 * StgSetTimes [ole32.@]
7811 * StgSetTimes [OLE32.@]
7815 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7816 FILETIME const *patime, FILETIME const *pmtime)
7818 IStorage *stg = NULL;
7819 HRESULT r;
7821 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7823 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7824 0, 0, &stg);
7825 if( SUCCEEDED(r) )
7827 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7828 IStorage_Release(stg);
7831 return r;
7834 /******************************************************************************
7835 * StgIsStorageILockBytes [OLE32.@]
7837 * Determines if the ILockBytes contains a storage object.
7839 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7841 BYTE sig[sizeof(STORAGE_magic)];
7842 ULARGE_INTEGER offset;
7843 ULONG read = 0;
7845 offset.u.HighPart = 0;
7846 offset.u.LowPart = 0;
7848 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
7850 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
7851 return S_OK;
7853 return S_FALSE;
7856 /******************************************************************************
7857 * WriteClassStg [OLE32.@]
7859 * This method will store the specified CLSID in the specified storage object
7861 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7863 if(!pStg)
7864 return E_INVALIDARG;
7866 if(!rclsid)
7867 return STG_E_INVALIDPOINTER;
7869 return IStorage_SetClass(pStg, rclsid);
7872 /***********************************************************************
7873 * ReadClassStg (OLE32.@)
7875 * This method reads the CLSID previously written to a storage object with
7876 * the WriteClassStg.
7878 * PARAMS
7879 * pstg [I] IStorage pointer
7880 * pclsid [O] Pointer to where the CLSID is written
7882 * RETURNS
7883 * Success: S_OK.
7884 * Failure: HRESULT code.
7886 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7888 STATSTG pstatstg;
7889 HRESULT hRes;
7891 TRACE("(%p, %p)\n", pstg, pclsid);
7893 if(!pstg || !pclsid)
7894 return E_INVALIDARG;
7897 * read a STATSTG structure (contains the clsid) from the storage
7899 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7901 if(SUCCEEDED(hRes))
7902 *pclsid=pstatstg.clsid;
7904 return hRes;
7907 /***********************************************************************
7908 * OleLoadFromStream (OLE32.@)
7910 * This function loads an object from stream
7912 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7914 CLSID clsid;
7915 HRESULT res;
7916 LPPERSISTSTREAM xstm;
7918 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7920 res=ReadClassStm(pStm,&clsid);
7921 if (FAILED(res))
7922 return res;
7923 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7924 if (FAILED(res))
7925 return res;
7926 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7927 if (FAILED(res)) {
7928 IUnknown_Release((IUnknown*)*ppvObj);
7929 return res;
7931 res=IPersistStream_Load(xstm,pStm);
7932 IPersistStream_Release(xstm);
7933 /* FIXME: all refcounts ok at this point? I think they should be:
7934 * pStm : unchanged
7935 * ppvObj : 1
7936 * xstm : 0 (released)
7938 return res;
7941 /***********************************************************************
7942 * OleSaveToStream (OLE32.@)
7944 * This function saves an object with the IPersistStream interface on it
7945 * to the specified stream.
7947 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7950 CLSID clsid;
7951 HRESULT res;
7953 TRACE("(%p,%p)\n",pPStm,pStm);
7955 res=IPersistStream_GetClassID(pPStm,&clsid);
7957 if (SUCCEEDED(res)){
7959 res=WriteClassStm(pStm,&clsid);
7961 if (SUCCEEDED(res))
7963 res=IPersistStream_Save(pPStm,pStm,TRUE);
7966 TRACE("Finished Save\n");
7967 return res;
7970 /****************************************************************************
7971 * This method validate a STGM parameter that can contain the values below
7973 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7974 * The stgm values contained in 0xffff0000 are bitmasks.
7976 * STGM_DIRECT 0x00000000
7977 * STGM_TRANSACTED 0x00010000
7978 * STGM_SIMPLE 0x08000000
7980 * STGM_READ 0x00000000
7981 * STGM_WRITE 0x00000001
7982 * STGM_READWRITE 0x00000002
7984 * STGM_SHARE_DENY_NONE 0x00000040
7985 * STGM_SHARE_DENY_READ 0x00000030
7986 * STGM_SHARE_DENY_WRITE 0x00000020
7987 * STGM_SHARE_EXCLUSIVE 0x00000010
7989 * STGM_PRIORITY 0x00040000
7990 * STGM_DELETEONRELEASE 0x04000000
7992 * STGM_CREATE 0x00001000
7993 * STGM_CONVERT 0x00020000
7994 * STGM_FAILIFTHERE 0x00000000
7996 * STGM_NOSCRATCH 0x00100000
7997 * STGM_NOSNAPSHOT 0x00200000
7999 static HRESULT validateSTGM(DWORD stgm)
8001 DWORD access = STGM_ACCESS_MODE(stgm);
8002 DWORD share = STGM_SHARE_MODE(stgm);
8003 DWORD create = STGM_CREATE_MODE(stgm);
8005 if (stgm&~STGM_KNOWN_FLAGS)
8007 ERR("unknown flags %08x\n", stgm);
8008 return E_FAIL;
8011 switch (access)
8013 case STGM_READ:
8014 case STGM_WRITE:
8015 case STGM_READWRITE:
8016 break;
8017 default:
8018 return E_FAIL;
8021 switch (share)
8023 case STGM_SHARE_DENY_NONE:
8024 case STGM_SHARE_DENY_READ:
8025 case STGM_SHARE_DENY_WRITE:
8026 case STGM_SHARE_EXCLUSIVE:
8027 break;
8028 default:
8029 return E_FAIL;
8032 switch (create)
8034 case STGM_CREATE:
8035 case STGM_FAILIFTHERE:
8036 break;
8037 default:
8038 return E_FAIL;
8042 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8044 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8045 return E_FAIL;
8048 * STGM_CREATE | STGM_CONVERT
8049 * if both are false, STGM_FAILIFTHERE is set to TRUE
8051 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8052 return E_FAIL;
8055 * STGM_NOSCRATCH requires STGM_TRANSACTED
8057 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8058 return E_FAIL;
8061 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8062 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8064 if ( (stgm & STGM_NOSNAPSHOT) &&
8065 (!(stgm & STGM_TRANSACTED) ||
8066 share == STGM_SHARE_EXCLUSIVE ||
8067 share == STGM_SHARE_DENY_WRITE) )
8068 return E_FAIL;
8070 return S_OK;
8073 /****************************************************************************
8074 * GetShareModeFromSTGM
8076 * This method will return a share mode flag from a STGM value.
8077 * The STGM value is assumed valid.
8079 static DWORD GetShareModeFromSTGM(DWORD stgm)
8081 switch (STGM_SHARE_MODE(stgm))
8083 case STGM_SHARE_DENY_NONE:
8084 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8085 case STGM_SHARE_DENY_READ:
8086 return FILE_SHARE_WRITE;
8087 case STGM_SHARE_DENY_WRITE:
8088 return FILE_SHARE_READ;
8089 case STGM_SHARE_EXCLUSIVE:
8090 return 0;
8092 ERR("Invalid share mode!\n");
8093 assert(0);
8094 return 0;
8097 /****************************************************************************
8098 * GetAccessModeFromSTGM
8100 * This method will return an access mode flag from a STGM value.
8101 * The STGM value is assumed valid.
8103 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8105 switch (STGM_ACCESS_MODE(stgm))
8107 case STGM_READ:
8108 return GENERIC_READ;
8109 case STGM_WRITE:
8110 case STGM_READWRITE:
8111 return GENERIC_READ | GENERIC_WRITE;
8113 ERR("Invalid access mode!\n");
8114 assert(0);
8115 return 0;
8118 /****************************************************************************
8119 * GetCreationModeFromSTGM
8121 * This method will return a creation mode flag from a STGM value.
8122 * The STGM value is assumed valid.
8124 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8126 switch(STGM_CREATE_MODE(stgm))
8128 case STGM_CREATE:
8129 return CREATE_ALWAYS;
8130 case STGM_CONVERT:
8131 FIXME("STGM_CONVERT not implemented!\n");
8132 return CREATE_NEW;
8133 case STGM_FAILIFTHERE:
8134 return CREATE_NEW;
8136 ERR("Invalid create mode!\n");
8137 assert(0);
8138 return 0;
8142 /*************************************************************************
8143 * OLECONVERT_LoadOLE10 [Internal]
8145 * Loads the OLE10 STREAM to memory
8147 * PARAMS
8148 * pOleStream [I] The OLESTREAM
8149 * pData [I] Data Structure for the OLESTREAM Data
8151 * RETURNS
8152 * Success: S_OK
8153 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8154 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8156 * NOTES
8157 * This function is used by OleConvertOLESTREAMToIStorage only.
8159 * Memory allocated for pData must be freed by the caller
8161 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8163 DWORD dwSize;
8164 HRESULT hRes = S_OK;
8165 int nTryCnt=0;
8166 int max_try = 6;
8168 pData->pData = NULL;
8169 pData->pstrOleObjFileName = NULL;
8171 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8173 /* Get the OleID */
8174 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8175 if(dwSize != sizeof(pData->dwOleID))
8177 hRes = CONVERT10_E_OLESTREAM_GET;
8179 else if(pData->dwOleID != OLESTREAM_ID)
8181 hRes = CONVERT10_E_OLESTREAM_FMT;
8183 else
8185 hRes = S_OK;
8186 break;
8190 if(hRes == S_OK)
8192 /* Get the TypeID... more info needed for this field */
8193 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8194 if(dwSize != sizeof(pData->dwTypeID))
8196 hRes = CONVERT10_E_OLESTREAM_GET;
8199 if(hRes == S_OK)
8201 if(pData->dwTypeID != 0)
8203 /* Get the length of the OleTypeName */
8204 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8205 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8207 hRes = CONVERT10_E_OLESTREAM_GET;
8210 if(hRes == S_OK)
8212 if(pData->dwOleTypeNameLength > 0)
8214 /* Get the OleTypeName */
8215 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8216 if(dwSize != pData->dwOleTypeNameLength)
8218 hRes = CONVERT10_E_OLESTREAM_GET;
8222 if(bStrem1)
8224 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8225 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8227 hRes = CONVERT10_E_OLESTREAM_GET;
8229 if(hRes == S_OK)
8231 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8232 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8233 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8234 if(pData->pstrOleObjFileName)
8236 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8237 if(dwSize != pData->dwOleObjFileNameLength)
8239 hRes = CONVERT10_E_OLESTREAM_GET;
8242 else
8243 hRes = CONVERT10_E_OLESTREAM_GET;
8246 else
8248 /* Get the Width of the Metafile */
8249 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8250 if(dwSize != sizeof(pData->dwMetaFileWidth))
8252 hRes = CONVERT10_E_OLESTREAM_GET;
8254 if(hRes == S_OK)
8256 /* Get the Height of the Metafile */
8257 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8258 if(dwSize != sizeof(pData->dwMetaFileHeight))
8260 hRes = CONVERT10_E_OLESTREAM_GET;
8264 if(hRes == S_OK)
8266 /* Get the Length of the Data */
8267 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8268 if(dwSize != sizeof(pData->dwDataLength))
8270 hRes = CONVERT10_E_OLESTREAM_GET;
8274 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8276 if(!bStrem1) /* if it is a second OLE stream data */
8278 pData->dwDataLength -= 8;
8279 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8280 if(dwSize != sizeof(pData->strUnknown))
8282 hRes = CONVERT10_E_OLESTREAM_GET;
8286 if(hRes == S_OK)
8288 if(pData->dwDataLength > 0)
8290 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8292 /* Get Data (ex. IStorage, Metafile, or BMP) */
8293 if(pData->pData)
8295 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8296 if(dwSize != pData->dwDataLength)
8298 hRes = CONVERT10_E_OLESTREAM_GET;
8301 else
8303 hRes = CONVERT10_E_OLESTREAM_GET;
8309 return hRes;
8312 /*************************************************************************
8313 * OLECONVERT_SaveOLE10 [Internal]
8315 * Saves the OLE10 STREAM From memory
8317 * PARAMS
8318 * pData [I] Data Structure for the OLESTREAM Data
8319 * pOleStream [I] The OLESTREAM to save
8321 * RETURNS
8322 * Success: S_OK
8323 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8325 * NOTES
8326 * This function is used by OleConvertIStorageToOLESTREAM only.
8329 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8331 DWORD dwSize;
8332 HRESULT hRes = S_OK;
8335 /* Set the OleID */
8336 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8337 if(dwSize != sizeof(pData->dwOleID))
8339 hRes = CONVERT10_E_OLESTREAM_PUT;
8342 if(hRes == S_OK)
8344 /* Set the TypeID */
8345 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8346 if(dwSize != sizeof(pData->dwTypeID))
8348 hRes = CONVERT10_E_OLESTREAM_PUT;
8352 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8354 /* Set the Length of the OleTypeName */
8355 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8356 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8358 hRes = CONVERT10_E_OLESTREAM_PUT;
8361 if(hRes == S_OK)
8363 if(pData->dwOleTypeNameLength > 0)
8365 /* Set the OleTypeName */
8366 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8367 if(dwSize != pData->dwOleTypeNameLength)
8369 hRes = CONVERT10_E_OLESTREAM_PUT;
8374 if(hRes == S_OK)
8376 /* Set the width of the Metafile */
8377 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8378 if(dwSize != sizeof(pData->dwMetaFileWidth))
8380 hRes = CONVERT10_E_OLESTREAM_PUT;
8384 if(hRes == S_OK)
8386 /* Set the height of the Metafile */
8387 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8388 if(dwSize != sizeof(pData->dwMetaFileHeight))
8390 hRes = CONVERT10_E_OLESTREAM_PUT;
8394 if(hRes == S_OK)
8396 /* Set the length of the Data */
8397 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8398 if(dwSize != sizeof(pData->dwDataLength))
8400 hRes = CONVERT10_E_OLESTREAM_PUT;
8404 if(hRes == S_OK)
8406 if(pData->dwDataLength > 0)
8408 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8409 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8410 if(dwSize != pData->dwDataLength)
8412 hRes = CONVERT10_E_OLESTREAM_PUT;
8417 return hRes;
8420 /*************************************************************************
8421 * OLECONVERT_GetOLE20FromOLE10[Internal]
8423 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8424 * opens it, and copies the content to the dest IStorage for
8425 * OleConvertOLESTREAMToIStorage
8428 * PARAMS
8429 * pDestStorage [I] The IStorage to copy the data to
8430 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8431 * nBufferLength [I] The size of the buffer
8433 * RETURNS
8434 * Nothing
8436 * NOTES
8440 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8442 HRESULT hRes;
8443 HANDLE hFile;
8444 IStorage *pTempStorage;
8445 DWORD dwNumOfBytesWritten;
8446 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8447 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8449 /* Create a temp File */
8450 GetTempPathW(MAX_PATH, wstrTempDir);
8451 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8452 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8454 if(hFile != INVALID_HANDLE_VALUE)
8456 /* Write IStorage Data to File */
8457 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8458 CloseHandle(hFile);
8460 /* Open and copy temp storage to the Dest Storage */
8461 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8462 if(hRes == S_OK)
8464 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8465 IStorage_Release(pTempStorage);
8467 DeleteFileW(wstrTempFile);
8472 /*************************************************************************
8473 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8475 * Saves the OLE10 STREAM From memory
8477 * PARAMS
8478 * pStorage [I] The Src IStorage to copy
8479 * pData [I] The Dest Memory to write to.
8481 * RETURNS
8482 * The size in bytes allocated for pData
8484 * NOTES
8485 * Memory allocated for pData must be freed by the caller
8487 * Used by OleConvertIStorageToOLESTREAM only.
8490 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8492 HANDLE hFile;
8493 HRESULT hRes;
8494 DWORD nDataLength = 0;
8495 IStorage *pTempStorage;
8496 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8497 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8499 *pData = NULL;
8501 /* Create temp Storage */
8502 GetTempPathW(MAX_PATH, wstrTempDir);
8503 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8504 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8506 if(hRes == S_OK)
8508 /* Copy Src Storage to the Temp Storage */
8509 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8510 IStorage_Release(pTempStorage);
8512 /* Open Temp Storage as a file and copy to memory */
8513 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8514 if(hFile != INVALID_HANDLE_VALUE)
8516 nDataLength = GetFileSize(hFile, NULL);
8517 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8518 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8519 CloseHandle(hFile);
8521 DeleteFileW(wstrTempFile);
8523 return nDataLength;
8526 /*************************************************************************
8527 * OLECONVERT_CreateOleStream [Internal]
8529 * Creates the "\001OLE" stream in the IStorage if necessary.
8531 * PARAMS
8532 * pStorage [I] Dest storage to create the stream in
8534 * RETURNS
8535 * Nothing
8537 * NOTES
8538 * This function is used by OleConvertOLESTREAMToIStorage only.
8540 * This stream is still unknown, MS Word seems to have extra data
8541 * but since the data is stored in the OLESTREAM there should be
8542 * no need to recreate the stream. If the stream is manually
8543 * deleted it will create it with this default data.
8546 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8548 HRESULT hRes;
8549 IStream *pStream;
8550 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8551 BYTE pOleStreamHeader [] =
8553 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8554 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8555 0x00, 0x00, 0x00, 0x00
8558 /* Create stream if not present */
8559 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8560 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8562 if(hRes == S_OK)
8564 /* Write default Data */
8565 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8566 IStream_Release(pStream);
8570 /* write a string to a stream, preceded by its length */
8571 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8573 HRESULT r;
8574 LPSTR str;
8575 DWORD len = 0;
8577 if( string )
8578 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8579 r = IStream_Write( stm, &len, sizeof(len), NULL);
8580 if( FAILED( r ) )
8581 return r;
8582 if(len == 0)
8583 return r;
8584 str = CoTaskMemAlloc( len );
8585 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8586 r = IStream_Write( stm, str, len, NULL);
8587 CoTaskMemFree( str );
8588 return r;
8591 /* read a string preceded by its length from a stream */
8592 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8594 HRESULT r;
8595 DWORD len, count = 0;
8596 LPSTR str;
8597 LPWSTR wstr;
8599 r = IStream_Read( stm, &len, sizeof(len), &count );
8600 if( FAILED( r ) )
8601 return r;
8602 if( count != sizeof(len) )
8603 return E_OUTOFMEMORY;
8605 TRACE("%d bytes\n",len);
8607 str = CoTaskMemAlloc( len );
8608 if( !str )
8609 return E_OUTOFMEMORY;
8610 count = 0;
8611 r = IStream_Read( stm, str, len, &count );
8612 if( FAILED( r ) )
8613 return r;
8614 if( count != len )
8616 CoTaskMemFree( str );
8617 return E_OUTOFMEMORY;
8620 TRACE("Read string %s\n",debugstr_an(str,len));
8622 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8623 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8624 if( wstr )
8625 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8626 CoTaskMemFree( str );
8628 *string = wstr;
8630 return r;
8634 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8635 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8637 IStream *pstm;
8638 HRESULT r = S_OK;
8639 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8641 static const BYTE unknown1[12] =
8642 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8643 0xFF, 0xFF, 0xFF, 0xFF};
8644 static const BYTE unknown2[16] =
8645 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8646 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8648 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8649 debugstr_w(lpszUserType), debugstr_w(szClipName),
8650 debugstr_w(szProgIDName));
8652 /* Create a CompObj stream */
8653 r = IStorage_CreateStream(pstg, szwStreamName,
8654 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8655 if( FAILED (r) )
8656 return r;
8658 /* Write CompObj Structure to stream */
8659 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8661 if( SUCCEEDED( r ) )
8662 r = WriteClassStm( pstm, clsid );
8664 if( SUCCEEDED( r ) )
8665 r = STREAM_WriteString( pstm, lpszUserType );
8666 if( SUCCEEDED( r ) )
8667 r = STREAM_WriteString( pstm, szClipName );
8668 if( SUCCEEDED( r ) )
8669 r = STREAM_WriteString( pstm, szProgIDName );
8670 if( SUCCEEDED( r ) )
8671 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8673 IStream_Release( pstm );
8675 return r;
8678 /***********************************************************************
8679 * WriteFmtUserTypeStg (OLE32.@)
8681 HRESULT WINAPI WriteFmtUserTypeStg(
8682 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8684 HRESULT r;
8685 WCHAR szwClipName[0x40];
8686 CLSID clsid = CLSID_NULL;
8687 LPWSTR wstrProgID = NULL;
8688 DWORD n;
8690 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8692 /* get the clipboard format name */
8693 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8694 szwClipName[n]=0;
8696 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8698 /* FIXME: There's room to save a CLSID and its ProgID, but
8699 the CLSID is not looked up in the registry and in all the
8700 tests I wrote it was CLSID_NULL. Where does it come from?
8703 /* get the real program ID. This may fail, but that's fine */
8704 ProgIDFromCLSID(&clsid, &wstrProgID);
8706 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8708 r = STORAGE_WriteCompObj( pstg, &clsid,
8709 lpszUserType, szwClipName, wstrProgID );
8711 CoTaskMemFree(wstrProgID);
8713 return r;
8717 /******************************************************************************
8718 * ReadFmtUserTypeStg [OLE32.@]
8720 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8722 HRESULT r;
8723 IStream *stm = 0;
8724 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8725 unsigned char unknown1[12];
8726 unsigned char unknown2[16];
8727 DWORD count;
8728 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8729 CLSID clsid;
8731 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8733 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8734 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8735 if( FAILED ( r ) )
8737 WARN("Failed to open stream r = %08x\n", r);
8738 return r;
8741 /* read the various parts of the structure */
8742 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8743 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8744 goto end;
8745 r = ReadClassStm( stm, &clsid );
8746 if( FAILED( r ) )
8747 goto end;
8749 r = STREAM_ReadString( stm, &szCLSIDName );
8750 if( FAILED( r ) )
8751 goto end;
8753 r = STREAM_ReadString( stm, &szOleTypeName );
8754 if( FAILED( r ) )
8755 goto end;
8757 r = STREAM_ReadString( stm, &szProgIDName );
8758 if( FAILED( r ) )
8759 goto end;
8761 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8762 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8763 goto end;
8765 /* ok, success... now we just need to store what we found */
8766 if( pcf )
8767 *pcf = RegisterClipboardFormatW( szOleTypeName );
8768 CoTaskMemFree( szOleTypeName );
8770 if( lplpszUserType )
8771 *lplpszUserType = szCLSIDName;
8772 CoTaskMemFree( szProgIDName );
8774 end:
8775 IStream_Release( stm );
8777 return r;
8781 /*************************************************************************
8782 * OLECONVERT_CreateCompObjStream [Internal]
8784 * Creates a "\001CompObj" is the destination IStorage if necessary.
8786 * PARAMS
8787 * pStorage [I] The dest IStorage to create the CompObj Stream
8788 * if necessary.
8789 * strOleTypeName [I] The ProgID
8791 * RETURNS
8792 * Success: S_OK
8793 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8795 * NOTES
8796 * This function is used by OleConvertOLESTREAMToIStorage only.
8798 * The stream data is stored in the OLESTREAM and there should be
8799 * no need to recreate the stream. If the stream is manually
8800 * deleted it will attempt to create it by querying the registry.
8804 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8806 IStream *pStream;
8807 HRESULT hStorageRes, hRes = S_OK;
8808 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8809 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8810 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8812 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8813 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8815 /* Initialize the CompObj structure */
8816 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8817 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8818 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8821 /* Create a CompObj stream if it doesn't exist */
8822 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8823 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8824 if(hStorageRes == S_OK)
8826 /* copy the OleTypeName to the compobj struct */
8827 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8828 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8830 /* copy the OleTypeName to the compobj struct */
8831 /* Note: in the test made, these were Identical */
8832 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8833 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8835 /* Get the CLSID */
8836 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8837 bufferW, OLESTREAM_MAX_STR_LEN );
8838 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8840 if(hRes == S_OK)
8842 HKEY hKey;
8843 LONG hErr;
8844 /* Get the CLSID Default Name from the Registry */
8845 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8846 if(hErr == ERROR_SUCCESS)
8848 char strTemp[OLESTREAM_MAX_STR_LEN];
8849 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8850 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8851 if(hErr == ERROR_SUCCESS)
8853 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8855 RegCloseKey(hKey);
8859 /* Write CompObj Structure to stream */
8860 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8862 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8864 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8865 if(IStorageCompObj.dwCLSIDNameLength > 0)
8867 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8869 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8870 if(IStorageCompObj.dwOleTypeNameLength > 0)
8872 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8874 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8875 if(IStorageCompObj.dwProgIDNameLength > 0)
8877 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8879 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8880 IStream_Release(pStream);
8882 return hRes;
8886 /*************************************************************************
8887 * OLECONVERT_CreateOlePresStream[Internal]
8889 * Creates the "\002OlePres000" Stream with the Metafile data
8891 * PARAMS
8892 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8893 * dwExtentX [I] Width of the Metafile
8894 * dwExtentY [I] Height of the Metafile
8895 * pData [I] Metafile data
8896 * dwDataLength [I] Size of the Metafile data
8898 * RETURNS
8899 * Success: S_OK
8900 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8902 * NOTES
8903 * This function is used by OleConvertOLESTREAMToIStorage only.
8906 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8908 HRESULT hRes;
8909 IStream *pStream;
8910 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8911 BYTE pOlePresStreamHeader [] =
8913 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8914 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8915 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8916 0x00, 0x00, 0x00, 0x00
8919 BYTE pOlePresStreamHeaderEmpty [] =
8921 0x00, 0x00, 0x00, 0x00,
8922 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8923 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8924 0x00, 0x00, 0x00, 0x00
8927 /* Create the OlePres000 Stream */
8928 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8929 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8931 if(hRes == S_OK)
8933 DWORD nHeaderSize;
8934 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8936 memset(&OlePres, 0, sizeof(OlePres));
8937 /* Do we have any metafile data to save */
8938 if(dwDataLength > 0)
8940 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8941 nHeaderSize = sizeof(pOlePresStreamHeader);
8943 else
8945 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8946 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8948 /* Set width and height of the metafile */
8949 OlePres.dwExtentX = dwExtentX;
8950 OlePres.dwExtentY = -dwExtentY;
8952 /* Set Data and Length */
8953 if(dwDataLength > sizeof(METAFILEPICT16))
8955 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8956 OlePres.pData = &(pData[8]);
8958 /* Save OlePres000 Data to Stream */
8959 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8960 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8961 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8962 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8963 if(OlePres.dwSize > 0)
8965 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8967 IStream_Release(pStream);
8971 /*************************************************************************
8972 * OLECONVERT_CreateOle10NativeStream [Internal]
8974 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8976 * PARAMS
8977 * pStorage [I] Dest storage to create the stream in
8978 * pData [I] Ole10 Native Data (ex. bmp)
8979 * dwDataLength [I] Size of the Ole10 Native Data
8981 * RETURNS
8982 * Nothing
8984 * NOTES
8985 * This function is used by OleConvertOLESTREAMToIStorage only.
8987 * Might need to verify the data and return appropriate error message
8990 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8992 HRESULT hRes;
8993 IStream *pStream;
8994 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8996 /* Create the Ole10Native Stream */
8997 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8998 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9000 if(hRes == S_OK)
9002 /* Write info to stream */
9003 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9004 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9005 IStream_Release(pStream);
9010 /*************************************************************************
9011 * OLECONVERT_GetOLE10ProgID [Internal]
9013 * Finds the ProgID (or OleTypeID) from the IStorage
9015 * PARAMS
9016 * pStorage [I] The Src IStorage to get the ProgID
9017 * strProgID [I] the ProgID string to get
9018 * dwSize [I] the size of the string
9020 * RETURNS
9021 * Success: S_OK
9022 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9024 * NOTES
9025 * This function is used by OleConvertIStorageToOLESTREAM only.
9029 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9031 HRESULT hRes;
9032 IStream *pStream;
9033 LARGE_INTEGER iSeekPos;
9034 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9035 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9037 /* Open the CompObj Stream */
9038 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9039 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9040 if(hRes == S_OK)
9043 /*Get the OleType from the CompObj Stream */
9044 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9045 iSeekPos.u.HighPart = 0;
9047 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9048 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9049 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9050 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9051 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9052 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9053 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9055 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9056 if(*dwSize > 0)
9058 IStream_Read(pStream, strProgID, *dwSize, NULL);
9060 IStream_Release(pStream);
9062 else
9064 STATSTG stat;
9065 LPOLESTR wstrProgID;
9067 /* Get the OleType from the registry */
9068 REFCLSID clsid = &(stat.clsid);
9069 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9070 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9071 if(hRes == S_OK)
9073 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9077 return hRes;
9080 /*************************************************************************
9081 * OLECONVERT_GetOle10PresData [Internal]
9083 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9085 * PARAMS
9086 * pStorage [I] Src IStroage
9087 * pOleStream [I] Dest OleStream Mem Struct
9089 * RETURNS
9090 * Nothing
9092 * NOTES
9093 * This function is used by OleConvertIStorageToOLESTREAM only.
9095 * Memory allocated for pData must be freed by the caller
9099 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9102 HRESULT hRes;
9103 IStream *pStream;
9104 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9106 /* Initialize Default data for OLESTREAM */
9107 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9108 pOleStreamData[0].dwTypeID = 2;
9109 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9110 pOleStreamData[1].dwTypeID = 0;
9111 pOleStreamData[0].dwMetaFileWidth = 0;
9112 pOleStreamData[0].dwMetaFileHeight = 0;
9113 pOleStreamData[0].pData = NULL;
9114 pOleStreamData[1].pData = NULL;
9116 /* Open Ole10Native Stream */
9117 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9118 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9119 if(hRes == S_OK)
9122 /* Read Size and Data */
9123 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9124 if(pOleStreamData->dwDataLength > 0)
9126 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9127 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9129 IStream_Release(pStream);
9135 /*************************************************************************
9136 * OLECONVERT_GetOle20PresData[Internal]
9138 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9140 * PARAMS
9141 * pStorage [I] Src IStroage
9142 * pOleStreamData [I] Dest OleStream Mem Struct
9144 * RETURNS
9145 * Nothing
9147 * NOTES
9148 * This function is used by OleConvertIStorageToOLESTREAM only.
9150 * Memory allocated for pData must be freed by the caller
9152 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9154 HRESULT hRes;
9155 IStream *pStream;
9156 OLECONVERT_ISTORAGE_OLEPRES olePress;
9157 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9159 /* Initialize Default data for OLESTREAM */
9160 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9161 pOleStreamData[0].dwTypeID = 2;
9162 pOleStreamData[0].dwMetaFileWidth = 0;
9163 pOleStreamData[0].dwMetaFileHeight = 0;
9164 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9165 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9166 pOleStreamData[1].dwTypeID = 0;
9167 pOleStreamData[1].dwOleTypeNameLength = 0;
9168 pOleStreamData[1].strOleTypeName[0] = 0;
9169 pOleStreamData[1].dwMetaFileWidth = 0;
9170 pOleStreamData[1].dwMetaFileHeight = 0;
9171 pOleStreamData[1].pData = NULL;
9172 pOleStreamData[1].dwDataLength = 0;
9175 /* Open OlePress000 stream */
9176 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9177 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9178 if(hRes == S_OK)
9180 LARGE_INTEGER iSeekPos;
9181 METAFILEPICT16 MetaFilePict;
9182 static const char strMetafilePictName[] = "METAFILEPICT";
9184 /* Set the TypeID for a Metafile */
9185 pOleStreamData[1].dwTypeID = 5;
9187 /* Set the OleTypeName to Metafile */
9188 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9189 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9191 iSeekPos.u.HighPart = 0;
9192 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9194 /* Get Presentation Data */
9195 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9196 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9197 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9198 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9200 /*Set width and Height */
9201 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9202 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9203 if(olePress.dwSize > 0)
9205 /* Set Length */
9206 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9208 /* Set MetaFilePict struct */
9209 MetaFilePict.mm = 8;
9210 MetaFilePict.xExt = olePress.dwExtentX;
9211 MetaFilePict.yExt = olePress.dwExtentY;
9212 MetaFilePict.hMF = 0;
9214 /* Get Metafile Data */
9215 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9216 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9217 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9219 IStream_Release(pStream);
9223 /*************************************************************************
9224 * OleConvertOLESTREAMToIStorage [OLE32.@]
9226 * Read info on MSDN
9228 * TODO
9229 * DVTARGETDEVICE parameter is not handled
9230 * Still unsure of some mem fields for OLE 10 Stream
9231 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9232 * and "\001OLE" streams
9235 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9236 LPOLESTREAM pOleStream,
9237 LPSTORAGE pstg,
9238 const DVTARGETDEVICE* ptd)
9240 int i;
9241 HRESULT hRes=S_OK;
9242 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9244 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9246 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9248 if(ptd != NULL)
9250 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9253 if(pstg == NULL || pOleStream == NULL)
9255 hRes = E_INVALIDARG;
9258 if(hRes == S_OK)
9260 /* Load the OLESTREAM to Memory */
9261 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9264 if(hRes == S_OK)
9266 /* Load the OLESTREAM to Memory (part 2)*/
9267 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9270 if(hRes == S_OK)
9273 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9275 /* Do we have the IStorage Data in the OLESTREAM */
9276 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9278 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9279 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9281 else
9283 /* It must be an original OLE 1.0 source */
9284 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9287 else
9289 /* It must be an original OLE 1.0 source */
9290 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9293 /* Create CompObj Stream if necessary */
9294 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9295 if(hRes == S_OK)
9297 /*Create the Ole Stream if necessary */
9298 OLECONVERT_CreateOleStream(pstg);
9303 /* Free allocated memory */
9304 for(i=0; i < 2; i++)
9306 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9307 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9308 pOleStreamData[i].pstrOleObjFileName = NULL;
9310 return hRes;
9313 /*************************************************************************
9314 * OleConvertIStorageToOLESTREAM [OLE32.@]
9316 * Read info on MSDN
9318 * Read info on MSDN
9320 * TODO
9321 * Still unsure of some mem fields for OLE 10 Stream
9322 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9323 * and "\001OLE" streams.
9326 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9327 LPSTORAGE pstg,
9328 LPOLESTREAM pOleStream)
9330 int i;
9331 HRESULT hRes = S_OK;
9332 IStream *pStream;
9333 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9334 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9336 TRACE("%p %p\n", pstg, pOleStream);
9338 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9340 if(pstg == NULL || pOleStream == NULL)
9342 hRes = E_INVALIDARG;
9344 if(hRes == S_OK)
9346 /* Get the ProgID */
9347 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9348 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9350 if(hRes == S_OK)
9352 /* Was it originally Ole10 */
9353 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9354 if(hRes == S_OK)
9356 IStream_Release(pStream);
9357 /* Get Presentation Data for Ole10Native */
9358 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9360 else
9362 /* Get Presentation Data (OLE20) */
9363 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9366 /* Save OLESTREAM */
9367 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9368 if(hRes == S_OK)
9370 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9375 /* Free allocated memory */
9376 for(i=0; i < 2; i++)
9378 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9381 return hRes;
9384 enum stream_1ole_flags {
9385 OleStream_LinkedObject = 0x00000001,
9386 OleStream_Convert = 0x00000100
9389 /***********************************************************************
9390 * GetConvertStg (OLE32.@)
9392 HRESULT WINAPI GetConvertStg(IStorage *stg)
9394 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9395 static const DWORD version_magic = 0x02000001;
9396 DWORD header[2];
9397 IStream *stream;
9398 HRESULT hr;
9399 ULONG len;
9401 TRACE("%p\n", stg);
9403 if (!stg) return E_INVALIDARG;
9405 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
9406 if (FAILED(hr)) return hr;
9408 len = 0;
9409 hr = IStream_Read(stream, header, sizeof(header), &len);
9410 IStream_Release(stream);
9411 if (FAILED(hr)) return hr;
9413 if (header[0] != version_magic)
9415 ERR("got wrong version magic for \1Ole stream, 0x%08x\n", header[0]);
9416 return E_FAIL;
9419 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
9422 /******************************************************************************
9423 * StgIsStorageFile [OLE32.@]
9424 * Verify if the file contains a storage object
9426 * PARAMS
9427 * fn [ I] Filename
9429 * RETURNS
9430 * S_OK if file has magic bytes as a storage object
9431 * S_FALSE if file is not storage
9433 HRESULT WINAPI
9434 StgIsStorageFile(LPCOLESTR fn)
9436 HANDLE hf;
9437 BYTE magic[8];
9438 DWORD bytes_read;
9440 TRACE("%s\n", debugstr_w(fn));
9441 hf = CreateFileW(fn, GENERIC_READ,
9442 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9443 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9445 if (hf == INVALID_HANDLE_VALUE)
9446 return STG_E_FILENOTFOUND;
9448 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9450 WARN(" unable to read file\n");
9451 CloseHandle(hf);
9452 return S_FALSE;
9455 CloseHandle(hf);
9457 if (bytes_read != 8) {
9458 TRACE(" too short\n");
9459 return S_FALSE;
9462 if (!memcmp(magic,STORAGE_magic,8)) {
9463 TRACE(" -> YES\n");
9464 return S_OK;
9467 TRACE(" -> Invalid header.\n");
9468 return S_FALSE;
9471 /***********************************************************************
9472 * WriteClassStm (OLE32.@)
9474 * Writes a CLSID to a stream.
9476 * PARAMS
9477 * pStm [I] Stream to write to.
9478 * rclsid [I] CLSID to write.
9480 * RETURNS
9481 * Success: S_OK.
9482 * Failure: HRESULT code.
9484 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9486 TRACE("(%p,%p)\n",pStm,rclsid);
9488 if (!pStm || !rclsid)
9489 return E_INVALIDARG;
9491 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9494 /***********************************************************************
9495 * ReadClassStm (OLE32.@)
9497 * Reads a CLSID from a stream.
9499 * PARAMS
9500 * pStm [I] Stream to read from.
9501 * rclsid [O] CLSID to read.
9503 * RETURNS
9504 * Success: S_OK.
9505 * Failure: HRESULT code.
9507 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9509 ULONG nbByte;
9510 HRESULT res;
9512 TRACE("(%p,%p)\n",pStm,pclsid);
9514 if (!pStm || !pclsid)
9515 return E_INVALIDARG;
9517 /* clear the output args */
9518 *pclsid = CLSID_NULL;
9520 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9522 if (FAILED(res))
9523 return res;
9525 if (nbByte != sizeof(CLSID))
9526 return STG_E_READFAULT;
9527 else
9528 return S_OK;