gameux: Add interface registration routines.
[wine/hramrach.git] / dlls / ole32 / storage32.c
blob185b8b0d764bffdfc32bdfc595e2c196dbafb675
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 const char rootEntryName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry;
86 StorageBaseImpl *parentStorage;
88 typedef struct StorageInternalImpl StorageInternalImpl;
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
92 DWORD openFlags, DirRef storageDirEntry);
93 static void StorageImpl_Destroy(StorageBaseImpl* iface);
94 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
95 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
96 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
97 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
98 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
99 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
100 static void StorageImpl_SaveFileHeader(StorageImpl* This);
102 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
103 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
104 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
105 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
106 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
108 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
109 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
110 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
112 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
113 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
114 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
115 ULONG blockIndex, ULONG offset, DWORD value);
116 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
117 ULONG blockIndex, ULONG offset, DWORD* value);
119 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
120 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
122 typedef struct TransactedDirEntry
124 /* If applicable, a reference to the original DirEntry in the transacted
125 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
126 DirRef transactedParentEntry;
128 /* True if this entry is being used. */
129 int inuse;
131 /* True if data is up to date. */
132 int read;
134 /* True if this entry has been modified. */
135 int dirty;
137 /* True if this entry's stream has been modified. */
138 int stream_dirty;
140 /* True if this entry has been deleted in the transacted storage, but the
141 * delete has not yet been committed. */
142 int deleted;
144 /* If this entry's stream has been modified, a reference to where the stream
145 * is stored in the snapshot file. */
146 DirRef stream_entry;
148 /* This directory entry's data, including any changes that have been made. */
149 DirEntry data;
151 /* A reference to the parent of this node. This is only valid while we are
152 * committing changes. */
153 DirRef parent;
155 /* A reference to a newly-created entry in the transacted parent. This is
156 * always equal to transactedParentEntry except when committing changes. */
157 DirRef newTransactedParentEntry;
158 } TransactedDirEntry;
160 /****************************************************************************
161 * Transacted storage object.
163 typedef struct TransactedSnapshotImpl
165 struct StorageBaseImpl base;
168 * Modified streams are temporarily saved to the scratch file.
170 StorageBaseImpl *scratch;
172 /* The directory structure is kept here, so that we can track how these
173 * entries relate to those in the parent storage. */
174 TransactedDirEntry *entries;
175 ULONG entries_size;
176 ULONG firstFreeEntry;
179 * Changes are committed to the transacted parent.
181 StorageBaseImpl *transactedParent;
182 } TransactedSnapshotImpl;
184 /* Generic function to create a transacted wrapper for a direct storage object. */
185 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
187 /* OLESTREAM memory structure to use for Get and Put Routines */
188 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
189 typedef struct
191 DWORD dwOleID;
192 DWORD dwTypeID;
193 DWORD dwOleTypeNameLength;
194 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
195 CHAR *pstrOleObjFileName;
196 DWORD dwOleObjFileNameLength;
197 DWORD dwMetaFileWidth;
198 DWORD dwMetaFileHeight;
199 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
200 DWORD dwDataLength;
201 BYTE *pData;
202 }OLECONVERT_OLESTREAM_DATA;
204 /* CompObj Stream structure */
205 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
206 typedef struct
208 BYTE byUnknown1[12];
209 CLSID clsid;
210 DWORD dwCLSIDNameLength;
211 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
212 DWORD dwOleTypeNameLength;
213 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
214 DWORD dwProgIDNameLength;
215 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
216 BYTE byUnknown2[16];
217 }OLECONVERT_ISTORAGE_COMPOBJ;
220 /* Ole Presentation Stream structure */
221 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
222 typedef struct
224 BYTE byUnknown1[28];
225 DWORD dwExtentX;
226 DWORD dwExtentY;
227 DWORD dwSize;
228 BYTE *pData;
229 }OLECONVERT_ISTORAGE_OLEPRES;
233 /***********************************************************************
234 * Forward declaration of internal functions used by the method DestroyElement
236 static HRESULT deleteStorageContents(
237 StorageBaseImpl *parentStorage,
238 DirRef indexToDelete,
239 DirEntry entryDataToDelete);
241 static HRESULT deleteStreamContents(
242 StorageBaseImpl *parentStorage,
243 DirRef indexToDelete,
244 DirEntry entryDataToDelete);
246 static HRESULT removeFromTree(
247 StorageBaseImpl *This,
248 DirRef parentStorageIndex,
249 DirRef deletedIndex);
251 /***********************************************************************
252 * Declaration of the functions used to manipulate DirEntry
255 static HRESULT insertIntoTree(
256 StorageBaseImpl *This,
257 DirRef parentStorageIndex,
258 DirRef newEntryIndex);
260 static LONG entryNameCmp(
261 const OLECHAR *name1,
262 const OLECHAR *name2);
264 static DirRef findElement(
265 StorageBaseImpl *storage,
266 DirRef storageEntry,
267 const OLECHAR *name,
268 DirEntry *data);
270 static HRESULT findTreeParent(
271 StorageBaseImpl *storage,
272 DirRef storageEntry,
273 const OLECHAR *childName,
274 DirEntry *parentData,
275 DirRef *parentEntry,
276 ULONG *relation);
278 /***********************************************************************
279 * Declaration of miscellaneous functions...
281 static HRESULT validateSTGM(DWORD stgmValue);
283 static DWORD GetShareModeFromSTGM(DWORD stgm);
284 static DWORD GetAccessModeFromSTGM(DWORD stgm);
285 static DWORD GetCreationModeFromSTGM(DWORD stgm);
287 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
290 /****************************************************************************
291 * IEnumSTATSTGImpl definitions.
293 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
294 * This class allows iterating through the content of a storage and to find
295 * specific items inside it.
297 struct IEnumSTATSTGImpl
299 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
300 * since we want to cast this in an IEnumSTATSTG pointer */
302 LONG ref; /* Reference count */
303 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
304 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
306 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
310 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
311 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
313 /************************************************************************
314 ** Block Functions
317 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
319 return (index+1) * This->bigBlockSize;
322 /************************************************************************
323 ** Storage32BaseImpl implementation
325 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
326 ULARGE_INTEGER offset,
327 void* buffer,
328 ULONG size,
329 ULONG* bytesRead)
331 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
334 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
335 ULARGE_INTEGER offset,
336 const void* buffer,
337 const ULONG size,
338 ULONG* bytesWritten)
340 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
343 /************************************************************************
344 * Storage32BaseImpl_QueryInterface (IUnknown)
346 * This method implements the common QueryInterface for all IStorage32
347 * implementations contained in this file.
349 * See Windows documentation for more details on IUnknown methods.
351 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
352 IStorage* iface,
353 REFIID riid,
354 void** ppvObject)
356 StorageBaseImpl *This = (StorageBaseImpl *)iface;
358 if ( (This==0) || (ppvObject==0) )
359 return E_INVALIDARG;
361 *ppvObject = 0;
363 if (IsEqualGUID(&IID_IUnknown, riid) ||
364 IsEqualGUID(&IID_IStorage, riid))
366 *ppvObject = This;
368 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
370 *ppvObject = &This->pssVtbl;
373 if ((*ppvObject)==0)
374 return E_NOINTERFACE;
376 IStorage_AddRef(iface);
378 return S_OK;
381 /************************************************************************
382 * Storage32BaseImpl_AddRef (IUnknown)
384 * This method implements the common AddRef for all IStorage32
385 * implementations contained in this file.
387 * See Windows documentation for more details on IUnknown methods.
389 static ULONG WINAPI StorageBaseImpl_AddRef(
390 IStorage* iface)
392 StorageBaseImpl *This = (StorageBaseImpl *)iface;
393 ULONG ref = InterlockedIncrement(&This->ref);
395 TRACE("(%p) AddRef to %d\n", This, ref);
397 return ref;
400 /************************************************************************
401 * Storage32BaseImpl_Release (IUnknown)
403 * This method implements the common Release for all IStorage32
404 * implementations contained in this file.
406 * See Windows documentation for more details on IUnknown methods.
408 static ULONG WINAPI StorageBaseImpl_Release(
409 IStorage* iface)
411 StorageBaseImpl *This = (StorageBaseImpl *)iface;
413 ULONG ref = InterlockedDecrement(&This->ref);
415 TRACE("(%p) ReleaseRef to %d\n", This, ref);
417 if (ref == 0)
420 * Since we are using a system of base-classes, we want to call the
421 * destructor of the appropriate derived class. To do this, we are
422 * using virtual functions to implement the destructor.
424 StorageBaseImpl_Destroy(This);
427 return ref;
430 /************************************************************************
431 * Storage32BaseImpl_OpenStream (IStorage)
433 * This method will open the specified stream object from the current storage.
435 * See Windows documentation for more details on IStorage methods.
437 static HRESULT WINAPI StorageBaseImpl_OpenStream(
438 IStorage* iface,
439 const OLECHAR* pwcsName, /* [string][in] */
440 void* reserved1, /* [unique][in] */
441 DWORD grfMode, /* [in] */
442 DWORD reserved2, /* [in] */
443 IStream** ppstm) /* [out] */
445 StorageBaseImpl *This = (StorageBaseImpl *)iface;
446 StgStreamImpl* newStream;
447 DirEntry currentEntry;
448 DirRef streamEntryRef;
449 HRESULT res = STG_E_UNKNOWN;
451 TRACE("(%p, %s, %p, %x, %d, %p)\n",
452 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
454 if ( (pwcsName==NULL) || (ppstm==0) )
456 res = E_INVALIDARG;
457 goto end;
460 *ppstm = NULL;
462 if ( FAILED( validateSTGM(grfMode) ) ||
463 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
465 res = STG_E_INVALIDFLAG;
466 goto end;
470 * As documented.
472 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
474 res = STG_E_INVALIDFUNCTION;
475 goto end;
478 if (This->reverted)
480 res = STG_E_REVERTED;
481 goto end;
485 * Check that we're compatible with the parent's storage mode, but
486 * only if we are not in transacted mode
488 if(!(This->openFlags & STGM_TRANSACTED)) {
489 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
491 res = STG_E_INVALIDFLAG;
492 goto end;
497 * Search for the element with the given name
499 streamEntryRef = findElement(
500 This,
501 This->storageDirEntry,
502 pwcsName,
503 &currentEntry);
506 * If it was found, construct the stream object and return a pointer to it.
508 if ( (streamEntryRef!=DIRENTRY_NULL) &&
509 (currentEntry.stgType==STGTY_STREAM) )
511 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
513 /* A single stream cannot be opened a second time. */
514 res = STG_E_ACCESSDENIED;
515 goto end;
518 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
520 if (newStream!=0)
522 newStream->grfMode = grfMode;
523 *ppstm = (IStream*)newStream;
525 IStream_AddRef(*ppstm);
527 res = S_OK;
528 goto end;
531 res = E_OUTOFMEMORY;
532 goto end;
535 res = STG_E_FILENOTFOUND;
537 end:
538 if (res == S_OK)
539 TRACE("<-- IStream %p\n", *ppstm);
540 TRACE("<-- %08x\n", res);
541 return res;
544 /************************************************************************
545 * Storage32BaseImpl_OpenStorage (IStorage)
547 * This method will open a new storage object from the current storage.
549 * See Windows documentation for more details on IStorage methods.
551 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
552 IStorage* iface,
553 const OLECHAR* pwcsName, /* [string][unique][in] */
554 IStorage* pstgPriority, /* [unique][in] */
555 DWORD grfMode, /* [in] */
556 SNB snbExclude, /* [unique][in] */
557 DWORD reserved, /* [in] */
558 IStorage** ppstg) /* [out] */
560 StorageBaseImpl *This = (StorageBaseImpl *)iface;
561 StorageInternalImpl* newStorage;
562 StorageBaseImpl* newTransactedStorage;
563 DirEntry currentEntry;
564 DirRef storageEntryRef;
565 HRESULT res = STG_E_UNKNOWN;
567 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
568 iface, debugstr_w(pwcsName), pstgPriority,
569 grfMode, snbExclude, reserved, ppstg);
571 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
573 res = E_INVALIDARG;
574 goto end;
577 if (This->openFlags & STGM_SIMPLE)
579 res = STG_E_INVALIDFUNCTION;
580 goto end;
583 /* as documented */
584 if (snbExclude != NULL)
586 res = STG_E_INVALIDPARAMETER;
587 goto end;
590 if ( FAILED( validateSTGM(grfMode) ))
592 res = STG_E_INVALIDFLAG;
593 goto end;
597 * As documented.
599 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
600 (grfMode & STGM_DELETEONRELEASE) ||
601 (grfMode & STGM_PRIORITY) )
603 res = STG_E_INVALIDFUNCTION;
604 goto end;
607 if (This->reverted)
608 return STG_E_REVERTED;
611 * Check that we're compatible with the parent's storage mode,
612 * but only if we are not transacted
614 if(!(This->openFlags & STGM_TRANSACTED)) {
615 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
617 res = STG_E_ACCESSDENIED;
618 goto end;
622 *ppstg = NULL;
624 storageEntryRef = findElement(
625 This,
626 This->storageDirEntry,
627 pwcsName,
628 &currentEntry);
630 if ( (storageEntryRef!=DIRENTRY_NULL) &&
631 (currentEntry.stgType==STGTY_STORAGE) )
633 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
635 /* A single storage cannot be opened a second time. */
636 res = STG_E_ACCESSDENIED;
637 goto end;
640 newStorage = StorageInternalImpl_Construct(
641 This,
642 grfMode,
643 storageEntryRef);
645 if (newStorage != 0)
647 if (grfMode & STGM_TRANSACTED)
649 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
651 if (FAILED(res))
653 HeapFree(GetProcessHeap(), 0, newStorage);
654 goto end;
657 *ppstg = (IStorage*)newTransactedStorage;
659 else
661 *ppstg = (IStorage*)newStorage;
664 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
666 res = S_OK;
667 goto end;
670 res = STG_E_INSUFFICIENTMEMORY;
671 goto end;
674 res = STG_E_FILENOTFOUND;
676 end:
677 TRACE("<-- %08x\n", res);
678 return res;
681 /************************************************************************
682 * Storage32BaseImpl_EnumElements (IStorage)
684 * This method will create an enumerator object that can be used to
685 * retrieve information about all the elements in the storage object.
687 * See Windows documentation for more details on IStorage methods.
689 static HRESULT WINAPI StorageBaseImpl_EnumElements(
690 IStorage* iface,
691 DWORD reserved1, /* [in] */
692 void* reserved2, /* [size_is][unique][in] */
693 DWORD reserved3, /* [in] */
694 IEnumSTATSTG** ppenum) /* [out] */
696 StorageBaseImpl *This = (StorageBaseImpl *)iface;
697 IEnumSTATSTGImpl* newEnum;
699 TRACE("(%p, %d, %p, %d, %p)\n",
700 iface, reserved1, reserved2, reserved3, ppenum);
702 if ( (This==0) || (ppenum==0))
703 return E_INVALIDARG;
705 if (This->reverted)
706 return STG_E_REVERTED;
708 newEnum = IEnumSTATSTGImpl_Construct(
709 This,
710 This->storageDirEntry);
712 if (newEnum!=0)
714 *ppenum = (IEnumSTATSTG*)newEnum;
716 IEnumSTATSTG_AddRef(*ppenum);
718 return S_OK;
721 return E_OUTOFMEMORY;
724 /************************************************************************
725 * Storage32BaseImpl_Stat (IStorage)
727 * This method will retrieve information about this storage object.
729 * See Windows documentation for more details on IStorage methods.
731 static HRESULT WINAPI StorageBaseImpl_Stat(
732 IStorage* iface,
733 STATSTG* pstatstg, /* [out] */
734 DWORD grfStatFlag) /* [in] */
736 StorageBaseImpl *This = (StorageBaseImpl *)iface;
737 DirEntry currentEntry;
738 HRESULT res = STG_E_UNKNOWN;
740 TRACE("(%p, %p, %x)\n",
741 iface, pstatstg, grfStatFlag);
743 if ( (This==0) || (pstatstg==0))
745 res = E_INVALIDARG;
746 goto end;
749 if (This->reverted)
751 res = STG_E_REVERTED;
752 goto end;
755 res = StorageBaseImpl_ReadDirEntry(
756 This,
757 This->storageDirEntry,
758 &currentEntry);
760 if (SUCCEEDED(res))
762 StorageUtl_CopyDirEntryToSTATSTG(
763 This,
764 pstatstg,
765 &currentEntry,
766 grfStatFlag);
768 pstatstg->grfMode = This->openFlags;
769 pstatstg->grfStateBits = This->stateBits;
772 end:
773 if (res == S_OK)
775 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);
777 TRACE("<-- %08x\n", res);
778 return res;
781 /************************************************************************
782 * Storage32BaseImpl_RenameElement (IStorage)
784 * This method will rename the specified element.
786 * See Windows documentation for more details on IStorage methods.
788 static HRESULT WINAPI StorageBaseImpl_RenameElement(
789 IStorage* iface,
790 const OLECHAR* pwcsOldName, /* [in] */
791 const OLECHAR* pwcsNewName) /* [in] */
793 StorageBaseImpl *This = (StorageBaseImpl *)iface;
794 DirEntry currentEntry;
795 DirRef currentEntryRef;
797 TRACE("(%p, %s, %s)\n",
798 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
800 if (This->reverted)
801 return STG_E_REVERTED;
803 currentEntryRef = findElement(This,
804 This->storageDirEntry,
805 pwcsNewName,
806 &currentEntry);
808 if (currentEntryRef != DIRENTRY_NULL)
811 * There is already an element with the new name
813 return STG_E_FILEALREADYEXISTS;
817 * Search for the old element name
819 currentEntryRef = findElement(This,
820 This->storageDirEntry,
821 pwcsOldName,
822 &currentEntry);
824 if (currentEntryRef != DIRENTRY_NULL)
826 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
827 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
829 WARN("Element is already open; cannot rename.\n");
830 return STG_E_ACCESSDENIED;
833 /* Remove the element from its current position in the tree */
834 removeFromTree(This, This->storageDirEntry,
835 currentEntryRef);
837 /* Change the name of the element */
838 strcpyW(currentEntry.name, pwcsNewName);
840 /* Delete any sibling links */
841 currentEntry.leftChild = DIRENTRY_NULL;
842 currentEntry.rightChild = DIRENTRY_NULL;
844 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
845 &currentEntry);
847 /* Insert the element in a new position in the tree */
848 insertIntoTree(This, This->storageDirEntry,
849 currentEntryRef);
851 else
854 * There is no element with the old name
856 return STG_E_FILENOTFOUND;
859 return S_OK;
862 /************************************************************************
863 * Storage32BaseImpl_CreateStream (IStorage)
865 * This method will create a stream object within this storage
867 * See Windows documentation for more details on IStorage methods.
869 static HRESULT WINAPI StorageBaseImpl_CreateStream(
870 IStorage* iface,
871 const OLECHAR* pwcsName, /* [string][in] */
872 DWORD grfMode, /* [in] */
873 DWORD reserved1, /* [in] */
874 DWORD reserved2, /* [in] */
875 IStream** ppstm) /* [out] */
877 StorageBaseImpl *This = (StorageBaseImpl *)iface;
878 StgStreamImpl* newStream;
879 DirEntry currentEntry, newStreamEntry;
880 DirRef currentEntryRef, newStreamEntryRef;
881 HRESULT hr;
883 TRACE("(%p, %s, %x, %d, %d, %p)\n",
884 iface, debugstr_w(pwcsName), grfMode,
885 reserved1, reserved2, ppstm);
887 if (ppstm == 0)
888 return STG_E_INVALIDPOINTER;
890 if (pwcsName == 0)
891 return STG_E_INVALIDNAME;
893 if (reserved1 || reserved2)
894 return STG_E_INVALIDPARAMETER;
896 if ( FAILED( validateSTGM(grfMode) ))
897 return STG_E_INVALIDFLAG;
899 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
900 return STG_E_INVALIDFLAG;
902 if (This->reverted)
903 return STG_E_REVERTED;
906 * As documented.
908 if ((grfMode & STGM_DELETEONRELEASE) ||
909 (grfMode & STGM_TRANSACTED))
910 return STG_E_INVALIDFUNCTION;
913 * Don't worry about permissions in transacted mode, as we can always write
914 * changes; we just can't always commit them.
916 if(!(This->openFlags & STGM_TRANSACTED)) {
917 /* Can't create a stream on read-only storage */
918 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
919 return STG_E_ACCESSDENIED;
921 /* Can't create a stream with greater access than the parent. */
922 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
923 return STG_E_ACCESSDENIED;
926 if(This->openFlags & STGM_SIMPLE)
927 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
929 *ppstm = 0;
931 currentEntryRef = findElement(This,
932 This->storageDirEntry,
933 pwcsName,
934 &currentEntry);
936 if (currentEntryRef != DIRENTRY_NULL)
939 * An element with this name already exists
941 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
943 IStorage_DestroyElement(iface, pwcsName);
945 else
946 return STG_E_FILEALREADYEXISTS;
950 * memset the empty entry
952 memset(&newStreamEntry, 0, sizeof(DirEntry));
954 newStreamEntry.sizeOfNameString =
955 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
957 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
958 return STG_E_INVALIDNAME;
960 strcpyW(newStreamEntry.name, pwcsName);
962 newStreamEntry.stgType = STGTY_STREAM;
963 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
964 newStreamEntry.size.u.LowPart = 0;
965 newStreamEntry.size.u.HighPart = 0;
967 newStreamEntry.leftChild = DIRENTRY_NULL;
968 newStreamEntry.rightChild = DIRENTRY_NULL;
969 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
971 /* call CoFileTime to get the current time
972 newStreamEntry.ctime
973 newStreamEntry.mtime
976 /* newStreamEntry.clsid */
979 * Create an entry with the new data
981 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
982 if (FAILED(hr))
983 return hr;
986 * Insert the new entry in the parent storage's tree.
988 hr = insertIntoTree(
989 This,
990 This->storageDirEntry,
991 newStreamEntryRef);
992 if (FAILED(hr))
994 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
995 return hr;
999 * Open the stream to return it.
1001 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1003 if (newStream != 0)
1005 *ppstm = (IStream*)newStream;
1007 IStream_AddRef(*ppstm);
1009 else
1011 return STG_E_INSUFFICIENTMEMORY;
1014 return S_OK;
1017 /************************************************************************
1018 * Storage32BaseImpl_SetClass (IStorage)
1020 * This method will write the specified CLSID in the directory entry of this
1021 * storage.
1023 * See Windows documentation for more details on IStorage methods.
1025 static HRESULT WINAPI StorageBaseImpl_SetClass(
1026 IStorage* iface,
1027 REFCLSID clsid) /* [in] */
1029 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1030 HRESULT hRes;
1031 DirEntry currentEntry;
1033 TRACE("(%p, %p)\n", iface, clsid);
1035 if (This->reverted)
1036 return STG_E_REVERTED;
1038 hRes = StorageBaseImpl_ReadDirEntry(This,
1039 This->storageDirEntry,
1040 &currentEntry);
1041 if (SUCCEEDED(hRes))
1043 currentEntry.clsid = *clsid;
1045 hRes = StorageBaseImpl_WriteDirEntry(This,
1046 This->storageDirEntry,
1047 &currentEntry);
1050 return hRes;
1053 /************************************************************************
1054 ** Storage32Impl implementation
1057 /************************************************************************
1058 * Storage32BaseImpl_CreateStorage (IStorage)
1060 * This method will create the storage object within the provided storage.
1062 * See Windows documentation for more details on IStorage methods.
1064 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1065 IStorage* iface,
1066 const OLECHAR *pwcsName, /* [string][in] */
1067 DWORD grfMode, /* [in] */
1068 DWORD reserved1, /* [in] */
1069 DWORD reserved2, /* [in] */
1070 IStorage **ppstg) /* [out] */
1072 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1074 DirEntry currentEntry;
1075 DirEntry newEntry;
1076 DirRef currentEntryRef;
1077 DirRef newEntryRef;
1078 HRESULT hr;
1080 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1081 iface, debugstr_w(pwcsName), grfMode,
1082 reserved1, reserved2, ppstg);
1084 if (ppstg == 0)
1085 return STG_E_INVALIDPOINTER;
1087 if (This->openFlags & STGM_SIMPLE)
1089 return STG_E_INVALIDFUNCTION;
1092 if (pwcsName == 0)
1093 return STG_E_INVALIDNAME;
1095 *ppstg = NULL;
1097 if ( FAILED( validateSTGM(grfMode) ) ||
1098 (grfMode & STGM_DELETEONRELEASE) )
1100 WARN("bad grfMode: 0x%x\n", grfMode);
1101 return STG_E_INVALIDFLAG;
1104 if (This->reverted)
1105 return STG_E_REVERTED;
1108 * Check that we're compatible with the parent's storage mode
1110 if ( !(This->openFlags & STGM_TRANSACTED) &&
1111 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1113 WARN("access denied\n");
1114 return STG_E_ACCESSDENIED;
1117 currentEntryRef = findElement(This,
1118 This->storageDirEntry,
1119 pwcsName,
1120 &currentEntry);
1122 if (currentEntryRef != DIRENTRY_NULL)
1125 * An element with this name already exists
1127 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1128 ((This->openFlags & STGM_TRANSACTED) ||
1129 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1131 hr = IStorage_DestroyElement(iface, pwcsName);
1132 if (FAILED(hr))
1133 return hr;
1135 else
1137 WARN("file already exists\n");
1138 return STG_E_FILEALREADYEXISTS;
1141 else if (!(This->openFlags & STGM_TRANSACTED) &&
1142 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1144 WARN("read-only storage\n");
1145 return STG_E_ACCESSDENIED;
1148 memset(&newEntry, 0, sizeof(DirEntry));
1150 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1152 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1154 FIXME("name too long\n");
1155 return STG_E_INVALIDNAME;
1158 strcpyW(newEntry.name, pwcsName);
1160 newEntry.stgType = STGTY_STORAGE;
1161 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1162 newEntry.size.u.LowPart = 0;
1163 newEntry.size.u.HighPart = 0;
1165 newEntry.leftChild = DIRENTRY_NULL;
1166 newEntry.rightChild = DIRENTRY_NULL;
1167 newEntry.dirRootEntry = DIRENTRY_NULL;
1169 /* call CoFileTime to get the current time
1170 newEntry.ctime
1171 newEntry.mtime
1174 /* newEntry.clsid */
1177 * Create a new directory entry for the storage
1179 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1180 if (FAILED(hr))
1181 return hr;
1184 * Insert the new directory entry into the parent storage's tree
1186 hr = insertIntoTree(
1187 This,
1188 This->storageDirEntry,
1189 newEntryRef);
1190 if (FAILED(hr))
1192 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1193 return hr;
1197 * Open it to get a pointer to return.
1199 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1201 if( (hr != S_OK) || (*ppstg == NULL))
1203 return hr;
1207 return S_OK;
1211 /***************************************************************************
1213 * Internal Method
1215 * Reserve a directory entry in the file and initialize it.
1217 static HRESULT StorageImpl_CreateDirEntry(
1218 StorageBaseImpl *base,
1219 const DirEntry *newData,
1220 DirRef *index)
1222 StorageImpl *storage = (StorageImpl*)base;
1223 ULONG currentEntryIndex = 0;
1224 ULONG newEntryIndex = DIRENTRY_NULL;
1225 HRESULT hr = S_OK;
1226 BYTE currentData[RAW_DIRENTRY_SIZE];
1227 WORD sizeOfNameString;
1231 hr = StorageImpl_ReadRawDirEntry(storage,
1232 currentEntryIndex,
1233 currentData);
1235 if (SUCCEEDED(hr))
1237 StorageUtl_ReadWord(
1238 currentData,
1239 OFFSET_PS_NAMELENGTH,
1240 &sizeOfNameString);
1242 if (sizeOfNameString == 0)
1245 * The entry exists and is available, we found it.
1247 newEntryIndex = currentEntryIndex;
1250 else
1253 * We exhausted the directory entries, we will create more space below
1255 newEntryIndex = currentEntryIndex;
1257 currentEntryIndex++;
1259 } while (newEntryIndex == DIRENTRY_NULL);
1262 * grow the directory stream
1264 if (FAILED(hr))
1266 BYTE emptyData[RAW_DIRENTRY_SIZE];
1267 ULARGE_INTEGER newSize;
1268 ULONG entryIndex;
1269 ULONG lastEntry = 0;
1270 ULONG blockCount = 0;
1273 * obtain the new count of blocks in the directory stream
1275 blockCount = BlockChainStream_GetCount(
1276 storage->rootBlockChain)+1;
1279 * initialize the size used by the directory stream
1281 newSize.u.HighPart = 0;
1282 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1285 * add a block to the directory stream
1287 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1290 * memset the empty entry in order to initialize the unused newly
1291 * created entries
1293 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1296 * initialize them
1298 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1300 for(
1301 entryIndex = newEntryIndex + 1;
1302 entryIndex < lastEntry;
1303 entryIndex++)
1305 StorageImpl_WriteRawDirEntry(
1306 storage,
1307 entryIndex,
1308 emptyData);
1311 StorageImpl_SaveFileHeader(storage);
1314 UpdateRawDirEntry(currentData, newData);
1316 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1318 if (SUCCEEDED(hr))
1319 *index = newEntryIndex;
1321 return hr;
1324 /***************************************************************************
1326 * Internal Method
1328 * Mark a directory entry in the file as free.
1330 static HRESULT StorageImpl_DestroyDirEntry(
1331 StorageBaseImpl *base,
1332 DirRef index)
1334 HRESULT hr;
1335 BYTE emptyData[RAW_DIRENTRY_SIZE];
1336 StorageImpl *storage = (StorageImpl*)base;
1338 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1340 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1342 return hr;
1346 /****************************************************************************
1348 * Internal Method
1350 * Case insensitive comparison of DirEntry.name by first considering
1351 * their size.
1353 * Returns <0 when name1 < name2
1354 * >0 when name1 > name2
1355 * 0 when name1 == name2
1357 static LONG entryNameCmp(
1358 const OLECHAR *name1,
1359 const OLECHAR *name2)
1361 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1363 while (diff == 0 && *name1 != 0)
1366 * We compare the string themselves only when they are of the same length
1368 diff = toupperW(*name1++) - toupperW(*name2++);
1371 return diff;
1374 /****************************************************************************
1376 * Internal Method
1378 * Add a directory entry to a storage
1380 static HRESULT insertIntoTree(
1381 StorageBaseImpl *This,
1382 DirRef parentStorageIndex,
1383 DirRef newEntryIndex)
1385 DirEntry currentEntry;
1386 DirEntry newEntry;
1389 * Read the inserted entry
1391 StorageBaseImpl_ReadDirEntry(This,
1392 newEntryIndex,
1393 &newEntry);
1396 * Read the storage entry
1398 StorageBaseImpl_ReadDirEntry(This,
1399 parentStorageIndex,
1400 &currentEntry);
1402 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1405 * The root storage contains some element, therefore, start the research
1406 * for the appropriate location.
1408 BOOL found = 0;
1409 DirRef current, next, previous, currentEntryId;
1412 * Keep a reference to the root of the storage's element tree
1414 currentEntryId = currentEntry.dirRootEntry;
1417 * Read
1419 StorageBaseImpl_ReadDirEntry(This,
1420 currentEntry.dirRootEntry,
1421 &currentEntry);
1423 previous = currentEntry.leftChild;
1424 next = currentEntry.rightChild;
1425 current = currentEntryId;
1427 while (found == 0)
1429 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1431 if (diff < 0)
1433 if (previous != DIRENTRY_NULL)
1435 StorageBaseImpl_ReadDirEntry(This,
1436 previous,
1437 &currentEntry);
1438 current = previous;
1440 else
1442 currentEntry.leftChild = newEntryIndex;
1443 StorageBaseImpl_WriteDirEntry(This,
1444 current,
1445 &currentEntry);
1446 found = 1;
1449 else if (diff > 0)
1451 if (next != DIRENTRY_NULL)
1453 StorageBaseImpl_ReadDirEntry(This,
1454 next,
1455 &currentEntry);
1456 current = next;
1458 else
1460 currentEntry.rightChild = newEntryIndex;
1461 StorageBaseImpl_WriteDirEntry(This,
1462 current,
1463 &currentEntry);
1464 found = 1;
1467 else
1470 * Trying to insert an item with the same name in the
1471 * subtree structure.
1473 return STG_E_FILEALREADYEXISTS;
1476 previous = currentEntry.leftChild;
1477 next = currentEntry.rightChild;
1480 else
1483 * The storage is empty, make the new entry the root of its element tree
1485 currentEntry.dirRootEntry = newEntryIndex;
1486 StorageBaseImpl_WriteDirEntry(This,
1487 parentStorageIndex,
1488 &currentEntry);
1491 return S_OK;
1494 /****************************************************************************
1496 * Internal Method
1498 * Find and read the element of a storage with the given name.
1500 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1501 const OLECHAR *name, DirEntry *data)
1503 DirRef currentEntry;
1505 /* Read the storage entry to find the root of the tree. */
1506 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1508 currentEntry = data->dirRootEntry;
1510 while (currentEntry != DIRENTRY_NULL)
1512 LONG cmp;
1514 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1516 cmp = entryNameCmp(name, data->name);
1518 if (cmp == 0)
1519 /* found it */
1520 break;
1522 else if (cmp < 0)
1523 currentEntry = data->leftChild;
1525 else if (cmp > 0)
1526 currentEntry = data->rightChild;
1529 return currentEntry;
1532 /****************************************************************************
1534 * Internal Method
1536 * Find and read the binary tree parent of the element with the given name.
1538 * If there is no such element, find a place where it could be inserted and
1539 * return STG_E_FILENOTFOUND.
1541 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1542 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1543 ULONG *relation)
1545 DirRef childEntry;
1546 DirEntry childData;
1548 /* Read the storage entry to find the root of the tree. */
1549 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1551 *parentEntry = storageEntry;
1552 *relation = DIRENTRY_RELATION_DIR;
1554 childEntry = parentData->dirRootEntry;
1556 while (childEntry != DIRENTRY_NULL)
1558 LONG cmp;
1560 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1562 cmp = entryNameCmp(childName, childData.name);
1564 if (cmp == 0)
1565 /* found it */
1566 break;
1568 else if (cmp < 0)
1570 *parentData = childData;
1571 *parentEntry = childEntry;
1572 *relation = DIRENTRY_RELATION_PREVIOUS;
1574 childEntry = parentData->leftChild;
1577 else if (cmp > 0)
1579 *parentData = childData;
1580 *parentEntry = childEntry;
1581 *relation = DIRENTRY_RELATION_NEXT;
1583 childEntry = parentData->rightChild;
1587 if (childEntry == DIRENTRY_NULL)
1588 return STG_E_FILENOTFOUND;
1589 else
1590 return S_OK;
1594 /*************************************************************************
1595 * CopyTo (IStorage)
1597 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1598 IStorage* iface,
1599 DWORD ciidExclude, /* [in] */
1600 const IID* rgiidExclude, /* [size_is][unique][in] */
1601 SNB snbExclude, /* [unique][in] */
1602 IStorage* pstgDest) /* [unique][in] */
1604 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1606 IEnumSTATSTG *elements = 0;
1607 STATSTG curElement, strStat;
1608 HRESULT hr;
1609 IStorage *pstgTmp, *pstgChild;
1610 IStream *pstrTmp, *pstrChild;
1611 DirRef srcEntryRef;
1612 DirEntry srcEntry;
1613 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1614 int i;
1616 TRACE("(%p, %d, %p, %p, %p)\n",
1617 iface, ciidExclude, rgiidExclude,
1618 snbExclude, pstgDest);
1620 if ( pstgDest == 0 )
1621 return STG_E_INVALIDPOINTER;
1624 * Enumerate the elements
1626 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1628 if ( hr != S_OK )
1629 return hr;
1632 * set the class ID
1634 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1635 IStorage_SetClass( pstgDest, &curElement.clsid );
1637 for(i = 0; i < ciidExclude; ++i)
1639 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1640 skip_storage = TRUE;
1641 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1642 skip_stream = TRUE;
1643 else
1644 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1650 * Obtain the next element
1652 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1654 if ( hr == S_FALSE )
1656 hr = S_OK; /* done, every element has been copied */
1657 break;
1660 if ( snbExclude )
1662 WCHAR **snb = snbExclude;
1663 skip = FALSE;
1664 while ( *snb != NULL && !skip )
1666 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1667 skip = TRUE;
1668 ++snb;
1672 if ( skip )
1673 goto cleanup;
1675 if (curElement.type == STGTY_STORAGE)
1677 if(skip_storage)
1678 goto cleanup;
1681 * open child source storage
1683 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1684 STGM_READ|STGM_SHARE_EXCLUSIVE,
1685 NULL, 0, &pstgChild );
1687 if (hr != S_OK)
1688 goto cleanup;
1691 * create a new storage in destination storage
1693 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1694 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1695 0, 0,
1696 &pstgTmp );
1698 * if it already exist, don't create a new one use this one
1700 if (hr == STG_E_FILEALREADYEXISTS)
1702 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1703 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1704 NULL, 0, &pstgTmp );
1707 if (hr == S_OK)
1710 * do the copy recursively
1712 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1713 NULL, pstgTmp );
1715 IStorage_Release( pstgTmp );
1718 IStorage_Release( pstgChild );
1720 else if (curElement.type == STGTY_STREAM)
1722 if(skip_stream)
1723 goto cleanup;
1726 * create a new stream in destination storage. If the stream already
1727 * exist, it will be deleted and a new one will be created.
1729 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1730 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1731 0, 0, &pstrTmp );
1733 if (hr != S_OK)
1734 goto cleanup;
1737 * open child stream storage. This operation must succeed even if the
1738 * stream is already open, so we use internal functions to do it.
1740 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1741 &srcEntry);
1742 if (!srcEntryRef)
1744 ERR("source stream not found\n");
1745 hr = STG_E_DOCFILECORRUPT;
1748 if (hr == S_OK)
1750 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1751 if (pstrChild)
1752 IStream_AddRef(pstrChild);
1753 else
1754 hr = E_OUTOFMEMORY;
1757 if (hr == S_OK)
1760 * Get the size of the source stream
1762 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1765 * Set the size of the destination stream.
1767 IStream_SetSize(pstrTmp, strStat.cbSize);
1770 * do the copy
1772 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1773 NULL, NULL );
1775 IStream_Release( pstrChild );
1778 IStream_Release( pstrTmp );
1780 else
1782 WARN("unknown element type: %d\n", curElement.type);
1785 cleanup:
1786 CoTaskMemFree(curElement.pwcsName);
1787 } while (hr == S_OK);
1790 * Clean-up
1792 IEnumSTATSTG_Release(elements);
1794 return hr;
1797 /*************************************************************************
1798 * MoveElementTo (IStorage)
1800 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1801 IStorage* iface,
1802 const OLECHAR *pwcsName, /* [string][in] */
1803 IStorage *pstgDest, /* [unique][in] */
1804 const OLECHAR *pwcsNewName,/* [string][in] */
1805 DWORD grfFlags) /* [in] */
1807 FIXME("(%p %s %p %s %u): stub\n", iface,
1808 debugstr_w(pwcsName), pstgDest,
1809 debugstr_w(pwcsNewName), grfFlags);
1810 return E_NOTIMPL;
1813 /*************************************************************************
1814 * Commit (IStorage)
1816 * Ensures that any changes made to a storage object open in transacted mode
1817 * are reflected in the parent storage
1819 * In a non-transacted mode, this ensures all cached writes are completed.
1821 static HRESULT WINAPI StorageImpl_Commit(
1822 IStorage* iface,
1823 DWORD grfCommitFlags)/* [in] */
1825 StorageBaseImpl* const base=(StorageBaseImpl*)iface;
1826 TRACE("(%p %d)\n", iface, grfCommitFlags);
1827 return StorageBaseImpl_Flush(base);
1830 /*************************************************************************
1831 * Revert (IStorage)
1833 * Discard all changes that have been made since the last commit operation
1835 static HRESULT WINAPI StorageImpl_Revert(
1836 IStorage* iface)
1838 TRACE("(%p)\n", iface);
1839 return S_OK;
1842 /*************************************************************************
1843 * DestroyElement (IStorage)
1845 * Strategy: This implementation is built this way for simplicity not for speed.
1846 * I always delete the topmost element of the enumeration and adjust
1847 * the deleted element pointer all the time. This takes longer to
1848 * do but allow to reinvoke DestroyElement whenever we encounter a
1849 * storage object. The optimisation resides in the usage of another
1850 * enumeration strategy that would give all the leaves of a storage
1851 * first. (postfix order)
1853 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1854 IStorage* iface,
1855 const OLECHAR *pwcsName)/* [string][in] */
1857 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1859 HRESULT hr = S_OK;
1860 DirEntry entryToDelete;
1861 DirRef entryToDeleteRef;
1863 TRACE("(%p, %s)\n",
1864 iface, debugstr_w(pwcsName));
1866 if (pwcsName==NULL)
1867 return STG_E_INVALIDPOINTER;
1869 if (This->reverted)
1870 return STG_E_REVERTED;
1872 if ( !(This->openFlags & STGM_TRANSACTED) &&
1873 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1874 return STG_E_ACCESSDENIED;
1876 entryToDeleteRef = findElement(
1877 This,
1878 This->storageDirEntry,
1879 pwcsName,
1880 &entryToDelete);
1882 if ( entryToDeleteRef == DIRENTRY_NULL )
1884 return STG_E_FILENOTFOUND;
1887 if ( entryToDelete.stgType == STGTY_STORAGE )
1889 hr = deleteStorageContents(
1890 This,
1891 entryToDeleteRef,
1892 entryToDelete);
1894 else if ( entryToDelete.stgType == STGTY_STREAM )
1896 hr = deleteStreamContents(
1897 This,
1898 entryToDeleteRef,
1899 entryToDelete);
1902 if (hr!=S_OK)
1903 return hr;
1906 * Remove the entry from its parent storage
1908 hr = removeFromTree(
1909 This,
1910 This->storageDirEntry,
1911 entryToDeleteRef);
1914 * Invalidate the entry
1916 if (SUCCEEDED(hr))
1917 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1919 return hr;
1923 /******************************************************************************
1924 * Internal stream list handlers
1927 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1929 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1930 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1933 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1935 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1936 list_remove(&(strm->StrmListEntry));
1939 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1941 StgStreamImpl *strm;
1943 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1945 if (strm->dirEntry == streamEntry)
1947 return TRUE;
1951 return FALSE;
1954 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1956 StorageInternalImpl *childstg;
1958 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1960 if (childstg->base.storageDirEntry == storageEntry)
1962 return TRUE;
1966 return FALSE;
1969 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1971 struct list *cur, *cur2;
1972 StgStreamImpl *strm=NULL;
1973 StorageInternalImpl *childstg=NULL;
1975 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1976 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1977 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1978 strm->parentStorage = NULL;
1979 list_remove(cur);
1982 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1983 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1984 StorageBaseImpl_Invalidate( &childstg->base );
1987 if (stg->transactedChild)
1989 StorageBaseImpl_Invalidate(stg->transactedChild);
1991 stg->transactedChild = NULL;
1996 /*********************************************************************
1998 * Internal Method
2000 * Delete the contents of a storage entry.
2003 static HRESULT deleteStorageContents(
2004 StorageBaseImpl *parentStorage,
2005 DirRef indexToDelete,
2006 DirEntry entryDataToDelete)
2008 IEnumSTATSTG *elements = 0;
2009 IStorage *childStorage = 0;
2010 STATSTG currentElement;
2011 HRESULT hr;
2012 HRESULT destroyHr = S_OK;
2013 StorageInternalImpl *stg, *stg2;
2015 /* Invalidate any open storage objects. */
2016 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2018 if (stg->base.storageDirEntry == indexToDelete)
2020 StorageBaseImpl_Invalidate(&stg->base);
2025 * Open the storage and enumerate it
2027 hr = StorageBaseImpl_OpenStorage(
2028 (IStorage*)parentStorage,
2029 entryDataToDelete.name,
2031 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2034 &childStorage);
2036 if (hr != S_OK)
2038 return hr;
2042 * Enumerate the elements
2044 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2049 * Obtain the next element
2051 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2052 if (hr==S_OK)
2054 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2056 CoTaskMemFree(currentElement.pwcsName);
2060 * We need to Reset the enumeration every time because we delete elements
2061 * and the enumeration could be invalid
2063 IEnumSTATSTG_Reset(elements);
2065 } while ((hr == S_OK) && (destroyHr == S_OK));
2067 IStorage_Release(childStorage);
2068 IEnumSTATSTG_Release(elements);
2070 return destroyHr;
2073 /*********************************************************************
2075 * Internal Method
2077 * Perform the deletion of a stream's data
2080 static HRESULT deleteStreamContents(
2081 StorageBaseImpl *parentStorage,
2082 DirRef indexToDelete,
2083 DirEntry entryDataToDelete)
2085 IStream *pis;
2086 HRESULT hr;
2087 ULARGE_INTEGER size;
2088 StgStreamImpl *strm, *strm2;
2090 /* Invalidate any open stream objects. */
2091 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2093 if (strm->dirEntry == indexToDelete)
2095 TRACE("Stream deleted %p\n", strm);
2096 strm->parentStorage = NULL;
2097 list_remove(&strm->StrmListEntry);
2101 size.u.HighPart = 0;
2102 size.u.LowPart = 0;
2104 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2105 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2107 if (hr!=S_OK)
2109 return(hr);
2113 * Zap the stream
2115 hr = IStream_SetSize(pis, size);
2117 if(hr != S_OK)
2119 return hr;
2123 * Release the stream object.
2125 IStream_Release(pis);
2127 return S_OK;
2130 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2132 switch (relation)
2134 case DIRENTRY_RELATION_PREVIOUS:
2135 entry->leftChild = new_target;
2136 break;
2137 case DIRENTRY_RELATION_NEXT:
2138 entry->rightChild = new_target;
2139 break;
2140 case DIRENTRY_RELATION_DIR:
2141 entry->dirRootEntry = new_target;
2142 break;
2143 default:
2144 assert(0);
2148 /*************************************************************************
2150 * Internal Method
2152 * This method removes a directory entry from its parent storage tree without
2153 * freeing any resources attached to it.
2155 static HRESULT removeFromTree(
2156 StorageBaseImpl *This,
2157 DirRef parentStorageIndex,
2158 DirRef deletedIndex)
2160 HRESULT hr = S_OK;
2161 DirEntry entryToDelete;
2162 DirEntry parentEntry;
2163 DirRef parentEntryRef;
2164 ULONG typeOfRelation;
2166 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2168 if (hr != S_OK)
2169 return hr;
2172 * Find the element that links to the one we want to delete.
2174 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2175 &parentEntry, &parentEntryRef, &typeOfRelation);
2177 if (hr != S_OK)
2178 return hr;
2180 if (entryToDelete.leftChild != DIRENTRY_NULL)
2183 * Replace the deleted entry with its left child
2185 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2187 hr = StorageBaseImpl_WriteDirEntry(
2188 This,
2189 parentEntryRef,
2190 &parentEntry);
2191 if(FAILED(hr))
2193 return hr;
2196 if (entryToDelete.rightChild != DIRENTRY_NULL)
2199 * We need to reinsert the right child somewhere. We already know it and
2200 * its children are greater than everything in the left tree, so we
2201 * insert it at the rightmost point in the left tree.
2203 DirRef newRightChildParent = entryToDelete.leftChild;
2204 DirEntry newRightChildParentEntry;
2208 hr = StorageBaseImpl_ReadDirEntry(
2209 This,
2210 newRightChildParent,
2211 &newRightChildParentEntry);
2212 if (FAILED(hr))
2214 return hr;
2217 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2218 newRightChildParent = newRightChildParentEntry.rightChild;
2219 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2221 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2223 hr = StorageBaseImpl_WriteDirEntry(
2224 This,
2225 newRightChildParent,
2226 &newRightChildParentEntry);
2227 if (FAILED(hr))
2229 return hr;
2233 else
2236 * Replace the deleted entry with its right child
2238 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2240 hr = StorageBaseImpl_WriteDirEntry(
2241 This,
2242 parentEntryRef,
2243 &parentEntry);
2244 if(FAILED(hr))
2246 return hr;
2250 return hr;
2254 /******************************************************************************
2255 * SetElementTimes (IStorage)
2257 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2258 IStorage* iface,
2259 const OLECHAR *pwcsName,/* [string][in] */
2260 const FILETIME *pctime, /* [in] */
2261 const FILETIME *patime, /* [in] */
2262 const FILETIME *pmtime) /* [in] */
2264 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2265 return S_OK;
2268 /******************************************************************************
2269 * SetStateBits (IStorage)
2271 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2272 IStorage* iface,
2273 DWORD grfStateBits,/* [in] */
2274 DWORD grfMask) /* [in] */
2276 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2278 if (This->reverted)
2279 return STG_E_REVERTED;
2281 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2282 return S_OK;
2285 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2286 DirRef index, const DirEntry *data)
2288 StorageImpl *This = (StorageImpl*)base;
2289 return StorageImpl_WriteDirEntry(This, index, data);
2292 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2293 DirRef index, DirEntry *data)
2295 StorageImpl *This = (StorageImpl*)base;
2296 return StorageImpl_ReadDirEntry(This, index, data);
2299 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2301 int i;
2303 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2305 if (!This->blockChainCache[i])
2307 return &This->blockChainCache[i];
2311 i = This->blockChainToEvict;
2313 BlockChainStream_Destroy(This->blockChainCache[i]);
2314 This->blockChainCache[i] = NULL;
2316 This->blockChainToEvict++;
2317 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2318 This->blockChainToEvict = 0;
2320 return &This->blockChainCache[i];
2323 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2324 DirRef index)
2326 int i, free_index=-1;
2328 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2330 if (!This->blockChainCache[i])
2332 if (free_index == -1) free_index = i;
2334 else if (This->blockChainCache[i]->ownerDirEntry == index)
2336 return &This->blockChainCache[i];
2340 if (free_index == -1)
2342 free_index = This->blockChainToEvict;
2344 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2345 This->blockChainCache[free_index] = NULL;
2347 This->blockChainToEvict++;
2348 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2349 This->blockChainToEvict = 0;
2352 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2353 return &This->blockChainCache[free_index];
2356 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2358 int i;
2360 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2362 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2364 BlockChainStream_Destroy(This->blockChainCache[i]);
2365 This->blockChainCache[i] = NULL;
2366 return;
2371 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2372 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2374 StorageImpl *This = (StorageImpl*)base;
2375 DirEntry data;
2376 HRESULT hr;
2377 ULONG bytesToRead;
2379 hr = StorageImpl_ReadDirEntry(This, index, &data);
2380 if (FAILED(hr)) return hr;
2382 if (data.size.QuadPart == 0)
2384 *bytesRead = 0;
2385 return S_OK;
2388 if (offset.QuadPart + size > data.size.QuadPart)
2390 bytesToRead = data.size.QuadPart - offset.QuadPart;
2392 else
2394 bytesToRead = size;
2397 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2399 SmallBlockChainStream *stream;
2401 stream = SmallBlockChainStream_Construct(This, NULL, index);
2402 if (!stream) return E_OUTOFMEMORY;
2404 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2406 SmallBlockChainStream_Destroy(stream);
2408 return hr;
2410 else
2412 BlockChainStream *stream = NULL;
2414 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2415 if (!stream) return E_OUTOFMEMORY;
2417 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2419 return hr;
2423 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2424 ULARGE_INTEGER newsize)
2426 StorageImpl *This = (StorageImpl*)base;
2427 DirEntry data;
2428 HRESULT hr;
2429 SmallBlockChainStream *smallblock=NULL;
2430 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2432 hr = StorageImpl_ReadDirEntry(This, index, &data);
2433 if (FAILED(hr)) return hr;
2435 /* In simple mode keep the stream size above the small block limit */
2436 if (This->base.openFlags & STGM_SIMPLE)
2437 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2439 if (data.size.QuadPart == newsize.QuadPart)
2440 return S_OK;
2442 /* Create a block chain object of the appropriate type */
2443 if (data.size.QuadPart == 0)
2445 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2447 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2448 if (!smallblock) return E_OUTOFMEMORY;
2450 else
2452 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2453 bigblock = *pbigblock;
2454 if (!bigblock) return E_OUTOFMEMORY;
2457 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2459 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2460 if (!smallblock) return E_OUTOFMEMORY;
2462 else
2464 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2465 bigblock = *pbigblock;
2466 if (!bigblock) return E_OUTOFMEMORY;
2469 /* Change the block chain type if necessary. */
2470 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2472 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2473 if (!bigblock)
2475 SmallBlockChainStream_Destroy(smallblock);
2476 return E_FAIL;
2479 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2480 *pbigblock = bigblock;
2482 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2484 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2485 if (!smallblock)
2486 return E_FAIL;
2489 /* Set the size of the block chain. */
2490 if (smallblock)
2492 SmallBlockChainStream_SetSize(smallblock, newsize);
2493 SmallBlockChainStream_Destroy(smallblock);
2495 else
2497 BlockChainStream_SetSize(bigblock, newsize);
2500 /* Set the size in the directory entry. */
2501 hr = StorageImpl_ReadDirEntry(This, index, &data);
2502 if (SUCCEEDED(hr))
2504 data.size = newsize;
2506 hr = StorageImpl_WriteDirEntry(This, index, &data);
2508 return hr;
2511 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2512 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2514 StorageImpl *This = (StorageImpl*)base;
2515 DirEntry data;
2516 HRESULT hr;
2517 ULARGE_INTEGER newSize;
2519 hr = StorageImpl_ReadDirEntry(This, index, &data);
2520 if (FAILED(hr)) return hr;
2522 /* Grow the stream if necessary */
2523 newSize.QuadPart = 0;
2524 newSize.QuadPart = offset.QuadPart + size;
2526 if (newSize.QuadPart > data.size.QuadPart)
2528 hr = StorageImpl_StreamSetSize(base, index, newSize);
2529 if (FAILED(hr))
2530 return hr;
2532 hr = StorageImpl_ReadDirEntry(This, index, &data);
2533 if (FAILED(hr)) return hr;
2536 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2538 SmallBlockChainStream *stream;
2540 stream = SmallBlockChainStream_Construct(This, NULL, index);
2541 if (!stream) return E_OUTOFMEMORY;
2543 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2545 SmallBlockChainStream_Destroy(stream);
2547 return hr;
2549 else
2551 BlockChainStream *stream;
2553 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2554 if (!stream) return E_OUTOFMEMORY;
2556 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2558 return hr;
2562 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2563 DirRef src)
2565 StorageImpl *This = (StorageImpl*)base;
2566 DirEntry dst_data, src_data;
2567 HRESULT hr;
2569 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2571 if (SUCCEEDED(hr))
2572 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2574 if (SUCCEEDED(hr))
2576 StorageImpl_DeleteCachedBlockChainStream(This, src);
2577 dst_data.startingBlock = src_data.startingBlock;
2578 dst_data.size = src_data.size;
2580 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2583 return hr;
2586 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2588 StorageImpl *This = (StorageImpl*) iface;
2589 STATSTG statstg;
2590 HRESULT hr;
2592 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2594 *result = statstg.pwcsName;
2596 return hr;
2600 * Virtual function table for the IStorage32Impl class.
2602 static const IStorageVtbl Storage32Impl_Vtbl =
2604 StorageBaseImpl_QueryInterface,
2605 StorageBaseImpl_AddRef,
2606 StorageBaseImpl_Release,
2607 StorageBaseImpl_CreateStream,
2608 StorageBaseImpl_OpenStream,
2609 StorageBaseImpl_CreateStorage,
2610 StorageBaseImpl_OpenStorage,
2611 StorageBaseImpl_CopyTo,
2612 StorageBaseImpl_MoveElementTo,
2613 StorageImpl_Commit,
2614 StorageImpl_Revert,
2615 StorageBaseImpl_EnumElements,
2616 StorageBaseImpl_DestroyElement,
2617 StorageBaseImpl_RenameElement,
2618 StorageBaseImpl_SetElementTimes,
2619 StorageBaseImpl_SetClass,
2620 StorageBaseImpl_SetStateBits,
2621 StorageBaseImpl_Stat
2624 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2626 StorageImpl_Destroy,
2627 StorageImpl_Invalidate,
2628 StorageImpl_Flush,
2629 StorageImpl_GetFilename,
2630 StorageImpl_CreateDirEntry,
2631 StorageImpl_BaseWriteDirEntry,
2632 StorageImpl_BaseReadDirEntry,
2633 StorageImpl_DestroyDirEntry,
2634 StorageImpl_StreamReadAt,
2635 StorageImpl_StreamWriteAt,
2636 StorageImpl_StreamSetSize,
2637 StorageImpl_StreamLink
2640 static HRESULT StorageImpl_Construct(
2641 HANDLE hFile,
2642 LPCOLESTR pwcsName,
2643 ILockBytes* pLkbyt,
2644 DWORD openFlags,
2645 BOOL fileBased,
2646 BOOL create,
2647 ULONG sector_size,
2648 StorageImpl** result)
2650 StorageImpl* This;
2651 HRESULT hr = S_OK;
2652 DirEntry currentEntry;
2653 DirRef currentEntryRef;
2655 if ( FAILED( validateSTGM(openFlags) ))
2656 return STG_E_INVALIDFLAG;
2658 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2659 if (!This)
2660 return E_OUTOFMEMORY;
2662 memset(This, 0, sizeof(StorageImpl));
2664 list_init(&This->base.strmHead);
2666 list_init(&This->base.storageHead);
2668 This->base.lpVtbl = &Storage32Impl_Vtbl;
2669 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2670 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2671 This->base.openFlags = (openFlags & ~STGM_CREATE);
2672 This->base.ref = 1;
2673 This->base.create = create;
2675 This->base.reverted = 0;
2678 * Initialize the big block cache.
2680 This->bigBlockSize = sector_size;
2681 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2682 if (hFile)
2683 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2684 else
2686 This->lockBytes = pLkbyt;
2687 ILockBytes_AddRef(pLkbyt);
2690 if (FAILED(hr))
2691 goto end;
2693 if (create)
2695 ULARGE_INTEGER size;
2696 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2699 * Initialize all header variables:
2700 * - The big block depot consists of one block and it is at block 0
2701 * - The directory table starts at block 1
2702 * - There is no small block depot
2704 memset( This->bigBlockDepotStart,
2705 BLOCK_UNUSED,
2706 sizeof(This->bigBlockDepotStart));
2708 This->bigBlockDepotCount = 1;
2709 This->bigBlockDepotStart[0] = 0;
2710 This->rootStartBlock = 1;
2711 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2712 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2713 if (sector_size == 4096)
2714 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2715 else
2716 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2717 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2718 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2719 This->extBigBlockDepotCount = 0;
2721 StorageImpl_SaveFileHeader(This);
2724 * Add one block for the big block depot and one block for the directory table
2726 size.u.HighPart = 0;
2727 size.u.LowPart = This->bigBlockSize * 3;
2728 ILockBytes_SetSize(This->lockBytes, size);
2731 * Initialize the big block depot
2733 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2734 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2735 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2736 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2738 else
2741 * Load the header for the file.
2743 hr = StorageImpl_LoadFileHeader(This);
2745 if (FAILED(hr))
2747 goto end;
2752 * There is no block depot cached yet.
2754 This->indexBlockDepotCached = 0xFFFFFFFF;
2757 * Start searching for free blocks with block 0.
2759 This->prevFreeBlock = 0;
2761 This->firstFreeSmallBlock = 0;
2764 * Create the block chain abstractions.
2766 if(!(This->rootBlockChain =
2767 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2769 hr = STG_E_READFAULT;
2770 goto end;
2773 if(!(This->smallBlockDepotChain =
2774 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2775 DIRENTRY_NULL)))
2777 hr = STG_E_READFAULT;
2778 goto end;
2782 * Write the root storage entry (memory only)
2784 if (create)
2786 DirEntry rootEntry;
2788 * Initialize the directory table
2790 memset(&rootEntry, 0, sizeof(rootEntry));
2791 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2792 sizeof(rootEntry.name)/sizeof(WCHAR) );
2793 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2794 rootEntry.stgType = STGTY_ROOT;
2795 rootEntry.leftChild = DIRENTRY_NULL;
2796 rootEntry.rightChild = DIRENTRY_NULL;
2797 rootEntry.dirRootEntry = DIRENTRY_NULL;
2798 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2799 rootEntry.size.u.HighPart = 0;
2800 rootEntry.size.u.LowPart = 0;
2802 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2806 * Find the ID of the root storage.
2808 currentEntryRef = 0;
2812 hr = StorageImpl_ReadDirEntry(
2813 This,
2814 currentEntryRef,
2815 &currentEntry);
2817 if (SUCCEEDED(hr))
2819 if ( (currentEntry.sizeOfNameString != 0 ) &&
2820 (currentEntry.stgType == STGTY_ROOT) )
2822 This->base.storageDirEntry = currentEntryRef;
2826 currentEntryRef++;
2828 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2830 if (FAILED(hr))
2832 hr = STG_E_READFAULT;
2833 goto end;
2837 * Create the block chain abstraction for the small block root chain.
2839 if(!(This->smallBlockRootChain =
2840 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2842 hr = STG_E_READFAULT;
2845 end:
2846 if (FAILED(hr))
2848 IStorage_Release((IStorage*)This);
2849 *result = NULL;
2851 else
2852 *result = This;
2854 return hr;
2857 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2859 StorageImpl *This = (StorageImpl*) iface;
2861 StorageBaseImpl_DeleteAll(&This->base);
2863 This->base.reverted = 1;
2866 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2868 StorageImpl *This = (StorageImpl*) iface;
2869 int i;
2870 TRACE("(%p)\n", This);
2872 StorageImpl_Flush(iface);
2874 StorageImpl_Invalidate(iface);
2876 BlockChainStream_Destroy(This->smallBlockRootChain);
2877 BlockChainStream_Destroy(This->rootBlockChain);
2878 BlockChainStream_Destroy(This->smallBlockDepotChain);
2880 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2881 BlockChainStream_Destroy(This->blockChainCache[i]);
2883 if (This->lockBytes)
2884 ILockBytes_Release(This->lockBytes);
2885 HeapFree(GetProcessHeap(), 0, This);
2888 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2890 StorageImpl *This = (StorageImpl*) iface;
2892 return ILockBytes_Flush(This->lockBytes);
2895 /******************************************************************************
2896 * Storage32Impl_GetNextFreeBigBlock
2898 * Returns the index of the next free big block.
2899 * If the big block depot is filled, this method will enlarge it.
2902 static ULONG StorageImpl_GetNextFreeBigBlock(
2903 StorageImpl* This)
2905 ULONG depotBlockIndexPos;
2906 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2907 BOOL success;
2908 ULONG depotBlockOffset;
2909 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2910 ULONG nextBlockIndex = BLOCK_SPECIAL;
2911 int depotIndex = 0;
2912 ULONG freeBlock = BLOCK_UNUSED;
2913 ULARGE_INTEGER neededSize;
2914 STATSTG statstg;
2916 depotIndex = This->prevFreeBlock / blocksPerDepot;
2917 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2920 * Scan the entire big block depot until we find a block marked free
2922 while (nextBlockIndex != BLOCK_UNUSED)
2924 if (depotIndex < COUNT_BBDEPOTINHEADER)
2926 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2929 * Grow the primary depot.
2931 if (depotBlockIndexPos == BLOCK_UNUSED)
2933 depotBlockIndexPos = depotIndex*blocksPerDepot;
2936 * Add a block depot.
2938 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2939 This->bigBlockDepotCount++;
2940 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2943 * Flag it as a block depot.
2945 StorageImpl_SetNextBlockInChain(This,
2946 depotBlockIndexPos,
2947 BLOCK_SPECIAL);
2949 /* Save new header information.
2951 StorageImpl_SaveFileHeader(This);
2954 else
2956 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2958 if (depotBlockIndexPos == BLOCK_UNUSED)
2961 * Grow the extended depot.
2963 ULONG extIndex = BLOCK_UNUSED;
2964 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2965 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2967 if (extBlockOffset == 0)
2969 /* We need an extended block.
2971 extIndex = Storage32Impl_AddExtBlockDepot(This);
2972 This->extBigBlockDepotCount++;
2973 depotBlockIndexPos = extIndex + 1;
2975 else
2976 depotBlockIndexPos = depotIndex * blocksPerDepot;
2979 * Add a block depot and mark it in the extended block.
2981 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2982 This->bigBlockDepotCount++;
2983 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2985 /* Flag the block depot.
2987 StorageImpl_SetNextBlockInChain(This,
2988 depotBlockIndexPos,
2989 BLOCK_SPECIAL);
2991 /* If necessary, flag the extended depot block.
2993 if (extIndex != BLOCK_UNUSED)
2994 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2996 /* Save header information.
2998 StorageImpl_SaveFileHeader(This);
3002 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3004 if (success)
3006 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3007 ( nextBlockIndex != BLOCK_UNUSED))
3009 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3011 if (nextBlockIndex == BLOCK_UNUSED)
3013 freeBlock = (depotIndex * blocksPerDepot) +
3014 (depotBlockOffset/sizeof(ULONG));
3017 depotBlockOffset += sizeof(ULONG);
3021 depotIndex++;
3022 depotBlockOffset = 0;
3026 * make sure that the block physically exists before using it
3028 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3030 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3032 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3033 ILockBytes_SetSize(This->lockBytes, neededSize);
3035 This->prevFreeBlock = freeBlock;
3037 return freeBlock;
3040 /******************************************************************************
3041 * Storage32Impl_AddBlockDepot
3043 * This will create a depot block, essentially it is a block initialized
3044 * to BLOCK_UNUSEDs.
3046 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3048 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3051 * Initialize blocks as free
3053 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3054 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3057 /******************************************************************************
3058 * Storage32Impl_GetExtDepotBlock
3060 * Returns the index of the block that corresponds to the specified depot
3061 * index. This method is only for depot indexes equal or greater than
3062 * COUNT_BBDEPOTINHEADER.
3064 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3066 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3067 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3068 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3069 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3070 ULONG blockIndex = BLOCK_UNUSED;
3071 ULONG extBlockIndex = This->extBigBlockDepotStart;
3073 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3075 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3076 return BLOCK_UNUSED;
3078 while (extBlockCount > 0)
3080 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3081 extBlockCount--;
3084 if (extBlockIndex != BLOCK_UNUSED)
3085 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3086 extBlockOffset * sizeof(ULONG), &blockIndex);
3088 return blockIndex;
3091 /******************************************************************************
3092 * Storage32Impl_SetExtDepotBlock
3094 * Associates the specified block index to the specified depot index.
3095 * This method is only for depot indexes equal or greater than
3096 * COUNT_BBDEPOTINHEADER.
3098 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3100 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3101 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3102 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3103 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3104 ULONG extBlockIndex = This->extBigBlockDepotStart;
3106 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3108 while (extBlockCount > 0)
3110 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3111 extBlockCount--;
3114 if (extBlockIndex != BLOCK_UNUSED)
3116 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3117 extBlockOffset * sizeof(ULONG),
3118 blockIndex);
3122 /******************************************************************************
3123 * Storage32Impl_AddExtBlockDepot
3125 * Creates an extended depot block.
3127 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3129 ULONG numExtBlocks = This->extBigBlockDepotCount;
3130 ULONG nextExtBlock = This->extBigBlockDepotStart;
3131 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3132 ULONG index = BLOCK_UNUSED;
3133 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3134 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3135 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3137 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3138 blocksPerDepotBlock;
3140 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3143 * The first extended block.
3145 This->extBigBlockDepotStart = index;
3147 else
3149 unsigned int i;
3151 * Follow the chain to the last one.
3153 for (i = 0; i < (numExtBlocks - 1); i++)
3155 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3159 * Add the new extended block to the chain.
3161 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3162 index);
3166 * Initialize this block.
3168 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3169 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3171 return index;
3174 /******************************************************************************
3175 * Storage32Impl_FreeBigBlock
3177 * This method will flag the specified block as free in the big block depot.
3179 static void StorageImpl_FreeBigBlock(
3180 StorageImpl* This,
3181 ULONG blockIndex)
3183 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3185 if (blockIndex < This->prevFreeBlock)
3186 This->prevFreeBlock = blockIndex;
3189 /************************************************************************
3190 * Storage32Impl_GetNextBlockInChain
3192 * This method will retrieve the block index of the next big block in
3193 * in the chain.
3195 * Params: This - Pointer to the Storage object.
3196 * blockIndex - Index of the block to retrieve the chain
3197 * for.
3198 * nextBlockIndex - receives the return value.
3200 * Returns: This method returns the index of the next block in the chain.
3201 * It will return the constants:
3202 * BLOCK_SPECIAL - If the block given was not part of a
3203 * chain.
3204 * BLOCK_END_OF_CHAIN - If the block given was the last in
3205 * a chain.
3206 * BLOCK_UNUSED - If the block given was not past of a chain
3207 * and is available.
3208 * BLOCK_EXTBBDEPOT - This block is part of the extended
3209 * big block depot.
3211 * See Windows documentation for more details on IStorage methods.
3213 static HRESULT StorageImpl_GetNextBlockInChain(
3214 StorageImpl* This,
3215 ULONG blockIndex,
3216 ULONG* nextBlockIndex)
3218 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3219 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3220 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3221 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3222 BOOL success;
3223 ULONG depotBlockIndexPos;
3224 int index, num_blocks;
3226 *nextBlockIndex = BLOCK_SPECIAL;
3228 if(depotBlockCount >= This->bigBlockDepotCount)
3230 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3231 This->bigBlockDepotCount);
3232 return STG_E_READFAULT;
3236 * Cache the currently accessed depot block.
3238 if (depotBlockCount != This->indexBlockDepotCached)
3240 This->indexBlockDepotCached = depotBlockCount;
3242 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3244 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3246 else
3249 * We have to look in the extended depot.
3251 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3254 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3256 if (!success)
3257 return STG_E_READFAULT;
3259 num_blocks = This->bigBlockSize / 4;
3261 for (index = 0; index < num_blocks; index++)
3263 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3264 This->blockDepotCached[index] = *nextBlockIndex;
3268 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3270 return S_OK;
3273 /******************************************************************************
3274 * Storage32Impl_GetNextExtendedBlock
3276 * Given an extended block this method will return the next extended block.
3278 * NOTES:
3279 * The last ULONG of an extended block is the block index of the next
3280 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3281 * depot.
3283 * Return values:
3284 * - The index of the next extended block
3285 * - BLOCK_UNUSED: there is no next extended block.
3286 * - Any other return values denotes failure.
3288 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3290 ULONG nextBlockIndex = BLOCK_SPECIAL;
3291 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3293 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3294 &nextBlockIndex);
3296 return nextBlockIndex;
3299 /******************************************************************************
3300 * Storage32Impl_SetNextBlockInChain
3302 * This method will write the index of the specified block's next block
3303 * in the big block depot.
3305 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3306 * do the following
3308 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3309 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3310 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3313 static void StorageImpl_SetNextBlockInChain(
3314 StorageImpl* This,
3315 ULONG blockIndex,
3316 ULONG nextBlock)
3318 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3319 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3320 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3321 ULONG depotBlockIndexPos;
3323 assert(depotBlockCount < This->bigBlockDepotCount);
3324 assert(blockIndex != nextBlock);
3326 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3328 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3330 else
3333 * We have to look in the extended depot.
3335 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3338 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3339 nextBlock);
3341 * Update the cached block depot, if necessary.
3343 if (depotBlockCount == This->indexBlockDepotCached)
3345 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3349 /******************************************************************************
3350 * Storage32Impl_LoadFileHeader
3352 * This method will read in the file header
3354 static HRESULT StorageImpl_LoadFileHeader(
3355 StorageImpl* This)
3357 HRESULT hr;
3358 BYTE headerBigBlock[HEADER_SIZE];
3359 int index;
3360 ULARGE_INTEGER offset;
3361 DWORD bytes_read;
3363 TRACE("\n");
3365 * Get a pointer to the big block of data containing the header.
3367 offset.u.HighPart = 0;
3368 offset.u.LowPart = 0;
3369 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3370 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3371 hr = STG_E_FILENOTFOUND;
3374 * Extract the information from the header.
3376 if (SUCCEEDED(hr))
3379 * Check for the "magic number" signature and return an error if it is not
3380 * found.
3382 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3384 return STG_E_OLDFORMAT;
3387 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3389 return STG_E_INVALIDHEADER;
3392 StorageUtl_ReadWord(
3393 headerBigBlock,
3394 OFFSET_BIGBLOCKSIZEBITS,
3395 &This->bigBlockSizeBits);
3397 StorageUtl_ReadWord(
3398 headerBigBlock,
3399 OFFSET_SMALLBLOCKSIZEBITS,
3400 &This->smallBlockSizeBits);
3402 StorageUtl_ReadDWord(
3403 headerBigBlock,
3404 OFFSET_BBDEPOTCOUNT,
3405 &This->bigBlockDepotCount);
3407 StorageUtl_ReadDWord(
3408 headerBigBlock,
3409 OFFSET_ROOTSTARTBLOCK,
3410 &This->rootStartBlock);
3412 StorageUtl_ReadDWord(
3413 headerBigBlock,
3414 OFFSET_SMALLBLOCKLIMIT,
3415 &This->smallBlockLimit);
3417 StorageUtl_ReadDWord(
3418 headerBigBlock,
3419 OFFSET_SBDEPOTSTART,
3420 &This->smallBlockDepotStart);
3422 StorageUtl_ReadDWord(
3423 headerBigBlock,
3424 OFFSET_EXTBBDEPOTSTART,
3425 &This->extBigBlockDepotStart);
3427 StorageUtl_ReadDWord(
3428 headerBigBlock,
3429 OFFSET_EXTBBDEPOTCOUNT,
3430 &This->extBigBlockDepotCount);
3432 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3434 StorageUtl_ReadDWord(
3435 headerBigBlock,
3436 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3437 &(This->bigBlockDepotStart[index]));
3441 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3443 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3444 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3447 * Right now, the code is making some assumptions about the size of the
3448 * blocks, just make sure they are what we're expecting.
3450 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3451 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3452 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3454 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3455 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3456 hr = STG_E_INVALIDHEADER;
3458 else
3459 hr = S_OK;
3462 return hr;
3465 /******************************************************************************
3466 * Storage32Impl_SaveFileHeader
3468 * This method will save to the file the header
3470 static void StorageImpl_SaveFileHeader(
3471 StorageImpl* This)
3473 BYTE headerBigBlock[HEADER_SIZE];
3474 int index;
3475 HRESULT hr;
3476 ULARGE_INTEGER offset;
3477 DWORD bytes_read, bytes_written;
3478 DWORD major_version, dirsectorcount;
3481 * Get a pointer to the big block of data containing the header.
3483 offset.u.HighPart = 0;
3484 offset.u.LowPart = 0;
3485 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3486 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3487 hr = STG_E_FILENOTFOUND;
3489 if (This->bigBlockSizeBits == 0x9)
3490 major_version = 3;
3491 else if (This->bigBlockSizeBits == 0xc)
3492 major_version = 4;
3493 else
3495 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3496 major_version = 4;
3500 * If the block read failed, the file is probably new.
3502 if (FAILED(hr))
3505 * Initialize for all unknown fields.
3507 memset(headerBigBlock, 0, HEADER_SIZE);
3510 * Initialize the magic number.
3512 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3516 * Write the information to the header.
3518 StorageUtl_WriteWord(
3519 headerBigBlock,
3520 OFFSET_MINORVERSION,
3521 0x3e);
3523 StorageUtl_WriteWord(
3524 headerBigBlock,
3525 OFFSET_MAJORVERSION,
3526 major_version);
3528 StorageUtl_WriteWord(
3529 headerBigBlock,
3530 OFFSET_BYTEORDERMARKER,
3531 (WORD)-2);
3533 StorageUtl_WriteWord(
3534 headerBigBlock,
3535 OFFSET_BIGBLOCKSIZEBITS,
3536 This->bigBlockSizeBits);
3538 StorageUtl_WriteWord(
3539 headerBigBlock,
3540 OFFSET_SMALLBLOCKSIZEBITS,
3541 This->smallBlockSizeBits);
3543 if (major_version >= 4)
3545 if (This->rootBlockChain)
3546 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3547 else
3548 /* This file is being created, and it will start out with one block. */
3549 dirsectorcount = 1;
3551 else
3552 /* This field must be 0 in versions older than 4 */
3553 dirsectorcount = 0;
3555 StorageUtl_WriteDWord(
3556 headerBigBlock,
3557 OFFSET_DIRSECTORCOUNT,
3558 dirsectorcount);
3560 StorageUtl_WriteDWord(
3561 headerBigBlock,
3562 OFFSET_BBDEPOTCOUNT,
3563 This->bigBlockDepotCount);
3565 StorageUtl_WriteDWord(
3566 headerBigBlock,
3567 OFFSET_ROOTSTARTBLOCK,
3568 This->rootStartBlock);
3570 StorageUtl_WriteDWord(
3571 headerBigBlock,
3572 OFFSET_SMALLBLOCKLIMIT,
3573 This->smallBlockLimit);
3575 StorageUtl_WriteDWord(
3576 headerBigBlock,
3577 OFFSET_SBDEPOTSTART,
3578 This->smallBlockDepotStart);
3580 StorageUtl_WriteDWord(
3581 headerBigBlock,
3582 OFFSET_SBDEPOTCOUNT,
3583 This->smallBlockDepotChain ?
3584 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3586 StorageUtl_WriteDWord(
3587 headerBigBlock,
3588 OFFSET_EXTBBDEPOTSTART,
3589 This->extBigBlockDepotStart);
3591 StorageUtl_WriteDWord(
3592 headerBigBlock,
3593 OFFSET_EXTBBDEPOTCOUNT,
3594 This->extBigBlockDepotCount);
3596 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3598 StorageUtl_WriteDWord(
3599 headerBigBlock,
3600 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3601 (This->bigBlockDepotStart[index]));
3605 * Write the big block back to the file.
3607 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3610 /******************************************************************************
3611 * StorageImpl_ReadRawDirEntry
3613 * This method will read the raw data from a directory entry in the file.
3615 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3617 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3619 ULARGE_INTEGER offset;
3620 HRESULT hr;
3621 ULONG bytesRead;
3623 offset.u.HighPart = 0;
3624 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3626 hr = BlockChainStream_ReadAt(
3627 This->rootBlockChain,
3628 offset,
3629 RAW_DIRENTRY_SIZE,
3630 buffer,
3631 &bytesRead);
3633 if (bytesRead != RAW_DIRENTRY_SIZE)
3634 return STG_E_READFAULT;
3636 return hr;
3639 /******************************************************************************
3640 * StorageImpl_WriteRawDirEntry
3642 * This method will write the raw data from a directory entry in the file.
3644 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3646 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3648 ULARGE_INTEGER offset;
3649 HRESULT hr;
3650 ULONG bytesRead;
3652 offset.u.HighPart = 0;
3653 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3655 hr = BlockChainStream_WriteAt(
3656 This->rootBlockChain,
3657 offset,
3658 RAW_DIRENTRY_SIZE,
3659 buffer,
3660 &bytesRead);
3662 return hr;
3665 /******************************************************************************
3666 * UpdateRawDirEntry
3668 * Update raw directory entry data from the fields in newData.
3670 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3672 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3674 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3676 memcpy(
3677 buffer + OFFSET_PS_NAME,
3678 newData->name,
3679 DIRENTRY_NAME_BUFFER_LEN );
3681 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3683 StorageUtl_WriteWord(
3684 buffer,
3685 OFFSET_PS_NAMELENGTH,
3686 newData->sizeOfNameString);
3688 StorageUtl_WriteDWord(
3689 buffer,
3690 OFFSET_PS_LEFTCHILD,
3691 newData->leftChild);
3693 StorageUtl_WriteDWord(
3694 buffer,
3695 OFFSET_PS_RIGHTCHILD,
3696 newData->rightChild);
3698 StorageUtl_WriteDWord(
3699 buffer,
3700 OFFSET_PS_DIRROOT,
3701 newData->dirRootEntry);
3703 StorageUtl_WriteGUID(
3704 buffer,
3705 OFFSET_PS_GUID,
3706 &newData->clsid);
3708 StorageUtl_WriteDWord(
3709 buffer,
3710 OFFSET_PS_CTIMELOW,
3711 newData->ctime.dwLowDateTime);
3713 StorageUtl_WriteDWord(
3714 buffer,
3715 OFFSET_PS_CTIMEHIGH,
3716 newData->ctime.dwHighDateTime);
3718 StorageUtl_WriteDWord(
3719 buffer,
3720 OFFSET_PS_MTIMELOW,
3721 newData->mtime.dwLowDateTime);
3723 StorageUtl_WriteDWord(
3724 buffer,
3725 OFFSET_PS_MTIMEHIGH,
3726 newData->ctime.dwHighDateTime);
3728 StorageUtl_WriteDWord(
3729 buffer,
3730 OFFSET_PS_STARTBLOCK,
3731 newData->startingBlock);
3733 StorageUtl_WriteDWord(
3734 buffer,
3735 OFFSET_PS_SIZE,
3736 newData->size.u.LowPart);
3739 /******************************************************************************
3740 * Storage32Impl_ReadDirEntry
3742 * This method will read the specified directory entry.
3744 HRESULT StorageImpl_ReadDirEntry(
3745 StorageImpl* This,
3746 DirRef index,
3747 DirEntry* buffer)
3749 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3750 HRESULT readRes;
3752 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3754 if (SUCCEEDED(readRes))
3756 memset(buffer->name, 0, sizeof(buffer->name));
3757 memcpy(
3758 buffer->name,
3759 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3760 DIRENTRY_NAME_BUFFER_LEN );
3761 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3763 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3765 StorageUtl_ReadWord(
3766 currentEntry,
3767 OFFSET_PS_NAMELENGTH,
3768 &buffer->sizeOfNameString);
3770 StorageUtl_ReadDWord(
3771 currentEntry,
3772 OFFSET_PS_LEFTCHILD,
3773 &buffer->leftChild);
3775 StorageUtl_ReadDWord(
3776 currentEntry,
3777 OFFSET_PS_RIGHTCHILD,
3778 &buffer->rightChild);
3780 StorageUtl_ReadDWord(
3781 currentEntry,
3782 OFFSET_PS_DIRROOT,
3783 &buffer->dirRootEntry);
3785 StorageUtl_ReadGUID(
3786 currentEntry,
3787 OFFSET_PS_GUID,
3788 &buffer->clsid);
3790 StorageUtl_ReadDWord(
3791 currentEntry,
3792 OFFSET_PS_CTIMELOW,
3793 &buffer->ctime.dwLowDateTime);
3795 StorageUtl_ReadDWord(
3796 currentEntry,
3797 OFFSET_PS_CTIMEHIGH,
3798 &buffer->ctime.dwHighDateTime);
3800 StorageUtl_ReadDWord(
3801 currentEntry,
3802 OFFSET_PS_MTIMELOW,
3803 &buffer->mtime.dwLowDateTime);
3805 StorageUtl_ReadDWord(
3806 currentEntry,
3807 OFFSET_PS_MTIMEHIGH,
3808 &buffer->mtime.dwHighDateTime);
3810 StorageUtl_ReadDWord(
3811 currentEntry,
3812 OFFSET_PS_STARTBLOCK,
3813 &buffer->startingBlock);
3815 StorageUtl_ReadDWord(
3816 currentEntry,
3817 OFFSET_PS_SIZE,
3818 &buffer->size.u.LowPart);
3820 buffer->size.u.HighPart = 0;
3823 return readRes;
3826 /*********************************************************************
3827 * Write the specified directory entry to the file
3829 HRESULT StorageImpl_WriteDirEntry(
3830 StorageImpl* This,
3831 DirRef index,
3832 const DirEntry* buffer)
3834 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3835 HRESULT writeRes;
3837 UpdateRawDirEntry(currentEntry, buffer);
3839 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3840 return writeRes;
3843 static BOOL StorageImpl_ReadBigBlock(
3844 StorageImpl* This,
3845 ULONG blockIndex,
3846 void* buffer)
3848 ULARGE_INTEGER ulOffset;
3849 DWORD read;
3851 ulOffset.u.HighPart = 0;
3852 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3854 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3855 return (read == This->bigBlockSize);
3858 static BOOL StorageImpl_ReadDWordFromBigBlock(
3859 StorageImpl* This,
3860 ULONG blockIndex,
3861 ULONG offset,
3862 DWORD* value)
3864 ULARGE_INTEGER ulOffset;
3865 DWORD read;
3866 DWORD tmp;
3868 ulOffset.u.HighPart = 0;
3869 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3870 ulOffset.u.LowPart += offset;
3872 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3873 *value = lendian32toh(tmp);
3874 return (read == sizeof(DWORD));
3877 static BOOL StorageImpl_WriteBigBlock(
3878 StorageImpl* This,
3879 ULONG blockIndex,
3880 const void* buffer)
3882 ULARGE_INTEGER ulOffset;
3883 DWORD wrote;
3885 ulOffset.u.HighPart = 0;
3886 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3888 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3889 return (wrote == This->bigBlockSize);
3892 static BOOL StorageImpl_WriteDWordToBigBlock(
3893 StorageImpl* This,
3894 ULONG blockIndex,
3895 ULONG offset,
3896 DWORD value)
3898 ULARGE_INTEGER ulOffset;
3899 DWORD wrote;
3901 ulOffset.u.HighPart = 0;
3902 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3903 ulOffset.u.LowPart += offset;
3905 value = htole32(value);
3906 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3907 return (wrote == sizeof(DWORD));
3910 /******************************************************************************
3911 * Storage32Impl_SmallBlocksToBigBlocks
3913 * This method will convert a small block chain to a big block chain.
3914 * The small block chain will be destroyed.
3916 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3917 StorageImpl* This,
3918 SmallBlockChainStream** ppsbChain)
3920 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3921 ULARGE_INTEGER size, offset;
3922 ULONG cbRead, cbWritten;
3923 ULARGE_INTEGER cbTotalRead;
3924 DirRef streamEntryRef;
3925 HRESULT resWrite = S_OK;
3926 HRESULT resRead;
3927 DirEntry streamEntry;
3928 BYTE *buffer;
3929 BlockChainStream *bbTempChain = NULL;
3930 BlockChainStream *bigBlockChain = NULL;
3933 * Create a temporary big block chain that doesn't have
3934 * an associated directory entry. This temporary chain will be
3935 * used to copy data from small blocks to big blocks.
3937 bbTempChain = BlockChainStream_Construct(This,
3938 &bbHeadOfChain,
3939 DIRENTRY_NULL);
3940 if(!bbTempChain) return NULL;
3942 * Grow the big block chain.
3944 size = SmallBlockChainStream_GetSize(*ppsbChain);
3945 BlockChainStream_SetSize(bbTempChain, size);
3948 * Copy the contents of the small block chain to the big block chain
3949 * by small block size increments.
3951 offset.u.LowPart = 0;
3952 offset.u.HighPart = 0;
3953 cbTotalRead.QuadPart = 0;
3955 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3958 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3959 offset,
3960 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3961 buffer,
3962 &cbRead);
3963 if (FAILED(resRead))
3964 break;
3966 if (cbRead > 0)
3968 cbTotalRead.QuadPart += cbRead;
3970 resWrite = BlockChainStream_WriteAt(bbTempChain,
3971 offset,
3972 cbRead,
3973 buffer,
3974 &cbWritten);
3976 if (FAILED(resWrite))
3977 break;
3979 offset.u.LowPart += cbRead;
3981 else
3983 resRead = STG_E_READFAULT;
3984 break;
3986 } while (cbTotalRead.QuadPart < size.QuadPart);
3987 HeapFree(GetProcessHeap(),0,buffer);
3989 size.u.HighPart = 0;
3990 size.u.LowPart = 0;
3992 if (FAILED(resRead) || FAILED(resWrite))
3994 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3995 BlockChainStream_SetSize(bbTempChain, size);
3996 BlockChainStream_Destroy(bbTempChain);
3997 return NULL;
4001 * Destroy the small block chain.
4003 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4004 SmallBlockChainStream_SetSize(*ppsbChain, size);
4005 SmallBlockChainStream_Destroy(*ppsbChain);
4006 *ppsbChain = 0;
4009 * Change the directory entry. This chain is now a big block chain
4010 * and it doesn't reside in the small blocks chain anymore.
4012 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4014 streamEntry.startingBlock = bbHeadOfChain;
4016 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4019 * Destroy the temporary entryless big block chain.
4020 * Create a new big block chain associated with this entry.
4022 BlockChainStream_Destroy(bbTempChain);
4023 bigBlockChain = BlockChainStream_Construct(This,
4024 NULL,
4025 streamEntryRef);
4027 return bigBlockChain;
4030 /******************************************************************************
4031 * Storage32Impl_BigBlocksToSmallBlocks
4033 * This method will convert a big block chain to a small block chain.
4034 * The big block chain will be destroyed on success.
4036 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4037 StorageImpl* This,
4038 BlockChainStream** ppbbChain)
4040 ULARGE_INTEGER size, offset, cbTotalRead;
4041 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4042 DirRef streamEntryRef;
4043 HRESULT resWrite = S_OK, resRead;
4044 DirEntry streamEntry;
4045 BYTE* buffer;
4046 SmallBlockChainStream* sbTempChain;
4048 TRACE("%p %p\n", This, ppbbChain);
4050 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4051 DIRENTRY_NULL);
4053 if(!sbTempChain)
4054 return NULL;
4056 size = BlockChainStream_GetSize(*ppbbChain);
4057 SmallBlockChainStream_SetSize(sbTempChain, size);
4059 offset.u.HighPart = 0;
4060 offset.u.LowPart = 0;
4061 cbTotalRead.QuadPart = 0;
4062 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4065 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4066 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4067 buffer, &cbRead);
4069 if(FAILED(resRead))
4070 break;
4072 if(cbRead > 0)
4074 cbTotalRead.QuadPart += cbRead;
4076 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4077 cbRead, buffer, &cbWritten);
4079 if(FAILED(resWrite))
4080 break;
4082 offset.u.LowPart += cbRead;
4084 else
4086 resRead = STG_E_READFAULT;
4087 break;
4089 }while(cbTotalRead.QuadPart < size.QuadPart);
4090 HeapFree(GetProcessHeap(), 0, buffer);
4092 size.u.HighPart = 0;
4093 size.u.LowPart = 0;
4095 if(FAILED(resRead) || FAILED(resWrite))
4097 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4098 SmallBlockChainStream_SetSize(sbTempChain, size);
4099 SmallBlockChainStream_Destroy(sbTempChain);
4100 return NULL;
4103 /* destroy the original big block chain */
4104 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4105 BlockChainStream_SetSize(*ppbbChain, size);
4106 BlockChainStream_Destroy(*ppbbChain);
4107 *ppbbChain = NULL;
4109 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4110 streamEntry.startingBlock = sbHeadOfChain;
4111 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4113 SmallBlockChainStream_Destroy(sbTempChain);
4114 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4117 static HRESULT StorageBaseImpl_CopyStream(
4118 StorageBaseImpl *dst, DirRef dst_entry,
4119 StorageBaseImpl *src, DirRef src_entry)
4121 HRESULT hr;
4122 BYTE data[4096];
4123 DirEntry srcdata;
4124 ULARGE_INTEGER bytes_copied;
4125 ULONG bytestocopy, bytesread, byteswritten;
4127 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4129 if (SUCCEEDED(hr))
4131 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4133 bytes_copied.QuadPart = 0;
4134 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4136 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4138 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4139 data, &bytesread);
4140 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4142 if (SUCCEEDED(hr))
4143 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4144 data, &byteswritten);
4145 if (SUCCEEDED(hr))
4147 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4148 bytes_copied.QuadPart += byteswritten;
4153 return hr;
4156 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4158 DirRef result=This->firstFreeEntry;
4160 while (result < This->entries_size && This->entries[result].inuse)
4161 result++;
4163 if (result == This->entries_size)
4165 ULONG new_size = This->entries_size * 2;
4166 TransactedDirEntry *new_entries;
4168 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4169 if (!new_entries) return DIRENTRY_NULL;
4171 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4172 HeapFree(GetProcessHeap(), 0, This->entries);
4174 This->entries = new_entries;
4175 This->entries_size = new_size;
4178 This->entries[result].inuse = 1;
4180 This->firstFreeEntry = result+1;
4182 return result;
4185 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4186 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4188 DirRef stubEntryRef;
4189 TransactedDirEntry *entry;
4191 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4193 if (stubEntryRef != DIRENTRY_NULL)
4195 entry = &This->entries[stubEntryRef];
4197 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4199 entry->read = 0;
4202 return stubEntryRef;
4205 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4206 TransactedSnapshotImpl *This, DirRef entry)
4208 HRESULT hr=S_OK;
4209 DirEntry data;
4211 if (!This->entries[entry].read)
4213 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4214 This->entries[entry].transactedParentEntry,
4215 &data);
4217 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4219 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4221 if (data.leftChild == DIRENTRY_NULL)
4222 hr = E_OUTOFMEMORY;
4225 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4227 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4229 if (data.rightChild == DIRENTRY_NULL)
4230 hr = E_OUTOFMEMORY;
4233 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4235 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4237 if (data.dirRootEntry == DIRENTRY_NULL)
4238 hr = E_OUTOFMEMORY;
4241 if (SUCCEEDED(hr))
4243 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4244 This->entries[entry].read = 1;
4248 return hr;
4251 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4252 TransactedSnapshotImpl *This, DirRef entry)
4254 HRESULT hr = S_OK;
4256 if (!This->entries[entry].stream_dirty)
4258 DirEntry new_entrydata;
4260 memset(&new_entrydata, 0, sizeof(DirEntry));
4261 new_entrydata.name[0] = 'S';
4262 new_entrydata.sizeOfNameString = 1;
4263 new_entrydata.stgType = STGTY_STREAM;
4264 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4265 new_entrydata.leftChild = DIRENTRY_NULL;
4266 new_entrydata.rightChild = DIRENTRY_NULL;
4267 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4269 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4270 &This->entries[entry].stream_entry);
4272 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4274 hr = StorageBaseImpl_CopyStream(
4275 This->scratch, This->entries[entry].stream_entry,
4276 This->transactedParent, This->entries[entry].transactedParentEntry);
4278 if (FAILED(hr))
4279 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4282 if (SUCCEEDED(hr))
4283 This->entries[entry].stream_dirty = 1;
4285 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4287 /* Since this entry is modified, and we aren't using its stream data, we
4288 * no longer care about the original entry. */
4289 DirRef delete_ref;
4290 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4292 if (delete_ref != DIRENTRY_NULL)
4293 This->entries[delete_ref].deleted = 1;
4295 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4299 return hr;
4302 /* Find the first entry in a depth-first traversal. */
4303 static DirRef TransactedSnapshotImpl_FindFirstChild(
4304 TransactedSnapshotImpl* This, DirRef parent)
4306 DirRef cursor, prev;
4307 TransactedDirEntry *entry;
4309 cursor = parent;
4310 entry = &This->entries[cursor];
4311 while (entry->read)
4313 if (entry->data.leftChild != DIRENTRY_NULL)
4315 prev = cursor;
4316 cursor = entry->data.leftChild;
4317 entry = &This->entries[cursor];
4318 entry->parent = prev;
4320 else if (entry->data.rightChild != DIRENTRY_NULL)
4322 prev = cursor;
4323 cursor = entry->data.rightChild;
4324 entry = &This->entries[cursor];
4325 entry->parent = prev;
4327 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4329 prev = cursor;
4330 cursor = entry->data.dirRootEntry;
4331 entry = &This->entries[cursor];
4332 entry->parent = prev;
4334 else
4335 break;
4338 return cursor;
4341 /* Find the next entry in a depth-first traversal. */
4342 static DirRef TransactedSnapshotImpl_FindNextChild(
4343 TransactedSnapshotImpl* This, DirRef current)
4345 DirRef parent;
4346 TransactedDirEntry *parent_entry;
4348 parent = This->entries[current].parent;
4349 parent_entry = &This->entries[parent];
4351 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4353 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4355 This->entries[parent_entry->data.rightChild].parent = parent;
4356 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4359 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4361 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4362 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4366 return parent;
4369 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4370 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4371 TransactedSnapshotImpl* This, DirRef entry)
4373 return entry != DIRENTRY_NULL &&
4374 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4377 /* Destroy the entries created by CopyTree. */
4378 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4379 TransactedSnapshotImpl* This, DirRef stop)
4381 DirRef cursor;
4382 TransactedDirEntry *entry;
4383 ULARGE_INTEGER zero;
4385 zero.QuadPart = 0;
4387 if (!This->entries[This->base.storageDirEntry].read)
4388 return;
4390 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4392 if (cursor == DIRENTRY_NULL)
4393 return;
4395 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4397 while (cursor != DIRENTRY_NULL && cursor != stop)
4399 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4401 entry = &This->entries[cursor];
4403 if (entry->stream_dirty)
4404 StorageBaseImpl_StreamSetSize(This->transactedParent,
4405 entry->newTransactedParentEntry, zero);
4407 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4408 entry->newTransactedParentEntry);
4410 entry->newTransactedParentEntry = entry->transactedParentEntry;
4413 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4417 /* Make a copy of our edited tree that we can use in the parent. */
4418 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4420 DirRef cursor;
4421 TransactedDirEntry *entry;
4422 HRESULT hr = S_OK;
4424 cursor = This->base.storageDirEntry;
4425 entry = &This->entries[cursor];
4426 entry->parent = DIRENTRY_NULL;
4427 entry->newTransactedParentEntry = entry->transactedParentEntry;
4429 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4430 return S_OK;
4432 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4434 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4435 entry = &This->entries[cursor];
4437 while (cursor != DIRENTRY_NULL)
4439 /* Make a copy of this entry in the transacted parent. */
4440 if (!entry->read ||
4441 (!entry->dirty && !entry->stream_dirty &&
4442 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4443 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4444 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4445 entry->newTransactedParentEntry = entry->transactedParentEntry;
4446 else
4448 DirEntry newData;
4450 memcpy(&newData, &entry->data, sizeof(DirEntry));
4452 newData.size.QuadPart = 0;
4453 newData.startingBlock = BLOCK_END_OF_CHAIN;
4455 if (newData.leftChild != DIRENTRY_NULL)
4456 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4458 if (newData.rightChild != DIRENTRY_NULL)
4459 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4461 if (newData.dirRootEntry != DIRENTRY_NULL)
4462 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4464 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4465 &entry->newTransactedParentEntry);
4466 if (FAILED(hr))
4468 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4469 return hr;
4472 if (entry->stream_dirty)
4474 hr = StorageBaseImpl_CopyStream(
4475 This->transactedParent, entry->newTransactedParentEntry,
4476 This->scratch, entry->stream_entry);
4478 else if (entry->data.size.QuadPart)
4480 hr = StorageBaseImpl_StreamLink(
4481 This->transactedParent, entry->newTransactedParentEntry,
4482 entry->transactedParentEntry);
4485 if (FAILED(hr))
4487 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4488 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4489 return hr;
4493 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4494 entry = &This->entries[cursor];
4497 return hr;
4500 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4501 IStorage* iface,
4502 DWORD grfCommitFlags) /* [in] */
4504 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4505 TransactedDirEntry *root_entry;
4506 DirRef i, dir_root_ref;
4507 DirEntry data;
4508 ULARGE_INTEGER zero;
4509 HRESULT hr;
4511 zero.QuadPart = 0;
4513 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4515 /* Cannot commit a read-only transacted storage */
4516 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4517 return STG_E_ACCESSDENIED;
4519 /* To prevent data loss, we create the new structure in the file before we
4520 * delete the old one, so that in case of errors the old data is intact. We
4521 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4522 * needed in the rare situation where we have just enough free disk space to
4523 * overwrite the existing data. */
4525 root_entry = &This->entries[This->base.storageDirEntry];
4527 if (!root_entry->read)
4528 return S_OK;
4530 hr = TransactedSnapshotImpl_CopyTree(This);
4531 if (FAILED(hr)) return hr;
4533 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4534 dir_root_ref = DIRENTRY_NULL;
4535 else
4536 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4538 hr = StorageBaseImpl_Flush(This->transactedParent);
4540 /* Update the storage to use the new data in one step. */
4541 if (SUCCEEDED(hr))
4542 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4543 root_entry->transactedParentEntry, &data);
4545 if (SUCCEEDED(hr))
4547 data.dirRootEntry = dir_root_ref;
4548 data.clsid = root_entry->data.clsid;
4549 data.ctime = root_entry->data.ctime;
4550 data.mtime = root_entry->data.mtime;
4552 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4553 root_entry->transactedParentEntry, &data);
4556 /* Try to flush after updating the root storage, but if the flush fails, keep
4557 * going, on the theory that it'll either succeed later or the subsequent
4558 * writes will fail. */
4559 StorageBaseImpl_Flush(This->transactedParent);
4561 if (SUCCEEDED(hr))
4563 /* Destroy the old now-orphaned data. */
4564 for (i=0; i<This->entries_size; i++)
4566 TransactedDirEntry *entry = &This->entries[i];
4567 if (entry->inuse)
4569 if (entry->deleted)
4571 StorageBaseImpl_StreamSetSize(This->transactedParent,
4572 entry->transactedParentEntry, zero);
4573 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4574 entry->transactedParentEntry);
4575 memset(entry, 0, sizeof(TransactedDirEntry));
4576 This->firstFreeEntry = min(i, This->firstFreeEntry);
4578 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4580 if (entry->transactedParentEntry != DIRENTRY_NULL)
4581 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4582 entry->transactedParentEntry);
4583 if (entry->stream_dirty)
4585 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4586 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4587 entry->stream_dirty = 0;
4589 entry->dirty = 0;
4590 entry->transactedParentEntry = entry->newTransactedParentEntry;
4595 else
4597 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4600 if (SUCCEEDED(hr))
4601 hr = StorageBaseImpl_Flush(This->transactedParent);
4603 return hr;
4606 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4607 IStorage* iface)
4609 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4610 ULARGE_INTEGER zero;
4611 ULONG i;
4613 TRACE("(%p)\n", iface);
4615 /* Destroy the open objects. */
4616 StorageBaseImpl_DeleteAll(&This->base);
4618 /* Clear out the scratch file. */
4619 zero.QuadPart = 0;
4620 for (i=0; i<This->entries_size; i++)
4622 if (This->entries[i].stream_dirty)
4624 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4625 zero);
4627 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4631 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4633 This->firstFreeEntry = 0;
4634 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4636 return S_OK;
4639 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4641 if (!This->reverted)
4643 TRACE("Storage invalidated (stg=%p)\n", This);
4645 This->reverted = 1;
4647 StorageBaseImpl_DeleteAll(This);
4651 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4653 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4655 TransactedSnapshotImpl_Revert((IStorage*)iface);
4657 IStorage_Release((IStorage*)This->transactedParent);
4659 IStorage_Release((IStorage*)This->scratch);
4661 HeapFree(GetProcessHeap(), 0, This->entries);
4663 HeapFree(GetProcessHeap(), 0, This);
4666 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4668 /* We only need to flush when committing. */
4669 return S_OK;
4672 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4674 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4676 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4679 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4680 const DirEntry *newData, DirRef *index)
4682 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4683 DirRef new_ref;
4684 TransactedDirEntry *new_entry;
4686 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4687 if (new_ref == DIRENTRY_NULL)
4688 return E_OUTOFMEMORY;
4690 new_entry = &This->entries[new_ref];
4692 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4693 new_entry->read = 1;
4694 new_entry->dirty = 1;
4695 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4697 *index = new_ref;
4699 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4701 return S_OK;
4704 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4705 DirRef index, const DirEntry *data)
4707 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4708 HRESULT hr;
4710 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4712 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4713 if (FAILED(hr)) return hr;
4715 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4717 if (index != This->base.storageDirEntry)
4719 This->entries[index].dirty = 1;
4721 if (data->size.QuadPart == 0 &&
4722 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4724 /* Since this entry is modified, and we aren't using its stream data, we
4725 * no longer care about the original entry. */
4726 DirRef delete_ref;
4727 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4729 if (delete_ref != DIRENTRY_NULL)
4730 This->entries[delete_ref].deleted = 1;
4732 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4736 return S_OK;
4739 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4740 DirRef index, DirEntry *data)
4742 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4743 HRESULT hr;
4745 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4746 if (FAILED(hr)) return hr;
4748 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4750 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4752 return S_OK;
4755 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4756 DirRef index)
4758 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4760 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4761 This->entries[index].data.size.QuadPart != 0)
4763 /* If we deleted this entry while it has stream data. We must have left the
4764 * data because some other entry is using it, and we need to leave the
4765 * original entry alone. */
4766 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4767 This->firstFreeEntry = min(index, This->firstFreeEntry);
4769 else
4771 This->entries[index].deleted = 1;
4774 return S_OK;
4777 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4778 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4780 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4782 if (This->entries[index].stream_dirty)
4784 return StorageBaseImpl_StreamReadAt(This->scratch,
4785 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4787 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4789 /* This stream doesn't live in the parent, and we haven't allocated storage
4790 * for it yet */
4791 *bytesRead = 0;
4792 return S_OK;
4794 else
4796 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4797 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4801 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4802 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4804 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4805 HRESULT hr;
4807 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4808 if (FAILED(hr)) return hr;
4810 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4811 if (FAILED(hr)) return hr;
4813 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4814 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4816 if (SUCCEEDED(hr) && size != 0)
4817 This->entries[index].data.size.QuadPart = max(
4818 This->entries[index].data.size.QuadPart,
4819 offset.QuadPart + size);
4821 return hr;
4824 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4825 DirRef index, ULARGE_INTEGER newsize)
4827 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4828 HRESULT hr;
4830 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4831 if (FAILED(hr)) return hr;
4833 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4834 return S_OK;
4836 if (newsize.QuadPart == 0)
4838 /* Destroy any parent references or entries in the scratch file. */
4839 if (This->entries[index].stream_dirty)
4841 ULARGE_INTEGER zero;
4842 zero.QuadPart = 0;
4843 StorageBaseImpl_StreamSetSize(This->scratch,
4844 This->entries[index].stream_entry, zero);
4845 StorageBaseImpl_DestroyDirEntry(This->scratch,
4846 This->entries[index].stream_entry);
4847 This->entries[index].stream_dirty = 0;
4849 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4851 DirRef delete_ref;
4852 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4854 if (delete_ref != DIRENTRY_NULL)
4855 This->entries[delete_ref].deleted = 1;
4857 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4860 else
4862 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4863 if (FAILED(hr)) return hr;
4865 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4866 This->entries[index].stream_entry, newsize);
4869 if (SUCCEEDED(hr))
4870 This->entries[index].data.size = newsize;
4872 return hr;
4875 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
4876 DirRef dst, DirRef src)
4878 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4879 HRESULT hr;
4880 TransactedDirEntry *dst_entry, *src_entry;
4882 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
4883 if (FAILED(hr)) return hr;
4885 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
4886 if (FAILED(hr)) return hr;
4888 dst_entry = &This->entries[dst];
4889 src_entry = &This->entries[src];
4891 dst_entry->stream_dirty = src_entry->stream_dirty;
4892 dst_entry->stream_entry = src_entry->stream_entry;
4893 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
4894 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
4895 dst_entry->data.size = src_entry->data.size;
4897 return S_OK;
4900 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4902 StorageBaseImpl_QueryInterface,
4903 StorageBaseImpl_AddRef,
4904 StorageBaseImpl_Release,
4905 StorageBaseImpl_CreateStream,
4906 StorageBaseImpl_OpenStream,
4907 StorageBaseImpl_CreateStorage,
4908 StorageBaseImpl_OpenStorage,
4909 StorageBaseImpl_CopyTo,
4910 StorageBaseImpl_MoveElementTo,
4911 TransactedSnapshotImpl_Commit,
4912 TransactedSnapshotImpl_Revert,
4913 StorageBaseImpl_EnumElements,
4914 StorageBaseImpl_DestroyElement,
4915 StorageBaseImpl_RenameElement,
4916 StorageBaseImpl_SetElementTimes,
4917 StorageBaseImpl_SetClass,
4918 StorageBaseImpl_SetStateBits,
4919 StorageBaseImpl_Stat
4922 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4924 TransactedSnapshotImpl_Destroy,
4925 TransactedSnapshotImpl_Invalidate,
4926 TransactedSnapshotImpl_Flush,
4927 TransactedSnapshotImpl_GetFilename,
4928 TransactedSnapshotImpl_CreateDirEntry,
4929 TransactedSnapshotImpl_WriteDirEntry,
4930 TransactedSnapshotImpl_ReadDirEntry,
4931 TransactedSnapshotImpl_DestroyDirEntry,
4932 TransactedSnapshotImpl_StreamReadAt,
4933 TransactedSnapshotImpl_StreamWriteAt,
4934 TransactedSnapshotImpl_StreamSetSize,
4935 TransactedSnapshotImpl_StreamLink
4938 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4939 TransactedSnapshotImpl** result)
4941 HRESULT hr;
4943 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4944 if (*result)
4946 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4948 /* This is OK because the property set storage functions use the IStorage functions. */
4949 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4951 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4953 list_init(&(*result)->base.strmHead);
4955 list_init(&(*result)->base.storageHead);
4957 (*result)->base.ref = 1;
4959 (*result)->base.openFlags = parentStorage->openFlags;
4961 /* Create a new temporary storage to act as the scratch file. */
4962 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
4963 0, (IStorage**)&(*result)->scratch);
4965 if (SUCCEEDED(hr))
4967 ULONG num_entries = 20;
4969 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
4971 (*result)->entries_size = num_entries;
4973 (*result)->firstFreeEntry = 0;
4975 if ((*result)->entries)
4977 /* parentStorage already has 1 reference, which we take over here. */
4978 (*result)->transactedParent = parentStorage;
4980 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4982 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
4984 else
4986 IStorage_Release((IStorage*)(*result)->scratch);
4988 hr = E_OUTOFMEMORY;
4992 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4994 return hr;
4996 else
4997 return E_OUTOFMEMORY;
5000 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5001 StorageBaseImpl** result)
5003 static int fixme=0;
5005 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5007 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5010 return TransactedSnapshotImpl_Construct(parentStorage,
5011 (TransactedSnapshotImpl**)result);
5014 static HRESULT Storage_Construct(
5015 HANDLE hFile,
5016 LPCOLESTR pwcsName,
5017 ILockBytes* pLkbyt,
5018 DWORD openFlags,
5019 BOOL fileBased,
5020 BOOL create,
5021 ULONG sector_size,
5022 StorageBaseImpl** result)
5024 StorageImpl *newStorage;
5025 StorageBaseImpl *newTransactedStorage;
5026 HRESULT hr;
5028 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5029 if (FAILED(hr)) goto end;
5031 if (openFlags & STGM_TRANSACTED)
5033 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5034 if (FAILED(hr))
5035 IStorage_Release((IStorage*)newStorage);
5036 else
5037 *result = newTransactedStorage;
5039 else
5040 *result = &newStorage->base;
5042 end:
5043 return hr;
5046 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5048 StorageInternalImpl* This = (StorageInternalImpl*) base;
5050 if (!This->base.reverted)
5052 TRACE("Storage invalidated (stg=%p)\n", This);
5054 This->base.reverted = 1;
5056 This->parentStorage = NULL;
5058 StorageBaseImpl_DeleteAll(&This->base);
5060 list_remove(&This->ParentListEntry);
5064 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5066 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5068 StorageInternalImpl_Invalidate(&This->base);
5070 HeapFree(GetProcessHeap(), 0, This);
5073 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5075 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5077 return StorageBaseImpl_Flush(This->parentStorage);
5080 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5082 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5084 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5087 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5088 const DirEntry *newData, DirRef *index)
5090 StorageInternalImpl* This = (StorageInternalImpl*) base;
5092 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5093 newData, index);
5096 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5097 DirRef index, const DirEntry *data)
5099 StorageInternalImpl* This = (StorageInternalImpl*) base;
5101 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5102 index, data);
5105 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5106 DirRef index, DirEntry *data)
5108 StorageInternalImpl* This = (StorageInternalImpl*) base;
5110 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5111 index, data);
5114 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5115 DirRef index)
5117 StorageInternalImpl* This = (StorageInternalImpl*) base;
5119 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5120 index);
5123 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5124 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5126 StorageInternalImpl* This = (StorageInternalImpl*) base;
5128 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5129 index, offset, size, buffer, bytesRead);
5132 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5133 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5135 StorageInternalImpl* This = (StorageInternalImpl*) base;
5137 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5138 index, offset, size, buffer, bytesWritten);
5141 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5142 DirRef index, ULARGE_INTEGER newsize)
5144 StorageInternalImpl* This = (StorageInternalImpl*) base;
5146 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5147 index, newsize);
5150 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5151 DirRef dst, DirRef src)
5153 StorageInternalImpl* This = (StorageInternalImpl*) base;
5155 return StorageBaseImpl_StreamLink(This->parentStorage,
5156 dst, src);
5159 /******************************************************************************
5161 ** Storage32InternalImpl_Commit
5164 static HRESULT WINAPI StorageInternalImpl_Commit(
5165 IStorage* iface,
5166 DWORD grfCommitFlags) /* [in] */
5168 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5169 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5170 return StorageBaseImpl_Flush(base);
5173 /******************************************************************************
5175 ** Storage32InternalImpl_Revert
5178 static HRESULT WINAPI StorageInternalImpl_Revert(
5179 IStorage* iface)
5181 FIXME("(%p): stub\n", iface);
5182 return S_OK;
5185 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5187 IStorage_Release((IStorage*)This->parentStorage);
5188 HeapFree(GetProcessHeap(), 0, This);
5191 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5192 IEnumSTATSTG* iface,
5193 REFIID riid,
5194 void** ppvObject)
5196 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5198 if (ppvObject==0)
5199 return E_INVALIDARG;
5201 *ppvObject = 0;
5203 if (IsEqualGUID(&IID_IUnknown, riid) ||
5204 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5206 *ppvObject = This;
5207 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
5208 return S_OK;
5211 return E_NOINTERFACE;
5214 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5215 IEnumSTATSTG* iface)
5217 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5218 return InterlockedIncrement(&This->ref);
5221 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5222 IEnumSTATSTG* iface)
5224 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5226 ULONG newRef;
5228 newRef = InterlockedDecrement(&This->ref);
5230 if (newRef==0)
5232 IEnumSTATSTGImpl_Destroy(This);
5235 return newRef;
5238 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5239 IEnumSTATSTGImpl* This,
5240 DirRef *ref)
5242 DirRef result = DIRENTRY_NULL;
5243 DirRef searchNode;
5244 DirEntry entry;
5245 HRESULT hr;
5246 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5248 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5249 This->parentStorage->storageDirEntry, &entry);
5250 searchNode = entry.dirRootEntry;
5252 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5254 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5256 if (SUCCEEDED(hr))
5258 LONG diff = entryNameCmp( entry.name, This->name);
5260 if (diff <= 0)
5262 searchNode = entry.rightChild;
5264 else
5266 result = searchNode;
5267 memcpy(result_name, entry.name, sizeof(result_name));
5268 searchNode = entry.leftChild;
5273 if (SUCCEEDED(hr))
5275 *ref = result;
5276 if (result != DIRENTRY_NULL)
5277 memcpy(This->name, result_name, sizeof(result_name));
5280 return hr;
5283 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5284 IEnumSTATSTG* iface,
5285 ULONG celt,
5286 STATSTG* rgelt,
5287 ULONG* pceltFetched)
5289 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5291 DirEntry currentEntry;
5292 STATSTG* currentReturnStruct = rgelt;
5293 ULONG objectFetched = 0;
5294 DirRef currentSearchNode;
5295 HRESULT hr=S_OK;
5297 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5298 return E_INVALIDARG;
5300 if (This->parentStorage->reverted)
5301 return STG_E_REVERTED;
5304 * To avoid the special case, get another pointer to a ULONG value if
5305 * the caller didn't supply one.
5307 if (pceltFetched==0)
5308 pceltFetched = &objectFetched;
5311 * Start the iteration, we will iterate until we hit the end of the
5312 * linked list or until we hit the number of items to iterate through
5314 *pceltFetched = 0;
5316 while ( *pceltFetched < celt )
5318 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5320 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5321 break;
5324 * Read the entry from the storage.
5326 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5327 currentSearchNode,
5328 &currentEntry);
5331 * Copy the information to the return buffer.
5333 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5334 currentReturnStruct,
5335 &currentEntry,
5336 STATFLAG_DEFAULT);
5339 * Step to the next item in the iteration
5341 (*pceltFetched)++;
5342 currentReturnStruct++;
5345 if (SUCCEEDED(hr) && *pceltFetched != celt)
5346 hr = S_FALSE;
5348 return hr;
5352 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5353 IEnumSTATSTG* iface,
5354 ULONG celt)
5356 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5358 ULONG objectFetched = 0;
5359 DirRef currentSearchNode;
5360 HRESULT hr=S_OK;
5362 if (This->parentStorage->reverted)
5363 return STG_E_REVERTED;
5365 while ( (objectFetched < celt) )
5367 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5369 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5370 break;
5372 objectFetched++;
5375 if (SUCCEEDED(hr) && objectFetched != celt)
5376 return S_FALSE;
5378 return hr;
5381 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5382 IEnumSTATSTG* iface)
5384 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5386 if (This->parentStorage->reverted)
5387 return STG_E_REVERTED;
5389 This->name[0] = 0;
5391 return S_OK;
5394 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5395 IEnumSTATSTG* iface,
5396 IEnumSTATSTG** ppenum)
5398 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5400 IEnumSTATSTGImpl* newClone;
5402 if (This->parentStorage->reverted)
5403 return STG_E_REVERTED;
5406 * Perform a sanity check on the parameters.
5408 if (ppenum==0)
5409 return E_INVALIDARG;
5411 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5412 This->storageDirEntry);
5416 * The new clone enumeration must point to the same current node as
5417 * the ole one.
5419 memcpy(newClone->name, This->name, sizeof(newClone->name));
5421 *ppenum = (IEnumSTATSTG*)newClone;
5424 * Don't forget to nail down a reference to the clone before
5425 * returning it.
5427 IEnumSTATSTGImpl_AddRef(*ppenum);
5429 return S_OK;
5433 * Virtual function table for the IEnumSTATSTGImpl class.
5435 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5437 IEnumSTATSTGImpl_QueryInterface,
5438 IEnumSTATSTGImpl_AddRef,
5439 IEnumSTATSTGImpl_Release,
5440 IEnumSTATSTGImpl_Next,
5441 IEnumSTATSTGImpl_Skip,
5442 IEnumSTATSTGImpl_Reset,
5443 IEnumSTATSTGImpl_Clone
5446 /******************************************************************************
5447 ** IEnumSTATSTGImpl implementation
5450 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5451 StorageBaseImpl* parentStorage,
5452 DirRef storageDirEntry)
5454 IEnumSTATSTGImpl* newEnumeration;
5456 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5458 if (newEnumeration!=0)
5461 * Set-up the virtual function table and reference count.
5463 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5464 newEnumeration->ref = 0;
5467 * We want to nail-down the reference to the storage in case the
5468 * enumeration out-lives the storage in the client application.
5470 newEnumeration->parentStorage = parentStorage;
5471 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5473 newEnumeration->storageDirEntry = storageDirEntry;
5476 * Make sure the current node of the iterator is the first one.
5478 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
5481 return newEnumeration;
5485 * Virtual function table for the Storage32InternalImpl class.
5487 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5489 StorageBaseImpl_QueryInterface,
5490 StorageBaseImpl_AddRef,
5491 StorageBaseImpl_Release,
5492 StorageBaseImpl_CreateStream,
5493 StorageBaseImpl_OpenStream,
5494 StorageBaseImpl_CreateStorage,
5495 StorageBaseImpl_OpenStorage,
5496 StorageBaseImpl_CopyTo,
5497 StorageBaseImpl_MoveElementTo,
5498 StorageInternalImpl_Commit,
5499 StorageInternalImpl_Revert,
5500 StorageBaseImpl_EnumElements,
5501 StorageBaseImpl_DestroyElement,
5502 StorageBaseImpl_RenameElement,
5503 StorageBaseImpl_SetElementTimes,
5504 StorageBaseImpl_SetClass,
5505 StorageBaseImpl_SetStateBits,
5506 StorageBaseImpl_Stat
5509 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5511 StorageInternalImpl_Destroy,
5512 StorageInternalImpl_Invalidate,
5513 StorageInternalImpl_Flush,
5514 StorageInternalImpl_GetFilename,
5515 StorageInternalImpl_CreateDirEntry,
5516 StorageInternalImpl_WriteDirEntry,
5517 StorageInternalImpl_ReadDirEntry,
5518 StorageInternalImpl_DestroyDirEntry,
5519 StorageInternalImpl_StreamReadAt,
5520 StorageInternalImpl_StreamWriteAt,
5521 StorageInternalImpl_StreamSetSize,
5522 StorageInternalImpl_StreamLink
5525 /******************************************************************************
5526 ** Storage32InternalImpl implementation
5529 static StorageInternalImpl* StorageInternalImpl_Construct(
5530 StorageBaseImpl* parentStorage,
5531 DWORD openFlags,
5532 DirRef storageDirEntry)
5534 StorageInternalImpl* newStorage;
5536 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5538 if (newStorage!=0)
5540 list_init(&newStorage->base.strmHead);
5542 list_init(&newStorage->base.storageHead);
5545 * Initialize the virtual function table.
5547 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5548 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5549 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5550 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5552 newStorage->base.reverted = 0;
5554 newStorage->base.ref = 1;
5556 newStorage->parentStorage = parentStorage;
5559 * Keep a reference to the directory entry of this storage
5561 newStorage->base.storageDirEntry = storageDirEntry;
5563 newStorage->base.create = 0;
5565 return newStorage;
5568 return 0;
5571 /******************************************************************************
5572 ** StorageUtl implementation
5575 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5577 WORD tmp;
5579 memcpy(&tmp, buffer+offset, sizeof(WORD));
5580 *value = lendian16toh(tmp);
5583 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5585 value = htole16(value);
5586 memcpy(buffer+offset, &value, sizeof(WORD));
5589 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5591 DWORD tmp;
5593 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5594 *value = lendian32toh(tmp);
5597 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5599 value = htole32(value);
5600 memcpy(buffer+offset, &value, sizeof(DWORD));
5603 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5604 ULARGE_INTEGER* value)
5606 #ifdef WORDS_BIGENDIAN
5607 ULARGE_INTEGER tmp;
5609 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5610 value->u.LowPart = htole32(tmp.u.HighPart);
5611 value->u.HighPart = htole32(tmp.u.LowPart);
5612 #else
5613 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5614 #endif
5617 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5618 const ULARGE_INTEGER *value)
5620 #ifdef WORDS_BIGENDIAN
5621 ULARGE_INTEGER tmp;
5623 tmp.u.LowPart = htole32(value->u.HighPart);
5624 tmp.u.HighPart = htole32(value->u.LowPart);
5625 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5626 #else
5627 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5628 #endif
5631 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5633 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5634 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5635 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5637 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5640 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5642 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5643 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5644 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5646 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5649 void StorageUtl_CopyDirEntryToSTATSTG(
5650 StorageBaseImpl* storage,
5651 STATSTG* destination,
5652 const DirEntry* source,
5653 int statFlags)
5656 * The copy of the string occurs only when the flag is not set
5658 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5660 /* Use the filename for the root storage. */
5661 destination->pwcsName = 0;
5662 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5664 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5665 (source->name[0] == 0) )
5667 destination->pwcsName = 0;
5669 else
5671 destination->pwcsName =
5672 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5674 strcpyW(destination->pwcsName, source->name);
5677 switch (source->stgType)
5679 case STGTY_STORAGE:
5680 case STGTY_ROOT:
5681 destination->type = STGTY_STORAGE;
5682 break;
5683 case STGTY_STREAM:
5684 destination->type = STGTY_STREAM;
5685 break;
5686 default:
5687 destination->type = STGTY_STREAM;
5688 break;
5691 destination->cbSize = source->size;
5693 currentReturnStruct->mtime = {0}; TODO
5694 currentReturnStruct->ctime = {0};
5695 currentReturnStruct->atime = {0};
5697 destination->grfMode = 0;
5698 destination->grfLocksSupported = 0;
5699 destination->clsid = source->clsid;
5700 destination->grfStateBits = 0;
5701 destination->reserved = 0;
5704 /******************************************************************************
5705 ** BlockChainStream implementation
5708 /* Read and save the index of all blocks in this stream. */
5709 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5711 ULONG next_sector, next_offset;
5712 HRESULT hr;
5713 struct BlockChainRun *last_run;
5715 if (This->indexCacheLen == 0)
5717 last_run = NULL;
5718 next_offset = 0;
5719 next_sector = BlockChainStream_GetHeadOfChain(This);
5721 else
5723 last_run = &This->indexCache[This->indexCacheLen-1];
5724 next_offset = last_run->lastOffset+1;
5725 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5726 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5727 &next_sector);
5728 if (FAILED(hr)) return hr;
5731 while (next_sector != BLOCK_END_OF_CHAIN)
5733 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5735 /* Add the current block to the cache. */
5736 if (This->indexCacheSize == 0)
5738 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5739 if (!This->indexCache) return E_OUTOFMEMORY;
5740 This->indexCacheSize = 16;
5742 else if (This->indexCacheSize == This->indexCacheLen)
5744 struct BlockChainRun *new_cache;
5745 ULONG new_size;
5747 new_size = This->indexCacheSize * 2;
5748 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5749 if (!new_cache) return E_OUTOFMEMORY;
5750 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5752 HeapFree(GetProcessHeap(), 0, This->indexCache);
5753 This->indexCache = new_cache;
5754 This->indexCacheSize = new_size;
5757 This->indexCacheLen++;
5758 last_run = &This->indexCache[This->indexCacheLen-1];
5759 last_run->firstSector = next_sector;
5760 last_run->firstOffset = next_offset;
5763 last_run->lastOffset = next_offset;
5765 /* Find the next block. */
5766 next_offset++;
5767 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5768 if (FAILED(hr)) return hr;
5771 if (This->indexCacheLen)
5773 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5774 This->numBlocks = last_run->lastOffset+1;
5776 else
5778 This->tailIndex = BLOCK_END_OF_CHAIN;
5779 This->numBlocks = 0;
5782 return S_OK;
5785 /* Locate the nth block in this stream. */
5786 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5788 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5789 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5791 if (offset >= This->numBlocks)
5792 return BLOCK_END_OF_CHAIN;
5794 while (min_run < max_run)
5796 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5797 if (offset < This->indexCache[run_to_check].firstOffset)
5799 max_offset = This->indexCache[run_to_check].firstOffset-1;
5800 max_run = run_to_check-1;
5802 else if (offset > This->indexCache[run_to_check].lastOffset)
5804 min_offset = This->indexCache[run_to_check].lastOffset+1;
5805 min_run = run_to_check+1;
5807 else
5808 /* Block is in this run. */
5809 min_run = max_run = run_to_check;
5812 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5815 BlockChainStream* BlockChainStream_Construct(
5816 StorageImpl* parentStorage,
5817 ULONG* headOfStreamPlaceHolder,
5818 DirRef dirEntry)
5820 BlockChainStream* newStream;
5822 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5824 newStream->parentStorage = parentStorage;
5825 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5826 newStream->ownerDirEntry = dirEntry;
5827 newStream->indexCache = NULL;
5828 newStream->indexCacheLen = 0;
5829 newStream->indexCacheSize = 0;
5831 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5833 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5834 HeapFree(GetProcessHeap(), 0, newStream);
5835 return NULL;
5838 return newStream;
5841 void BlockChainStream_Destroy(BlockChainStream* This)
5843 if (This)
5844 HeapFree(GetProcessHeap(), 0, This->indexCache);
5845 HeapFree(GetProcessHeap(), 0, This);
5848 /******************************************************************************
5849 * BlockChainStream_GetHeadOfChain
5851 * Returns the head of this stream chain.
5852 * Some special chains don't have directory entries, their heads are kept in
5853 * This->headOfStreamPlaceHolder.
5856 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5858 DirEntry chainEntry;
5859 HRESULT hr;
5861 if (This->headOfStreamPlaceHolder != 0)
5862 return *(This->headOfStreamPlaceHolder);
5864 if (This->ownerDirEntry != DIRENTRY_NULL)
5866 hr = StorageImpl_ReadDirEntry(
5867 This->parentStorage,
5868 This->ownerDirEntry,
5869 &chainEntry);
5871 if (SUCCEEDED(hr))
5873 return chainEntry.startingBlock;
5877 return BLOCK_END_OF_CHAIN;
5880 /******************************************************************************
5881 * BlockChainStream_GetCount
5883 * Returns the number of blocks that comprises this chain.
5884 * This is not the size of the stream as the last block may not be full!
5886 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5888 return This->numBlocks;
5891 /******************************************************************************
5892 * BlockChainStream_ReadAt
5894 * Reads a specified number of bytes from this chain at the specified offset.
5895 * bytesRead may be NULL.
5896 * Failure will be returned if the specified number of bytes has not been read.
5898 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5899 ULARGE_INTEGER offset,
5900 ULONG size,
5901 void* buffer,
5902 ULONG* bytesRead)
5904 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5905 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5906 ULONG bytesToReadInBuffer;
5907 ULONG blockIndex;
5908 BYTE* bufferWalker;
5909 ULARGE_INTEGER stream_size;
5911 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5914 * Find the first block in the stream that contains part of the buffer.
5916 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5918 *bytesRead = 0;
5920 stream_size = BlockChainStream_GetSize(This);
5921 if (stream_size.QuadPart > offset.QuadPart)
5922 size = min(stream_size.QuadPart - offset.QuadPart, size);
5923 else
5924 return S_OK;
5927 * Start reading the buffer.
5929 bufferWalker = buffer;
5931 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5933 ULARGE_INTEGER ulOffset;
5934 DWORD bytesReadAt;
5936 * Calculate how many bytes we can copy from this big block.
5938 bytesToReadInBuffer =
5939 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5941 TRACE("block %i\n",blockIndex);
5942 ulOffset.u.HighPart = 0;
5943 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5944 offsetInBlock;
5946 StorageImpl_ReadAt(This->parentStorage,
5947 ulOffset,
5948 bufferWalker,
5949 bytesToReadInBuffer,
5950 &bytesReadAt);
5952 * Step to the next big block.
5954 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5955 return STG_E_DOCFILECORRUPT;
5957 bufferWalker += bytesReadAt;
5958 size -= bytesReadAt;
5959 *bytesRead += bytesReadAt;
5960 offsetInBlock = 0; /* There is no offset on the next block */
5962 if (bytesToReadInBuffer != bytesReadAt)
5963 break;
5966 return S_OK;
5969 /******************************************************************************
5970 * BlockChainStream_WriteAt
5972 * Writes the specified number of bytes to this chain at the specified offset.
5973 * Will fail if not all specified number of bytes have been written.
5975 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5976 ULARGE_INTEGER offset,
5977 ULONG size,
5978 const void* buffer,
5979 ULONG* bytesWritten)
5981 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5982 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5983 ULONG bytesToWrite;
5984 ULONG blockIndex;
5985 const BYTE* bufferWalker;
5988 * Find the first block in the stream that contains part of the buffer.
5990 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5992 /* BlockChainStream_SetSize should have already been called to ensure we have
5993 * enough blocks in the chain to write into */
5994 if (blockIndex == BLOCK_END_OF_CHAIN)
5996 ERR("not enough blocks in chain to write data\n");
5997 return STG_E_DOCFILECORRUPT;
6000 *bytesWritten = 0;
6001 bufferWalker = buffer;
6003 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6005 ULARGE_INTEGER ulOffset;
6006 DWORD bytesWrittenAt;
6008 * Calculate how many bytes we can copy from this big block.
6010 bytesToWrite =
6011 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6013 TRACE("block %i\n",blockIndex);
6014 ulOffset.u.HighPart = 0;
6015 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6016 offsetInBlock;
6018 StorageImpl_WriteAt(This->parentStorage,
6019 ulOffset,
6020 bufferWalker,
6021 bytesToWrite,
6022 &bytesWrittenAt);
6025 * Step to the next big block.
6027 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
6028 &blockIndex)))
6029 return STG_E_DOCFILECORRUPT;
6031 bufferWalker += bytesWrittenAt;
6032 size -= bytesWrittenAt;
6033 *bytesWritten += bytesWrittenAt;
6034 offsetInBlock = 0; /* There is no offset on the next block */
6036 if (bytesWrittenAt != bytesToWrite)
6037 break;
6040 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6043 /******************************************************************************
6044 * BlockChainStream_Shrink
6046 * Shrinks this chain in the big block depot.
6048 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6049 ULARGE_INTEGER newSize)
6051 ULONG blockIndex;
6052 ULONG numBlocks;
6055 * Figure out how many blocks are needed to contain the new size
6057 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6059 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6060 numBlocks++;
6062 if (numBlocks)
6065 * Go to the new end of chain
6067 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6069 /* Mark the new end of chain */
6070 StorageImpl_SetNextBlockInChain(
6071 This->parentStorage,
6072 blockIndex,
6073 BLOCK_END_OF_CHAIN);
6075 This->tailIndex = blockIndex;
6077 else
6079 if (This->headOfStreamPlaceHolder != 0)
6081 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6083 else
6085 DirEntry chainEntry;
6086 assert(This->ownerDirEntry != DIRENTRY_NULL);
6088 StorageImpl_ReadDirEntry(
6089 This->parentStorage,
6090 This->ownerDirEntry,
6091 &chainEntry);
6093 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6095 StorageImpl_WriteDirEntry(
6096 This->parentStorage,
6097 This->ownerDirEntry,
6098 &chainEntry);
6101 This->tailIndex = BLOCK_END_OF_CHAIN;
6104 This->numBlocks = numBlocks;
6107 * Mark the extra blocks as free
6109 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6111 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6112 StorageImpl_FreeBigBlock(This->parentStorage,
6113 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6114 if (last_run->lastOffset == last_run->firstOffset)
6115 This->indexCacheLen--;
6116 else
6117 last_run->lastOffset--;
6120 return TRUE;
6123 /******************************************************************************
6124 * BlockChainStream_Enlarge
6126 * Grows this chain in the big block depot.
6128 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6129 ULARGE_INTEGER newSize)
6131 ULONG blockIndex, currentBlock;
6132 ULONG newNumBlocks;
6133 ULONG oldNumBlocks = 0;
6135 blockIndex = BlockChainStream_GetHeadOfChain(This);
6138 * Empty chain. Create the head.
6140 if (blockIndex == BLOCK_END_OF_CHAIN)
6142 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6143 StorageImpl_SetNextBlockInChain(This->parentStorage,
6144 blockIndex,
6145 BLOCK_END_OF_CHAIN);
6147 if (This->headOfStreamPlaceHolder != 0)
6149 *(This->headOfStreamPlaceHolder) = blockIndex;
6151 else
6153 DirEntry chainEntry;
6154 assert(This->ownerDirEntry != DIRENTRY_NULL);
6156 StorageImpl_ReadDirEntry(
6157 This->parentStorage,
6158 This->ownerDirEntry,
6159 &chainEntry);
6161 chainEntry.startingBlock = blockIndex;
6163 StorageImpl_WriteDirEntry(
6164 This->parentStorage,
6165 This->ownerDirEntry,
6166 &chainEntry);
6169 This->tailIndex = blockIndex;
6170 This->numBlocks = 1;
6174 * Figure out how many blocks are needed to contain this stream
6176 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6178 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6179 newNumBlocks++;
6182 * Go to the current end of chain
6184 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6186 currentBlock = blockIndex;
6188 while (blockIndex != BLOCK_END_OF_CHAIN)
6190 This->numBlocks++;
6191 currentBlock = blockIndex;
6193 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6194 &blockIndex)))
6195 return FALSE;
6198 This->tailIndex = currentBlock;
6201 currentBlock = This->tailIndex;
6202 oldNumBlocks = This->numBlocks;
6205 * Add new blocks to the chain
6207 if (oldNumBlocks < newNumBlocks)
6209 while (oldNumBlocks < newNumBlocks)
6211 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6213 StorageImpl_SetNextBlockInChain(
6214 This->parentStorage,
6215 currentBlock,
6216 blockIndex);
6218 StorageImpl_SetNextBlockInChain(
6219 This->parentStorage,
6220 blockIndex,
6221 BLOCK_END_OF_CHAIN);
6223 currentBlock = blockIndex;
6224 oldNumBlocks++;
6227 This->tailIndex = blockIndex;
6228 This->numBlocks = newNumBlocks;
6231 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6232 return FALSE;
6234 return TRUE;
6237 /******************************************************************************
6238 * BlockChainStream_SetSize
6240 * Sets the size of this stream. The big block depot will be updated.
6241 * The file will grow if we grow the chain.
6243 * TODO: Free the actual blocks in the file when we shrink the chain.
6244 * Currently, the blocks are still in the file. So the file size
6245 * doesn't shrink even if we shrink streams.
6247 BOOL BlockChainStream_SetSize(
6248 BlockChainStream* This,
6249 ULARGE_INTEGER newSize)
6251 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6253 if (newSize.u.LowPart == size.u.LowPart)
6254 return TRUE;
6256 if (newSize.u.LowPart < size.u.LowPart)
6258 BlockChainStream_Shrink(This, newSize);
6260 else
6262 BlockChainStream_Enlarge(This, newSize);
6265 return TRUE;
6268 /******************************************************************************
6269 * BlockChainStream_GetSize
6271 * Returns the size of this chain.
6272 * Will return the block count if this chain doesn't have a directory entry.
6274 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6276 DirEntry chainEntry;
6278 if(This->headOfStreamPlaceHolder == NULL)
6281 * This chain has a directory entry so use the size value from there.
6283 StorageImpl_ReadDirEntry(
6284 This->parentStorage,
6285 This->ownerDirEntry,
6286 &chainEntry);
6288 return chainEntry.size;
6290 else
6293 * this chain is a chain that does not have a directory entry, figure out the
6294 * size by making the product number of used blocks times the
6295 * size of them
6297 ULARGE_INTEGER result;
6298 result.u.HighPart = 0;
6300 result.u.LowPart =
6301 BlockChainStream_GetCount(This) *
6302 This->parentStorage->bigBlockSize;
6304 return result;
6308 /******************************************************************************
6309 ** SmallBlockChainStream implementation
6312 SmallBlockChainStream* SmallBlockChainStream_Construct(
6313 StorageImpl* parentStorage,
6314 ULONG* headOfStreamPlaceHolder,
6315 DirRef dirEntry)
6317 SmallBlockChainStream* newStream;
6319 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6321 newStream->parentStorage = parentStorage;
6322 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6323 newStream->ownerDirEntry = dirEntry;
6325 return newStream;
6328 void SmallBlockChainStream_Destroy(
6329 SmallBlockChainStream* This)
6331 HeapFree(GetProcessHeap(), 0, This);
6334 /******************************************************************************
6335 * SmallBlockChainStream_GetHeadOfChain
6337 * Returns the head of this chain of small blocks.
6339 static ULONG SmallBlockChainStream_GetHeadOfChain(
6340 SmallBlockChainStream* This)
6342 DirEntry chainEntry;
6343 HRESULT hr;
6345 if (This->headOfStreamPlaceHolder != NULL)
6346 return *(This->headOfStreamPlaceHolder);
6348 if (This->ownerDirEntry)
6350 hr = StorageImpl_ReadDirEntry(
6351 This->parentStorage,
6352 This->ownerDirEntry,
6353 &chainEntry);
6355 if (SUCCEEDED(hr))
6357 return chainEntry.startingBlock;
6362 return BLOCK_END_OF_CHAIN;
6365 /******************************************************************************
6366 * SmallBlockChainStream_GetNextBlockInChain
6368 * Returns the index of the next small block in this chain.
6370 * Return Values:
6371 * - BLOCK_END_OF_CHAIN: end of this chain
6372 * - BLOCK_UNUSED: small block 'blockIndex' is free
6374 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6375 SmallBlockChainStream* This,
6376 ULONG blockIndex,
6377 ULONG* nextBlockInChain)
6379 ULARGE_INTEGER offsetOfBlockInDepot;
6380 DWORD buffer;
6381 ULONG bytesRead;
6382 HRESULT res;
6384 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6386 offsetOfBlockInDepot.u.HighPart = 0;
6387 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6390 * Read those bytes in the buffer from the small block file.
6392 res = BlockChainStream_ReadAt(
6393 This->parentStorage->smallBlockDepotChain,
6394 offsetOfBlockInDepot,
6395 sizeof(DWORD),
6396 &buffer,
6397 &bytesRead);
6399 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6400 res = STG_E_READFAULT;
6402 if (SUCCEEDED(res))
6404 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6405 return S_OK;
6408 return res;
6411 /******************************************************************************
6412 * SmallBlockChainStream_SetNextBlockInChain
6414 * Writes the index of the next block of the specified block in the small
6415 * block depot.
6416 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6417 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6419 static void SmallBlockChainStream_SetNextBlockInChain(
6420 SmallBlockChainStream* This,
6421 ULONG blockIndex,
6422 ULONG nextBlock)
6424 ULARGE_INTEGER offsetOfBlockInDepot;
6425 DWORD buffer;
6426 ULONG bytesWritten;
6428 offsetOfBlockInDepot.u.HighPart = 0;
6429 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6431 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6434 * Read those bytes in the buffer from the small block file.
6436 BlockChainStream_WriteAt(
6437 This->parentStorage->smallBlockDepotChain,
6438 offsetOfBlockInDepot,
6439 sizeof(DWORD),
6440 &buffer,
6441 &bytesWritten);
6444 /******************************************************************************
6445 * SmallBlockChainStream_FreeBlock
6447 * Flag small block 'blockIndex' as free in the small block depot.
6449 static void SmallBlockChainStream_FreeBlock(
6450 SmallBlockChainStream* This,
6451 ULONG blockIndex)
6453 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6456 /******************************************************************************
6457 * SmallBlockChainStream_GetNextFreeBlock
6459 * Returns the index of a free small block. The small block depot will be
6460 * enlarged if necessary. The small block chain will also be enlarged if
6461 * necessary.
6463 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6464 SmallBlockChainStream* This)
6466 ULARGE_INTEGER offsetOfBlockInDepot;
6467 DWORD buffer;
6468 ULONG bytesRead;
6469 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6470 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6471 HRESULT res = S_OK;
6472 ULONG smallBlocksPerBigBlock;
6473 DirEntry rootEntry;
6474 ULONG blocksRequired;
6475 ULARGE_INTEGER old_size, size_required;
6477 offsetOfBlockInDepot.u.HighPart = 0;
6480 * Scan the small block depot for a free block
6482 while (nextBlockIndex != BLOCK_UNUSED)
6484 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6486 res = BlockChainStream_ReadAt(
6487 This->parentStorage->smallBlockDepotChain,
6488 offsetOfBlockInDepot,
6489 sizeof(DWORD),
6490 &buffer,
6491 &bytesRead);
6494 * If we run out of space for the small block depot, enlarge it
6496 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6498 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6500 if (nextBlockIndex != BLOCK_UNUSED)
6501 blockIndex++;
6503 else
6505 ULONG count =
6506 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6508 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6509 ULARGE_INTEGER newSize, offset;
6510 ULONG bytesWritten;
6512 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6513 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6516 * Initialize all the small blocks to free
6518 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6519 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6520 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6521 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6523 StorageImpl_SaveFileHeader(This->parentStorage);
6527 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6529 smallBlocksPerBigBlock =
6530 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6533 * Verify if we have to allocate big blocks to contain small blocks
6535 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6537 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6539 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6541 if (size_required.QuadPart > old_size.QuadPart)
6543 BlockChainStream_SetSize(
6544 This->parentStorage->smallBlockRootChain,
6545 size_required);
6547 StorageImpl_ReadDirEntry(
6548 This->parentStorage,
6549 This->parentStorage->base.storageDirEntry,
6550 &rootEntry);
6552 rootEntry.size = size_required;
6554 StorageImpl_WriteDirEntry(
6555 This->parentStorage,
6556 This->parentStorage->base.storageDirEntry,
6557 &rootEntry);
6560 return blockIndex;
6563 /******************************************************************************
6564 * SmallBlockChainStream_ReadAt
6566 * Reads a specified number of bytes from this chain at the specified offset.
6567 * bytesRead may be NULL.
6568 * Failure will be returned if the specified number of bytes has not been read.
6570 HRESULT SmallBlockChainStream_ReadAt(
6571 SmallBlockChainStream* This,
6572 ULARGE_INTEGER offset,
6573 ULONG size,
6574 void* buffer,
6575 ULONG* bytesRead)
6577 HRESULT rc = S_OK;
6578 ULARGE_INTEGER offsetInBigBlockFile;
6579 ULONG blockNoInSequence =
6580 offset.u.LowPart / This->parentStorage->smallBlockSize;
6582 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6583 ULONG bytesToReadInBuffer;
6584 ULONG blockIndex;
6585 ULONG bytesReadFromBigBlockFile;
6586 BYTE* bufferWalker;
6587 ULARGE_INTEGER stream_size;
6590 * This should never happen on a small block file.
6592 assert(offset.u.HighPart==0);
6594 *bytesRead = 0;
6596 stream_size = SmallBlockChainStream_GetSize(This);
6597 if (stream_size.QuadPart > offset.QuadPart)
6598 size = min(stream_size.QuadPart - offset.QuadPart, size);
6599 else
6600 return S_OK;
6603 * Find the first block in the stream that contains part of the buffer.
6605 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6607 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6609 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6610 if(FAILED(rc))
6611 return rc;
6612 blockNoInSequence--;
6616 * Start reading the buffer.
6618 bufferWalker = buffer;
6620 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6623 * Calculate how many bytes we can copy from this small block.
6625 bytesToReadInBuffer =
6626 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6629 * Calculate the offset of the small block in the small block file.
6631 offsetInBigBlockFile.u.HighPart = 0;
6632 offsetInBigBlockFile.u.LowPart =
6633 blockIndex * This->parentStorage->smallBlockSize;
6635 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6638 * Read those bytes in the buffer from the small block file.
6639 * The small block has already been identified so it shouldn't fail
6640 * unless the file is corrupt.
6642 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6643 offsetInBigBlockFile,
6644 bytesToReadInBuffer,
6645 bufferWalker,
6646 &bytesReadFromBigBlockFile);
6648 if (FAILED(rc))
6649 return rc;
6651 if (!bytesReadFromBigBlockFile)
6652 return STG_E_DOCFILECORRUPT;
6655 * Step to the next big block.
6657 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6658 if(FAILED(rc))
6659 return STG_E_DOCFILECORRUPT;
6661 bufferWalker += bytesReadFromBigBlockFile;
6662 size -= bytesReadFromBigBlockFile;
6663 *bytesRead += bytesReadFromBigBlockFile;
6664 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6667 return S_OK;
6670 /******************************************************************************
6671 * SmallBlockChainStream_WriteAt
6673 * Writes the specified number of bytes to this chain at the specified offset.
6674 * Will fail if not all specified number of bytes have been written.
6676 HRESULT SmallBlockChainStream_WriteAt(
6677 SmallBlockChainStream* This,
6678 ULARGE_INTEGER offset,
6679 ULONG size,
6680 const void* buffer,
6681 ULONG* bytesWritten)
6683 ULARGE_INTEGER offsetInBigBlockFile;
6684 ULONG blockNoInSequence =
6685 offset.u.LowPart / This->parentStorage->smallBlockSize;
6687 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6688 ULONG bytesToWriteInBuffer;
6689 ULONG blockIndex;
6690 ULONG bytesWrittenToBigBlockFile;
6691 const BYTE* bufferWalker;
6692 HRESULT res;
6695 * This should never happen on a small block file.
6697 assert(offset.u.HighPart==0);
6700 * Find the first block in the stream that contains part of the buffer.
6702 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6704 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6706 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6707 return STG_E_DOCFILECORRUPT;
6708 blockNoInSequence--;
6712 * Start writing the buffer.
6714 *bytesWritten = 0;
6715 bufferWalker = buffer;
6716 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6719 * Calculate how many bytes we can copy to this small block.
6721 bytesToWriteInBuffer =
6722 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6725 * Calculate the offset of the small block in the small block file.
6727 offsetInBigBlockFile.u.HighPart = 0;
6728 offsetInBigBlockFile.u.LowPart =
6729 blockIndex * This->parentStorage->smallBlockSize;
6731 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6734 * Write those bytes in the buffer to the small block file.
6736 res = BlockChainStream_WriteAt(
6737 This->parentStorage->smallBlockRootChain,
6738 offsetInBigBlockFile,
6739 bytesToWriteInBuffer,
6740 bufferWalker,
6741 &bytesWrittenToBigBlockFile);
6742 if (FAILED(res))
6743 return res;
6746 * Step to the next big block.
6748 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6749 &blockIndex)))
6750 return FALSE;
6751 bufferWalker += bytesWrittenToBigBlockFile;
6752 size -= bytesWrittenToBigBlockFile;
6753 *bytesWritten += bytesWrittenToBigBlockFile;
6754 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6757 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6760 /******************************************************************************
6761 * SmallBlockChainStream_Shrink
6763 * Shrinks this chain in the small block depot.
6765 static BOOL SmallBlockChainStream_Shrink(
6766 SmallBlockChainStream* This,
6767 ULARGE_INTEGER newSize)
6769 ULONG blockIndex, extraBlock;
6770 ULONG numBlocks;
6771 ULONG count = 0;
6773 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6775 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6776 numBlocks++;
6778 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6781 * Go to the new end of chain
6783 while (count < numBlocks)
6785 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6786 &blockIndex)))
6787 return FALSE;
6788 count++;
6792 * If the count is 0, we have a special case, the head of the chain was
6793 * just freed.
6795 if (count == 0)
6797 DirEntry chainEntry;
6799 StorageImpl_ReadDirEntry(This->parentStorage,
6800 This->ownerDirEntry,
6801 &chainEntry);
6803 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6805 StorageImpl_WriteDirEntry(This->parentStorage,
6806 This->ownerDirEntry,
6807 &chainEntry);
6810 * We start freeing the chain at the head block.
6812 extraBlock = blockIndex;
6814 else
6816 /* Get the next block before marking the new end */
6817 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6818 &extraBlock)))
6819 return FALSE;
6821 /* Mark the new end of chain */
6822 SmallBlockChainStream_SetNextBlockInChain(
6823 This,
6824 blockIndex,
6825 BLOCK_END_OF_CHAIN);
6829 * Mark the extra blocks as free
6831 while (extraBlock != BLOCK_END_OF_CHAIN)
6833 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6834 &blockIndex)))
6835 return FALSE;
6836 SmallBlockChainStream_FreeBlock(This, extraBlock);
6837 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
6838 extraBlock = blockIndex;
6841 return TRUE;
6844 /******************************************************************************
6845 * SmallBlockChainStream_Enlarge
6847 * Grows this chain in the small block depot.
6849 static BOOL SmallBlockChainStream_Enlarge(
6850 SmallBlockChainStream* This,
6851 ULARGE_INTEGER newSize)
6853 ULONG blockIndex, currentBlock;
6854 ULONG newNumBlocks;
6855 ULONG oldNumBlocks = 0;
6857 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6860 * Empty chain. Create the head.
6862 if (blockIndex == BLOCK_END_OF_CHAIN)
6864 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6865 SmallBlockChainStream_SetNextBlockInChain(
6866 This,
6867 blockIndex,
6868 BLOCK_END_OF_CHAIN);
6870 if (This->headOfStreamPlaceHolder != NULL)
6872 *(This->headOfStreamPlaceHolder) = blockIndex;
6874 else
6876 DirEntry chainEntry;
6878 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6879 &chainEntry);
6881 chainEntry.startingBlock = blockIndex;
6883 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6884 &chainEntry);
6888 currentBlock = blockIndex;
6891 * Figure out how many blocks are needed to contain this stream
6893 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6895 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6896 newNumBlocks++;
6899 * Go to the current end of chain
6901 while (blockIndex != BLOCK_END_OF_CHAIN)
6903 oldNumBlocks++;
6904 currentBlock = blockIndex;
6905 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6906 return FALSE;
6910 * Add new blocks to the chain
6912 while (oldNumBlocks < newNumBlocks)
6914 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6915 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6917 SmallBlockChainStream_SetNextBlockInChain(
6918 This,
6919 blockIndex,
6920 BLOCK_END_OF_CHAIN);
6922 currentBlock = blockIndex;
6923 oldNumBlocks++;
6926 return TRUE;
6929 /******************************************************************************
6930 * SmallBlockChainStream_SetSize
6932 * Sets the size of this stream.
6933 * The file will grow if we grow the chain.
6935 * TODO: Free the actual blocks in the file when we shrink the chain.
6936 * Currently, the blocks are still in the file. So the file size
6937 * doesn't shrink even if we shrink streams.
6939 BOOL SmallBlockChainStream_SetSize(
6940 SmallBlockChainStream* This,
6941 ULARGE_INTEGER newSize)
6943 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6945 if (newSize.u.LowPart == size.u.LowPart)
6946 return TRUE;
6948 if (newSize.u.LowPart < size.u.LowPart)
6950 SmallBlockChainStream_Shrink(This, newSize);
6952 else
6954 SmallBlockChainStream_Enlarge(This, newSize);
6957 return TRUE;
6960 /******************************************************************************
6961 * SmallBlockChainStream_GetCount
6963 * Returns the number of small blocks that comprises this chain.
6964 * This is not the size of the stream as the last block may not be full!
6967 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6969 ULONG blockIndex;
6970 ULONG count = 0;
6972 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6974 while(blockIndex != BLOCK_END_OF_CHAIN)
6976 count++;
6978 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6979 blockIndex, &blockIndex)))
6980 return 0;
6983 return count;
6986 /******************************************************************************
6987 * SmallBlockChainStream_GetSize
6989 * Returns the size of this chain.
6991 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6993 DirEntry chainEntry;
6995 if(This->headOfStreamPlaceHolder != NULL)
6997 ULARGE_INTEGER result;
6998 result.u.HighPart = 0;
7000 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7001 This->parentStorage->smallBlockSize;
7003 return result;
7006 StorageImpl_ReadDirEntry(
7007 This->parentStorage,
7008 This->ownerDirEntry,
7009 &chainEntry);
7011 return chainEntry.size;
7014 static HRESULT create_storagefile(
7015 LPCOLESTR pwcsName,
7016 DWORD grfMode,
7017 DWORD grfAttrs,
7018 STGOPTIONS* pStgOptions,
7019 REFIID riid,
7020 void** ppstgOpen)
7022 StorageBaseImpl* newStorage = 0;
7023 HANDLE hFile = INVALID_HANDLE_VALUE;
7024 HRESULT hr = STG_E_INVALIDFLAG;
7025 DWORD shareMode;
7026 DWORD accessMode;
7027 DWORD creationMode;
7028 DWORD fileAttributes;
7029 WCHAR tempFileName[MAX_PATH];
7031 if (ppstgOpen == 0)
7032 return STG_E_INVALIDPOINTER;
7034 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7035 return STG_E_INVALIDPARAMETER;
7037 /* if no share mode given then DENY_NONE is the default */
7038 if (STGM_SHARE_MODE(grfMode) == 0)
7039 grfMode |= STGM_SHARE_DENY_NONE;
7041 if ( FAILED( validateSTGM(grfMode) ))
7042 goto end;
7044 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7045 switch(STGM_ACCESS_MODE(grfMode))
7047 case STGM_WRITE:
7048 case STGM_READWRITE:
7049 break;
7050 default:
7051 goto end;
7054 /* in direct mode, can only use SHARE_EXCLUSIVE */
7055 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7056 goto end;
7058 /* but in transacted mode, any share mode is valid */
7061 * Generate a unique name.
7063 if (pwcsName == 0)
7065 WCHAR tempPath[MAX_PATH];
7066 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7068 memset(tempPath, 0, sizeof(tempPath));
7069 memset(tempFileName, 0, sizeof(tempFileName));
7071 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7072 tempPath[0] = '.';
7074 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7075 pwcsName = tempFileName;
7076 else
7078 hr = STG_E_INSUFFICIENTMEMORY;
7079 goto end;
7082 creationMode = TRUNCATE_EXISTING;
7084 else
7086 creationMode = GetCreationModeFromSTGM(grfMode);
7090 * Interpret the STGM value grfMode
7092 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7093 accessMode = GetAccessModeFromSTGM(grfMode);
7095 if (grfMode & STGM_DELETEONRELEASE)
7096 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7097 else
7098 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7100 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7102 static int fixme;
7103 if (!fixme++)
7104 FIXME("Storage share mode not implemented.\n");
7107 *ppstgOpen = 0;
7109 hFile = CreateFileW(pwcsName,
7110 accessMode,
7111 shareMode,
7112 NULL,
7113 creationMode,
7114 fileAttributes,
7117 if (hFile == INVALID_HANDLE_VALUE)
7119 if(GetLastError() == ERROR_FILE_EXISTS)
7120 hr = STG_E_FILEALREADYEXISTS;
7121 else
7122 hr = E_FAIL;
7123 goto end;
7127 * Allocate and initialize the new IStorage32object.
7129 hr = Storage_Construct(
7130 hFile,
7131 pwcsName,
7132 NULL,
7133 grfMode,
7134 TRUE,
7135 TRUE,
7136 pStgOptions->ulSectorSize,
7137 &newStorage);
7139 if (FAILED(hr))
7141 goto end;
7144 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7146 IStorage_Release((IStorage*)newStorage);
7148 end:
7149 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7151 return hr;
7154 /******************************************************************************
7155 * StgCreateDocfile [OLE32.@]
7156 * Creates a new compound file storage object
7158 * PARAMS
7159 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7160 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7161 * reserved [ ?] unused?, usually 0
7162 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7164 * RETURNS
7165 * S_OK if the file was successfully created
7166 * some STG_E_ value if error
7167 * NOTES
7168 * if pwcsName is NULL, create file with new unique name
7169 * the function can returns
7170 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7171 * (unrealized now)
7173 HRESULT WINAPI StgCreateDocfile(
7174 LPCOLESTR pwcsName,
7175 DWORD grfMode,
7176 DWORD reserved,
7177 IStorage **ppstgOpen)
7179 STGOPTIONS stgoptions = {1, 0, 512};
7181 TRACE("(%s, %x, %d, %p)\n",
7182 debugstr_w(pwcsName), grfMode,
7183 reserved, ppstgOpen);
7185 if (ppstgOpen == 0)
7186 return STG_E_INVALIDPOINTER;
7187 if (reserved != 0)
7188 return STG_E_INVALIDPARAMETER;
7190 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7193 /******************************************************************************
7194 * StgCreateStorageEx [OLE32.@]
7196 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7198 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7199 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7201 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7203 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7204 return STG_E_INVALIDPARAMETER;
7207 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7209 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7210 return STG_E_INVALIDPARAMETER;
7213 if (stgfmt == STGFMT_FILE)
7215 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7216 return STG_E_INVALIDPARAMETER;
7219 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7221 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7225 ERR("Invalid stgfmt argument\n");
7226 return STG_E_INVALIDPARAMETER;
7229 /******************************************************************************
7230 * StgCreatePropSetStg [OLE32.@]
7232 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7233 IPropertySetStorage **ppPropSetStg)
7235 HRESULT hr;
7237 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7238 if (reserved)
7239 hr = STG_E_INVALIDPARAMETER;
7240 else
7241 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7242 (void**)ppPropSetStg);
7243 return hr;
7246 /******************************************************************************
7247 * StgOpenStorageEx [OLE32.@]
7249 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7251 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7252 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7254 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7256 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7257 return STG_E_INVALIDPARAMETER;
7260 switch (stgfmt)
7262 case STGFMT_FILE:
7263 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7264 return STG_E_INVALIDPARAMETER;
7266 case STGFMT_STORAGE:
7267 break;
7269 case STGFMT_DOCFILE:
7270 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7272 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7273 return STG_E_INVALIDPARAMETER;
7275 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7276 break;
7278 case STGFMT_ANY:
7279 WARN("STGFMT_ANY assuming storage\n");
7280 break;
7282 default:
7283 return STG_E_INVALIDPARAMETER;
7286 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7290 /******************************************************************************
7291 * StgOpenStorage [OLE32.@]
7293 HRESULT WINAPI StgOpenStorage(
7294 const OLECHAR *pwcsName,
7295 IStorage *pstgPriority,
7296 DWORD grfMode,
7297 SNB snbExclude,
7298 DWORD reserved,
7299 IStorage **ppstgOpen)
7301 StorageBaseImpl* newStorage = 0;
7302 HRESULT hr = S_OK;
7303 HANDLE hFile = 0;
7304 DWORD shareMode;
7305 DWORD accessMode;
7307 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7308 debugstr_w(pwcsName), pstgPriority, grfMode,
7309 snbExclude, reserved, ppstgOpen);
7311 if (pwcsName == 0)
7313 hr = STG_E_INVALIDNAME;
7314 goto end;
7317 if (ppstgOpen == 0)
7319 hr = STG_E_INVALIDPOINTER;
7320 goto end;
7323 if (reserved)
7325 hr = STG_E_INVALIDPARAMETER;
7326 goto end;
7329 if (grfMode & STGM_PRIORITY)
7331 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7332 return STG_E_INVALIDFLAG;
7333 if (grfMode & STGM_DELETEONRELEASE)
7334 return STG_E_INVALIDFUNCTION;
7335 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7336 return STG_E_INVALIDFLAG;
7337 grfMode &= ~0xf0; /* remove the existing sharing mode */
7338 grfMode |= STGM_SHARE_DENY_NONE;
7340 /* STGM_PRIORITY stops other IStorage objects on the same file from
7341 * committing until the STGM_PRIORITY IStorage is closed. it also
7342 * stops non-transacted mode StgOpenStorage calls with write access from
7343 * succeeding. obviously, both of these cannot be achieved through just
7344 * file share flags */
7345 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7349 * Validate the sharing mode
7351 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7352 switch(STGM_SHARE_MODE(grfMode))
7354 case STGM_SHARE_EXCLUSIVE:
7355 case STGM_SHARE_DENY_WRITE:
7356 break;
7357 default:
7358 hr = STG_E_INVALIDFLAG;
7359 goto end;
7362 if ( FAILED( validateSTGM(grfMode) ) ||
7363 (grfMode&STGM_CREATE))
7365 hr = STG_E_INVALIDFLAG;
7366 goto end;
7369 /* shared reading requires transacted mode */
7370 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7371 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7372 !(grfMode&STGM_TRANSACTED) )
7374 hr = STG_E_INVALIDFLAG;
7375 goto end;
7379 * Interpret the STGM value grfMode
7381 shareMode = GetShareModeFromSTGM(grfMode);
7382 accessMode = GetAccessModeFromSTGM(grfMode);
7384 *ppstgOpen = 0;
7386 hFile = CreateFileW( pwcsName,
7387 accessMode,
7388 shareMode,
7389 NULL,
7390 OPEN_EXISTING,
7391 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7394 if (hFile==INVALID_HANDLE_VALUE)
7396 DWORD last_error = GetLastError();
7398 hr = E_FAIL;
7400 switch (last_error)
7402 case ERROR_FILE_NOT_FOUND:
7403 hr = STG_E_FILENOTFOUND;
7404 break;
7406 case ERROR_PATH_NOT_FOUND:
7407 hr = STG_E_PATHNOTFOUND;
7408 break;
7410 case ERROR_ACCESS_DENIED:
7411 case ERROR_WRITE_PROTECT:
7412 hr = STG_E_ACCESSDENIED;
7413 break;
7415 case ERROR_SHARING_VIOLATION:
7416 hr = STG_E_SHAREVIOLATION;
7417 break;
7419 default:
7420 hr = E_FAIL;
7423 goto end;
7427 * Refuse to open the file if it's too small to be a structured storage file
7428 * FIXME: verify the file when reading instead of here
7430 if (GetFileSize(hFile, NULL) < 0x100)
7432 CloseHandle(hFile);
7433 hr = STG_E_FILEALREADYEXISTS;
7434 goto end;
7438 * Allocate and initialize the new IStorage32object.
7440 hr = Storage_Construct(
7441 hFile,
7442 pwcsName,
7443 NULL,
7444 grfMode,
7445 TRUE,
7446 FALSE,
7447 512,
7448 &newStorage);
7450 if (FAILED(hr))
7453 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7455 if(hr == STG_E_INVALIDHEADER)
7456 hr = STG_E_FILEALREADYEXISTS;
7457 goto end;
7461 * Get an "out" pointer for the caller.
7463 *ppstgOpen = (IStorage*)newStorage;
7465 end:
7466 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7467 return hr;
7470 /******************************************************************************
7471 * StgCreateDocfileOnILockBytes [OLE32.@]
7473 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7474 ILockBytes *plkbyt,
7475 DWORD grfMode,
7476 DWORD reserved,
7477 IStorage** ppstgOpen)
7479 StorageBaseImpl* newStorage = 0;
7480 HRESULT hr = S_OK;
7482 if ((ppstgOpen == 0) || (plkbyt == 0))
7483 return STG_E_INVALIDPOINTER;
7486 * Allocate and initialize the new IStorage object.
7488 hr = Storage_Construct(
7491 plkbyt,
7492 grfMode,
7493 FALSE,
7494 TRUE,
7495 512,
7496 &newStorage);
7498 if (FAILED(hr))
7500 return hr;
7504 * Get an "out" pointer for the caller.
7506 *ppstgOpen = (IStorage*)newStorage;
7508 return hr;
7511 /******************************************************************************
7512 * StgOpenStorageOnILockBytes [OLE32.@]
7514 HRESULT WINAPI StgOpenStorageOnILockBytes(
7515 ILockBytes *plkbyt,
7516 IStorage *pstgPriority,
7517 DWORD grfMode,
7518 SNB snbExclude,
7519 DWORD reserved,
7520 IStorage **ppstgOpen)
7522 StorageBaseImpl* newStorage = 0;
7523 HRESULT hr = S_OK;
7525 if ((plkbyt == 0) || (ppstgOpen == 0))
7526 return STG_E_INVALIDPOINTER;
7528 if ( FAILED( validateSTGM(grfMode) ))
7529 return STG_E_INVALIDFLAG;
7531 *ppstgOpen = 0;
7534 * Allocate and initialize the new IStorage object.
7536 hr = Storage_Construct(
7539 plkbyt,
7540 grfMode,
7541 FALSE,
7542 FALSE,
7543 512,
7544 &newStorage);
7546 if (FAILED(hr))
7548 return hr;
7552 * Get an "out" pointer for the caller.
7554 *ppstgOpen = (IStorage*)newStorage;
7556 return hr;
7559 /******************************************************************************
7560 * StgSetTimes [ole32.@]
7561 * StgSetTimes [OLE32.@]
7565 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7566 FILETIME const *patime, FILETIME const *pmtime)
7568 IStorage *stg = NULL;
7569 HRESULT r;
7571 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7573 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7574 0, 0, &stg);
7575 if( SUCCEEDED(r) )
7577 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7578 IStorage_Release(stg);
7581 return r;
7584 /******************************************************************************
7585 * StgIsStorageILockBytes [OLE32.@]
7587 * Determines if the ILockBytes contains a storage object.
7589 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7591 BYTE sig[8];
7592 ULARGE_INTEGER offset;
7594 offset.u.HighPart = 0;
7595 offset.u.LowPart = 0;
7597 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7599 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7600 return S_OK;
7602 return S_FALSE;
7605 /******************************************************************************
7606 * WriteClassStg [OLE32.@]
7608 * This method will store the specified CLSID in the specified storage object
7610 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7612 HRESULT hRes;
7614 if(!pStg)
7615 return E_INVALIDARG;
7617 if(!rclsid)
7618 return STG_E_INVALIDPOINTER;
7620 hRes = IStorage_SetClass(pStg, rclsid);
7622 return hRes;
7625 /***********************************************************************
7626 * ReadClassStg (OLE32.@)
7628 * This method reads the CLSID previously written to a storage object with
7629 * the WriteClassStg.
7631 * PARAMS
7632 * pstg [I] IStorage pointer
7633 * pclsid [O] Pointer to where the CLSID is written
7635 * RETURNS
7636 * Success: S_OK.
7637 * Failure: HRESULT code.
7639 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7641 STATSTG pstatstg;
7642 HRESULT hRes;
7644 TRACE("(%p, %p)\n", pstg, pclsid);
7646 if(!pstg || !pclsid)
7647 return E_INVALIDARG;
7650 * read a STATSTG structure (contains the clsid) from the storage
7652 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7654 if(SUCCEEDED(hRes))
7655 *pclsid=pstatstg.clsid;
7657 return hRes;
7660 /***********************************************************************
7661 * OleLoadFromStream (OLE32.@)
7663 * This function loads an object from stream
7665 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7667 CLSID clsid;
7668 HRESULT res;
7669 LPPERSISTSTREAM xstm;
7671 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7673 res=ReadClassStm(pStm,&clsid);
7674 if (FAILED(res))
7675 return res;
7676 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7677 if (FAILED(res))
7678 return res;
7679 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7680 if (FAILED(res)) {
7681 IUnknown_Release((IUnknown*)*ppvObj);
7682 return res;
7684 res=IPersistStream_Load(xstm,pStm);
7685 IPersistStream_Release(xstm);
7686 /* FIXME: all refcounts ok at this point? I think they should be:
7687 * pStm : unchanged
7688 * ppvObj : 1
7689 * xstm : 0 (released)
7691 return res;
7694 /***********************************************************************
7695 * OleSaveToStream (OLE32.@)
7697 * This function saves an object with the IPersistStream interface on it
7698 * to the specified stream.
7700 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7703 CLSID clsid;
7704 HRESULT res;
7706 TRACE("(%p,%p)\n",pPStm,pStm);
7708 res=IPersistStream_GetClassID(pPStm,&clsid);
7710 if (SUCCEEDED(res)){
7712 res=WriteClassStm(pStm,&clsid);
7714 if (SUCCEEDED(res))
7716 res=IPersistStream_Save(pPStm,pStm,TRUE);
7719 TRACE("Finished Save\n");
7720 return res;
7723 /****************************************************************************
7724 * This method validate a STGM parameter that can contain the values below
7726 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7727 * The stgm values contained in 0xffff0000 are bitmasks.
7729 * STGM_DIRECT 0x00000000
7730 * STGM_TRANSACTED 0x00010000
7731 * STGM_SIMPLE 0x08000000
7733 * STGM_READ 0x00000000
7734 * STGM_WRITE 0x00000001
7735 * STGM_READWRITE 0x00000002
7737 * STGM_SHARE_DENY_NONE 0x00000040
7738 * STGM_SHARE_DENY_READ 0x00000030
7739 * STGM_SHARE_DENY_WRITE 0x00000020
7740 * STGM_SHARE_EXCLUSIVE 0x00000010
7742 * STGM_PRIORITY 0x00040000
7743 * STGM_DELETEONRELEASE 0x04000000
7745 * STGM_CREATE 0x00001000
7746 * STGM_CONVERT 0x00020000
7747 * STGM_FAILIFTHERE 0x00000000
7749 * STGM_NOSCRATCH 0x00100000
7750 * STGM_NOSNAPSHOT 0x00200000
7752 static HRESULT validateSTGM(DWORD stgm)
7754 DWORD access = STGM_ACCESS_MODE(stgm);
7755 DWORD share = STGM_SHARE_MODE(stgm);
7756 DWORD create = STGM_CREATE_MODE(stgm);
7758 if (stgm&~STGM_KNOWN_FLAGS)
7760 ERR("unknown flags %08x\n", stgm);
7761 return E_FAIL;
7764 switch (access)
7766 case STGM_READ:
7767 case STGM_WRITE:
7768 case STGM_READWRITE:
7769 break;
7770 default:
7771 return E_FAIL;
7774 switch (share)
7776 case STGM_SHARE_DENY_NONE:
7777 case STGM_SHARE_DENY_READ:
7778 case STGM_SHARE_DENY_WRITE:
7779 case STGM_SHARE_EXCLUSIVE:
7780 break;
7781 default:
7782 return E_FAIL;
7785 switch (create)
7787 case STGM_CREATE:
7788 case STGM_FAILIFTHERE:
7789 break;
7790 default:
7791 return E_FAIL;
7795 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7797 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7798 return E_FAIL;
7801 * STGM_CREATE | STGM_CONVERT
7802 * if both are false, STGM_FAILIFTHERE is set to TRUE
7804 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7805 return E_FAIL;
7808 * STGM_NOSCRATCH requires STGM_TRANSACTED
7810 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7811 return E_FAIL;
7814 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7815 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7817 if ( (stgm & STGM_NOSNAPSHOT) &&
7818 (!(stgm & STGM_TRANSACTED) ||
7819 share == STGM_SHARE_EXCLUSIVE ||
7820 share == STGM_SHARE_DENY_WRITE) )
7821 return E_FAIL;
7823 return S_OK;
7826 /****************************************************************************
7827 * GetShareModeFromSTGM
7829 * This method will return a share mode flag from a STGM value.
7830 * The STGM value is assumed valid.
7832 static DWORD GetShareModeFromSTGM(DWORD stgm)
7834 switch (STGM_SHARE_MODE(stgm))
7836 case STGM_SHARE_DENY_NONE:
7837 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7838 case STGM_SHARE_DENY_READ:
7839 return FILE_SHARE_WRITE;
7840 case STGM_SHARE_DENY_WRITE:
7841 return FILE_SHARE_READ;
7842 case STGM_SHARE_EXCLUSIVE:
7843 return 0;
7845 ERR("Invalid share mode!\n");
7846 assert(0);
7847 return 0;
7850 /****************************************************************************
7851 * GetAccessModeFromSTGM
7853 * This method will return an access mode flag from a STGM value.
7854 * The STGM value is assumed valid.
7856 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7858 switch (STGM_ACCESS_MODE(stgm))
7860 case STGM_READ:
7861 return GENERIC_READ;
7862 case STGM_WRITE:
7863 case STGM_READWRITE:
7864 return GENERIC_READ | GENERIC_WRITE;
7866 ERR("Invalid access mode!\n");
7867 assert(0);
7868 return 0;
7871 /****************************************************************************
7872 * GetCreationModeFromSTGM
7874 * This method will return a creation mode flag from a STGM value.
7875 * The STGM value is assumed valid.
7877 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7879 switch(STGM_CREATE_MODE(stgm))
7881 case STGM_CREATE:
7882 return CREATE_ALWAYS;
7883 case STGM_CONVERT:
7884 FIXME("STGM_CONVERT not implemented!\n");
7885 return CREATE_NEW;
7886 case STGM_FAILIFTHERE:
7887 return CREATE_NEW;
7889 ERR("Invalid create mode!\n");
7890 assert(0);
7891 return 0;
7895 /*************************************************************************
7896 * OLECONVERT_LoadOLE10 [Internal]
7898 * Loads the OLE10 STREAM to memory
7900 * PARAMS
7901 * pOleStream [I] The OLESTREAM
7902 * pData [I] Data Structure for the OLESTREAM Data
7904 * RETURNS
7905 * Success: S_OK
7906 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7907 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7909 * NOTES
7910 * This function is used by OleConvertOLESTREAMToIStorage only.
7912 * Memory allocated for pData must be freed by the caller
7914 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7916 DWORD dwSize;
7917 HRESULT hRes = S_OK;
7918 int nTryCnt=0;
7919 int max_try = 6;
7921 pData->pData = NULL;
7922 pData->pstrOleObjFileName = NULL;
7924 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7926 /* Get the OleID */
7927 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7928 if(dwSize != sizeof(pData->dwOleID))
7930 hRes = CONVERT10_E_OLESTREAM_GET;
7932 else if(pData->dwOleID != OLESTREAM_ID)
7934 hRes = CONVERT10_E_OLESTREAM_FMT;
7936 else
7938 hRes = S_OK;
7939 break;
7943 if(hRes == S_OK)
7945 /* Get the TypeID... more info needed for this field */
7946 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7947 if(dwSize != sizeof(pData->dwTypeID))
7949 hRes = CONVERT10_E_OLESTREAM_GET;
7952 if(hRes == S_OK)
7954 if(pData->dwTypeID != 0)
7956 /* Get the length of the OleTypeName */
7957 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7958 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7960 hRes = CONVERT10_E_OLESTREAM_GET;
7963 if(hRes == S_OK)
7965 if(pData->dwOleTypeNameLength > 0)
7967 /* Get the OleTypeName */
7968 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7969 if(dwSize != pData->dwOleTypeNameLength)
7971 hRes = CONVERT10_E_OLESTREAM_GET;
7975 if(bStrem1)
7977 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7978 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7980 hRes = CONVERT10_E_OLESTREAM_GET;
7982 if(hRes == S_OK)
7984 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7985 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7986 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7987 if(pData->pstrOleObjFileName)
7989 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7990 if(dwSize != pData->dwOleObjFileNameLength)
7992 hRes = CONVERT10_E_OLESTREAM_GET;
7995 else
7996 hRes = CONVERT10_E_OLESTREAM_GET;
7999 else
8001 /* Get the Width of the Metafile */
8002 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8003 if(dwSize != sizeof(pData->dwMetaFileWidth))
8005 hRes = CONVERT10_E_OLESTREAM_GET;
8007 if(hRes == S_OK)
8009 /* Get the Height of the Metafile */
8010 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8011 if(dwSize != sizeof(pData->dwMetaFileHeight))
8013 hRes = CONVERT10_E_OLESTREAM_GET;
8017 if(hRes == S_OK)
8019 /* Get the Length of the Data */
8020 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8021 if(dwSize != sizeof(pData->dwDataLength))
8023 hRes = CONVERT10_E_OLESTREAM_GET;
8027 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8029 if(!bStrem1) /* if it is a second OLE stream data */
8031 pData->dwDataLength -= 8;
8032 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8033 if(dwSize != sizeof(pData->strUnknown))
8035 hRes = CONVERT10_E_OLESTREAM_GET;
8039 if(hRes == S_OK)
8041 if(pData->dwDataLength > 0)
8043 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8045 /* Get Data (ex. IStorage, Metafile, or BMP) */
8046 if(pData->pData)
8048 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8049 if(dwSize != pData->dwDataLength)
8051 hRes = CONVERT10_E_OLESTREAM_GET;
8054 else
8056 hRes = CONVERT10_E_OLESTREAM_GET;
8062 return hRes;
8065 /*************************************************************************
8066 * OLECONVERT_SaveOLE10 [Internal]
8068 * Saves the OLE10 STREAM From memory
8070 * PARAMS
8071 * pData [I] Data Structure for the OLESTREAM Data
8072 * pOleStream [I] The OLESTREAM to save
8074 * RETURNS
8075 * Success: S_OK
8076 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8078 * NOTES
8079 * This function is used by OleConvertIStorageToOLESTREAM only.
8082 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8084 DWORD dwSize;
8085 HRESULT hRes = S_OK;
8088 /* Set the OleID */
8089 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8090 if(dwSize != sizeof(pData->dwOleID))
8092 hRes = CONVERT10_E_OLESTREAM_PUT;
8095 if(hRes == S_OK)
8097 /* Set the TypeID */
8098 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8099 if(dwSize != sizeof(pData->dwTypeID))
8101 hRes = CONVERT10_E_OLESTREAM_PUT;
8105 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8107 /* Set the Length of the OleTypeName */
8108 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8109 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8111 hRes = CONVERT10_E_OLESTREAM_PUT;
8114 if(hRes == S_OK)
8116 if(pData->dwOleTypeNameLength > 0)
8118 /* Set the OleTypeName */
8119 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8120 if(dwSize != pData->dwOleTypeNameLength)
8122 hRes = CONVERT10_E_OLESTREAM_PUT;
8127 if(hRes == S_OK)
8129 /* Set the width of the Metafile */
8130 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8131 if(dwSize != sizeof(pData->dwMetaFileWidth))
8133 hRes = CONVERT10_E_OLESTREAM_PUT;
8137 if(hRes == S_OK)
8139 /* Set the height of the Metafile */
8140 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8141 if(dwSize != sizeof(pData->dwMetaFileHeight))
8143 hRes = CONVERT10_E_OLESTREAM_PUT;
8147 if(hRes == S_OK)
8149 /* Set the length of the Data */
8150 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8151 if(dwSize != sizeof(pData->dwDataLength))
8153 hRes = CONVERT10_E_OLESTREAM_PUT;
8157 if(hRes == S_OK)
8159 if(pData->dwDataLength > 0)
8161 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8162 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8163 if(dwSize != pData->dwDataLength)
8165 hRes = CONVERT10_E_OLESTREAM_PUT;
8170 return hRes;
8173 /*************************************************************************
8174 * OLECONVERT_GetOLE20FromOLE10[Internal]
8176 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8177 * opens it, and copies the content to the dest IStorage for
8178 * OleConvertOLESTREAMToIStorage
8181 * PARAMS
8182 * pDestStorage [I] The IStorage to copy the data to
8183 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8184 * nBufferLength [I] The size of the buffer
8186 * RETURNS
8187 * Nothing
8189 * NOTES
8193 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8195 HRESULT hRes;
8196 HANDLE hFile;
8197 IStorage *pTempStorage;
8198 DWORD dwNumOfBytesWritten;
8199 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8200 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8202 /* Create a temp File */
8203 GetTempPathW(MAX_PATH, wstrTempDir);
8204 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8205 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8207 if(hFile != INVALID_HANDLE_VALUE)
8209 /* Write IStorage Data to File */
8210 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8211 CloseHandle(hFile);
8213 /* Open and copy temp storage to the Dest Storage */
8214 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8215 if(hRes == S_OK)
8217 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8218 IStorage_Release(pTempStorage);
8220 DeleteFileW(wstrTempFile);
8225 /*************************************************************************
8226 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8228 * Saves the OLE10 STREAM From memory
8230 * PARAMS
8231 * pStorage [I] The Src IStorage to copy
8232 * pData [I] The Dest Memory to write to.
8234 * RETURNS
8235 * The size in bytes allocated for pData
8237 * NOTES
8238 * Memory allocated for pData must be freed by the caller
8240 * Used by OleConvertIStorageToOLESTREAM only.
8243 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8245 HANDLE hFile;
8246 HRESULT hRes;
8247 DWORD nDataLength = 0;
8248 IStorage *pTempStorage;
8249 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8250 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8252 *pData = NULL;
8254 /* Create temp Storage */
8255 GetTempPathW(MAX_PATH, wstrTempDir);
8256 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8257 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8259 if(hRes == S_OK)
8261 /* Copy Src Storage to the Temp Storage */
8262 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8263 IStorage_Release(pTempStorage);
8265 /* Open Temp Storage as a file and copy to memory */
8266 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8267 if(hFile != INVALID_HANDLE_VALUE)
8269 nDataLength = GetFileSize(hFile, NULL);
8270 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8271 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8272 CloseHandle(hFile);
8274 DeleteFileW(wstrTempFile);
8276 return nDataLength;
8279 /*************************************************************************
8280 * OLECONVERT_CreateOleStream [Internal]
8282 * Creates the "\001OLE" stream in the IStorage if necessary.
8284 * PARAMS
8285 * pStorage [I] Dest storage to create the stream in
8287 * RETURNS
8288 * Nothing
8290 * NOTES
8291 * This function is used by OleConvertOLESTREAMToIStorage only.
8293 * This stream is still unknown, MS Word seems to have extra data
8294 * but since the data is stored in the OLESTREAM there should be
8295 * no need to recreate the stream. If the stream is manually
8296 * deleted it will create it with this default data.
8299 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8301 HRESULT hRes;
8302 IStream *pStream;
8303 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8304 BYTE pOleStreamHeader [] =
8306 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8307 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8308 0x00, 0x00, 0x00, 0x00
8311 /* Create stream if not present */
8312 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8313 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8315 if(hRes == S_OK)
8317 /* Write default Data */
8318 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8319 IStream_Release(pStream);
8323 /* write a string to a stream, preceded by its length */
8324 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8326 HRESULT r;
8327 LPSTR str;
8328 DWORD len = 0;
8330 if( string )
8331 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8332 r = IStream_Write( stm, &len, sizeof(len), NULL);
8333 if( FAILED( r ) )
8334 return r;
8335 if(len == 0)
8336 return r;
8337 str = CoTaskMemAlloc( len );
8338 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8339 r = IStream_Write( stm, str, len, NULL);
8340 CoTaskMemFree( str );
8341 return r;
8344 /* read a string preceded by its length from a stream */
8345 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8347 HRESULT r;
8348 DWORD len, count = 0;
8349 LPSTR str;
8350 LPWSTR wstr;
8352 r = IStream_Read( stm, &len, sizeof(len), &count );
8353 if( FAILED( r ) )
8354 return r;
8355 if( count != sizeof(len) )
8356 return E_OUTOFMEMORY;
8358 TRACE("%d bytes\n",len);
8360 str = CoTaskMemAlloc( len );
8361 if( !str )
8362 return E_OUTOFMEMORY;
8363 count = 0;
8364 r = IStream_Read( stm, str, len, &count );
8365 if( FAILED( r ) )
8366 return r;
8367 if( count != len )
8369 CoTaskMemFree( str );
8370 return E_OUTOFMEMORY;
8373 TRACE("Read string %s\n",debugstr_an(str,len));
8375 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8376 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8377 if( wstr )
8378 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8379 CoTaskMemFree( str );
8381 *string = wstr;
8383 return r;
8387 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8388 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8390 IStream *pstm;
8391 HRESULT r = S_OK;
8392 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8394 static const BYTE unknown1[12] =
8395 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8396 0xFF, 0xFF, 0xFF, 0xFF};
8397 static const BYTE unknown2[16] =
8398 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8399 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8401 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8402 debugstr_w(lpszUserType), debugstr_w(szClipName),
8403 debugstr_w(szProgIDName));
8405 /* Create a CompObj stream */
8406 r = IStorage_CreateStream(pstg, szwStreamName,
8407 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8408 if( FAILED (r) )
8409 return r;
8411 /* Write CompObj Structure to stream */
8412 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8414 if( SUCCEEDED( r ) )
8415 r = WriteClassStm( pstm, clsid );
8417 if( SUCCEEDED( r ) )
8418 r = STREAM_WriteString( pstm, lpszUserType );
8419 if( SUCCEEDED( r ) )
8420 r = STREAM_WriteString( pstm, szClipName );
8421 if( SUCCEEDED( r ) )
8422 r = STREAM_WriteString( pstm, szProgIDName );
8423 if( SUCCEEDED( r ) )
8424 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8426 IStream_Release( pstm );
8428 return r;
8431 /***********************************************************************
8432 * WriteFmtUserTypeStg (OLE32.@)
8434 HRESULT WINAPI WriteFmtUserTypeStg(
8435 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8437 HRESULT r;
8438 WCHAR szwClipName[0x40];
8439 CLSID clsid = CLSID_NULL;
8440 LPWSTR wstrProgID = NULL;
8441 DWORD n;
8443 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8445 /* get the clipboard format name */
8446 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8447 szwClipName[n]=0;
8449 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8451 /* FIXME: There's room to save a CLSID and its ProgID, but
8452 the CLSID is not looked up in the registry and in all the
8453 tests I wrote it was CLSID_NULL. Where does it come from?
8456 /* get the real program ID. This may fail, but that's fine */
8457 ProgIDFromCLSID(&clsid, &wstrProgID);
8459 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8461 r = STORAGE_WriteCompObj( pstg, &clsid,
8462 lpszUserType, szwClipName, wstrProgID );
8464 CoTaskMemFree(wstrProgID);
8466 return r;
8470 /******************************************************************************
8471 * ReadFmtUserTypeStg [OLE32.@]
8473 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8475 HRESULT r;
8476 IStream *stm = 0;
8477 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8478 unsigned char unknown1[12];
8479 unsigned char unknown2[16];
8480 DWORD count;
8481 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8482 CLSID clsid;
8484 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8486 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8487 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8488 if( FAILED ( r ) )
8490 WARN("Failed to open stream r = %08x\n", r);
8491 return r;
8494 /* read the various parts of the structure */
8495 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8496 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8497 goto end;
8498 r = ReadClassStm( stm, &clsid );
8499 if( FAILED( r ) )
8500 goto end;
8502 r = STREAM_ReadString( stm, &szCLSIDName );
8503 if( FAILED( r ) )
8504 goto end;
8506 r = STREAM_ReadString( stm, &szOleTypeName );
8507 if( FAILED( r ) )
8508 goto end;
8510 r = STREAM_ReadString( stm, &szProgIDName );
8511 if( FAILED( r ) )
8512 goto end;
8514 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8515 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8516 goto end;
8518 /* ok, success... now we just need to store what we found */
8519 if( pcf )
8520 *pcf = RegisterClipboardFormatW( szOleTypeName );
8521 CoTaskMemFree( szOleTypeName );
8523 if( lplpszUserType )
8524 *lplpszUserType = szCLSIDName;
8525 CoTaskMemFree( szProgIDName );
8527 end:
8528 IStream_Release( stm );
8530 return r;
8534 /*************************************************************************
8535 * OLECONVERT_CreateCompObjStream [Internal]
8537 * Creates a "\001CompObj" is the destination IStorage if necessary.
8539 * PARAMS
8540 * pStorage [I] The dest IStorage to create the CompObj Stream
8541 * if necessary.
8542 * strOleTypeName [I] The ProgID
8544 * RETURNS
8545 * Success: S_OK
8546 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8548 * NOTES
8549 * This function is used by OleConvertOLESTREAMToIStorage only.
8551 * The stream data is stored in the OLESTREAM and there should be
8552 * no need to recreate the stream. If the stream is manually
8553 * deleted it will attempt to create it by querying the registry.
8557 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8559 IStream *pStream;
8560 HRESULT hStorageRes, hRes = S_OK;
8561 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8562 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8563 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8565 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8566 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8568 /* Initialize the CompObj structure */
8569 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8570 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8571 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8574 /* Create a CompObj stream if it doesn't exist */
8575 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8576 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8577 if(hStorageRes == S_OK)
8579 /* copy the OleTypeName to the compobj struct */
8580 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8581 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8583 /* copy the OleTypeName to the compobj struct */
8584 /* Note: in the test made, these were Identical */
8585 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8586 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8588 /* Get the CLSID */
8589 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8590 bufferW, OLESTREAM_MAX_STR_LEN );
8591 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8593 if(hRes == S_OK)
8595 HKEY hKey;
8596 LONG hErr;
8597 /* Get the CLSID Default Name from the Registry */
8598 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8599 if(hErr == ERROR_SUCCESS)
8601 char strTemp[OLESTREAM_MAX_STR_LEN];
8602 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8603 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8604 if(hErr == ERROR_SUCCESS)
8606 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8608 RegCloseKey(hKey);
8612 /* Write CompObj Structure to stream */
8613 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8615 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8617 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8618 if(IStorageCompObj.dwCLSIDNameLength > 0)
8620 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8622 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8623 if(IStorageCompObj.dwOleTypeNameLength > 0)
8625 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8627 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8628 if(IStorageCompObj.dwProgIDNameLength > 0)
8630 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8632 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8633 IStream_Release(pStream);
8635 return hRes;
8639 /*************************************************************************
8640 * OLECONVERT_CreateOlePresStream[Internal]
8642 * Creates the "\002OlePres000" Stream with the Metafile data
8644 * PARAMS
8645 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8646 * dwExtentX [I] Width of the Metafile
8647 * dwExtentY [I] Height of the Metafile
8648 * pData [I] Metafile data
8649 * dwDataLength [I] Size of the Metafile data
8651 * RETURNS
8652 * Success: S_OK
8653 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8655 * NOTES
8656 * This function is used by OleConvertOLESTREAMToIStorage only.
8659 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8661 HRESULT hRes;
8662 IStream *pStream;
8663 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8664 BYTE pOlePresStreamHeader [] =
8666 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8667 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8668 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8669 0x00, 0x00, 0x00, 0x00
8672 BYTE pOlePresStreamHeaderEmpty [] =
8674 0x00, 0x00, 0x00, 0x00,
8675 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8676 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8677 0x00, 0x00, 0x00, 0x00
8680 /* Create the OlePres000 Stream */
8681 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8682 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8684 if(hRes == S_OK)
8686 DWORD nHeaderSize;
8687 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8689 memset(&OlePres, 0, sizeof(OlePres));
8690 /* Do we have any metafile data to save */
8691 if(dwDataLength > 0)
8693 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8694 nHeaderSize = sizeof(pOlePresStreamHeader);
8696 else
8698 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8699 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8701 /* Set width and height of the metafile */
8702 OlePres.dwExtentX = dwExtentX;
8703 OlePres.dwExtentY = -dwExtentY;
8705 /* Set Data and Length */
8706 if(dwDataLength > sizeof(METAFILEPICT16))
8708 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8709 OlePres.pData = &(pData[8]);
8711 /* Save OlePres000 Data to Stream */
8712 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8713 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8714 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8715 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8716 if(OlePres.dwSize > 0)
8718 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8720 IStream_Release(pStream);
8724 /*************************************************************************
8725 * OLECONVERT_CreateOle10NativeStream [Internal]
8727 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8729 * PARAMS
8730 * pStorage [I] Dest storage to create the stream in
8731 * pData [I] Ole10 Native Data (ex. bmp)
8732 * dwDataLength [I] Size of the Ole10 Native Data
8734 * RETURNS
8735 * Nothing
8737 * NOTES
8738 * This function is used by OleConvertOLESTREAMToIStorage only.
8740 * Might need to verify the data and return appropriate error message
8743 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8745 HRESULT hRes;
8746 IStream *pStream;
8747 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8749 /* Create the Ole10Native Stream */
8750 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8751 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8753 if(hRes == S_OK)
8755 /* Write info to stream */
8756 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8757 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8758 IStream_Release(pStream);
8763 /*************************************************************************
8764 * OLECONVERT_GetOLE10ProgID [Internal]
8766 * Finds the ProgID (or OleTypeID) from the IStorage
8768 * PARAMS
8769 * pStorage [I] The Src IStorage to get the ProgID
8770 * strProgID [I] the ProgID string to get
8771 * dwSize [I] the size of the string
8773 * RETURNS
8774 * Success: S_OK
8775 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8777 * NOTES
8778 * This function is used by OleConvertIStorageToOLESTREAM only.
8782 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8784 HRESULT hRes;
8785 IStream *pStream;
8786 LARGE_INTEGER iSeekPos;
8787 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8788 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8790 /* Open the CompObj Stream */
8791 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8792 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8793 if(hRes == S_OK)
8796 /*Get the OleType from the CompObj Stream */
8797 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8798 iSeekPos.u.HighPart = 0;
8800 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8801 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8802 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8803 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8804 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8805 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8806 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8808 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8809 if(*dwSize > 0)
8811 IStream_Read(pStream, strProgID, *dwSize, NULL);
8813 IStream_Release(pStream);
8815 else
8817 STATSTG stat;
8818 LPOLESTR wstrProgID;
8820 /* Get the OleType from the registry */
8821 REFCLSID clsid = &(stat.clsid);
8822 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8823 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8824 if(hRes == S_OK)
8826 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8830 return hRes;
8833 /*************************************************************************
8834 * OLECONVERT_GetOle10PresData [Internal]
8836 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8838 * PARAMS
8839 * pStorage [I] Src IStroage
8840 * pOleStream [I] Dest OleStream Mem Struct
8842 * RETURNS
8843 * Nothing
8845 * NOTES
8846 * This function is used by OleConvertIStorageToOLESTREAM only.
8848 * Memory allocated for pData must be freed by the caller
8852 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8855 HRESULT hRes;
8856 IStream *pStream;
8857 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8859 /* Initialize Default data for OLESTREAM */
8860 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8861 pOleStreamData[0].dwTypeID = 2;
8862 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8863 pOleStreamData[1].dwTypeID = 0;
8864 pOleStreamData[0].dwMetaFileWidth = 0;
8865 pOleStreamData[0].dwMetaFileHeight = 0;
8866 pOleStreamData[0].pData = NULL;
8867 pOleStreamData[1].pData = NULL;
8869 /* Open Ole10Native Stream */
8870 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8871 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8872 if(hRes == S_OK)
8875 /* Read Size and Data */
8876 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8877 if(pOleStreamData->dwDataLength > 0)
8879 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8880 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8882 IStream_Release(pStream);
8888 /*************************************************************************
8889 * OLECONVERT_GetOle20PresData[Internal]
8891 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8893 * PARAMS
8894 * pStorage [I] Src IStroage
8895 * pOleStreamData [I] Dest OleStream Mem Struct
8897 * RETURNS
8898 * Nothing
8900 * NOTES
8901 * This function is used by OleConvertIStorageToOLESTREAM only.
8903 * Memory allocated for pData must be freed by the caller
8905 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8907 HRESULT hRes;
8908 IStream *pStream;
8909 OLECONVERT_ISTORAGE_OLEPRES olePress;
8910 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8912 /* Initialize Default data for OLESTREAM */
8913 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8914 pOleStreamData[0].dwTypeID = 2;
8915 pOleStreamData[0].dwMetaFileWidth = 0;
8916 pOleStreamData[0].dwMetaFileHeight = 0;
8917 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8918 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8919 pOleStreamData[1].dwTypeID = 0;
8920 pOleStreamData[1].dwOleTypeNameLength = 0;
8921 pOleStreamData[1].strOleTypeName[0] = 0;
8922 pOleStreamData[1].dwMetaFileWidth = 0;
8923 pOleStreamData[1].dwMetaFileHeight = 0;
8924 pOleStreamData[1].pData = NULL;
8925 pOleStreamData[1].dwDataLength = 0;
8928 /* Open OlePress000 stream */
8929 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8930 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8931 if(hRes == S_OK)
8933 LARGE_INTEGER iSeekPos;
8934 METAFILEPICT16 MetaFilePict;
8935 static const char strMetafilePictName[] = "METAFILEPICT";
8937 /* Set the TypeID for a Metafile */
8938 pOleStreamData[1].dwTypeID = 5;
8940 /* Set the OleTypeName to Metafile */
8941 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8942 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8944 iSeekPos.u.HighPart = 0;
8945 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8947 /* Get Presentation Data */
8948 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8949 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8950 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8951 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8953 /*Set width and Height */
8954 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8955 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8956 if(olePress.dwSize > 0)
8958 /* Set Length */
8959 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8961 /* Set MetaFilePict struct */
8962 MetaFilePict.mm = 8;
8963 MetaFilePict.xExt = olePress.dwExtentX;
8964 MetaFilePict.yExt = olePress.dwExtentY;
8965 MetaFilePict.hMF = 0;
8967 /* Get Metafile Data */
8968 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8969 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8970 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8972 IStream_Release(pStream);
8976 /*************************************************************************
8977 * OleConvertOLESTREAMToIStorage [OLE32.@]
8979 * Read info on MSDN
8981 * TODO
8982 * DVTARGETDEVICE parameter is not handled
8983 * Still unsure of some mem fields for OLE 10 Stream
8984 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8985 * and "\001OLE" streams
8988 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8989 LPOLESTREAM pOleStream,
8990 LPSTORAGE pstg,
8991 const DVTARGETDEVICE* ptd)
8993 int i;
8994 HRESULT hRes=S_OK;
8995 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8997 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8999 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9001 if(ptd != NULL)
9003 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9006 if(pstg == NULL || pOleStream == NULL)
9008 hRes = E_INVALIDARG;
9011 if(hRes == S_OK)
9013 /* Load the OLESTREAM to Memory */
9014 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9017 if(hRes == S_OK)
9019 /* Load the OLESTREAM to Memory (part 2)*/
9020 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9023 if(hRes == S_OK)
9026 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9028 /* Do we have the IStorage Data in the OLESTREAM */
9029 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9031 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9032 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9034 else
9036 /* It must be an original OLE 1.0 source */
9037 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9040 else
9042 /* It must be an original OLE 1.0 source */
9043 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9046 /* Create CompObj Stream if necessary */
9047 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9048 if(hRes == S_OK)
9050 /*Create the Ole Stream if necessary */
9051 OLECONVERT_CreateOleStream(pstg);
9056 /* Free allocated memory */
9057 for(i=0; i < 2; i++)
9059 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9060 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9061 pOleStreamData[i].pstrOleObjFileName = NULL;
9063 return hRes;
9066 /*************************************************************************
9067 * OleConvertIStorageToOLESTREAM [OLE32.@]
9069 * Read info on MSDN
9071 * Read info on MSDN
9073 * TODO
9074 * Still unsure of some mem fields for OLE 10 Stream
9075 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9076 * and "\001OLE" streams.
9079 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9080 LPSTORAGE pstg,
9081 LPOLESTREAM pOleStream)
9083 int i;
9084 HRESULT hRes = S_OK;
9085 IStream *pStream;
9086 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9087 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9089 TRACE("%p %p\n", pstg, pOleStream);
9091 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9093 if(pstg == NULL || pOleStream == NULL)
9095 hRes = E_INVALIDARG;
9097 if(hRes == S_OK)
9099 /* Get the ProgID */
9100 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9101 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9103 if(hRes == S_OK)
9105 /* Was it originally Ole10 */
9106 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9107 if(hRes == S_OK)
9109 IStream_Release(pStream);
9110 /* Get Presentation Data for Ole10Native */
9111 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9113 else
9115 /* Get Presentation Data (OLE20) */
9116 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9119 /* Save OLESTREAM */
9120 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9121 if(hRes == S_OK)
9123 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9128 /* Free allocated memory */
9129 for(i=0; i < 2; i++)
9131 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9134 return hRes;
9137 /***********************************************************************
9138 * GetConvertStg (OLE32.@)
9140 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9141 FIXME("unimplemented stub!\n");
9142 return E_FAIL;
9145 /******************************************************************************
9146 * StgIsStorageFile [OLE32.@]
9147 * Verify if the file contains a storage object
9149 * PARAMS
9150 * fn [ I] Filename
9152 * RETURNS
9153 * S_OK if file has magic bytes as a storage object
9154 * S_FALSE if file is not storage
9156 HRESULT WINAPI
9157 StgIsStorageFile(LPCOLESTR fn)
9159 HANDLE hf;
9160 BYTE magic[8];
9161 DWORD bytes_read;
9163 TRACE("%s\n", debugstr_w(fn));
9164 hf = CreateFileW(fn, GENERIC_READ,
9165 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9166 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9168 if (hf == INVALID_HANDLE_VALUE)
9169 return STG_E_FILENOTFOUND;
9171 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9173 WARN(" unable to read file\n");
9174 CloseHandle(hf);
9175 return S_FALSE;
9178 CloseHandle(hf);
9180 if (bytes_read != 8) {
9181 TRACE(" too short\n");
9182 return S_FALSE;
9185 if (!memcmp(magic,STORAGE_magic,8)) {
9186 TRACE(" -> YES\n");
9187 return S_OK;
9190 TRACE(" -> Invalid header.\n");
9191 return S_FALSE;
9194 /***********************************************************************
9195 * WriteClassStm (OLE32.@)
9197 * Writes a CLSID to a stream.
9199 * PARAMS
9200 * pStm [I] Stream to write to.
9201 * rclsid [I] CLSID to write.
9203 * RETURNS
9204 * Success: S_OK.
9205 * Failure: HRESULT code.
9207 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9209 TRACE("(%p,%p)\n",pStm,rclsid);
9211 if (!pStm || !rclsid)
9212 return E_INVALIDARG;
9214 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9217 /***********************************************************************
9218 * ReadClassStm (OLE32.@)
9220 * Reads a CLSID from a stream.
9222 * PARAMS
9223 * pStm [I] Stream to read from.
9224 * rclsid [O] CLSID to read.
9226 * RETURNS
9227 * Success: S_OK.
9228 * Failure: HRESULT code.
9230 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9232 ULONG nbByte;
9233 HRESULT res;
9235 TRACE("(%p,%p)\n",pStm,pclsid);
9237 if (!pStm || !pclsid)
9238 return E_INVALIDARG;
9240 /* clear the output args */
9241 *pclsid = CLSID_NULL;
9243 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9245 if (FAILED(res))
9246 return res;
9248 if (nbByte != sizeof(CLSID))
9249 return STG_E_READFAULT;
9250 else
9251 return S_OK;