include: Specify calling convention explicitly in idl files where needed.
[wine/hramrach.git] / dlls / ole32 / storage32.c
blob8b6a3af4c8a531e2409cfbe5108eeacfdacfce5a
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 StorageBaseImpl_Flush(This);
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 StorageBaseImpl_Flush(This);
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 if (SUCCEEDED(hRes))
1051 hRes = StorageBaseImpl_Flush(This);
1053 return hRes;
1056 /************************************************************************
1057 ** Storage32Impl implementation
1060 /************************************************************************
1061 * Storage32BaseImpl_CreateStorage (IStorage)
1063 * This method will create the storage object within the provided storage.
1065 * See Windows documentation for more details on IStorage methods.
1067 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1068 IStorage* iface,
1069 const OLECHAR *pwcsName, /* [string][in] */
1070 DWORD grfMode, /* [in] */
1071 DWORD reserved1, /* [in] */
1072 DWORD reserved2, /* [in] */
1073 IStorage **ppstg) /* [out] */
1075 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1077 DirEntry currentEntry;
1078 DirEntry newEntry;
1079 DirRef currentEntryRef;
1080 DirRef newEntryRef;
1081 HRESULT hr;
1083 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1084 iface, debugstr_w(pwcsName), grfMode,
1085 reserved1, reserved2, ppstg);
1087 if (ppstg == 0)
1088 return STG_E_INVALIDPOINTER;
1090 if (This->openFlags & STGM_SIMPLE)
1092 return STG_E_INVALIDFUNCTION;
1095 if (pwcsName == 0)
1096 return STG_E_INVALIDNAME;
1098 *ppstg = NULL;
1100 if ( FAILED( validateSTGM(grfMode) ) ||
1101 (grfMode & STGM_DELETEONRELEASE) )
1103 WARN("bad grfMode: 0x%x\n", grfMode);
1104 return STG_E_INVALIDFLAG;
1107 if (This->reverted)
1108 return STG_E_REVERTED;
1111 * Check that we're compatible with the parent's storage mode
1113 if ( !(This->openFlags & STGM_TRANSACTED) &&
1114 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1116 WARN("access denied\n");
1117 return STG_E_ACCESSDENIED;
1120 currentEntryRef = findElement(This,
1121 This->storageDirEntry,
1122 pwcsName,
1123 &currentEntry);
1125 if (currentEntryRef != DIRENTRY_NULL)
1128 * An element with this name already exists
1130 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1131 ((This->openFlags & STGM_TRANSACTED) ||
1132 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1134 hr = IStorage_DestroyElement(iface, pwcsName);
1135 if (FAILED(hr))
1136 return hr;
1138 else
1140 WARN("file already exists\n");
1141 return STG_E_FILEALREADYEXISTS;
1144 else if (!(This->openFlags & STGM_TRANSACTED) &&
1145 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1147 WARN("read-only storage\n");
1148 return STG_E_ACCESSDENIED;
1151 memset(&newEntry, 0, sizeof(DirEntry));
1153 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1155 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1157 FIXME("name too long\n");
1158 return STG_E_INVALIDNAME;
1161 strcpyW(newEntry.name, pwcsName);
1163 newEntry.stgType = STGTY_STORAGE;
1164 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1165 newEntry.size.u.LowPart = 0;
1166 newEntry.size.u.HighPart = 0;
1168 newEntry.leftChild = DIRENTRY_NULL;
1169 newEntry.rightChild = DIRENTRY_NULL;
1170 newEntry.dirRootEntry = DIRENTRY_NULL;
1172 /* call CoFileTime to get the current time
1173 newEntry.ctime
1174 newEntry.mtime
1177 /* newEntry.clsid */
1180 * Create a new directory entry for the storage
1182 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1183 if (FAILED(hr))
1184 return hr;
1187 * Insert the new directory entry into the parent storage's tree
1189 hr = insertIntoTree(
1190 This,
1191 This->storageDirEntry,
1192 newEntryRef);
1193 if (FAILED(hr))
1195 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1196 return hr;
1200 * Open it to get a pointer to return.
1202 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1204 if( (hr != S_OK) || (*ppstg == NULL))
1206 return hr;
1209 if (SUCCEEDED(hr))
1210 hr = StorageBaseImpl_Flush(This);
1212 return S_OK;
1216 /***************************************************************************
1218 * Internal Method
1220 * Reserve a directory entry in the file and initialize it.
1222 static HRESULT StorageImpl_CreateDirEntry(
1223 StorageBaseImpl *base,
1224 const DirEntry *newData,
1225 DirRef *index)
1227 StorageImpl *storage = (StorageImpl*)base;
1228 ULONG currentEntryIndex = 0;
1229 ULONG newEntryIndex = DIRENTRY_NULL;
1230 HRESULT hr = S_OK;
1231 BYTE currentData[RAW_DIRENTRY_SIZE];
1232 WORD sizeOfNameString;
1236 hr = StorageImpl_ReadRawDirEntry(storage,
1237 currentEntryIndex,
1238 currentData);
1240 if (SUCCEEDED(hr))
1242 StorageUtl_ReadWord(
1243 currentData,
1244 OFFSET_PS_NAMELENGTH,
1245 &sizeOfNameString);
1247 if (sizeOfNameString == 0)
1250 * The entry exists and is available, we found it.
1252 newEntryIndex = currentEntryIndex;
1255 else
1258 * We exhausted the directory entries, we will create more space below
1260 newEntryIndex = currentEntryIndex;
1262 currentEntryIndex++;
1264 } while (newEntryIndex == DIRENTRY_NULL);
1267 * grow the directory stream
1269 if (FAILED(hr))
1271 BYTE emptyData[RAW_DIRENTRY_SIZE];
1272 ULARGE_INTEGER newSize;
1273 ULONG entryIndex;
1274 ULONG lastEntry = 0;
1275 ULONG blockCount = 0;
1278 * obtain the new count of blocks in the directory stream
1280 blockCount = BlockChainStream_GetCount(
1281 storage->rootBlockChain)+1;
1284 * initialize the size used by the directory stream
1286 newSize.u.HighPart = 0;
1287 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1290 * add a block to the directory stream
1292 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1295 * memset the empty entry in order to initialize the unused newly
1296 * created entries
1298 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1301 * initialize them
1303 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1305 for(
1306 entryIndex = newEntryIndex + 1;
1307 entryIndex < lastEntry;
1308 entryIndex++)
1310 StorageImpl_WriteRawDirEntry(
1311 storage,
1312 entryIndex,
1313 emptyData);
1316 StorageImpl_SaveFileHeader(storage);
1319 UpdateRawDirEntry(currentData, newData);
1321 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1323 if (SUCCEEDED(hr))
1324 *index = newEntryIndex;
1326 return hr;
1329 /***************************************************************************
1331 * Internal Method
1333 * Mark a directory entry in the file as free.
1335 static HRESULT StorageImpl_DestroyDirEntry(
1336 StorageBaseImpl *base,
1337 DirRef index)
1339 HRESULT hr;
1340 BYTE emptyData[RAW_DIRENTRY_SIZE];
1341 StorageImpl *storage = (StorageImpl*)base;
1343 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1345 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1347 return hr;
1351 /****************************************************************************
1353 * Internal Method
1355 * Case insensitive comparison of DirEntry.name by first considering
1356 * their size.
1358 * Returns <0 when name1 < name2
1359 * >0 when name1 > name2
1360 * 0 when name1 == name2
1362 static LONG entryNameCmp(
1363 const OLECHAR *name1,
1364 const OLECHAR *name2)
1366 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1368 while (diff == 0 && *name1 != 0)
1371 * We compare the string themselves only when they are of the same length
1373 diff = toupperW(*name1++) - toupperW(*name2++);
1376 return diff;
1379 /****************************************************************************
1381 * Internal Method
1383 * Add a directory entry to a storage
1385 static HRESULT insertIntoTree(
1386 StorageBaseImpl *This,
1387 DirRef parentStorageIndex,
1388 DirRef newEntryIndex)
1390 DirEntry currentEntry;
1391 DirEntry newEntry;
1394 * Read the inserted entry
1396 StorageBaseImpl_ReadDirEntry(This,
1397 newEntryIndex,
1398 &newEntry);
1401 * Read the storage entry
1403 StorageBaseImpl_ReadDirEntry(This,
1404 parentStorageIndex,
1405 &currentEntry);
1407 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1410 * The root storage contains some element, therefore, start the research
1411 * for the appropriate location.
1413 BOOL found = 0;
1414 DirRef current, next, previous, currentEntryId;
1417 * Keep a reference to the root of the storage's element tree
1419 currentEntryId = currentEntry.dirRootEntry;
1422 * Read
1424 StorageBaseImpl_ReadDirEntry(This,
1425 currentEntry.dirRootEntry,
1426 &currentEntry);
1428 previous = currentEntry.leftChild;
1429 next = currentEntry.rightChild;
1430 current = currentEntryId;
1432 while (found == 0)
1434 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1436 if (diff < 0)
1438 if (previous != DIRENTRY_NULL)
1440 StorageBaseImpl_ReadDirEntry(This,
1441 previous,
1442 &currentEntry);
1443 current = previous;
1445 else
1447 currentEntry.leftChild = newEntryIndex;
1448 StorageBaseImpl_WriteDirEntry(This,
1449 current,
1450 &currentEntry);
1451 found = 1;
1454 else if (diff > 0)
1456 if (next != DIRENTRY_NULL)
1458 StorageBaseImpl_ReadDirEntry(This,
1459 next,
1460 &currentEntry);
1461 current = next;
1463 else
1465 currentEntry.rightChild = newEntryIndex;
1466 StorageBaseImpl_WriteDirEntry(This,
1467 current,
1468 &currentEntry);
1469 found = 1;
1472 else
1475 * Trying to insert an item with the same name in the
1476 * subtree structure.
1478 return STG_E_FILEALREADYEXISTS;
1481 previous = currentEntry.leftChild;
1482 next = currentEntry.rightChild;
1485 else
1488 * The storage is empty, make the new entry the root of its element tree
1490 currentEntry.dirRootEntry = newEntryIndex;
1491 StorageBaseImpl_WriteDirEntry(This,
1492 parentStorageIndex,
1493 &currentEntry);
1496 return S_OK;
1499 /****************************************************************************
1501 * Internal Method
1503 * Find and read the element of a storage with the given name.
1505 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1506 const OLECHAR *name, DirEntry *data)
1508 DirRef currentEntry;
1510 /* Read the storage entry to find the root of the tree. */
1511 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1513 currentEntry = data->dirRootEntry;
1515 while (currentEntry != DIRENTRY_NULL)
1517 LONG cmp;
1519 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1521 cmp = entryNameCmp(name, data->name);
1523 if (cmp == 0)
1524 /* found it */
1525 break;
1527 else if (cmp < 0)
1528 currentEntry = data->leftChild;
1530 else if (cmp > 0)
1531 currentEntry = data->rightChild;
1534 return currentEntry;
1537 /****************************************************************************
1539 * Internal Method
1541 * Find and read the binary tree parent of the element with the given name.
1543 * If there is no such element, find a place where it could be inserted and
1544 * return STG_E_FILENOTFOUND.
1546 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1547 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1548 ULONG *relation)
1550 DirRef childEntry;
1551 DirEntry childData;
1553 /* Read the storage entry to find the root of the tree. */
1554 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1556 *parentEntry = storageEntry;
1557 *relation = DIRENTRY_RELATION_DIR;
1559 childEntry = parentData->dirRootEntry;
1561 while (childEntry != DIRENTRY_NULL)
1563 LONG cmp;
1565 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1567 cmp = entryNameCmp(childName, childData.name);
1569 if (cmp == 0)
1570 /* found it */
1571 break;
1573 else if (cmp < 0)
1575 *parentData = childData;
1576 *parentEntry = childEntry;
1577 *relation = DIRENTRY_RELATION_PREVIOUS;
1579 childEntry = parentData->leftChild;
1582 else if (cmp > 0)
1584 *parentData = childData;
1585 *parentEntry = childEntry;
1586 *relation = DIRENTRY_RELATION_NEXT;
1588 childEntry = parentData->rightChild;
1592 if (childEntry == DIRENTRY_NULL)
1593 return STG_E_FILENOTFOUND;
1594 else
1595 return S_OK;
1599 /*************************************************************************
1600 * CopyTo (IStorage)
1602 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1603 IStorage* iface,
1604 DWORD ciidExclude, /* [in] */
1605 const IID* rgiidExclude, /* [size_is][unique][in] */
1606 SNB snbExclude, /* [unique][in] */
1607 IStorage* pstgDest) /* [unique][in] */
1609 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1611 IEnumSTATSTG *elements = 0;
1612 STATSTG curElement, strStat;
1613 HRESULT hr;
1614 IStorage *pstgTmp, *pstgChild;
1615 IStream *pstrTmp, *pstrChild;
1616 DirRef srcEntryRef;
1617 DirEntry srcEntry;
1618 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1619 int i;
1621 TRACE("(%p, %d, %p, %p, %p)\n",
1622 iface, ciidExclude, rgiidExclude,
1623 snbExclude, pstgDest);
1625 if ( pstgDest == 0 )
1626 return STG_E_INVALIDPOINTER;
1629 * Enumerate the elements
1631 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1633 if ( hr != S_OK )
1634 return hr;
1637 * set the class ID
1639 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1640 IStorage_SetClass( pstgDest, &curElement.clsid );
1642 for(i = 0; i < ciidExclude; ++i)
1644 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1645 skip_storage = TRUE;
1646 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1647 skip_stream = TRUE;
1648 else
1649 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1655 * Obtain the next element
1657 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1659 if ( hr == S_FALSE )
1661 hr = S_OK; /* done, every element has been copied */
1662 break;
1665 if ( snbExclude )
1667 WCHAR **snb = snbExclude;
1668 skip = FALSE;
1669 while ( *snb != NULL && !skip )
1671 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1672 skip = TRUE;
1673 ++snb;
1677 if ( skip )
1678 goto cleanup;
1680 if (curElement.type == STGTY_STORAGE)
1682 if(skip_storage)
1683 goto cleanup;
1686 * open child source storage
1688 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1689 STGM_READ|STGM_SHARE_EXCLUSIVE,
1690 NULL, 0, &pstgChild );
1692 if (hr != S_OK)
1693 goto cleanup;
1696 * create a new storage in destination storage
1698 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1699 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1700 0, 0,
1701 &pstgTmp );
1703 * if it already exist, don't create a new one use this one
1705 if (hr == STG_E_FILEALREADYEXISTS)
1707 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1708 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1709 NULL, 0, &pstgTmp );
1712 if (hr == S_OK)
1715 * do the copy recursively
1717 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1718 NULL, pstgTmp );
1720 IStorage_Release( pstgTmp );
1723 IStorage_Release( pstgChild );
1725 else if (curElement.type == STGTY_STREAM)
1727 if(skip_stream)
1728 goto cleanup;
1731 * create a new stream in destination storage. If the stream already
1732 * exist, it will be deleted and a new one will be created.
1734 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1735 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1736 0, 0, &pstrTmp );
1738 if (hr != S_OK)
1739 goto cleanup;
1742 * open child stream storage. This operation must succeed even if the
1743 * stream is already open, so we use internal functions to do it.
1745 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1746 &srcEntry);
1747 if (!srcEntryRef)
1749 ERR("source stream not found\n");
1750 hr = STG_E_DOCFILECORRUPT;
1753 if (hr == S_OK)
1755 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1756 if (pstrChild)
1757 IStream_AddRef(pstrChild);
1758 else
1759 hr = E_OUTOFMEMORY;
1762 if (hr == S_OK)
1765 * Get the size of the source stream
1767 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1770 * Set the size of the destination stream.
1772 IStream_SetSize(pstrTmp, strStat.cbSize);
1775 * do the copy
1777 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1778 NULL, NULL );
1780 IStream_Release( pstrChild );
1783 IStream_Release( pstrTmp );
1785 else
1787 WARN("unknown element type: %d\n", curElement.type);
1790 cleanup:
1791 CoTaskMemFree(curElement.pwcsName);
1792 } while (hr == S_OK);
1795 * Clean-up
1797 IEnumSTATSTG_Release(elements);
1799 return hr;
1802 /*************************************************************************
1803 * MoveElementTo (IStorage)
1805 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1806 IStorage* iface,
1807 const OLECHAR *pwcsName, /* [string][in] */
1808 IStorage *pstgDest, /* [unique][in] */
1809 const OLECHAR *pwcsNewName,/* [string][in] */
1810 DWORD grfFlags) /* [in] */
1812 FIXME("(%p %s %p %s %u): stub\n", iface,
1813 debugstr_w(pwcsName), pstgDest,
1814 debugstr_w(pwcsNewName), grfFlags);
1815 return E_NOTIMPL;
1818 /*************************************************************************
1819 * Commit (IStorage)
1821 * Ensures that any changes made to a storage object open in transacted mode
1822 * are reflected in the parent storage
1824 * In a non-transacted mode, this ensures all cached writes are completed.
1826 static HRESULT WINAPI StorageImpl_Commit(
1827 IStorage* iface,
1828 DWORD grfCommitFlags)/* [in] */
1830 StorageBaseImpl* const base=(StorageBaseImpl*)iface;
1831 TRACE("(%p %d)\n", iface, grfCommitFlags);
1832 return StorageBaseImpl_Flush(base);
1835 /*************************************************************************
1836 * Revert (IStorage)
1838 * Discard all changes that have been made since the last commit operation
1840 static HRESULT WINAPI StorageImpl_Revert(
1841 IStorage* iface)
1843 TRACE("(%p)\n", iface);
1844 return S_OK;
1847 /*************************************************************************
1848 * DestroyElement (IStorage)
1850 * Strategy: This implementation is built this way for simplicity not for speed.
1851 * I always delete the topmost element of the enumeration and adjust
1852 * the deleted element pointer all the time. This takes longer to
1853 * do but allow to reinvoke DestroyElement whenever we encounter a
1854 * storage object. The optimisation resides in the usage of another
1855 * enumeration strategy that would give all the leaves of a storage
1856 * first. (postfix order)
1858 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1859 IStorage* iface,
1860 const OLECHAR *pwcsName)/* [string][in] */
1862 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1864 HRESULT hr = S_OK;
1865 DirEntry entryToDelete;
1866 DirRef entryToDeleteRef;
1868 TRACE("(%p, %s)\n",
1869 iface, debugstr_w(pwcsName));
1871 if (pwcsName==NULL)
1872 return STG_E_INVALIDPOINTER;
1874 if (This->reverted)
1875 return STG_E_REVERTED;
1877 if ( !(This->openFlags & STGM_TRANSACTED) &&
1878 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1879 return STG_E_ACCESSDENIED;
1881 entryToDeleteRef = findElement(
1882 This,
1883 This->storageDirEntry,
1884 pwcsName,
1885 &entryToDelete);
1887 if ( entryToDeleteRef == DIRENTRY_NULL )
1889 return STG_E_FILENOTFOUND;
1892 if ( entryToDelete.stgType == STGTY_STORAGE )
1894 hr = deleteStorageContents(
1895 This,
1896 entryToDeleteRef,
1897 entryToDelete);
1899 else if ( entryToDelete.stgType == STGTY_STREAM )
1901 hr = deleteStreamContents(
1902 This,
1903 entryToDeleteRef,
1904 entryToDelete);
1907 if (hr!=S_OK)
1908 return hr;
1911 * Remove the entry from its parent storage
1913 hr = removeFromTree(
1914 This,
1915 This->storageDirEntry,
1916 entryToDeleteRef);
1919 * Invalidate the entry
1921 if (SUCCEEDED(hr))
1922 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1924 if (SUCCEEDED(hr))
1925 hr = StorageBaseImpl_Flush(This);
1927 return hr;
1931 /******************************************************************************
1932 * Internal stream list handlers
1935 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1937 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1938 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1941 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1943 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1944 list_remove(&(strm->StrmListEntry));
1947 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1949 StgStreamImpl *strm;
1951 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1953 if (strm->dirEntry == streamEntry)
1955 return TRUE;
1959 return FALSE;
1962 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1964 StorageInternalImpl *childstg;
1966 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1968 if (childstg->base.storageDirEntry == storageEntry)
1970 return TRUE;
1974 return FALSE;
1977 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1979 struct list *cur, *cur2;
1980 StgStreamImpl *strm=NULL;
1981 StorageInternalImpl *childstg=NULL;
1983 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1984 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1985 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1986 strm->parentStorage = NULL;
1987 list_remove(cur);
1990 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1991 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1992 StorageBaseImpl_Invalidate( &childstg->base );
1995 if (stg->transactedChild)
1997 StorageBaseImpl_Invalidate(stg->transactedChild);
1999 stg->transactedChild = NULL;
2004 /*********************************************************************
2006 * Internal Method
2008 * Delete the contents of a storage entry.
2011 static HRESULT deleteStorageContents(
2012 StorageBaseImpl *parentStorage,
2013 DirRef indexToDelete,
2014 DirEntry entryDataToDelete)
2016 IEnumSTATSTG *elements = 0;
2017 IStorage *childStorage = 0;
2018 STATSTG currentElement;
2019 HRESULT hr;
2020 HRESULT destroyHr = S_OK;
2021 StorageInternalImpl *stg, *stg2;
2023 /* Invalidate any open storage objects. */
2024 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2026 if (stg->base.storageDirEntry == indexToDelete)
2028 StorageBaseImpl_Invalidate(&stg->base);
2033 * Open the storage and enumerate it
2035 hr = StorageBaseImpl_OpenStorage(
2036 (IStorage*)parentStorage,
2037 entryDataToDelete.name,
2039 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2042 &childStorage);
2044 if (hr != S_OK)
2046 return hr;
2050 * Enumerate the elements
2052 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2057 * Obtain the next element
2059 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2060 if (hr==S_OK)
2062 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2064 CoTaskMemFree(currentElement.pwcsName);
2068 * We need to Reset the enumeration every time because we delete elements
2069 * and the enumeration could be invalid
2071 IEnumSTATSTG_Reset(elements);
2073 } while ((hr == S_OK) && (destroyHr == S_OK));
2075 IStorage_Release(childStorage);
2076 IEnumSTATSTG_Release(elements);
2078 return destroyHr;
2081 /*********************************************************************
2083 * Internal Method
2085 * Perform the deletion of a stream's data
2088 static HRESULT deleteStreamContents(
2089 StorageBaseImpl *parentStorage,
2090 DirRef indexToDelete,
2091 DirEntry entryDataToDelete)
2093 IStream *pis;
2094 HRESULT hr;
2095 ULARGE_INTEGER size;
2096 StgStreamImpl *strm, *strm2;
2098 /* Invalidate any open stream objects. */
2099 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2101 if (strm->dirEntry == indexToDelete)
2103 TRACE("Stream deleted %p\n", strm);
2104 strm->parentStorage = NULL;
2105 list_remove(&strm->StrmListEntry);
2109 size.u.HighPart = 0;
2110 size.u.LowPart = 0;
2112 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2113 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2115 if (hr!=S_OK)
2117 return(hr);
2121 * Zap the stream
2123 hr = IStream_SetSize(pis, size);
2125 if(hr != S_OK)
2127 return hr;
2131 * Release the stream object.
2133 IStream_Release(pis);
2135 return S_OK;
2138 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2140 switch (relation)
2142 case DIRENTRY_RELATION_PREVIOUS:
2143 entry->leftChild = new_target;
2144 break;
2145 case DIRENTRY_RELATION_NEXT:
2146 entry->rightChild = new_target;
2147 break;
2148 case DIRENTRY_RELATION_DIR:
2149 entry->dirRootEntry = new_target;
2150 break;
2151 default:
2152 assert(0);
2156 /*************************************************************************
2158 * Internal Method
2160 * This method removes a directory entry from its parent storage tree without
2161 * freeing any resources attached to it.
2163 static HRESULT removeFromTree(
2164 StorageBaseImpl *This,
2165 DirRef parentStorageIndex,
2166 DirRef deletedIndex)
2168 HRESULT hr = S_OK;
2169 DirEntry entryToDelete;
2170 DirEntry parentEntry;
2171 DirRef parentEntryRef;
2172 ULONG typeOfRelation;
2174 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2176 if (hr != S_OK)
2177 return hr;
2180 * Find the element that links to the one we want to delete.
2182 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2183 &parentEntry, &parentEntryRef, &typeOfRelation);
2185 if (hr != S_OK)
2186 return hr;
2188 if (entryToDelete.leftChild != DIRENTRY_NULL)
2191 * Replace the deleted entry with its left child
2193 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2195 hr = StorageBaseImpl_WriteDirEntry(
2196 This,
2197 parentEntryRef,
2198 &parentEntry);
2199 if(FAILED(hr))
2201 return hr;
2204 if (entryToDelete.rightChild != DIRENTRY_NULL)
2207 * We need to reinsert the right child somewhere. We already know it and
2208 * its children are greater than everything in the left tree, so we
2209 * insert it at the rightmost point in the left tree.
2211 DirRef newRightChildParent = entryToDelete.leftChild;
2212 DirEntry newRightChildParentEntry;
2216 hr = StorageBaseImpl_ReadDirEntry(
2217 This,
2218 newRightChildParent,
2219 &newRightChildParentEntry);
2220 if (FAILED(hr))
2222 return hr;
2225 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2226 newRightChildParent = newRightChildParentEntry.rightChild;
2227 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2229 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2231 hr = StorageBaseImpl_WriteDirEntry(
2232 This,
2233 newRightChildParent,
2234 &newRightChildParentEntry);
2235 if (FAILED(hr))
2237 return hr;
2241 else
2244 * Replace the deleted entry with its right child
2246 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2248 hr = StorageBaseImpl_WriteDirEntry(
2249 This,
2250 parentEntryRef,
2251 &parentEntry);
2252 if(FAILED(hr))
2254 return hr;
2258 return hr;
2262 /******************************************************************************
2263 * SetElementTimes (IStorage)
2265 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2266 IStorage* iface,
2267 const OLECHAR *pwcsName,/* [string][in] */
2268 const FILETIME *pctime, /* [in] */
2269 const FILETIME *patime, /* [in] */
2270 const FILETIME *pmtime) /* [in] */
2272 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2273 return S_OK;
2276 /******************************************************************************
2277 * SetStateBits (IStorage)
2279 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2280 IStorage* iface,
2281 DWORD grfStateBits,/* [in] */
2282 DWORD grfMask) /* [in] */
2284 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2286 if (This->reverted)
2287 return STG_E_REVERTED;
2289 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2290 return S_OK;
2293 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2294 DirRef index, const DirEntry *data)
2296 StorageImpl *This = (StorageImpl*)base;
2297 return StorageImpl_WriteDirEntry(This, index, data);
2300 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2301 DirRef index, DirEntry *data)
2303 StorageImpl *This = (StorageImpl*)base;
2304 return StorageImpl_ReadDirEntry(This, index, data);
2307 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2309 int i;
2311 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2313 if (!This->blockChainCache[i])
2315 return &This->blockChainCache[i];
2319 i = This->blockChainToEvict;
2321 BlockChainStream_Destroy(This->blockChainCache[i]);
2322 This->blockChainCache[i] = NULL;
2324 This->blockChainToEvict++;
2325 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2326 This->blockChainToEvict = 0;
2328 return &This->blockChainCache[i];
2331 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2332 DirRef index)
2334 int i, free_index=-1;
2336 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2338 if (!This->blockChainCache[i])
2340 if (free_index == -1) free_index = i;
2342 else if (This->blockChainCache[i]->ownerDirEntry == index)
2344 return &This->blockChainCache[i];
2348 if (free_index == -1)
2350 free_index = This->blockChainToEvict;
2352 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2353 This->blockChainCache[free_index] = NULL;
2355 This->blockChainToEvict++;
2356 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2357 This->blockChainToEvict = 0;
2360 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2361 return &This->blockChainCache[free_index];
2364 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2366 int i;
2368 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2370 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2372 BlockChainStream_Destroy(This->blockChainCache[i]);
2373 This->blockChainCache[i] = NULL;
2374 return;
2379 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2380 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2382 StorageImpl *This = (StorageImpl*)base;
2383 DirEntry data;
2384 HRESULT hr;
2385 ULONG bytesToRead;
2387 hr = StorageImpl_ReadDirEntry(This, index, &data);
2388 if (FAILED(hr)) return hr;
2390 if (data.size.QuadPart == 0)
2392 *bytesRead = 0;
2393 return S_OK;
2396 if (offset.QuadPart + size > data.size.QuadPart)
2398 bytesToRead = data.size.QuadPart - offset.QuadPart;
2400 else
2402 bytesToRead = size;
2405 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2407 SmallBlockChainStream *stream;
2409 stream = SmallBlockChainStream_Construct(This, NULL, index);
2410 if (!stream) return E_OUTOFMEMORY;
2412 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2414 SmallBlockChainStream_Destroy(stream);
2416 return hr;
2418 else
2420 BlockChainStream *stream = NULL;
2422 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2423 if (!stream) return E_OUTOFMEMORY;
2425 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2427 return hr;
2431 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2432 ULARGE_INTEGER newsize)
2434 StorageImpl *This = (StorageImpl*)base;
2435 DirEntry data;
2436 HRESULT hr;
2437 SmallBlockChainStream *smallblock=NULL;
2438 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2440 hr = StorageImpl_ReadDirEntry(This, index, &data);
2441 if (FAILED(hr)) return hr;
2443 /* In simple mode keep the stream size above the small block limit */
2444 if (This->base.openFlags & STGM_SIMPLE)
2445 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2447 if (data.size.QuadPart == newsize.QuadPart)
2448 return S_OK;
2450 /* Create a block chain object of the appropriate type */
2451 if (data.size.QuadPart == 0)
2453 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2455 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2456 if (!smallblock) return E_OUTOFMEMORY;
2458 else
2460 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2461 bigblock = *pbigblock;
2462 if (!bigblock) return E_OUTOFMEMORY;
2465 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2467 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2468 if (!smallblock) return E_OUTOFMEMORY;
2470 else
2472 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2473 bigblock = *pbigblock;
2474 if (!bigblock) return E_OUTOFMEMORY;
2477 /* Change the block chain type if necessary. */
2478 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2480 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2481 if (!bigblock)
2483 SmallBlockChainStream_Destroy(smallblock);
2484 return E_FAIL;
2487 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2488 *pbigblock = bigblock;
2490 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2492 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2493 if (!smallblock)
2494 return E_FAIL;
2497 /* Set the size of the block chain. */
2498 if (smallblock)
2500 SmallBlockChainStream_SetSize(smallblock, newsize);
2501 SmallBlockChainStream_Destroy(smallblock);
2503 else
2505 BlockChainStream_SetSize(bigblock, newsize);
2508 /* Set the size in the directory entry. */
2509 hr = StorageImpl_ReadDirEntry(This, index, &data);
2510 if (SUCCEEDED(hr))
2512 data.size = newsize;
2514 hr = StorageImpl_WriteDirEntry(This, index, &data);
2516 return hr;
2519 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2520 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2522 StorageImpl *This = (StorageImpl*)base;
2523 DirEntry data;
2524 HRESULT hr;
2525 ULARGE_INTEGER newSize;
2527 hr = StorageImpl_ReadDirEntry(This, index, &data);
2528 if (FAILED(hr)) return hr;
2530 /* Grow the stream if necessary */
2531 newSize.QuadPart = 0;
2532 newSize.QuadPart = offset.QuadPart + size;
2534 if (newSize.QuadPart > data.size.QuadPart)
2536 hr = StorageImpl_StreamSetSize(base, index, newSize);
2537 if (FAILED(hr))
2538 return hr;
2540 hr = StorageImpl_ReadDirEntry(This, index, &data);
2541 if (FAILED(hr)) return hr;
2544 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2546 SmallBlockChainStream *stream;
2548 stream = SmallBlockChainStream_Construct(This, NULL, index);
2549 if (!stream) return E_OUTOFMEMORY;
2551 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2553 SmallBlockChainStream_Destroy(stream);
2555 return hr;
2557 else
2559 BlockChainStream *stream;
2561 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2562 if (!stream) return E_OUTOFMEMORY;
2564 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2566 return hr;
2570 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2571 DirRef src)
2573 StorageImpl *This = (StorageImpl*)base;
2574 DirEntry dst_data, src_data;
2575 HRESULT hr;
2577 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2579 if (SUCCEEDED(hr))
2580 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2582 if (SUCCEEDED(hr))
2584 StorageImpl_DeleteCachedBlockChainStream(This, src);
2585 dst_data.startingBlock = src_data.startingBlock;
2586 dst_data.size = src_data.size;
2588 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2591 return hr;
2594 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2596 StorageImpl *This = (StorageImpl*) iface;
2597 STATSTG statstg;
2598 HRESULT hr;
2600 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2602 *result = statstg.pwcsName;
2604 return hr;
2608 * Virtual function table for the IStorage32Impl class.
2610 static const IStorageVtbl Storage32Impl_Vtbl =
2612 StorageBaseImpl_QueryInterface,
2613 StorageBaseImpl_AddRef,
2614 StorageBaseImpl_Release,
2615 StorageBaseImpl_CreateStream,
2616 StorageBaseImpl_OpenStream,
2617 StorageBaseImpl_CreateStorage,
2618 StorageBaseImpl_OpenStorage,
2619 StorageBaseImpl_CopyTo,
2620 StorageBaseImpl_MoveElementTo,
2621 StorageImpl_Commit,
2622 StorageImpl_Revert,
2623 StorageBaseImpl_EnumElements,
2624 StorageBaseImpl_DestroyElement,
2625 StorageBaseImpl_RenameElement,
2626 StorageBaseImpl_SetElementTimes,
2627 StorageBaseImpl_SetClass,
2628 StorageBaseImpl_SetStateBits,
2629 StorageBaseImpl_Stat
2632 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2634 StorageImpl_Destroy,
2635 StorageImpl_Invalidate,
2636 StorageImpl_Flush,
2637 StorageImpl_GetFilename,
2638 StorageImpl_CreateDirEntry,
2639 StorageImpl_BaseWriteDirEntry,
2640 StorageImpl_BaseReadDirEntry,
2641 StorageImpl_DestroyDirEntry,
2642 StorageImpl_StreamReadAt,
2643 StorageImpl_StreamWriteAt,
2644 StorageImpl_StreamSetSize,
2645 StorageImpl_StreamLink
2648 static HRESULT StorageImpl_Construct(
2649 HANDLE hFile,
2650 LPCOLESTR pwcsName,
2651 ILockBytes* pLkbyt,
2652 DWORD openFlags,
2653 BOOL fileBased,
2654 BOOL create,
2655 ULONG sector_size,
2656 StorageImpl** result)
2658 StorageImpl* This;
2659 HRESULT hr = S_OK;
2660 DirEntry currentEntry;
2661 DirRef currentEntryRef;
2663 if ( FAILED( validateSTGM(openFlags) ))
2664 return STG_E_INVALIDFLAG;
2666 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2667 if (!This)
2668 return E_OUTOFMEMORY;
2670 memset(This, 0, sizeof(StorageImpl));
2672 list_init(&This->base.strmHead);
2674 list_init(&This->base.storageHead);
2676 This->base.lpVtbl = &Storage32Impl_Vtbl;
2677 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2678 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2679 This->base.openFlags = (openFlags & ~STGM_CREATE);
2680 This->base.ref = 1;
2681 This->base.create = create;
2683 This->base.reverted = 0;
2686 * Initialize the big block cache.
2688 This->bigBlockSize = sector_size;
2689 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2690 if (hFile)
2691 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2692 else
2694 This->lockBytes = pLkbyt;
2695 ILockBytes_AddRef(pLkbyt);
2698 if (FAILED(hr))
2699 goto end;
2701 if (create)
2703 ULARGE_INTEGER size;
2704 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2707 * Initialize all header variables:
2708 * - The big block depot consists of one block and it is at block 0
2709 * - The directory table starts at block 1
2710 * - There is no small block depot
2712 memset( This->bigBlockDepotStart,
2713 BLOCK_UNUSED,
2714 sizeof(This->bigBlockDepotStart));
2716 This->bigBlockDepotCount = 1;
2717 This->bigBlockDepotStart[0] = 0;
2718 This->rootStartBlock = 1;
2719 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2720 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2721 if (sector_size == 4096)
2722 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2723 else
2724 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2725 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2726 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2727 This->extBigBlockDepotCount = 0;
2729 StorageImpl_SaveFileHeader(This);
2732 * Add one block for the big block depot and one block for the directory table
2734 size.u.HighPart = 0;
2735 size.u.LowPart = This->bigBlockSize * 3;
2736 ILockBytes_SetSize(This->lockBytes, size);
2739 * Initialize the big block depot
2741 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2742 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2743 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2744 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2746 else
2749 * Load the header for the file.
2751 hr = StorageImpl_LoadFileHeader(This);
2753 if (FAILED(hr))
2755 goto end;
2760 * There is no block depot cached yet.
2762 This->indexBlockDepotCached = 0xFFFFFFFF;
2765 * Start searching for free blocks with block 0.
2767 This->prevFreeBlock = 0;
2769 This->firstFreeSmallBlock = 0;
2772 * Create the block chain abstractions.
2774 if(!(This->rootBlockChain =
2775 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2777 hr = STG_E_READFAULT;
2778 goto end;
2781 if(!(This->smallBlockDepotChain =
2782 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2783 DIRENTRY_NULL)))
2785 hr = STG_E_READFAULT;
2786 goto end;
2790 * Write the root storage entry (memory only)
2792 if (create)
2794 DirEntry rootEntry;
2796 * Initialize the directory table
2798 memset(&rootEntry, 0, sizeof(rootEntry));
2799 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2800 sizeof(rootEntry.name)/sizeof(WCHAR) );
2801 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2802 rootEntry.stgType = STGTY_ROOT;
2803 rootEntry.leftChild = DIRENTRY_NULL;
2804 rootEntry.rightChild = DIRENTRY_NULL;
2805 rootEntry.dirRootEntry = DIRENTRY_NULL;
2806 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2807 rootEntry.size.u.HighPart = 0;
2808 rootEntry.size.u.LowPart = 0;
2810 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2814 * Find the ID of the root storage.
2816 currentEntryRef = 0;
2820 hr = StorageImpl_ReadDirEntry(
2821 This,
2822 currentEntryRef,
2823 &currentEntry);
2825 if (SUCCEEDED(hr))
2827 if ( (currentEntry.sizeOfNameString != 0 ) &&
2828 (currentEntry.stgType == STGTY_ROOT) )
2830 This->base.storageDirEntry = currentEntryRef;
2834 currentEntryRef++;
2836 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2838 if (FAILED(hr))
2840 hr = STG_E_READFAULT;
2841 goto end;
2845 * Create the block chain abstraction for the small block root chain.
2847 if(!(This->smallBlockRootChain =
2848 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2850 hr = STG_E_READFAULT;
2853 end:
2854 if (FAILED(hr))
2856 IStorage_Release((IStorage*)This);
2857 *result = NULL;
2859 else
2861 StorageImpl_Flush((StorageBaseImpl*)This);
2862 *result = This;
2865 return hr;
2868 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2870 StorageImpl *This = (StorageImpl*) iface;
2872 StorageBaseImpl_DeleteAll(&This->base);
2874 This->base.reverted = 1;
2877 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2879 StorageImpl *This = (StorageImpl*) iface;
2880 int i;
2881 TRACE("(%p)\n", This);
2883 StorageImpl_Flush(iface);
2885 StorageImpl_Invalidate(iface);
2887 BlockChainStream_Destroy(This->smallBlockRootChain);
2888 BlockChainStream_Destroy(This->rootBlockChain);
2889 BlockChainStream_Destroy(This->smallBlockDepotChain);
2891 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2892 BlockChainStream_Destroy(This->blockChainCache[i]);
2894 if (This->lockBytes)
2895 ILockBytes_Release(This->lockBytes);
2896 HeapFree(GetProcessHeap(), 0, This);
2899 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2901 StorageImpl *This = (StorageImpl*) iface;
2902 int i;
2903 HRESULT hr;
2904 TRACE("(%p)\n", This);
2906 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2908 if (SUCCEEDED(hr))
2909 hr = BlockChainStream_Flush(This->rootBlockChain);
2911 if (SUCCEEDED(hr))
2912 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2914 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
2915 if (This->blockChainCache[i])
2916 hr = BlockChainStream_Flush(This->blockChainCache[i]);
2918 if (SUCCEEDED(hr))
2919 hr = ILockBytes_Flush(This->lockBytes);
2921 return hr;
2924 /******************************************************************************
2925 * Storage32Impl_GetNextFreeBigBlock
2927 * Returns the index of the next free big block.
2928 * If the big block depot is filled, this method will enlarge it.
2931 static ULONG StorageImpl_GetNextFreeBigBlock(
2932 StorageImpl* This)
2934 ULONG depotBlockIndexPos;
2935 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2936 BOOL success;
2937 ULONG depotBlockOffset;
2938 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2939 ULONG nextBlockIndex = BLOCK_SPECIAL;
2940 int depotIndex = 0;
2941 ULONG freeBlock = BLOCK_UNUSED;
2942 ULARGE_INTEGER neededSize;
2943 STATSTG statstg;
2945 depotIndex = This->prevFreeBlock / blocksPerDepot;
2946 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2949 * Scan the entire big block depot until we find a block marked free
2951 while (nextBlockIndex != BLOCK_UNUSED)
2953 if (depotIndex < COUNT_BBDEPOTINHEADER)
2955 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2958 * Grow the primary depot.
2960 if (depotBlockIndexPos == BLOCK_UNUSED)
2962 depotBlockIndexPos = depotIndex*blocksPerDepot;
2965 * Add a block depot.
2967 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2968 This->bigBlockDepotCount++;
2969 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2972 * Flag it as a block depot.
2974 StorageImpl_SetNextBlockInChain(This,
2975 depotBlockIndexPos,
2976 BLOCK_SPECIAL);
2978 /* Save new header information.
2980 StorageImpl_SaveFileHeader(This);
2983 else
2985 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2987 if (depotBlockIndexPos == BLOCK_UNUSED)
2990 * Grow the extended depot.
2992 ULONG extIndex = BLOCK_UNUSED;
2993 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2994 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2996 if (extBlockOffset == 0)
2998 /* We need an extended block.
3000 extIndex = Storage32Impl_AddExtBlockDepot(This);
3001 This->extBigBlockDepotCount++;
3002 depotBlockIndexPos = extIndex + 1;
3004 else
3005 depotBlockIndexPos = depotIndex * blocksPerDepot;
3008 * Add a block depot and mark it in the extended block.
3010 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3011 This->bigBlockDepotCount++;
3012 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3014 /* Flag the block depot.
3016 StorageImpl_SetNextBlockInChain(This,
3017 depotBlockIndexPos,
3018 BLOCK_SPECIAL);
3020 /* If necessary, flag the extended depot block.
3022 if (extIndex != BLOCK_UNUSED)
3023 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3025 /* Save header information.
3027 StorageImpl_SaveFileHeader(This);
3031 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3033 if (success)
3035 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3036 ( nextBlockIndex != BLOCK_UNUSED))
3038 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3040 if (nextBlockIndex == BLOCK_UNUSED)
3042 freeBlock = (depotIndex * blocksPerDepot) +
3043 (depotBlockOffset/sizeof(ULONG));
3046 depotBlockOffset += sizeof(ULONG);
3050 depotIndex++;
3051 depotBlockOffset = 0;
3055 * make sure that the block physically exists before using it
3057 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3059 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3061 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3062 ILockBytes_SetSize(This->lockBytes, neededSize);
3064 This->prevFreeBlock = freeBlock;
3066 return freeBlock;
3069 /******************************************************************************
3070 * Storage32Impl_AddBlockDepot
3072 * This will create a depot block, essentially it is a block initialized
3073 * to BLOCK_UNUSEDs.
3075 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3077 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3080 * Initialize blocks as free
3082 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3083 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3086 /******************************************************************************
3087 * Storage32Impl_GetExtDepotBlock
3089 * Returns the index of the block that corresponds to the specified depot
3090 * index. This method is only for depot indexes equal or greater than
3091 * COUNT_BBDEPOTINHEADER.
3093 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3095 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3096 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3097 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3098 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3099 ULONG blockIndex = BLOCK_UNUSED;
3100 ULONG extBlockIndex = This->extBigBlockDepotStart;
3102 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3104 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3105 return BLOCK_UNUSED;
3107 while (extBlockCount > 0)
3109 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3110 extBlockCount--;
3113 if (extBlockIndex != BLOCK_UNUSED)
3114 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3115 extBlockOffset * sizeof(ULONG), &blockIndex);
3117 return blockIndex;
3120 /******************************************************************************
3121 * Storage32Impl_SetExtDepotBlock
3123 * Associates the specified block index to the specified depot index.
3124 * This method is only for depot indexes equal or greater than
3125 * COUNT_BBDEPOTINHEADER.
3127 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3129 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3130 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3131 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3132 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3133 ULONG extBlockIndex = This->extBigBlockDepotStart;
3135 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3137 while (extBlockCount > 0)
3139 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3140 extBlockCount--;
3143 if (extBlockIndex != BLOCK_UNUSED)
3145 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3146 extBlockOffset * sizeof(ULONG),
3147 blockIndex);
3151 /******************************************************************************
3152 * Storage32Impl_AddExtBlockDepot
3154 * Creates an extended depot block.
3156 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3158 ULONG numExtBlocks = This->extBigBlockDepotCount;
3159 ULONG nextExtBlock = This->extBigBlockDepotStart;
3160 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3161 ULONG index = BLOCK_UNUSED;
3162 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3163 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3164 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3166 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3167 blocksPerDepotBlock;
3169 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3172 * The first extended block.
3174 This->extBigBlockDepotStart = index;
3176 else
3178 unsigned int i;
3180 * Follow the chain to the last one.
3182 for (i = 0; i < (numExtBlocks - 1); i++)
3184 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3188 * Add the new extended block to the chain.
3190 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3191 index);
3195 * Initialize this block.
3197 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3198 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3200 return index;
3203 /******************************************************************************
3204 * Storage32Impl_FreeBigBlock
3206 * This method will flag the specified block as free in the big block depot.
3208 static void StorageImpl_FreeBigBlock(
3209 StorageImpl* This,
3210 ULONG blockIndex)
3212 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3214 if (blockIndex < This->prevFreeBlock)
3215 This->prevFreeBlock = blockIndex;
3218 /************************************************************************
3219 * Storage32Impl_GetNextBlockInChain
3221 * This method will retrieve the block index of the next big block in
3222 * in the chain.
3224 * Params: This - Pointer to the Storage object.
3225 * blockIndex - Index of the block to retrieve the chain
3226 * for.
3227 * nextBlockIndex - receives the return value.
3229 * Returns: This method returns the index of the next block in the chain.
3230 * It will return the constants:
3231 * BLOCK_SPECIAL - If the block given was not part of a
3232 * chain.
3233 * BLOCK_END_OF_CHAIN - If the block given was the last in
3234 * a chain.
3235 * BLOCK_UNUSED - If the block given was not past of a chain
3236 * and is available.
3237 * BLOCK_EXTBBDEPOT - This block is part of the extended
3238 * big block depot.
3240 * See Windows documentation for more details on IStorage methods.
3242 static HRESULT StorageImpl_GetNextBlockInChain(
3243 StorageImpl* This,
3244 ULONG blockIndex,
3245 ULONG* nextBlockIndex)
3247 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3248 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3249 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3250 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3251 BOOL success;
3252 ULONG depotBlockIndexPos;
3253 int index, num_blocks;
3255 *nextBlockIndex = BLOCK_SPECIAL;
3257 if(depotBlockCount >= This->bigBlockDepotCount)
3259 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3260 This->bigBlockDepotCount);
3261 return STG_E_READFAULT;
3265 * Cache the currently accessed depot block.
3267 if (depotBlockCount != This->indexBlockDepotCached)
3269 This->indexBlockDepotCached = depotBlockCount;
3271 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3273 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3275 else
3278 * We have to look in the extended depot.
3280 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3283 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3285 if (!success)
3286 return STG_E_READFAULT;
3288 num_blocks = This->bigBlockSize / 4;
3290 for (index = 0; index < num_blocks; index++)
3292 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3293 This->blockDepotCached[index] = *nextBlockIndex;
3297 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3299 return S_OK;
3302 /******************************************************************************
3303 * Storage32Impl_GetNextExtendedBlock
3305 * Given an extended block this method will return the next extended block.
3307 * NOTES:
3308 * The last ULONG of an extended block is the block index of the next
3309 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3310 * depot.
3312 * Return values:
3313 * - The index of the next extended block
3314 * - BLOCK_UNUSED: there is no next extended block.
3315 * - Any other return values denotes failure.
3317 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3319 ULONG nextBlockIndex = BLOCK_SPECIAL;
3320 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3322 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3323 &nextBlockIndex);
3325 return nextBlockIndex;
3328 /******************************************************************************
3329 * Storage32Impl_SetNextBlockInChain
3331 * This method will write the index of the specified block's next block
3332 * in the big block depot.
3334 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3335 * do the following
3337 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3338 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3339 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3342 static void StorageImpl_SetNextBlockInChain(
3343 StorageImpl* This,
3344 ULONG blockIndex,
3345 ULONG nextBlock)
3347 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3348 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3349 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3350 ULONG depotBlockIndexPos;
3352 assert(depotBlockCount < This->bigBlockDepotCount);
3353 assert(blockIndex != nextBlock);
3355 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3357 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3359 else
3362 * We have to look in the extended depot.
3364 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3367 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3368 nextBlock);
3370 * Update the cached block depot, if necessary.
3372 if (depotBlockCount == This->indexBlockDepotCached)
3374 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3378 /******************************************************************************
3379 * Storage32Impl_LoadFileHeader
3381 * This method will read in the file header
3383 static HRESULT StorageImpl_LoadFileHeader(
3384 StorageImpl* This)
3386 HRESULT hr;
3387 BYTE headerBigBlock[HEADER_SIZE];
3388 int index;
3389 ULARGE_INTEGER offset;
3390 DWORD bytes_read;
3392 TRACE("\n");
3394 * Get a pointer to the big block of data containing the header.
3396 offset.u.HighPart = 0;
3397 offset.u.LowPart = 0;
3398 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3399 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3400 hr = STG_E_FILENOTFOUND;
3403 * Extract the information from the header.
3405 if (SUCCEEDED(hr))
3408 * Check for the "magic number" signature and return an error if it is not
3409 * found.
3411 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3413 return STG_E_OLDFORMAT;
3416 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3418 return STG_E_INVALIDHEADER;
3421 StorageUtl_ReadWord(
3422 headerBigBlock,
3423 OFFSET_BIGBLOCKSIZEBITS,
3424 &This->bigBlockSizeBits);
3426 StorageUtl_ReadWord(
3427 headerBigBlock,
3428 OFFSET_SMALLBLOCKSIZEBITS,
3429 &This->smallBlockSizeBits);
3431 StorageUtl_ReadDWord(
3432 headerBigBlock,
3433 OFFSET_BBDEPOTCOUNT,
3434 &This->bigBlockDepotCount);
3436 StorageUtl_ReadDWord(
3437 headerBigBlock,
3438 OFFSET_ROOTSTARTBLOCK,
3439 &This->rootStartBlock);
3441 StorageUtl_ReadDWord(
3442 headerBigBlock,
3443 OFFSET_SMALLBLOCKLIMIT,
3444 &This->smallBlockLimit);
3446 StorageUtl_ReadDWord(
3447 headerBigBlock,
3448 OFFSET_SBDEPOTSTART,
3449 &This->smallBlockDepotStart);
3451 StorageUtl_ReadDWord(
3452 headerBigBlock,
3453 OFFSET_EXTBBDEPOTSTART,
3454 &This->extBigBlockDepotStart);
3456 StorageUtl_ReadDWord(
3457 headerBigBlock,
3458 OFFSET_EXTBBDEPOTCOUNT,
3459 &This->extBigBlockDepotCount);
3461 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3463 StorageUtl_ReadDWord(
3464 headerBigBlock,
3465 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3466 &(This->bigBlockDepotStart[index]));
3470 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3472 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3473 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3476 * Right now, the code is making some assumptions about the size of the
3477 * blocks, just make sure they are what we're expecting.
3479 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3480 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3481 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3483 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3484 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3485 hr = STG_E_INVALIDHEADER;
3487 else
3488 hr = S_OK;
3491 return hr;
3494 /******************************************************************************
3495 * Storage32Impl_SaveFileHeader
3497 * This method will save to the file the header
3499 static void StorageImpl_SaveFileHeader(
3500 StorageImpl* This)
3502 BYTE headerBigBlock[HEADER_SIZE];
3503 int index;
3504 HRESULT hr;
3505 ULARGE_INTEGER offset;
3506 DWORD bytes_read, bytes_written;
3507 DWORD major_version, dirsectorcount;
3510 * Get a pointer to the big block of data containing the header.
3512 offset.u.HighPart = 0;
3513 offset.u.LowPart = 0;
3514 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3515 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3516 hr = STG_E_FILENOTFOUND;
3518 if (This->bigBlockSizeBits == 0x9)
3519 major_version = 3;
3520 else if (This->bigBlockSizeBits == 0xc)
3521 major_version = 4;
3522 else
3524 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3525 major_version = 4;
3529 * If the block read failed, the file is probably new.
3531 if (FAILED(hr))
3534 * Initialize for all unknown fields.
3536 memset(headerBigBlock, 0, HEADER_SIZE);
3539 * Initialize the magic number.
3541 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3545 * Write the information to the header.
3547 StorageUtl_WriteWord(
3548 headerBigBlock,
3549 OFFSET_MINORVERSION,
3550 0x3e);
3552 StorageUtl_WriteWord(
3553 headerBigBlock,
3554 OFFSET_MAJORVERSION,
3555 major_version);
3557 StorageUtl_WriteWord(
3558 headerBigBlock,
3559 OFFSET_BYTEORDERMARKER,
3560 (WORD)-2);
3562 StorageUtl_WriteWord(
3563 headerBigBlock,
3564 OFFSET_BIGBLOCKSIZEBITS,
3565 This->bigBlockSizeBits);
3567 StorageUtl_WriteWord(
3568 headerBigBlock,
3569 OFFSET_SMALLBLOCKSIZEBITS,
3570 This->smallBlockSizeBits);
3572 if (major_version >= 4)
3574 if (This->rootBlockChain)
3575 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3576 else
3577 /* This file is being created, and it will start out with one block. */
3578 dirsectorcount = 1;
3580 else
3581 /* This field must be 0 in versions older than 4 */
3582 dirsectorcount = 0;
3584 StorageUtl_WriteDWord(
3585 headerBigBlock,
3586 OFFSET_DIRSECTORCOUNT,
3587 dirsectorcount);
3589 StorageUtl_WriteDWord(
3590 headerBigBlock,
3591 OFFSET_BBDEPOTCOUNT,
3592 This->bigBlockDepotCount);
3594 StorageUtl_WriteDWord(
3595 headerBigBlock,
3596 OFFSET_ROOTSTARTBLOCK,
3597 This->rootStartBlock);
3599 StorageUtl_WriteDWord(
3600 headerBigBlock,
3601 OFFSET_SMALLBLOCKLIMIT,
3602 This->smallBlockLimit);
3604 StorageUtl_WriteDWord(
3605 headerBigBlock,
3606 OFFSET_SBDEPOTSTART,
3607 This->smallBlockDepotStart);
3609 StorageUtl_WriteDWord(
3610 headerBigBlock,
3611 OFFSET_SBDEPOTCOUNT,
3612 This->smallBlockDepotChain ?
3613 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3615 StorageUtl_WriteDWord(
3616 headerBigBlock,
3617 OFFSET_EXTBBDEPOTSTART,
3618 This->extBigBlockDepotStart);
3620 StorageUtl_WriteDWord(
3621 headerBigBlock,
3622 OFFSET_EXTBBDEPOTCOUNT,
3623 This->extBigBlockDepotCount);
3625 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3627 StorageUtl_WriteDWord(
3628 headerBigBlock,
3629 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3630 (This->bigBlockDepotStart[index]));
3634 * Write the big block back to the file.
3636 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3639 /******************************************************************************
3640 * StorageImpl_ReadRawDirEntry
3642 * This method will read the raw data from a directory entry in the file.
3644 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3646 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, 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_ReadAt(
3656 This->rootBlockChain,
3657 offset,
3658 RAW_DIRENTRY_SIZE,
3659 buffer,
3660 &bytesRead);
3662 if (bytesRead != RAW_DIRENTRY_SIZE)
3663 return STG_E_READFAULT;
3665 return hr;
3668 /******************************************************************************
3669 * StorageImpl_WriteRawDirEntry
3671 * This method will write the raw data from a directory entry in the file.
3673 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3675 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3677 ULARGE_INTEGER offset;
3678 HRESULT hr;
3679 ULONG bytesRead;
3681 offset.u.HighPart = 0;
3682 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3684 hr = BlockChainStream_WriteAt(
3685 This->rootBlockChain,
3686 offset,
3687 RAW_DIRENTRY_SIZE,
3688 buffer,
3689 &bytesRead);
3691 return hr;
3694 /******************************************************************************
3695 * UpdateRawDirEntry
3697 * Update raw directory entry data from the fields in newData.
3699 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3701 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3703 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3705 memcpy(
3706 buffer + OFFSET_PS_NAME,
3707 newData->name,
3708 DIRENTRY_NAME_BUFFER_LEN );
3710 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3712 StorageUtl_WriteWord(
3713 buffer,
3714 OFFSET_PS_NAMELENGTH,
3715 newData->sizeOfNameString);
3717 StorageUtl_WriteDWord(
3718 buffer,
3719 OFFSET_PS_LEFTCHILD,
3720 newData->leftChild);
3722 StorageUtl_WriteDWord(
3723 buffer,
3724 OFFSET_PS_RIGHTCHILD,
3725 newData->rightChild);
3727 StorageUtl_WriteDWord(
3728 buffer,
3729 OFFSET_PS_DIRROOT,
3730 newData->dirRootEntry);
3732 StorageUtl_WriteGUID(
3733 buffer,
3734 OFFSET_PS_GUID,
3735 &newData->clsid);
3737 StorageUtl_WriteDWord(
3738 buffer,
3739 OFFSET_PS_CTIMELOW,
3740 newData->ctime.dwLowDateTime);
3742 StorageUtl_WriteDWord(
3743 buffer,
3744 OFFSET_PS_CTIMEHIGH,
3745 newData->ctime.dwHighDateTime);
3747 StorageUtl_WriteDWord(
3748 buffer,
3749 OFFSET_PS_MTIMELOW,
3750 newData->mtime.dwLowDateTime);
3752 StorageUtl_WriteDWord(
3753 buffer,
3754 OFFSET_PS_MTIMEHIGH,
3755 newData->ctime.dwHighDateTime);
3757 StorageUtl_WriteDWord(
3758 buffer,
3759 OFFSET_PS_STARTBLOCK,
3760 newData->startingBlock);
3762 StorageUtl_WriteDWord(
3763 buffer,
3764 OFFSET_PS_SIZE,
3765 newData->size.u.LowPart);
3768 /******************************************************************************
3769 * Storage32Impl_ReadDirEntry
3771 * This method will read the specified directory entry.
3773 HRESULT StorageImpl_ReadDirEntry(
3774 StorageImpl* This,
3775 DirRef index,
3776 DirEntry* buffer)
3778 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3779 HRESULT readRes;
3781 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3783 if (SUCCEEDED(readRes))
3785 memset(buffer->name, 0, sizeof(buffer->name));
3786 memcpy(
3787 buffer->name,
3788 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3789 DIRENTRY_NAME_BUFFER_LEN );
3790 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3792 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3794 StorageUtl_ReadWord(
3795 currentEntry,
3796 OFFSET_PS_NAMELENGTH,
3797 &buffer->sizeOfNameString);
3799 StorageUtl_ReadDWord(
3800 currentEntry,
3801 OFFSET_PS_LEFTCHILD,
3802 &buffer->leftChild);
3804 StorageUtl_ReadDWord(
3805 currentEntry,
3806 OFFSET_PS_RIGHTCHILD,
3807 &buffer->rightChild);
3809 StorageUtl_ReadDWord(
3810 currentEntry,
3811 OFFSET_PS_DIRROOT,
3812 &buffer->dirRootEntry);
3814 StorageUtl_ReadGUID(
3815 currentEntry,
3816 OFFSET_PS_GUID,
3817 &buffer->clsid);
3819 StorageUtl_ReadDWord(
3820 currentEntry,
3821 OFFSET_PS_CTIMELOW,
3822 &buffer->ctime.dwLowDateTime);
3824 StorageUtl_ReadDWord(
3825 currentEntry,
3826 OFFSET_PS_CTIMEHIGH,
3827 &buffer->ctime.dwHighDateTime);
3829 StorageUtl_ReadDWord(
3830 currentEntry,
3831 OFFSET_PS_MTIMELOW,
3832 &buffer->mtime.dwLowDateTime);
3834 StorageUtl_ReadDWord(
3835 currentEntry,
3836 OFFSET_PS_MTIMEHIGH,
3837 &buffer->mtime.dwHighDateTime);
3839 StorageUtl_ReadDWord(
3840 currentEntry,
3841 OFFSET_PS_STARTBLOCK,
3842 &buffer->startingBlock);
3844 StorageUtl_ReadDWord(
3845 currentEntry,
3846 OFFSET_PS_SIZE,
3847 &buffer->size.u.LowPart);
3849 buffer->size.u.HighPart = 0;
3852 return readRes;
3855 /*********************************************************************
3856 * Write the specified directory entry to the file
3858 HRESULT StorageImpl_WriteDirEntry(
3859 StorageImpl* This,
3860 DirRef index,
3861 const DirEntry* buffer)
3863 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3864 HRESULT writeRes;
3866 UpdateRawDirEntry(currentEntry, buffer);
3868 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3869 return writeRes;
3872 static BOOL StorageImpl_ReadBigBlock(
3873 StorageImpl* This,
3874 ULONG blockIndex,
3875 void* buffer)
3877 ULARGE_INTEGER ulOffset;
3878 DWORD read;
3880 ulOffset.u.HighPart = 0;
3881 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3883 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3884 return (read == This->bigBlockSize);
3887 static BOOL StorageImpl_ReadDWordFromBigBlock(
3888 StorageImpl* This,
3889 ULONG blockIndex,
3890 ULONG offset,
3891 DWORD* value)
3893 ULARGE_INTEGER ulOffset;
3894 DWORD read;
3895 DWORD tmp;
3897 ulOffset.u.HighPart = 0;
3898 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3899 ulOffset.u.LowPart += offset;
3901 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3902 *value = lendian32toh(tmp);
3903 return (read == sizeof(DWORD));
3906 static BOOL StorageImpl_WriteBigBlock(
3907 StorageImpl* This,
3908 ULONG blockIndex,
3909 const void* buffer)
3911 ULARGE_INTEGER ulOffset;
3912 DWORD wrote;
3914 ulOffset.u.HighPart = 0;
3915 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3917 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3918 return (wrote == This->bigBlockSize);
3921 static BOOL StorageImpl_WriteDWordToBigBlock(
3922 StorageImpl* This,
3923 ULONG blockIndex,
3924 ULONG offset,
3925 DWORD value)
3927 ULARGE_INTEGER ulOffset;
3928 DWORD wrote;
3930 ulOffset.u.HighPart = 0;
3931 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3932 ulOffset.u.LowPart += offset;
3934 value = htole32(value);
3935 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3936 return (wrote == sizeof(DWORD));
3939 /******************************************************************************
3940 * Storage32Impl_SmallBlocksToBigBlocks
3942 * This method will convert a small block chain to a big block chain.
3943 * The small block chain will be destroyed.
3945 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3946 StorageImpl* This,
3947 SmallBlockChainStream** ppsbChain)
3949 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3950 ULARGE_INTEGER size, offset;
3951 ULONG cbRead, cbWritten;
3952 ULARGE_INTEGER cbTotalRead;
3953 DirRef streamEntryRef;
3954 HRESULT resWrite = S_OK;
3955 HRESULT resRead;
3956 DirEntry streamEntry;
3957 BYTE *buffer;
3958 BlockChainStream *bbTempChain = NULL;
3959 BlockChainStream *bigBlockChain = NULL;
3962 * Create a temporary big block chain that doesn't have
3963 * an associated directory entry. This temporary chain will be
3964 * used to copy data from small blocks to big blocks.
3966 bbTempChain = BlockChainStream_Construct(This,
3967 &bbHeadOfChain,
3968 DIRENTRY_NULL);
3969 if(!bbTempChain) return NULL;
3971 * Grow the big block chain.
3973 size = SmallBlockChainStream_GetSize(*ppsbChain);
3974 BlockChainStream_SetSize(bbTempChain, size);
3977 * Copy the contents of the small block chain to the big block chain
3978 * by small block size increments.
3980 offset.u.LowPart = 0;
3981 offset.u.HighPart = 0;
3982 cbTotalRead.QuadPart = 0;
3984 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3987 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3988 offset,
3989 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3990 buffer,
3991 &cbRead);
3992 if (FAILED(resRead))
3993 break;
3995 if (cbRead > 0)
3997 cbTotalRead.QuadPart += cbRead;
3999 resWrite = BlockChainStream_WriteAt(bbTempChain,
4000 offset,
4001 cbRead,
4002 buffer,
4003 &cbWritten);
4005 if (FAILED(resWrite))
4006 break;
4008 offset.u.LowPart += cbRead;
4010 else
4012 resRead = STG_E_READFAULT;
4013 break;
4015 } while (cbTotalRead.QuadPart < size.QuadPart);
4016 HeapFree(GetProcessHeap(),0,buffer);
4018 size.u.HighPart = 0;
4019 size.u.LowPart = 0;
4021 if (FAILED(resRead) || FAILED(resWrite))
4023 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4024 BlockChainStream_SetSize(bbTempChain, size);
4025 BlockChainStream_Destroy(bbTempChain);
4026 return NULL;
4030 * Destroy the small block chain.
4032 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4033 SmallBlockChainStream_SetSize(*ppsbChain, size);
4034 SmallBlockChainStream_Destroy(*ppsbChain);
4035 *ppsbChain = 0;
4038 * Change the directory entry. This chain is now a big block chain
4039 * and it doesn't reside in the small blocks chain anymore.
4041 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4043 streamEntry.startingBlock = bbHeadOfChain;
4045 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4048 * Destroy the temporary entryless big block chain.
4049 * Create a new big block chain associated with this entry.
4051 BlockChainStream_Destroy(bbTempChain);
4052 bigBlockChain = BlockChainStream_Construct(This,
4053 NULL,
4054 streamEntryRef);
4056 return bigBlockChain;
4059 /******************************************************************************
4060 * Storage32Impl_BigBlocksToSmallBlocks
4062 * This method will convert a big block chain to a small block chain.
4063 * The big block chain will be destroyed on success.
4065 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4066 StorageImpl* This,
4067 BlockChainStream** ppbbChain)
4069 ULARGE_INTEGER size, offset, cbTotalRead;
4070 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4071 DirRef streamEntryRef;
4072 HRESULT resWrite = S_OK, resRead;
4073 DirEntry streamEntry;
4074 BYTE* buffer;
4075 SmallBlockChainStream* sbTempChain;
4077 TRACE("%p %p\n", This, ppbbChain);
4079 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4080 DIRENTRY_NULL);
4082 if(!sbTempChain)
4083 return NULL;
4085 size = BlockChainStream_GetSize(*ppbbChain);
4086 SmallBlockChainStream_SetSize(sbTempChain, size);
4088 offset.u.HighPart = 0;
4089 offset.u.LowPart = 0;
4090 cbTotalRead.QuadPart = 0;
4091 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4094 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4095 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4096 buffer, &cbRead);
4098 if(FAILED(resRead))
4099 break;
4101 if(cbRead > 0)
4103 cbTotalRead.QuadPart += cbRead;
4105 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4106 cbRead, buffer, &cbWritten);
4108 if(FAILED(resWrite))
4109 break;
4111 offset.u.LowPart += cbRead;
4113 else
4115 resRead = STG_E_READFAULT;
4116 break;
4118 }while(cbTotalRead.QuadPart < size.QuadPart);
4119 HeapFree(GetProcessHeap(), 0, buffer);
4121 size.u.HighPart = 0;
4122 size.u.LowPart = 0;
4124 if(FAILED(resRead) || FAILED(resWrite))
4126 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4127 SmallBlockChainStream_SetSize(sbTempChain, size);
4128 SmallBlockChainStream_Destroy(sbTempChain);
4129 return NULL;
4132 /* destroy the original big block chain */
4133 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4134 BlockChainStream_SetSize(*ppbbChain, size);
4135 BlockChainStream_Destroy(*ppbbChain);
4136 *ppbbChain = NULL;
4138 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4139 streamEntry.startingBlock = sbHeadOfChain;
4140 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4142 SmallBlockChainStream_Destroy(sbTempChain);
4143 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4146 static HRESULT StorageBaseImpl_CopyStream(
4147 StorageBaseImpl *dst, DirRef dst_entry,
4148 StorageBaseImpl *src, DirRef src_entry)
4150 HRESULT hr;
4151 BYTE data[4096];
4152 DirEntry srcdata;
4153 ULARGE_INTEGER bytes_copied;
4154 ULONG bytestocopy, bytesread, byteswritten;
4156 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4158 if (SUCCEEDED(hr))
4160 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4162 bytes_copied.QuadPart = 0;
4163 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4165 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4167 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4168 data, &bytesread);
4169 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4171 if (SUCCEEDED(hr))
4172 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4173 data, &byteswritten);
4174 if (SUCCEEDED(hr))
4176 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4177 bytes_copied.QuadPart += byteswritten;
4182 return hr;
4185 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4187 DirRef result=This->firstFreeEntry;
4189 while (result < This->entries_size && This->entries[result].inuse)
4190 result++;
4192 if (result == This->entries_size)
4194 ULONG new_size = This->entries_size * 2;
4195 TransactedDirEntry *new_entries;
4197 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4198 if (!new_entries) return DIRENTRY_NULL;
4200 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4201 HeapFree(GetProcessHeap(), 0, This->entries);
4203 This->entries = new_entries;
4204 This->entries_size = new_size;
4207 This->entries[result].inuse = 1;
4209 This->firstFreeEntry = result+1;
4211 return result;
4214 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4215 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4217 DirRef stubEntryRef;
4218 TransactedDirEntry *entry;
4220 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4222 if (stubEntryRef != DIRENTRY_NULL)
4224 entry = &This->entries[stubEntryRef];
4226 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4228 entry->read = 0;
4231 return stubEntryRef;
4234 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4235 TransactedSnapshotImpl *This, DirRef entry)
4237 HRESULT hr=S_OK;
4238 DirEntry data;
4240 if (!This->entries[entry].read)
4242 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4243 This->entries[entry].transactedParentEntry,
4244 &data);
4246 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4248 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4250 if (data.leftChild == DIRENTRY_NULL)
4251 hr = E_OUTOFMEMORY;
4254 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4256 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4258 if (data.rightChild == DIRENTRY_NULL)
4259 hr = E_OUTOFMEMORY;
4262 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4264 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4266 if (data.dirRootEntry == DIRENTRY_NULL)
4267 hr = E_OUTOFMEMORY;
4270 if (SUCCEEDED(hr))
4272 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4273 This->entries[entry].read = 1;
4277 return hr;
4280 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4281 TransactedSnapshotImpl *This, DirRef entry)
4283 HRESULT hr = S_OK;
4285 if (!This->entries[entry].stream_dirty)
4287 DirEntry new_entrydata;
4289 memset(&new_entrydata, 0, sizeof(DirEntry));
4290 new_entrydata.name[0] = 'S';
4291 new_entrydata.sizeOfNameString = 1;
4292 new_entrydata.stgType = STGTY_STREAM;
4293 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4294 new_entrydata.leftChild = DIRENTRY_NULL;
4295 new_entrydata.rightChild = DIRENTRY_NULL;
4296 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4298 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4299 &This->entries[entry].stream_entry);
4301 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4303 hr = StorageBaseImpl_CopyStream(
4304 This->scratch, This->entries[entry].stream_entry,
4305 This->transactedParent, This->entries[entry].transactedParentEntry);
4307 if (FAILED(hr))
4308 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4311 if (SUCCEEDED(hr))
4312 This->entries[entry].stream_dirty = 1;
4314 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4316 /* Since this entry is modified, and we aren't using its stream data, we
4317 * no longer care about the original entry. */
4318 DirRef delete_ref;
4319 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4321 if (delete_ref != DIRENTRY_NULL)
4322 This->entries[delete_ref].deleted = 1;
4324 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4328 return hr;
4331 /* Find the first entry in a depth-first traversal. */
4332 static DirRef TransactedSnapshotImpl_FindFirstChild(
4333 TransactedSnapshotImpl* This, DirRef parent)
4335 DirRef cursor, prev;
4336 TransactedDirEntry *entry;
4338 cursor = parent;
4339 entry = &This->entries[cursor];
4340 while (entry->read)
4342 if (entry->data.leftChild != DIRENTRY_NULL)
4344 prev = cursor;
4345 cursor = entry->data.leftChild;
4346 entry = &This->entries[cursor];
4347 entry->parent = prev;
4349 else if (entry->data.rightChild != DIRENTRY_NULL)
4351 prev = cursor;
4352 cursor = entry->data.rightChild;
4353 entry = &This->entries[cursor];
4354 entry->parent = prev;
4356 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4358 prev = cursor;
4359 cursor = entry->data.dirRootEntry;
4360 entry = &This->entries[cursor];
4361 entry->parent = prev;
4363 else
4364 break;
4367 return cursor;
4370 /* Find the next entry in a depth-first traversal. */
4371 static DirRef TransactedSnapshotImpl_FindNextChild(
4372 TransactedSnapshotImpl* This, DirRef current)
4374 DirRef parent;
4375 TransactedDirEntry *parent_entry;
4377 parent = This->entries[current].parent;
4378 parent_entry = &This->entries[parent];
4380 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4382 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4384 This->entries[parent_entry->data.rightChild].parent = parent;
4385 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4388 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4390 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4391 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4395 return parent;
4398 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4399 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4400 TransactedSnapshotImpl* This, DirRef entry)
4402 return entry != DIRENTRY_NULL &&
4403 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4406 /* Destroy the entries created by CopyTree. */
4407 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4408 TransactedSnapshotImpl* This, DirRef stop)
4410 DirRef cursor;
4411 TransactedDirEntry *entry;
4412 ULARGE_INTEGER zero;
4414 zero.QuadPart = 0;
4416 if (!This->entries[This->base.storageDirEntry].read)
4417 return;
4419 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4421 if (cursor == DIRENTRY_NULL)
4422 return;
4424 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4426 while (cursor != DIRENTRY_NULL && cursor != stop)
4428 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4430 entry = &This->entries[cursor];
4432 if (entry->stream_dirty)
4433 StorageBaseImpl_StreamSetSize(This->transactedParent,
4434 entry->newTransactedParentEntry, zero);
4436 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4437 entry->newTransactedParentEntry);
4439 entry->newTransactedParentEntry = entry->transactedParentEntry;
4442 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4446 /* Make a copy of our edited tree that we can use in the parent. */
4447 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4449 DirRef cursor;
4450 TransactedDirEntry *entry;
4451 HRESULT hr = S_OK;
4453 cursor = This->base.storageDirEntry;
4454 entry = &This->entries[cursor];
4455 entry->parent = DIRENTRY_NULL;
4456 entry->newTransactedParentEntry = entry->transactedParentEntry;
4458 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4459 return S_OK;
4461 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4463 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4464 entry = &This->entries[cursor];
4466 while (cursor != DIRENTRY_NULL)
4468 /* Make a copy of this entry in the transacted parent. */
4469 if (!entry->read ||
4470 (!entry->dirty && !entry->stream_dirty &&
4471 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4472 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4473 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4474 entry->newTransactedParentEntry = entry->transactedParentEntry;
4475 else
4477 DirEntry newData;
4479 memcpy(&newData, &entry->data, sizeof(DirEntry));
4481 newData.size.QuadPart = 0;
4482 newData.startingBlock = BLOCK_END_OF_CHAIN;
4484 if (newData.leftChild != DIRENTRY_NULL)
4485 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4487 if (newData.rightChild != DIRENTRY_NULL)
4488 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4490 if (newData.dirRootEntry != DIRENTRY_NULL)
4491 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4493 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4494 &entry->newTransactedParentEntry);
4495 if (FAILED(hr))
4497 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4498 return hr;
4501 if (entry->stream_dirty)
4503 hr = StorageBaseImpl_CopyStream(
4504 This->transactedParent, entry->newTransactedParentEntry,
4505 This->scratch, entry->stream_entry);
4507 else if (entry->data.size.QuadPart)
4509 hr = StorageBaseImpl_StreamLink(
4510 This->transactedParent, entry->newTransactedParentEntry,
4511 entry->transactedParentEntry);
4514 if (FAILED(hr))
4516 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4517 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4518 return hr;
4522 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4523 entry = &This->entries[cursor];
4526 return hr;
4529 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4530 IStorage* iface,
4531 DWORD grfCommitFlags) /* [in] */
4533 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4534 TransactedDirEntry *root_entry;
4535 DirRef i, dir_root_ref;
4536 DirEntry data;
4537 ULARGE_INTEGER zero;
4538 HRESULT hr;
4540 zero.QuadPart = 0;
4542 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4544 /* Cannot commit a read-only transacted storage */
4545 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4546 return STG_E_ACCESSDENIED;
4548 /* To prevent data loss, we create the new structure in the file before we
4549 * delete the old one, so that in case of errors the old data is intact. We
4550 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4551 * needed in the rare situation where we have just enough free disk space to
4552 * overwrite the existing data. */
4554 root_entry = &This->entries[This->base.storageDirEntry];
4556 if (!root_entry->read)
4557 return S_OK;
4559 hr = TransactedSnapshotImpl_CopyTree(This);
4560 if (FAILED(hr)) return hr;
4562 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4563 dir_root_ref = DIRENTRY_NULL;
4564 else
4565 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4567 hr = StorageBaseImpl_Flush(This->transactedParent);
4569 /* Update the storage to use the new data in one step. */
4570 if (SUCCEEDED(hr))
4571 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4572 root_entry->transactedParentEntry, &data);
4574 if (SUCCEEDED(hr))
4576 data.dirRootEntry = dir_root_ref;
4577 data.clsid = root_entry->data.clsid;
4578 data.ctime = root_entry->data.ctime;
4579 data.mtime = root_entry->data.mtime;
4581 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4582 root_entry->transactedParentEntry, &data);
4585 /* Try to flush after updating the root storage, but if the flush fails, keep
4586 * going, on the theory that it'll either succeed later or the subsequent
4587 * writes will fail. */
4588 StorageBaseImpl_Flush(This->transactedParent);
4590 if (SUCCEEDED(hr))
4592 /* Destroy the old now-orphaned data. */
4593 for (i=0; i<This->entries_size; i++)
4595 TransactedDirEntry *entry = &This->entries[i];
4596 if (entry->inuse)
4598 if (entry->deleted)
4600 StorageBaseImpl_StreamSetSize(This->transactedParent,
4601 entry->transactedParentEntry, zero);
4602 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4603 entry->transactedParentEntry);
4604 memset(entry, 0, sizeof(TransactedDirEntry));
4605 This->firstFreeEntry = min(i, This->firstFreeEntry);
4607 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4609 if (entry->transactedParentEntry != DIRENTRY_NULL)
4610 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4611 entry->transactedParentEntry);
4612 if (entry->stream_dirty)
4614 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4615 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4616 entry->stream_dirty = 0;
4618 entry->dirty = 0;
4619 entry->transactedParentEntry = entry->newTransactedParentEntry;
4624 else
4626 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4629 if (SUCCEEDED(hr))
4630 hr = StorageBaseImpl_Flush(This->transactedParent);
4632 return hr;
4635 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4636 IStorage* iface)
4638 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4639 ULARGE_INTEGER zero;
4640 ULONG i;
4642 TRACE("(%p)\n", iface);
4644 /* Destroy the open objects. */
4645 StorageBaseImpl_DeleteAll(&This->base);
4647 /* Clear out the scratch file. */
4648 zero.QuadPart = 0;
4649 for (i=0; i<This->entries_size; i++)
4651 if (This->entries[i].stream_dirty)
4653 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4654 zero);
4656 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4660 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4662 This->firstFreeEntry = 0;
4663 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4665 return S_OK;
4668 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4670 if (!This->reverted)
4672 TRACE("Storage invalidated (stg=%p)\n", This);
4674 This->reverted = 1;
4676 StorageBaseImpl_DeleteAll(This);
4680 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4682 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4684 TransactedSnapshotImpl_Revert((IStorage*)iface);
4686 IStorage_Release((IStorage*)This->transactedParent);
4688 IStorage_Release((IStorage*)This->scratch);
4690 HeapFree(GetProcessHeap(), 0, This->entries);
4692 HeapFree(GetProcessHeap(), 0, This);
4695 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4697 /* We only need to flush when committing. */
4698 return S_OK;
4701 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4703 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4705 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4708 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4709 const DirEntry *newData, DirRef *index)
4711 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4712 DirRef new_ref;
4713 TransactedDirEntry *new_entry;
4715 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4716 if (new_ref == DIRENTRY_NULL)
4717 return E_OUTOFMEMORY;
4719 new_entry = &This->entries[new_ref];
4721 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4722 new_entry->read = 1;
4723 new_entry->dirty = 1;
4724 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4726 *index = new_ref;
4728 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4730 return S_OK;
4733 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4734 DirRef index, const DirEntry *data)
4736 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4737 HRESULT hr;
4739 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4741 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4742 if (FAILED(hr)) return hr;
4744 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4746 if (index != This->base.storageDirEntry)
4748 This->entries[index].dirty = 1;
4750 if (data->size.QuadPart == 0 &&
4751 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4753 /* Since this entry is modified, and we aren't using its stream data, we
4754 * no longer care about the original entry. */
4755 DirRef delete_ref;
4756 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4758 if (delete_ref != DIRENTRY_NULL)
4759 This->entries[delete_ref].deleted = 1;
4761 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4765 return S_OK;
4768 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4769 DirRef index, DirEntry *data)
4771 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4772 HRESULT hr;
4774 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4775 if (FAILED(hr)) return hr;
4777 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4779 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4781 return S_OK;
4784 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4785 DirRef index)
4787 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4789 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4790 This->entries[index].data.size.QuadPart != 0)
4792 /* If we deleted this entry while it has stream data. We must have left the
4793 * data because some other entry is using it, and we need to leave the
4794 * original entry alone. */
4795 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4796 This->firstFreeEntry = min(index, This->firstFreeEntry);
4798 else
4800 This->entries[index].deleted = 1;
4803 return S_OK;
4806 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4807 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4809 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4811 if (This->entries[index].stream_dirty)
4813 return StorageBaseImpl_StreamReadAt(This->scratch,
4814 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4816 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4818 /* This stream doesn't live in the parent, and we haven't allocated storage
4819 * for it yet */
4820 *bytesRead = 0;
4821 return S_OK;
4823 else
4825 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4826 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4830 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4831 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4833 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4834 HRESULT hr;
4836 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4837 if (FAILED(hr)) return hr;
4839 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4840 if (FAILED(hr)) return hr;
4842 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4843 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4845 if (SUCCEEDED(hr) && size != 0)
4846 This->entries[index].data.size.QuadPart = max(
4847 This->entries[index].data.size.QuadPart,
4848 offset.QuadPart + size);
4850 return hr;
4853 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4854 DirRef index, ULARGE_INTEGER newsize)
4856 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4857 HRESULT hr;
4859 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4860 if (FAILED(hr)) return hr;
4862 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4863 return S_OK;
4865 if (newsize.QuadPart == 0)
4867 /* Destroy any parent references or entries in the scratch file. */
4868 if (This->entries[index].stream_dirty)
4870 ULARGE_INTEGER zero;
4871 zero.QuadPart = 0;
4872 StorageBaseImpl_StreamSetSize(This->scratch,
4873 This->entries[index].stream_entry, zero);
4874 StorageBaseImpl_DestroyDirEntry(This->scratch,
4875 This->entries[index].stream_entry);
4876 This->entries[index].stream_dirty = 0;
4878 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4880 DirRef delete_ref;
4881 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4883 if (delete_ref != DIRENTRY_NULL)
4884 This->entries[delete_ref].deleted = 1;
4886 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4889 else
4891 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4892 if (FAILED(hr)) return hr;
4894 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4895 This->entries[index].stream_entry, newsize);
4898 if (SUCCEEDED(hr))
4899 This->entries[index].data.size = newsize;
4901 return hr;
4904 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
4905 DirRef dst, DirRef src)
4907 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4908 HRESULT hr;
4909 TransactedDirEntry *dst_entry, *src_entry;
4911 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
4912 if (FAILED(hr)) return hr;
4914 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
4915 if (FAILED(hr)) return hr;
4917 dst_entry = &This->entries[dst];
4918 src_entry = &This->entries[src];
4920 dst_entry->stream_dirty = src_entry->stream_dirty;
4921 dst_entry->stream_entry = src_entry->stream_entry;
4922 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
4923 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
4924 dst_entry->data.size = src_entry->data.size;
4926 return S_OK;
4929 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4931 StorageBaseImpl_QueryInterface,
4932 StorageBaseImpl_AddRef,
4933 StorageBaseImpl_Release,
4934 StorageBaseImpl_CreateStream,
4935 StorageBaseImpl_OpenStream,
4936 StorageBaseImpl_CreateStorage,
4937 StorageBaseImpl_OpenStorage,
4938 StorageBaseImpl_CopyTo,
4939 StorageBaseImpl_MoveElementTo,
4940 TransactedSnapshotImpl_Commit,
4941 TransactedSnapshotImpl_Revert,
4942 StorageBaseImpl_EnumElements,
4943 StorageBaseImpl_DestroyElement,
4944 StorageBaseImpl_RenameElement,
4945 StorageBaseImpl_SetElementTimes,
4946 StorageBaseImpl_SetClass,
4947 StorageBaseImpl_SetStateBits,
4948 StorageBaseImpl_Stat
4951 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4953 TransactedSnapshotImpl_Destroy,
4954 TransactedSnapshotImpl_Invalidate,
4955 TransactedSnapshotImpl_Flush,
4956 TransactedSnapshotImpl_GetFilename,
4957 TransactedSnapshotImpl_CreateDirEntry,
4958 TransactedSnapshotImpl_WriteDirEntry,
4959 TransactedSnapshotImpl_ReadDirEntry,
4960 TransactedSnapshotImpl_DestroyDirEntry,
4961 TransactedSnapshotImpl_StreamReadAt,
4962 TransactedSnapshotImpl_StreamWriteAt,
4963 TransactedSnapshotImpl_StreamSetSize,
4964 TransactedSnapshotImpl_StreamLink
4967 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4968 TransactedSnapshotImpl** result)
4970 HRESULT hr;
4972 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4973 if (*result)
4975 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4977 /* This is OK because the property set storage functions use the IStorage functions. */
4978 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4980 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4982 list_init(&(*result)->base.strmHead);
4984 list_init(&(*result)->base.storageHead);
4986 (*result)->base.ref = 1;
4988 (*result)->base.openFlags = parentStorage->openFlags;
4990 /* Create a new temporary storage to act as the scratch file. */
4991 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
4992 0, (IStorage**)&(*result)->scratch);
4994 if (SUCCEEDED(hr))
4996 ULONG num_entries = 20;
4998 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5000 (*result)->entries_size = num_entries;
5002 (*result)->firstFreeEntry = 0;
5004 if ((*result)->entries)
5006 /* parentStorage already has 1 reference, which we take over here. */
5007 (*result)->transactedParent = parentStorage;
5009 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5011 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5013 else
5015 IStorage_Release((IStorage*)(*result)->scratch);
5017 hr = E_OUTOFMEMORY;
5021 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5023 return hr;
5025 else
5026 return E_OUTOFMEMORY;
5029 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5030 StorageBaseImpl** result)
5032 static int fixme=0;
5034 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5036 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5039 return TransactedSnapshotImpl_Construct(parentStorage,
5040 (TransactedSnapshotImpl**)result);
5043 static HRESULT Storage_Construct(
5044 HANDLE hFile,
5045 LPCOLESTR pwcsName,
5046 ILockBytes* pLkbyt,
5047 DWORD openFlags,
5048 BOOL fileBased,
5049 BOOL create,
5050 ULONG sector_size,
5051 StorageBaseImpl** result)
5053 StorageImpl *newStorage;
5054 StorageBaseImpl *newTransactedStorage;
5055 HRESULT hr;
5057 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5058 if (FAILED(hr)) goto end;
5060 if (openFlags & STGM_TRANSACTED)
5062 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5063 if (FAILED(hr))
5064 IStorage_Release((IStorage*)newStorage);
5065 else
5066 *result = newTransactedStorage;
5068 else
5069 *result = &newStorage->base;
5071 end:
5072 return hr;
5075 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5077 StorageInternalImpl* This = (StorageInternalImpl*) base;
5079 if (!This->base.reverted)
5081 TRACE("Storage invalidated (stg=%p)\n", This);
5083 This->base.reverted = 1;
5085 This->parentStorage = NULL;
5087 StorageBaseImpl_DeleteAll(&This->base);
5089 list_remove(&This->ParentListEntry);
5093 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5095 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5097 StorageInternalImpl_Invalidate(&This->base);
5099 HeapFree(GetProcessHeap(), 0, This);
5102 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5104 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5106 return StorageBaseImpl_Flush(This->parentStorage);
5109 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5111 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5113 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5116 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5117 const DirEntry *newData, DirRef *index)
5119 StorageInternalImpl* This = (StorageInternalImpl*) base;
5121 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5122 newData, index);
5125 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5126 DirRef index, const DirEntry *data)
5128 StorageInternalImpl* This = (StorageInternalImpl*) base;
5130 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5131 index, data);
5134 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5135 DirRef index, DirEntry *data)
5137 StorageInternalImpl* This = (StorageInternalImpl*) base;
5139 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5140 index, data);
5143 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5144 DirRef index)
5146 StorageInternalImpl* This = (StorageInternalImpl*) base;
5148 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5149 index);
5152 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5153 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5155 StorageInternalImpl* This = (StorageInternalImpl*) base;
5157 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5158 index, offset, size, buffer, bytesRead);
5161 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5162 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5164 StorageInternalImpl* This = (StorageInternalImpl*) base;
5166 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5167 index, offset, size, buffer, bytesWritten);
5170 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5171 DirRef index, ULARGE_INTEGER newsize)
5173 StorageInternalImpl* This = (StorageInternalImpl*) base;
5175 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5176 index, newsize);
5179 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5180 DirRef dst, DirRef src)
5182 StorageInternalImpl* This = (StorageInternalImpl*) base;
5184 return StorageBaseImpl_StreamLink(This->parentStorage,
5185 dst, src);
5188 /******************************************************************************
5190 ** Storage32InternalImpl_Commit
5193 static HRESULT WINAPI StorageInternalImpl_Commit(
5194 IStorage* iface,
5195 DWORD grfCommitFlags) /* [in] */
5197 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5198 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5199 return StorageBaseImpl_Flush(base);
5202 /******************************************************************************
5204 ** Storage32InternalImpl_Revert
5207 static HRESULT WINAPI StorageInternalImpl_Revert(
5208 IStorage* iface)
5210 FIXME("(%p): stub\n", iface);
5211 return S_OK;
5214 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5216 IStorage_Release((IStorage*)This->parentStorage);
5217 HeapFree(GetProcessHeap(), 0, This);
5220 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5221 IEnumSTATSTG* iface,
5222 REFIID riid,
5223 void** ppvObject)
5225 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5227 if (ppvObject==0)
5228 return E_INVALIDARG;
5230 *ppvObject = 0;
5232 if (IsEqualGUID(&IID_IUnknown, riid) ||
5233 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5235 *ppvObject = This;
5236 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
5237 return S_OK;
5240 return E_NOINTERFACE;
5243 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5244 IEnumSTATSTG* iface)
5246 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5247 return InterlockedIncrement(&This->ref);
5250 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5251 IEnumSTATSTG* iface)
5253 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5255 ULONG newRef;
5257 newRef = InterlockedDecrement(&This->ref);
5259 if (newRef==0)
5261 IEnumSTATSTGImpl_Destroy(This);
5264 return newRef;
5267 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5268 IEnumSTATSTGImpl* This,
5269 DirRef *ref)
5271 DirRef result = DIRENTRY_NULL;
5272 DirRef searchNode;
5273 DirEntry entry;
5274 HRESULT hr;
5275 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5277 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5278 This->parentStorage->storageDirEntry, &entry);
5279 searchNode = entry.dirRootEntry;
5281 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5283 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5285 if (SUCCEEDED(hr))
5287 LONG diff = entryNameCmp( entry.name, This->name);
5289 if (diff <= 0)
5291 searchNode = entry.rightChild;
5293 else
5295 result = searchNode;
5296 memcpy(result_name, entry.name, sizeof(result_name));
5297 searchNode = entry.leftChild;
5302 if (SUCCEEDED(hr))
5304 *ref = result;
5305 if (result != DIRENTRY_NULL)
5306 memcpy(This->name, result_name, sizeof(result_name));
5309 return hr;
5312 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5313 IEnumSTATSTG* iface,
5314 ULONG celt,
5315 STATSTG* rgelt,
5316 ULONG* pceltFetched)
5318 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5320 DirEntry currentEntry;
5321 STATSTG* currentReturnStruct = rgelt;
5322 ULONG objectFetched = 0;
5323 DirRef currentSearchNode;
5324 HRESULT hr=S_OK;
5326 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5327 return E_INVALIDARG;
5329 if (This->parentStorage->reverted)
5330 return STG_E_REVERTED;
5333 * To avoid the special case, get another pointer to a ULONG value if
5334 * the caller didn't supply one.
5336 if (pceltFetched==0)
5337 pceltFetched = &objectFetched;
5340 * Start the iteration, we will iterate until we hit the end of the
5341 * linked list or until we hit the number of items to iterate through
5343 *pceltFetched = 0;
5345 while ( *pceltFetched < celt )
5347 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5349 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5350 break;
5353 * Read the entry from the storage.
5355 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5356 currentSearchNode,
5357 &currentEntry);
5360 * Copy the information to the return buffer.
5362 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5363 currentReturnStruct,
5364 &currentEntry,
5365 STATFLAG_DEFAULT);
5368 * Step to the next item in the iteration
5370 (*pceltFetched)++;
5371 currentReturnStruct++;
5374 if (SUCCEEDED(hr) && *pceltFetched != celt)
5375 hr = S_FALSE;
5377 return hr;
5381 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5382 IEnumSTATSTG* iface,
5383 ULONG celt)
5385 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5387 ULONG objectFetched = 0;
5388 DirRef currentSearchNode;
5389 HRESULT hr=S_OK;
5391 if (This->parentStorage->reverted)
5392 return STG_E_REVERTED;
5394 while ( (objectFetched < celt) )
5396 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5398 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5399 break;
5401 objectFetched++;
5404 if (SUCCEEDED(hr) && objectFetched != celt)
5405 return S_FALSE;
5407 return hr;
5410 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5411 IEnumSTATSTG* iface)
5413 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5415 if (This->parentStorage->reverted)
5416 return STG_E_REVERTED;
5418 This->name[0] = 0;
5420 return S_OK;
5423 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5424 IEnumSTATSTG* iface,
5425 IEnumSTATSTG** ppenum)
5427 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5429 IEnumSTATSTGImpl* newClone;
5431 if (This->parentStorage->reverted)
5432 return STG_E_REVERTED;
5435 * Perform a sanity check on the parameters.
5437 if (ppenum==0)
5438 return E_INVALIDARG;
5440 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5441 This->storageDirEntry);
5445 * The new clone enumeration must point to the same current node as
5446 * the ole one.
5448 memcpy(newClone->name, This->name, sizeof(newClone->name));
5450 *ppenum = (IEnumSTATSTG*)newClone;
5453 * Don't forget to nail down a reference to the clone before
5454 * returning it.
5456 IEnumSTATSTGImpl_AddRef(*ppenum);
5458 return S_OK;
5462 * Virtual function table for the IEnumSTATSTGImpl class.
5464 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5466 IEnumSTATSTGImpl_QueryInterface,
5467 IEnumSTATSTGImpl_AddRef,
5468 IEnumSTATSTGImpl_Release,
5469 IEnumSTATSTGImpl_Next,
5470 IEnumSTATSTGImpl_Skip,
5471 IEnumSTATSTGImpl_Reset,
5472 IEnumSTATSTGImpl_Clone
5475 /******************************************************************************
5476 ** IEnumSTATSTGImpl implementation
5479 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5480 StorageBaseImpl* parentStorage,
5481 DirRef storageDirEntry)
5483 IEnumSTATSTGImpl* newEnumeration;
5485 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5487 if (newEnumeration!=0)
5490 * Set-up the virtual function table and reference count.
5492 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5493 newEnumeration->ref = 0;
5496 * We want to nail-down the reference to the storage in case the
5497 * enumeration out-lives the storage in the client application.
5499 newEnumeration->parentStorage = parentStorage;
5500 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5502 newEnumeration->storageDirEntry = storageDirEntry;
5505 * Make sure the current node of the iterator is the first one.
5507 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
5510 return newEnumeration;
5514 * Virtual function table for the Storage32InternalImpl class.
5516 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5518 StorageBaseImpl_QueryInterface,
5519 StorageBaseImpl_AddRef,
5520 StorageBaseImpl_Release,
5521 StorageBaseImpl_CreateStream,
5522 StorageBaseImpl_OpenStream,
5523 StorageBaseImpl_CreateStorage,
5524 StorageBaseImpl_OpenStorage,
5525 StorageBaseImpl_CopyTo,
5526 StorageBaseImpl_MoveElementTo,
5527 StorageInternalImpl_Commit,
5528 StorageInternalImpl_Revert,
5529 StorageBaseImpl_EnumElements,
5530 StorageBaseImpl_DestroyElement,
5531 StorageBaseImpl_RenameElement,
5532 StorageBaseImpl_SetElementTimes,
5533 StorageBaseImpl_SetClass,
5534 StorageBaseImpl_SetStateBits,
5535 StorageBaseImpl_Stat
5538 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5540 StorageInternalImpl_Destroy,
5541 StorageInternalImpl_Invalidate,
5542 StorageInternalImpl_Flush,
5543 StorageInternalImpl_GetFilename,
5544 StorageInternalImpl_CreateDirEntry,
5545 StorageInternalImpl_WriteDirEntry,
5546 StorageInternalImpl_ReadDirEntry,
5547 StorageInternalImpl_DestroyDirEntry,
5548 StorageInternalImpl_StreamReadAt,
5549 StorageInternalImpl_StreamWriteAt,
5550 StorageInternalImpl_StreamSetSize,
5551 StorageInternalImpl_StreamLink
5554 /******************************************************************************
5555 ** Storage32InternalImpl implementation
5558 static StorageInternalImpl* StorageInternalImpl_Construct(
5559 StorageBaseImpl* parentStorage,
5560 DWORD openFlags,
5561 DirRef storageDirEntry)
5563 StorageInternalImpl* newStorage;
5565 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5567 if (newStorage!=0)
5569 list_init(&newStorage->base.strmHead);
5571 list_init(&newStorage->base.storageHead);
5574 * Initialize the virtual function table.
5576 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5577 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5578 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5579 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5581 newStorage->base.reverted = 0;
5583 newStorage->base.ref = 1;
5585 newStorage->parentStorage = parentStorage;
5588 * Keep a reference to the directory entry of this storage
5590 newStorage->base.storageDirEntry = storageDirEntry;
5592 newStorage->base.create = 0;
5594 return newStorage;
5597 return 0;
5600 /******************************************************************************
5601 ** StorageUtl implementation
5604 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5606 WORD tmp;
5608 memcpy(&tmp, buffer+offset, sizeof(WORD));
5609 *value = lendian16toh(tmp);
5612 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5614 value = htole16(value);
5615 memcpy(buffer+offset, &value, sizeof(WORD));
5618 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5620 DWORD tmp;
5622 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5623 *value = lendian32toh(tmp);
5626 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5628 value = htole32(value);
5629 memcpy(buffer+offset, &value, sizeof(DWORD));
5632 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5633 ULARGE_INTEGER* value)
5635 #ifdef WORDS_BIGENDIAN
5636 ULARGE_INTEGER tmp;
5638 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5639 value->u.LowPart = htole32(tmp.u.HighPart);
5640 value->u.HighPart = htole32(tmp.u.LowPart);
5641 #else
5642 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5643 #endif
5646 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5647 const ULARGE_INTEGER *value)
5649 #ifdef WORDS_BIGENDIAN
5650 ULARGE_INTEGER tmp;
5652 tmp.u.LowPart = htole32(value->u.HighPart);
5653 tmp.u.HighPart = htole32(value->u.LowPart);
5654 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5655 #else
5656 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5657 #endif
5660 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5662 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5663 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5664 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5666 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5669 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5671 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5672 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5673 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5675 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5678 void StorageUtl_CopyDirEntryToSTATSTG(
5679 StorageBaseImpl* storage,
5680 STATSTG* destination,
5681 const DirEntry* source,
5682 int statFlags)
5685 * The copy of the string occurs only when the flag is not set
5687 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5689 /* Use the filename for the root storage. */
5690 destination->pwcsName = 0;
5691 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5693 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5694 (source->name[0] == 0) )
5696 destination->pwcsName = 0;
5698 else
5700 destination->pwcsName =
5701 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5703 strcpyW(destination->pwcsName, source->name);
5706 switch (source->stgType)
5708 case STGTY_STORAGE:
5709 case STGTY_ROOT:
5710 destination->type = STGTY_STORAGE;
5711 break;
5712 case STGTY_STREAM:
5713 destination->type = STGTY_STREAM;
5714 break;
5715 default:
5716 destination->type = STGTY_STREAM;
5717 break;
5720 destination->cbSize = source->size;
5722 currentReturnStruct->mtime = {0}; TODO
5723 currentReturnStruct->ctime = {0};
5724 currentReturnStruct->atime = {0};
5726 destination->grfMode = 0;
5727 destination->grfLocksSupported = 0;
5728 destination->clsid = source->clsid;
5729 destination->grfStateBits = 0;
5730 destination->reserved = 0;
5733 /******************************************************************************
5734 ** BlockChainStream implementation
5737 /* Read and save the index of all blocks in this stream. */
5738 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5740 ULONG next_sector, next_offset;
5741 HRESULT hr;
5742 struct BlockChainRun *last_run;
5744 if (This->indexCacheLen == 0)
5746 last_run = NULL;
5747 next_offset = 0;
5748 next_sector = BlockChainStream_GetHeadOfChain(This);
5750 else
5752 last_run = &This->indexCache[This->indexCacheLen-1];
5753 next_offset = last_run->lastOffset+1;
5754 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5755 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5756 &next_sector);
5757 if (FAILED(hr)) return hr;
5760 while (next_sector != BLOCK_END_OF_CHAIN)
5762 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5764 /* Add the current block to the cache. */
5765 if (This->indexCacheSize == 0)
5767 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5768 if (!This->indexCache) return E_OUTOFMEMORY;
5769 This->indexCacheSize = 16;
5771 else if (This->indexCacheSize == This->indexCacheLen)
5773 struct BlockChainRun *new_cache;
5774 ULONG new_size;
5776 new_size = This->indexCacheSize * 2;
5777 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5778 if (!new_cache) return E_OUTOFMEMORY;
5779 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5781 HeapFree(GetProcessHeap(), 0, This->indexCache);
5782 This->indexCache = new_cache;
5783 This->indexCacheSize = new_size;
5786 This->indexCacheLen++;
5787 last_run = &This->indexCache[This->indexCacheLen-1];
5788 last_run->firstSector = next_sector;
5789 last_run->firstOffset = next_offset;
5792 last_run->lastOffset = next_offset;
5794 /* Find the next block. */
5795 next_offset++;
5796 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5797 if (FAILED(hr)) return hr;
5800 if (This->indexCacheLen)
5802 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5803 This->numBlocks = last_run->lastOffset+1;
5805 else
5807 This->tailIndex = BLOCK_END_OF_CHAIN;
5808 This->numBlocks = 0;
5811 return S_OK;
5814 /* Locate the nth block in this stream. */
5815 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5817 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5818 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5820 if (offset >= This->numBlocks)
5821 return BLOCK_END_OF_CHAIN;
5823 while (min_run < max_run)
5825 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5826 if (offset < This->indexCache[run_to_check].firstOffset)
5828 max_offset = This->indexCache[run_to_check].firstOffset-1;
5829 max_run = run_to_check-1;
5831 else if (offset > This->indexCache[run_to_check].lastOffset)
5833 min_offset = This->indexCache[run_to_check].lastOffset+1;
5834 min_run = run_to_check+1;
5836 else
5837 /* Block is in this run. */
5838 min_run = max_run = run_to_check;
5841 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5844 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5845 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5847 BlockChainBlock *result=NULL;
5848 int i;
5850 for (i=0; i<2; i++)
5851 if (This->cachedBlocks[i].index == index)
5853 *sector = This->cachedBlocks[i].sector;
5854 *block = &This->cachedBlocks[i];
5855 return S_OK;
5858 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5859 if (*sector == BLOCK_END_OF_CHAIN)
5860 return STG_E_DOCFILECORRUPT;
5862 if (create)
5864 if (This->cachedBlocks[0].index == 0xffffffff)
5865 result = &This->cachedBlocks[0];
5866 else if (This->cachedBlocks[1].index == 0xffffffff)
5867 result = &This->cachedBlocks[1];
5868 else
5870 result = &This->cachedBlocks[This->blockToEvict++];
5871 if (This->blockToEvict == 2)
5872 This->blockToEvict = 0;
5875 if (result->dirty)
5877 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5878 return STG_E_WRITEFAULT;
5879 result->dirty = 0;
5882 result->read = 0;
5883 result->index = index;
5884 result->sector = *sector;
5887 *block = result;
5888 return S_OK;
5891 BlockChainStream* BlockChainStream_Construct(
5892 StorageImpl* parentStorage,
5893 ULONG* headOfStreamPlaceHolder,
5894 DirRef dirEntry)
5896 BlockChainStream* newStream;
5898 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5900 newStream->parentStorage = parentStorage;
5901 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5902 newStream->ownerDirEntry = dirEntry;
5903 newStream->indexCache = NULL;
5904 newStream->indexCacheLen = 0;
5905 newStream->indexCacheSize = 0;
5906 newStream->cachedBlocks[0].index = 0xffffffff;
5907 newStream->cachedBlocks[0].dirty = 0;
5908 newStream->cachedBlocks[1].index = 0xffffffff;
5909 newStream->cachedBlocks[1].dirty = 0;
5910 newStream->blockToEvict = 0;
5912 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5914 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5915 HeapFree(GetProcessHeap(), 0, newStream);
5916 return NULL;
5919 return newStream;
5922 HRESULT BlockChainStream_Flush(BlockChainStream* This)
5924 int i;
5925 if (!This) return S_OK;
5926 for (i=0; i<2; i++)
5928 if (This->cachedBlocks[i].dirty)
5930 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
5931 This->cachedBlocks[i].dirty = 0;
5932 else
5933 return STG_E_WRITEFAULT;
5936 return S_OK;
5939 void BlockChainStream_Destroy(BlockChainStream* This)
5941 if (This)
5943 BlockChainStream_Flush(This);
5944 HeapFree(GetProcessHeap(), 0, This->indexCache);
5946 HeapFree(GetProcessHeap(), 0, This);
5949 /******************************************************************************
5950 * BlockChainStream_GetHeadOfChain
5952 * Returns the head of this stream chain.
5953 * Some special chains don't have directory entries, their heads are kept in
5954 * This->headOfStreamPlaceHolder.
5957 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5959 DirEntry chainEntry;
5960 HRESULT hr;
5962 if (This->headOfStreamPlaceHolder != 0)
5963 return *(This->headOfStreamPlaceHolder);
5965 if (This->ownerDirEntry != DIRENTRY_NULL)
5967 hr = StorageImpl_ReadDirEntry(
5968 This->parentStorage,
5969 This->ownerDirEntry,
5970 &chainEntry);
5972 if (SUCCEEDED(hr))
5974 return chainEntry.startingBlock;
5978 return BLOCK_END_OF_CHAIN;
5981 /******************************************************************************
5982 * BlockChainStream_GetCount
5984 * Returns the number of blocks that comprises this chain.
5985 * This is not the size of the stream as the last block may not be full!
5987 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5989 return This->numBlocks;
5992 /******************************************************************************
5993 * BlockChainStream_ReadAt
5995 * Reads a specified number of bytes from this chain at the specified offset.
5996 * bytesRead may be NULL.
5997 * Failure will be returned if the specified number of bytes has not been read.
5999 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6000 ULARGE_INTEGER offset,
6001 ULONG size,
6002 void* buffer,
6003 ULONG* bytesRead)
6005 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6006 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6007 ULONG bytesToReadInBuffer;
6008 ULONG blockIndex;
6009 BYTE* bufferWalker;
6010 ULARGE_INTEGER stream_size;
6011 HRESULT hr;
6012 BlockChainBlock *cachedBlock;
6014 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6017 * Find the first block in the stream that contains part of the buffer.
6019 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6021 *bytesRead = 0;
6023 stream_size = BlockChainStream_GetSize(This);
6024 if (stream_size.QuadPart > offset.QuadPart)
6025 size = min(stream_size.QuadPart - offset.QuadPart, size);
6026 else
6027 return S_OK;
6030 * Start reading the buffer.
6032 bufferWalker = buffer;
6034 while (size > 0)
6036 ULARGE_INTEGER ulOffset;
6037 DWORD bytesReadAt;
6040 * Calculate how many bytes we can copy from this big block.
6042 bytesToReadInBuffer =
6043 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6045 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6047 if (FAILED(hr))
6048 return hr;
6050 if (!cachedBlock)
6052 /* Not in cache, and we're going to read past the end of the block. */
6053 ulOffset.u.HighPart = 0;
6054 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6055 offsetInBlock;
6057 StorageImpl_ReadAt(This->parentStorage,
6058 ulOffset,
6059 bufferWalker,
6060 bytesToReadInBuffer,
6061 &bytesReadAt);
6063 else
6065 if (!cachedBlock->read)
6067 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6068 return STG_E_READFAULT;
6070 cachedBlock->read = 1;
6073 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6074 bytesReadAt = bytesToReadInBuffer;
6077 blockNoInSequence++;
6078 bufferWalker += bytesReadAt;
6079 size -= bytesReadAt;
6080 *bytesRead += bytesReadAt;
6081 offsetInBlock = 0; /* There is no offset on the next block */
6083 if (bytesToReadInBuffer != bytesReadAt)
6084 break;
6087 return S_OK;
6090 /******************************************************************************
6091 * BlockChainStream_WriteAt
6093 * Writes the specified number of bytes to this chain at the specified offset.
6094 * Will fail if not all specified number of bytes have been written.
6096 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6097 ULARGE_INTEGER offset,
6098 ULONG size,
6099 const void* buffer,
6100 ULONG* bytesWritten)
6102 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6103 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6104 ULONG bytesToWrite;
6105 ULONG blockIndex;
6106 const BYTE* bufferWalker;
6107 HRESULT hr;
6108 BlockChainBlock *cachedBlock;
6110 *bytesWritten = 0;
6111 bufferWalker = buffer;
6113 while (size > 0)
6115 ULARGE_INTEGER ulOffset;
6116 DWORD bytesWrittenAt;
6119 * Calculate how many bytes we can copy to this big block.
6121 bytesToWrite =
6122 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6124 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6126 /* BlockChainStream_SetSize should have already been called to ensure we have
6127 * enough blocks in the chain to write into */
6128 if (FAILED(hr))
6130 ERR("not enough blocks in chain to write data\n");
6131 return hr;
6134 if (!cachedBlock)
6136 /* Not in cache, and we're going to write past the end of the block. */
6137 ulOffset.u.HighPart = 0;
6138 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6139 offsetInBlock;
6141 StorageImpl_WriteAt(This->parentStorage,
6142 ulOffset,
6143 bufferWalker,
6144 bytesToWrite,
6145 &bytesWrittenAt);
6147 else
6149 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6151 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6152 return STG_E_READFAULT;
6155 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6156 bytesWrittenAt = bytesToWrite;
6157 cachedBlock->read = 1;
6158 cachedBlock->dirty = 1;
6161 blockNoInSequence++;
6162 bufferWalker += bytesWrittenAt;
6163 size -= bytesWrittenAt;
6164 *bytesWritten += bytesWrittenAt;
6165 offsetInBlock = 0; /* There is no offset on the next block */
6167 if (bytesWrittenAt != bytesToWrite)
6168 break;
6171 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6174 /******************************************************************************
6175 * BlockChainStream_Shrink
6177 * Shrinks this chain in the big block depot.
6179 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6180 ULARGE_INTEGER newSize)
6182 ULONG blockIndex;
6183 ULONG numBlocks;
6184 int i;
6187 * Figure out how many blocks are needed to contain the new size
6189 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6191 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6192 numBlocks++;
6194 if (numBlocks)
6197 * Go to the new end of chain
6199 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6201 /* Mark the new end of chain */
6202 StorageImpl_SetNextBlockInChain(
6203 This->parentStorage,
6204 blockIndex,
6205 BLOCK_END_OF_CHAIN);
6207 This->tailIndex = blockIndex;
6209 else
6211 if (This->headOfStreamPlaceHolder != 0)
6213 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6215 else
6217 DirEntry chainEntry;
6218 assert(This->ownerDirEntry != DIRENTRY_NULL);
6220 StorageImpl_ReadDirEntry(
6221 This->parentStorage,
6222 This->ownerDirEntry,
6223 &chainEntry);
6225 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6227 StorageImpl_WriteDirEntry(
6228 This->parentStorage,
6229 This->ownerDirEntry,
6230 &chainEntry);
6233 This->tailIndex = BLOCK_END_OF_CHAIN;
6236 This->numBlocks = numBlocks;
6239 * Mark the extra blocks as free
6241 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6243 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6244 StorageImpl_FreeBigBlock(This->parentStorage,
6245 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6246 if (last_run->lastOffset == last_run->firstOffset)
6247 This->indexCacheLen--;
6248 else
6249 last_run->lastOffset--;
6253 * Reset the last accessed block cache.
6255 for (i=0; i<2; i++)
6257 if (This->cachedBlocks[i].index >= numBlocks)
6259 This->cachedBlocks[i].index = 0xffffffff;
6260 This->cachedBlocks[i].dirty = 0;
6264 return TRUE;
6267 /******************************************************************************
6268 * BlockChainStream_Enlarge
6270 * Grows this chain in the big block depot.
6272 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6273 ULARGE_INTEGER newSize)
6275 ULONG blockIndex, currentBlock;
6276 ULONG newNumBlocks;
6277 ULONG oldNumBlocks = 0;
6279 blockIndex = BlockChainStream_GetHeadOfChain(This);
6282 * Empty chain. Create the head.
6284 if (blockIndex == BLOCK_END_OF_CHAIN)
6286 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6287 StorageImpl_SetNextBlockInChain(This->parentStorage,
6288 blockIndex,
6289 BLOCK_END_OF_CHAIN);
6291 if (This->headOfStreamPlaceHolder != 0)
6293 *(This->headOfStreamPlaceHolder) = blockIndex;
6295 else
6297 DirEntry chainEntry;
6298 assert(This->ownerDirEntry != DIRENTRY_NULL);
6300 StorageImpl_ReadDirEntry(
6301 This->parentStorage,
6302 This->ownerDirEntry,
6303 &chainEntry);
6305 chainEntry.startingBlock = blockIndex;
6307 StorageImpl_WriteDirEntry(
6308 This->parentStorage,
6309 This->ownerDirEntry,
6310 &chainEntry);
6313 This->tailIndex = blockIndex;
6314 This->numBlocks = 1;
6318 * Figure out how many blocks are needed to contain this stream
6320 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6322 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6323 newNumBlocks++;
6326 * Go to the current end of chain
6328 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6330 currentBlock = blockIndex;
6332 while (blockIndex != BLOCK_END_OF_CHAIN)
6334 This->numBlocks++;
6335 currentBlock = blockIndex;
6337 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6338 &blockIndex)))
6339 return FALSE;
6342 This->tailIndex = currentBlock;
6345 currentBlock = This->tailIndex;
6346 oldNumBlocks = This->numBlocks;
6349 * Add new blocks to the chain
6351 if (oldNumBlocks < newNumBlocks)
6353 while (oldNumBlocks < newNumBlocks)
6355 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6357 StorageImpl_SetNextBlockInChain(
6358 This->parentStorage,
6359 currentBlock,
6360 blockIndex);
6362 StorageImpl_SetNextBlockInChain(
6363 This->parentStorage,
6364 blockIndex,
6365 BLOCK_END_OF_CHAIN);
6367 currentBlock = blockIndex;
6368 oldNumBlocks++;
6371 This->tailIndex = blockIndex;
6372 This->numBlocks = newNumBlocks;
6375 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6376 return FALSE;
6378 return TRUE;
6381 /******************************************************************************
6382 * BlockChainStream_SetSize
6384 * Sets the size of this stream. The big block depot will be updated.
6385 * The file will grow if we grow the chain.
6387 * TODO: Free the actual blocks in the file when we shrink the chain.
6388 * Currently, the blocks are still in the file. So the file size
6389 * doesn't shrink even if we shrink streams.
6391 BOOL BlockChainStream_SetSize(
6392 BlockChainStream* This,
6393 ULARGE_INTEGER newSize)
6395 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6397 if (newSize.u.LowPart == size.u.LowPart)
6398 return TRUE;
6400 if (newSize.u.LowPart < size.u.LowPart)
6402 BlockChainStream_Shrink(This, newSize);
6404 else
6406 BlockChainStream_Enlarge(This, newSize);
6409 return TRUE;
6412 /******************************************************************************
6413 * BlockChainStream_GetSize
6415 * Returns the size of this chain.
6416 * Will return the block count if this chain doesn't have a directory entry.
6418 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6420 DirEntry chainEntry;
6422 if(This->headOfStreamPlaceHolder == NULL)
6425 * This chain has a directory entry so use the size value from there.
6427 StorageImpl_ReadDirEntry(
6428 This->parentStorage,
6429 This->ownerDirEntry,
6430 &chainEntry);
6432 return chainEntry.size;
6434 else
6437 * this chain is a chain that does not have a directory entry, figure out the
6438 * size by making the product number of used blocks times the
6439 * size of them
6441 ULARGE_INTEGER result;
6442 result.u.HighPart = 0;
6444 result.u.LowPart =
6445 BlockChainStream_GetCount(This) *
6446 This->parentStorage->bigBlockSize;
6448 return result;
6452 /******************************************************************************
6453 ** SmallBlockChainStream implementation
6456 SmallBlockChainStream* SmallBlockChainStream_Construct(
6457 StorageImpl* parentStorage,
6458 ULONG* headOfStreamPlaceHolder,
6459 DirRef dirEntry)
6461 SmallBlockChainStream* newStream;
6463 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6465 newStream->parentStorage = parentStorage;
6466 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6467 newStream->ownerDirEntry = dirEntry;
6469 return newStream;
6472 void SmallBlockChainStream_Destroy(
6473 SmallBlockChainStream* This)
6475 HeapFree(GetProcessHeap(), 0, This);
6478 /******************************************************************************
6479 * SmallBlockChainStream_GetHeadOfChain
6481 * Returns the head of this chain of small blocks.
6483 static ULONG SmallBlockChainStream_GetHeadOfChain(
6484 SmallBlockChainStream* This)
6486 DirEntry chainEntry;
6487 HRESULT hr;
6489 if (This->headOfStreamPlaceHolder != NULL)
6490 return *(This->headOfStreamPlaceHolder);
6492 if (This->ownerDirEntry)
6494 hr = StorageImpl_ReadDirEntry(
6495 This->parentStorage,
6496 This->ownerDirEntry,
6497 &chainEntry);
6499 if (SUCCEEDED(hr))
6501 return chainEntry.startingBlock;
6506 return BLOCK_END_OF_CHAIN;
6509 /******************************************************************************
6510 * SmallBlockChainStream_GetNextBlockInChain
6512 * Returns the index of the next small block in this chain.
6514 * Return Values:
6515 * - BLOCK_END_OF_CHAIN: end of this chain
6516 * - BLOCK_UNUSED: small block 'blockIndex' is free
6518 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6519 SmallBlockChainStream* This,
6520 ULONG blockIndex,
6521 ULONG* nextBlockInChain)
6523 ULARGE_INTEGER offsetOfBlockInDepot;
6524 DWORD buffer;
6525 ULONG bytesRead;
6526 HRESULT res;
6528 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6530 offsetOfBlockInDepot.u.HighPart = 0;
6531 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6534 * Read those bytes in the buffer from the small block file.
6536 res = BlockChainStream_ReadAt(
6537 This->parentStorage->smallBlockDepotChain,
6538 offsetOfBlockInDepot,
6539 sizeof(DWORD),
6540 &buffer,
6541 &bytesRead);
6543 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6544 res = STG_E_READFAULT;
6546 if (SUCCEEDED(res))
6548 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6549 return S_OK;
6552 return res;
6555 /******************************************************************************
6556 * SmallBlockChainStream_SetNextBlockInChain
6558 * Writes the index of the next block of the specified block in the small
6559 * block depot.
6560 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6561 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6563 static void SmallBlockChainStream_SetNextBlockInChain(
6564 SmallBlockChainStream* This,
6565 ULONG blockIndex,
6566 ULONG nextBlock)
6568 ULARGE_INTEGER offsetOfBlockInDepot;
6569 DWORD buffer;
6570 ULONG bytesWritten;
6572 offsetOfBlockInDepot.u.HighPart = 0;
6573 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6575 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6578 * Read those bytes in the buffer from the small block file.
6580 BlockChainStream_WriteAt(
6581 This->parentStorage->smallBlockDepotChain,
6582 offsetOfBlockInDepot,
6583 sizeof(DWORD),
6584 &buffer,
6585 &bytesWritten);
6588 /******************************************************************************
6589 * SmallBlockChainStream_FreeBlock
6591 * Flag small block 'blockIndex' as free in the small block depot.
6593 static void SmallBlockChainStream_FreeBlock(
6594 SmallBlockChainStream* This,
6595 ULONG blockIndex)
6597 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6600 /******************************************************************************
6601 * SmallBlockChainStream_GetNextFreeBlock
6603 * Returns the index of a free small block. The small block depot will be
6604 * enlarged if necessary. The small block chain will also be enlarged if
6605 * necessary.
6607 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6608 SmallBlockChainStream* This)
6610 ULARGE_INTEGER offsetOfBlockInDepot;
6611 DWORD buffer;
6612 ULONG bytesRead;
6613 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6614 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6615 HRESULT res = S_OK;
6616 ULONG smallBlocksPerBigBlock;
6617 DirEntry rootEntry;
6618 ULONG blocksRequired;
6619 ULARGE_INTEGER old_size, size_required;
6621 offsetOfBlockInDepot.u.HighPart = 0;
6624 * Scan the small block depot for a free block
6626 while (nextBlockIndex != BLOCK_UNUSED)
6628 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6630 res = BlockChainStream_ReadAt(
6631 This->parentStorage->smallBlockDepotChain,
6632 offsetOfBlockInDepot,
6633 sizeof(DWORD),
6634 &buffer,
6635 &bytesRead);
6638 * If we run out of space for the small block depot, enlarge it
6640 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6642 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6644 if (nextBlockIndex != BLOCK_UNUSED)
6645 blockIndex++;
6647 else
6649 ULONG count =
6650 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6652 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6653 ULARGE_INTEGER newSize, offset;
6654 ULONG bytesWritten;
6656 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6657 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6660 * Initialize all the small blocks to free
6662 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6663 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6664 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6665 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6667 StorageImpl_SaveFileHeader(This->parentStorage);
6671 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6673 smallBlocksPerBigBlock =
6674 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6677 * Verify if we have to allocate big blocks to contain small blocks
6679 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6681 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6683 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6685 if (size_required.QuadPart > old_size.QuadPart)
6687 BlockChainStream_SetSize(
6688 This->parentStorage->smallBlockRootChain,
6689 size_required);
6691 StorageImpl_ReadDirEntry(
6692 This->parentStorage,
6693 This->parentStorage->base.storageDirEntry,
6694 &rootEntry);
6696 rootEntry.size = size_required;
6698 StorageImpl_WriteDirEntry(
6699 This->parentStorage,
6700 This->parentStorage->base.storageDirEntry,
6701 &rootEntry);
6704 return blockIndex;
6707 /******************************************************************************
6708 * SmallBlockChainStream_ReadAt
6710 * Reads a specified number of bytes from this chain at the specified offset.
6711 * bytesRead may be NULL.
6712 * Failure will be returned if the specified number of bytes has not been read.
6714 HRESULT SmallBlockChainStream_ReadAt(
6715 SmallBlockChainStream* This,
6716 ULARGE_INTEGER offset,
6717 ULONG size,
6718 void* buffer,
6719 ULONG* bytesRead)
6721 HRESULT rc = S_OK;
6722 ULARGE_INTEGER offsetInBigBlockFile;
6723 ULONG blockNoInSequence =
6724 offset.u.LowPart / This->parentStorage->smallBlockSize;
6726 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6727 ULONG bytesToReadInBuffer;
6728 ULONG blockIndex;
6729 ULONG bytesReadFromBigBlockFile;
6730 BYTE* bufferWalker;
6731 ULARGE_INTEGER stream_size;
6734 * This should never happen on a small block file.
6736 assert(offset.u.HighPart==0);
6738 *bytesRead = 0;
6740 stream_size = SmallBlockChainStream_GetSize(This);
6741 if (stream_size.QuadPart > offset.QuadPart)
6742 size = min(stream_size.QuadPart - offset.QuadPart, size);
6743 else
6744 return S_OK;
6747 * Find the first block in the stream that contains part of the buffer.
6749 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6751 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6753 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6754 if(FAILED(rc))
6755 return rc;
6756 blockNoInSequence--;
6760 * Start reading the buffer.
6762 bufferWalker = buffer;
6764 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6767 * Calculate how many bytes we can copy from this small block.
6769 bytesToReadInBuffer =
6770 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6773 * Calculate the offset of the small block in the small block file.
6775 offsetInBigBlockFile.u.HighPart = 0;
6776 offsetInBigBlockFile.u.LowPart =
6777 blockIndex * This->parentStorage->smallBlockSize;
6779 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6782 * Read those bytes in the buffer from the small block file.
6783 * The small block has already been identified so it shouldn't fail
6784 * unless the file is corrupt.
6786 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6787 offsetInBigBlockFile,
6788 bytesToReadInBuffer,
6789 bufferWalker,
6790 &bytesReadFromBigBlockFile);
6792 if (FAILED(rc))
6793 return rc;
6795 if (!bytesReadFromBigBlockFile)
6796 return STG_E_DOCFILECORRUPT;
6799 * Step to the next big block.
6801 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6802 if(FAILED(rc))
6803 return STG_E_DOCFILECORRUPT;
6805 bufferWalker += bytesReadFromBigBlockFile;
6806 size -= bytesReadFromBigBlockFile;
6807 *bytesRead += bytesReadFromBigBlockFile;
6808 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6811 return S_OK;
6814 /******************************************************************************
6815 * SmallBlockChainStream_WriteAt
6817 * Writes the specified number of bytes to this chain at the specified offset.
6818 * Will fail if not all specified number of bytes have been written.
6820 HRESULT SmallBlockChainStream_WriteAt(
6821 SmallBlockChainStream* This,
6822 ULARGE_INTEGER offset,
6823 ULONG size,
6824 const void* buffer,
6825 ULONG* bytesWritten)
6827 ULARGE_INTEGER offsetInBigBlockFile;
6828 ULONG blockNoInSequence =
6829 offset.u.LowPart / This->parentStorage->smallBlockSize;
6831 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6832 ULONG bytesToWriteInBuffer;
6833 ULONG blockIndex;
6834 ULONG bytesWrittenToBigBlockFile;
6835 const BYTE* bufferWalker;
6836 HRESULT res;
6839 * This should never happen on a small block file.
6841 assert(offset.u.HighPart==0);
6844 * Find the first block in the stream that contains part of the buffer.
6846 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6848 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6850 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6851 return STG_E_DOCFILECORRUPT;
6852 blockNoInSequence--;
6856 * Start writing the buffer.
6858 *bytesWritten = 0;
6859 bufferWalker = buffer;
6860 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6863 * Calculate how many bytes we can copy to this small block.
6865 bytesToWriteInBuffer =
6866 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6869 * Calculate the offset of the small block in the small block file.
6871 offsetInBigBlockFile.u.HighPart = 0;
6872 offsetInBigBlockFile.u.LowPart =
6873 blockIndex * This->parentStorage->smallBlockSize;
6875 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6878 * Write those bytes in the buffer to the small block file.
6880 res = BlockChainStream_WriteAt(
6881 This->parentStorage->smallBlockRootChain,
6882 offsetInBigBlockFile,
6883 bytesToWriteInBuffer,
6884 bufferWalker,
6885 &bytesWrittenToBigBlockFile);
6886 if (FAILED(res))
6887 return res;
6890 * Step to the next big block.
6892 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6893 &blockIndex)))
6894 return FALSE;
6895 bufferWalker += bytesWrittenToBigBlockFile;
6896 size -= bytesWrittenToBigBlockFile;
6897 *bytesWritten += bytesWrittenToBigBlockFile;
6898 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6901 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6904 /******************************************************************************
6905 * SmallBlockChainStream_Shrink
6907 * Shrinks this chain in the small block depot.
6909 static BOOL SmallBlockChainStream_Shrink(
6910 SmallBlockChainStream* This,
6911 ULARGE_INTEGER newSize)
6913 ULONG blockIndex, extraBlock;
6914 ULONG numBlocks;
6915 ULONG count = 0;
6917 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6919 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6920 numBlocks++;
6922 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6925 * Go to the new end of chain
6927 while (count < numBlocks)
6929 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6930 &blockIndex)))
6931 return FALSE;
6932 count++;
6936 * If the count is 0, we have a special case, the head of the chain was
6937 * just freed.
6939 if (count == 0)
6941 DirEntry chainEntry;
6943 StorageImpl_ReadDirEntry(This->parentStorage,
6944 This->ownerDirEntry,
6945 &chainEntry);
6947 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6949 StorageImpl_WriteDirEntry(This->parentStorage,
6950 This->ownerDirEntry,
6951 &chainEntry);
6954 * We start freeing the chain at the head block.
6956 extraBlock = blockIndex;
6958 else
6960 /* Get the next block before marking the new end */
6961 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6962 &extraBlock)))
6963 return FALSE;
6965 /* Mark the new end of chain */
6966 SmallBlockChainStream_SetNextBlockInChain(
6967 This,
6968 blockIndex,
6969 BLOCK_END_OF_CHAIN);
6973 * Mark the extra blocks as free
6975 while (extraBlock != BLOCK_END_OF_CHAIN)
6977 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6978 &blockIndex)))
6979 return FALSE;
6980 SmallBlockChainStream_FreeBlock(This, extraBlock);
6981 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
6982 extraBlock = blockIndex;
6985 return TRUE;
6988 /******************************************************************************
6989 * SmallBlockChainStream_Enlarge
6991 * Grows this chain in the small block depot.
6993 static BOOL SmallBlockChainStream_Enlarge(
6994 SmallBlockChainStream* This,
6995 ULARGE_INTEGER newSize)
6997 ULONG blockIndex, currentBlock;
6998 ULONG newNumBlocks;
6999 ULONG oldNumBlocks = 0;
7001 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7004 * Empty chain. Create the head.
7006 if (blockIndex == BLOCK_END_OF_CHAIN)
7008 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7009 SmallBlockChainStream_SetNextBlockInChain(
7010 This,
7011 blockIndex,
7012 BLOCK_END_OF_CHAIN);
7014 if (This->headOfStreamPlaceHolder != NULL)
7016 *(This->headOfStreamPlaceHolder) = blockIndex;
7018 else
7020 DirEntry chainEntry;
7022 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7023 &chainEntry);
7025 chainEntry.startingBlock = blockIndex;
7027 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7028 &chainEntry);
7032 currentBlock = blockIndex;
7035 * Figure out how many blocks are needed to contain this stream
7037 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7039 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7040 newNumBlocks++;
7043 * Go to the current end of chain
7045 while (blockIndex != BLOCK_END_OF_CHAIN)
7047 oldNumBlocks++;
7048 currentBlock = blockIndex;
7049 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7050 return FALSE;
7054 * Add new blocks to the chain
7056 while (oldNumBlocks < newNumBlocks)
7058 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7059 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7061 SmallBlockChainStream_SetNextBlockInChain(
7062 This,
7063 blockIndex,
7064 BLOCK_END_OF_CHAIN);
7066 currentBlock = blockIndex;
7067 oldNumBlocks++;
7070 return TRUE;
7073 /******************************************************************************
7074 * SmallBlockChainStream_SetSize
7076 * Sets the size of this stream.
7077 * The file will grow if we grow the chain.
7079 * TODO: Free the actual blocks in the file when we shrink the chain.
7080 * Currently, the blocks are still in the file. So the file size
7081 * doesn't shrink even if we shrink streams.
7083 BOOL SmallBlockChainStream_SetSize(
7084 SmallBlockChainStream* This,
7085 ULARGE_INTEGER newSize)
7087 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7089 if (newSize.u.LowPart == size.u.LowPart)
7090 return TRUE;
7092 if (newSize.u.LowPart < size.u.LowPart)
7094 SmallBlockChainStream_Shrink(This, newSize);
7096 else
7098 SmallBlockChainStream_Enlarge(This, newSize);
7101 return TRUE;
7104 /******************************************************************************
7105 * SmallBlockChainStream_GetCount
7107 * Returns the number of small blocks that comprises this chain.
7108 * This is not the size of the stream as the last block may not be full!
7111 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7113 ULONG blockIndex;
7114 ULONG count = 0;
7116 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7118 while(blockIndex != BLOCK_END_OF_CHAIN)
7120 count++;
7122 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7123 blockIndex, &blockIndex)))
7124 return 0;
7127 return count;
7130 /******************************************************************************
7131 * SmallBlockChainStream_GetSize
7133 * Returns the size of this chain.
7135 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7137 DirEntry chainEntry;
7139 if(This->headOfStreamPlaceHolder != NULL)
7141 ULARGE_INTEGER result;
7142 result.u.HighPart = 0;
7144 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7145 This->parentStorage->smallBlockSize;
7147 return result;
7150 StorageImpl_ReadDirEntry(
7151 This->parentStorage,
7152 This->ownerDirEntry,
7153 &chainEntry);
7155 return chainEntry.size;
7158 static HRESULT create_storagefile(
7159 LPCOLESTR pwcsName,
7160 DWORD grfMode,
7161 DWORD grfAttrs,
7162 STGOPTIONS* pStgOptions,
7163 REFIID riid,
7164 void** ppstgOpen)
7166 StorageBaseImpl* newStorage = 0;
7167 HANDLE hFile = INVALID_HANDLE_VALUE;
7168 HRESULT hr = STG_E_INVALIDFLAG;
7169 DWORD shareMode;
7170 DWORD accessMode;
7171 DWORD creationMode;
7172 DWORD fileAttributes;
7173 WCHAR tempFileName[MAX_PATH];
7175 if (ppstgOpen == 0)
7176 return STG_E_INVALIDPOINTER;
7178 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7179 return STG_E_INVALIDPARAMETER;
7181 /* if no share mode given then DENY_NONE is the default */
7182 if (STGM_SHARE_MODE(grfMode) == 0)
7183 grfMode |= STGM_SHARE_DENY_NONE;
7185 if ( FAILED( validateSTGM(grfMode) ))
7186 goto end;
7188 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7189 switch(STGM_ACCESS_MODE(grfMode))
7191 case STGM_WRITE:
7192 case STGM_READWRITE:
7193 break;
7194 default:
7195 goto end;
7198 /* in direct mode, can only use SHARE_EXCLUSIVE */
7199 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7200 goto end;
7202 /* but in transacted mode, any share mode is valid */
7205 * Generate a unique name.
7207 if (pwcsName == 0)
7209 WCHAR tempPath[MAX_PATH];
7210 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7212 memset(tempPath, 0, sizeof(tempPath));
7213 memset(tempFileName, 0, sizeof(tempFileName));
7215 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7216 tempPath[0] = '.';
7218 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7219 pwcsName = tempFileName;
7220 else
7222 hr = STG_E_INSUFFICIENTMEMORY;
7223 goto end;
7226 creationMode = TRUNCATE_EXISTING;
7228 else
7230 creationMode = GetCreationModeFromSTGM(grfMode);
7234 * Interpret the STGM value grfMode
7236 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7237 accessMode = GetAccessModeFromSTGM(grfMode);
7239 if (grfMode & STGM_DELETEONRELEASE)
7240 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7241 else
7242 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7244 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7246 static int fixme;
7247 if (!fixme++)
7248 FIXME("Storage share mode not implemented.\n");
7251 *ppstgOpen = 0;
7253 hFile = CreateFileW(pwcsName,
7254 accessMode,
7255 shareMode,
7256 NULL,
7257 creationMode,
7258 fileAttributes,
7261 if (hFile == INVALID_HANDLE_VALUE)
7263 if(GetLastError() == ERROR_FILE_EXISTS)
7264 hr = STG_E_FILEALREADYEXISTS;
7265 else
7266 hr = E_FAIL;
7267 goto end;
7271 * Allocate and initialize the new IStorage32object.
7273 hr = Storage_Construct(
7274 hFile,
7275 pwcsName,
7276 NULL,
7277 grfMode,
7278 TRUE,
7279 TRUE,
7280 pStgOptions->ulSectorSize,
7281 &newStorage);
7283 if (FAILED(hr))
7285 goto end;
7288 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7290 IStorage_Release((IStorage*)newStorage);
7292 end:
7293 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7295 return hr;
7298 /******************************************************************************
7299 * StgCreateDocfile [OLE32.@]
7300 * Creates a new compound file storage object
7302 * PARAMS
7303 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7304 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7305 * reserved [ ?] unused?, usually 0
7306 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7308 * RETURNS
7309 * S_OK if the file was successfully created
7310 * some STG_E_ value if error
7311 * NOTES
7312 * if pwcsName is NULL, create file with new unique name
7313 * the function can returns
7314 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7315 * (unrealized now)
7317 HRESULT WINAPI StgCreateDocfile(
7318 LPCOLESTR pwcsName,
7319 DWORD grfMode,
7320 DWORD reserved,
7321 IStorage **ppstgOpen)
7323 STGOPTIONS stgoptions = {1, 0, 512};
7325 TRACE("(%s, %x, %d, %p)\n",
7326 debugstr_w(pwcsName), grfMode,
7327 reserved, ppstgOpen);
7329 if (ppstgOpen == 0)
7330 return STG_E_INVALIDPOINTER;
7331 if (reserved != 0)
7332 return STG_E_INVALIDPARAMETER;
7334 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7337 /******************************************************************************
7338 * StgCreateStorageEx [OLE32.@]
7340 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7342 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7343 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7345 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7347 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7348 return STG_E_INVALIDPARAMETER;
7351 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7353 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7354 return STG_E_INVALIDPARAMETER;
7357 if (stgfmt == STGFMT_FILE)
7359 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7360 return STG_E_INVALIDPARAMETER;
7363 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7365 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7369 ERR("Invalid stgfmt argument\n");
7370 return STG_E_INVALIDPARAMETER;
7373 /******************************************************************************
7374 * StgCreatePropSetStg [OLE32.@]
7376 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7377 IPropertySetStorage **ppPropSetStg)
7379 HRESULT hr;
7381 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7382 if (reserved)
7383 hr = STG_E_INVALIDPARAMETER;
7384 else
7385 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7386 (void**)ppPropSetStg);
7387 return hr;
7390 /******************************************************************************
7391 * StgOpenStorageEx [OLE32.@]
7393 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7395 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7396 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7398 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7400 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7401 return STG_E_INVALIDPARAMETER;
7404 switch (stgfmt)
7406 case STGFMT_FILE:
7407 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7408 return STG_E_INVALIDPARAMETER;
7410 case STGFMT_STORAGE:
7411 break;
7413 case STGFMT_DOCFILE:
7414 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7416 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7417 return STG_E_INVALIDPARAMETER;
7419 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7420 break;
7422 case STGFMT_ANY:
7423 WARN("STGFMT_ANY assuming storage\n");
7424 break;
7426 default:
7427 return STG_E_INVALIDPARAMETER;
7430 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7434 /******************************************************************************
7435 * StgOpenStorage [OLE32.@]
7437 HRESULT WINAPI StgOpenStorage(
7438 const OLECHAR *pwcsName,
7439 IStorage *pstgPriority,
7440 DWORD grfMode,
7441 SNB snbExclude,
7442 DWORD reserved,
7443 IStorage **ppstgOpen)
7445 StorageBaseImpl* newStorage = 0;
7446 HRESULT hr = S_OK;
7447 HANDLE hFile = 0;
7448 DWORD shareMode;
7449 DWORD accessMode;
7451 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7452 debugstr_w(pwcsName), pstgPriority, grfMode,
7453 snbExclude, reserved, ppstgOpen);
7455 if (pwcsName == 0)
7457 hr = STG_E_INVALIDNAME;
7458 goto end;
7461 if (ppstgOpen == 0)
7463 hr = STG_E_INVALIDPOINTER;
7464 goto end;
7467 if (reserved)
7469 hr = STG_E_INVALIDPARAMETER;
7470 goto end;
7473 if (grfMode & STGM_PRIORITY)
7475 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7476 return STG_E_INVALIDFLAG;
7477 if (grfMode & STGM_DELETEONRELEASE)
7478 return STG_E_INVALIDFUNCTION;
7479 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7480 return STG_E_INVALIDFLAG;
7481 grfMode &= ~0xf0; /* remove the existing sharing mode */
7482 grfMode |= STGM_SHARE_DENY_NONE;
7484 /* STGM_PRIORITY stops other IStorage objects on the same file from
7485 * committing until the STGM_PRIORITY IStorage is closed. it also
7486 * stops non-transacted mode StgOpenStorage calls with write access from
7487 * succeeding. obviously, both of these cannot be achieved through just
7488 * file share flags */
7489 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7493 * Validate the sharing mode
7495 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7496 switch(STGM_SHARE_MODE(grfMode))
7498 case STGM_SHARE_EXCLUSIVE:
7499 case STGM_SHARE_DENY_WRITE:
7500 break;
7501 default:
7502 hr = STG_E_INVALIDFLAG;
7503 goto end;
7506 if ( FAILED( validateSTGM(grfMode) ) ||
7507 (grfMode&STGM_CREATE))
7509 hr = STG_E_INVALIDFLAG;
7510 goto end;
7513 /* shared reading requires transacted mode */
7514 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7515 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7516 !(grfMode&STGM_TRANSACTED) )
7518 hr = STG_E_INVALIDFLAG;
7519 goto end;
7523 * Interpret the STGM value grfMode
7525 shareMode = GetShareModeFromSTGM(grfMode);
7526 accessMode = GetAccessModeFromSTGM(grfMode);
7528 *ppstgOpen = 0;
7530 hFile = CreateFileW( pwcsName,
7531 accessMode,
7532 shareMode,
7533 NULL,
7534 OPEN_EXISTING,
7535 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7538 if (hFile==INVALID_HANDLE_VALUE)
7540 DWORD last_error = GetLastError();
7542 hr = E_FAIL;
7544 switch (last_error)
7546 case ERROR_FILE_NOT_FOUND:
7547 hr = STG_E_FILENOTFOUND;
7548 break;
7550 case ERROR_PATH_NOT_FOUND:
7551 hr = STG_E_PATHNOTFOUND;
7552 break;
7554 case ERROR_ACCESS_DENIED:
7555 case ERROR_WRITE_PROTECT:
7556 hr = STG_E_ACCESSDENIED;
7557 break;
7559 case ERROR_SHARING_VIOLATION:
7560 hr = STG_E_SHAREVIOLATION;
7561 break;
7563 default:
7564 hr = E_FAIL;
7567 goto end;
7571 * Refuse to open the file if it's too small to be a structured storage file
7572 * FIXME: verify the file when reading instead of here
7574 if (GetFileSize(hFile, NULL) < 0x100)
7576 CloseHandle(hFile);
7577 hr = STG_E_FILEALREADYEXISTS;
7578 goto end;
7582 * Allocate and initialize the new IStorage32object.
7584 hr = Storage_Construct(
7585 hFile,
7586 pwcsName,
7587 NULL,
7588 grfMode,
7589 TRUE,
7590 FALSE,
7591 512,
7592 &newStorage);
7594 if (FAILED(hr))
7597 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7599 if(hr == STG_E_INVALIDHEADER)
7600 hr = STG_E_FILEALREADYEXISTS;
7601 goto end;
7605 * Get an "out" pointer for the caller.
7607 *ppstgOpen = (IStorage*)newStorage;
7609 end:
7610 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7611 return hr;
7614 /******************************************************************************
7615 * StgCreateDocfileOnILockBytes [OLE32.@]
7617 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7618 ILockBytes *plkbyt,
7619 DWORD grfMode,
7620 DWORD reserved,
7621 IStorage** ppstgOpen)
7623 StorageBaseImpl* newStorage = 0;
7624 HRESULT hr = S_OK;
7626 if ((ppstgOpen == 0) || (plkbyt == 0))
7627 return STG_E_INVALIDPOINTER;
7630 * Allocate and initialize the new IStorage object.
7632 hr = Storage_Construct(
7635 plkbyt,
7636 grfMode,
7637 FALSE,
7638 TRUE,
7639 512,
7640 &newStorage);
7642 if (FAILED(hr))
7644 return hr;
7648 * Get an "out" pointer for the caller.
7650 *ppstgOpen = (IStorage*)newStorage;
7652 return hr;
7655 /******************************************************************************
7656 * StgOpenStorageOnILockBytes [OLE32.@]
7658 HRESULT WINAPI StgOpenStorageOnILockBytes(
7659 ILockBytes *plkbyt,
7660 IStorage *pstgPriority,
7661 DWORD grfMode,
7662 SNB snbExclude,
7663 DWORD reserved,
7664 IStorage **ppstgOpen)
7666 StorageBaseImpl* newStorage = 0;
7667 HRESULT hr = S_OK;
7669 if ((plkbyt == 0) || (ppstgOpen == 0))
7670 return STG_E_INVALIDPOINTER;
7672 if ( FAILED( validateSTGM(grfMode) ))
7673 return STG_E_INVALIDFLAG;
7675 *ppstgOpen = 0;
7678 * Allocate and initialize the new IStorage object.
7680 hr = Storage_Construct(
7683 plkbyt,
7684 grfMode,
7685 FALSE,
7686 FALSE,
7687 512,
7688 &newStorage);
7690 if (FAILED(hr))
7692 return hr;
7696 * Get an "out" pointer for the caller.
7698 *ppstgOpen = (IStorage*)newStorage;
7700 return hr;
7703 /******************************************************************************
7704 * StgSetTimes [ole32.@]
7705 * StgSetTimes [OLE32.@]
7709 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7710 FILETIME const *patime, FILETIME const *pmtime)
7712 IStorage *stg = NULL;
7713 HRESULT r;
7715 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7717 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7718 0, 0, &stg);
7719 if( SUCCEEDED(r) )
7721 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7722 IStorage_Release(stg);
7725 return r;
7728 /******************************************************************************
7729 * StgIsStorageILockBytes [OLE32.@]
7731 * Determines if the ILockBytes contains a storage object.
7733 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7735 BYTE sig[8];
7736 ULARGE_INTEGER offset;
7738 offset.u.HighPart = 0;
7739 offset.u.LowPart = 0;
7741 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7743 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7744 return S_OK;
7746 return S_FALSE;
7749 /******************************************************************************
7750 * WriteClassStg [OLE32.@]
7752 * This method will store the specified CLSID in the specified storage object
7754 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7756 HRESULT hRes;
7758 if(!pStg)
7759 return E_INVALIDARG;
7761 if(!rclsid)
7762 return STG_E_INVALIDPOINTER;
7764 hRes = IStorage_SetClass(pStg, rclsid);
7766 return hRes;
7769 /***********************************************************************
7770 * ReadClassStg (OLE32.@)
7772 * This method reads the CLSID previously written to a storage object with
7773 * the WriteClassStg.
7775 * PARAMS
7776 * pstg [I] IStorage pointer
7777 * pclsid [O] Pointer to where the CLSID is written
7779 * RETURNS
7780 * Success: S_OK.
7781 * Failure: HRESULT code.
7783 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7785 STATSTG pstatstg;
7786 HRESULT hRes;
7788 TRACE("(%p, %p)\n", pstg, pclsid);
7790 if(!pstg || !pclsid)
7791 return E_INVALIDARG;
7794 * read a STATSTG structure (contains the clsid) from the storage
7796 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7798 if(SUCCEEDED(hRes))
7799 *pclsid=pstatstg.clsid;
7801 return hRes;
7804 /***********************************************************************
7805 * OleLoadFromStream (OLE32.@)
7807 * This function loads an object from stream
7809 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7811 CLSID clsid;
7812 HRESULT res;
7813 LPPERSISTSTREAM xstm;
7815 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7817 res=ReadClassStm(pStm,&clsid);
7818 if (FAILED(res))
7819 return res;
7820 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7821 if (FAILED(res))
7822 return res;
7823 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7824 if (FAILED(res)) {
7825 IUnknown_Release((IUnknown*)*ppvObj);
7826 return res;
7828 res=IPersistStream_Load(xstm,pStm);
7829 IPersistStream_Release(xstm);
7830 /* FIXME: all refcounts ok at this point? I think they should be:
7831 * pStm : unchanged
7832 * ppvObj : 1
7833 * xstm : 0 (released)
7835 return res;
7838 /***********************************************************************
7839 * OleSaveToStream (OLE32.@)
7841 * This function saves an object with the IPersistStream interface on it
7842 * to the specified stream.
7844 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7847 CLSID clsid;
7848 HRESULT res;
7850 TRACE("(%p,%p)\n",pPStm,pStm);
7852 res=IPersistStream_GetClassID(pPStm,&clsid);
7854 if (SUCCEEDED(res)){
7856 res=WriteClassStm(pStm,&clsid);
7858 if (SUCCEEDED(res))
7860 res=IPersistStream_Save(pPStm,pStm,TRUE);
7863 TRACE("Finished Save\n");
7864 return res;
7867 /****************************************************************************
7868 * This method validate a STGM parameter that can contain the values below
7870 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7871 * The stgm values contained in 0xffff0000 are bitmasks.
7873 * STGM_DIRECT 0x00000000
7874 * STGM_TRANSACTED 0x00010000
7875 * STGM_SIMPLE 0x08000000
7877 * STGM_READ 0x00000000
7878 * STGM_WRITE 0x00000001
7879 * STGM_READWRITE 0x00000002
7881 * STGM_SHARE_DENY_NONE 0x00000040
7882 * STGM_SHARE_DENY_READ 0x00000030
7883 * STGM_SHARE_DENY_WRITE 0x00000020
7884 * STGM_SHARE_EXCLUSIVE 0x00000010
7886 * STGM_PRIORITY 0x00040000
7887 * STGM_DELETEONRELEASE 0x04000000
7889 * STGM_CREATE 0x00001000
7890 * STGM_CONVERT 0x00020000
7891 * STGM_FAILIFTHERE 0x00000000
7893 * STGM_NOSCRATCH 0x00100000
7894 * STGM_NOSNAPSHOT 0x00200000
7896 static HRESULT validateSTGM(DWORD stgm)
7898 DWORD access = STGM_ACCESS_MODE(stgm);
7899 DWORD share = STGM_SHARE_MODE(stgm);
7900 DWORD create = STGM_CREATE_MODE(stgm);
7902 if (stgm&~STGM_KNOWN_FLAGS)
7904 ERR("unknown flags %08x\n", stgm);
7905 return E_FAIL;
7908 switch (access)
7910 case STGM_READ:
7911 case STGM_WRITE:
7912 case STGM_READWRITE:
7913 break;
7914 default:
7915 return E_FAIL;
7918 switch (share)
7920 case STGM_SHARE_DENY_NONE:
7921 case STGM_SHARE_DENY_READ:
7922 case STGM_SHARE_DENY_WRITE:
7923 case STGM_SHARE_EXCLUSIVE:
7924 break;
7925 default:
7926 return E_FAIL;
7929 switch (create)
7931 case STGM_CREATE:
7932 case STGM_FAILIFTHERE:
7933 break;
7934 default:
7935 return E_FAIL;
7939 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7941 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7942 return E_FAIL;
7945 * STGM_CREATE | STGM_CONVERT
7946 * if both are false, STGM_FAILIFTHERE is set to TRUE
7948 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7949 return E_FAIL;
7952 * STGM_NOSCRATCH requires STGM_TRANSACTED
7954 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7955 return E_FAIL;
7958 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7959 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7961 if ( (stgm & STGM_NOSNAPSHOT) &&
7962 (!(stgm & STGM_TRANSACTED) ||
7963 share == STGM_SHARE_EXCLUSIVE ||
7964 share == STGM_SHARE_DENY_WRITE) )
7965 return E_FAIL;
7967 return S_OK;
7970 /****************************************************************************
7971 * GetShareModeFromSTGM
7973 * This method will return a share mode flag from a STGM value.
7974 * The STGM value is assumed valid.
7976 static DWORD GetShareModeFromSTGM(DWORD stgm)
7978 switch (STGM_SHARE_MODE(stgm))
7980 case STGM_SHARE_DENY_NONE:
7981 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7982 case STGM_SHARE_DENY_READ:
7983 return FILE_SHARE_WRITE;
7984 case STGM_SHARE_DENY_WRITE:
7985 return FILE_SHARE_READ;
7986 case STGM_SHARE_EXCLUSIVE:
7987 return 0;
7989 ERR("Invalid share mode!\n");
7990 assert(0);
7991 return 0;
7994 /****************************************************************************
7995 * GetAccessModeFromSTGM
7997 * This method will return an access mode flag from a STGM value.
7998 * The STGM value is assumed valid.
8000 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8002 switch (STGM_ACCESS_MODE(stgm))
8004 case STGM_READ:
8005 return GENERIC_READ;
8006 case STGM_WRITE:
8007 case STGM_READWRITE:
8008 return GENERIC_READ | GENERIC_WRITE;
8010 ERR("Invalid access mode!\n");
8011 assert(0);
8012 return 0;
8015 /****************************************************************************
8016 * GetCreationModeFromSTGM
8018 * This method will return a creation mode flag from a STGM value.
8019 * The STGM value is assumed valid.
8021 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8023 switch(STGM_CREATE_MODE(stgm))
8025 case STGM_CREATE:
8026 return CREATE_ALWAYS;
8027 case STGM_CONVERT:
8028 FIXME("STGM_CONVERT not implemented!\n");
8029 return CREATE_NEW;
8030 case STGM_FAILIFTHERE:
8031 return CREATE_NEW;
8033 ERR("Invalid create mode!\n");
8034 assert(0);
8035 return 0;
8039 /*************************************************************************
8040 * OLECONVERT_LoadOLE10 [Internal]
8042 * Loads the OLE10 STREAM to memory
8044 * PARAMS
8045 * pOleStream [I] The OLESTREAM
8046 * pData [I] Data Structure for the OLESTREAM Data
8048 * RETURNS
8049 * Success: S_OK
8050 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8051 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8053 * NOTES
8054 * This function is used by OleConvertOLESTREAMToIStorage only.
8056 * Memory allocated for pData must be freed by the caller
8058 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8060 DWORD dwSize;
8061 HRESULT hRes = S_OK;
8062 int nTryCnt=0;
8063 int max_try = 6;
8065 pData->pData = NULL;
8066 pData->pstrOleObjFileName = NULL;
8068 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8070 /* Get the OleID */
8071 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8072 if(dwSize != sizeof(pData->dwOleID))
8074 hRes = CONVERT10_E_OLESTREAM_GET;
8076 else if(pData->dwOleID != OLESTREAM_ID)
8078 hRes = CONVERT10_E_OLESTREAM_FMT;
8080 else
8082 hRes = S_OK;
8083 break;
8087 if(hRes == S_OK)
8089 /* Get the TypeID... more info needed for this field */
8090 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8091 if(dwSize != sizeof(pData->dwTypeID))
8093 hRes = CONVERT10_E_OLESTREAM_GET;
8096 if(hRes == S_OK)
8098 if(pData->dwTypeID != 0)
8100 /* Get the length of the OleTypeName */
8101 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8102 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8104 hRes = CONVERT10_E_OLESTREAM_GET;
8107 if(hRes == S_OK)
8109 if(pData->dwOleTypeNameLength > 0)
8111 /* Get the OleTypeName */
8112 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8113 if(dwSize != pData->dwOleTypeNameLength)
8115 hRes = CONVERT10_E_OLESTREAM_GET;
8119 if(bStrem1)
8121 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8122 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8124 hRes = CONVERT10_E_OLESTREAM_GET;
8126 if(hRes == S_OK)
8128 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8129 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8130 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8131 if(pData->pstrOleObjFileName)
8133 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8134 if(dwSize != pData->dwOleObjFileNameLength)
8136 hRes = CONVERT10_E_OLESTREAM_GET;
8139 else
8140 hRes = CONVERT10_E_OLESTREAM_GET;
8143 else
8145 /* Get the Width of the Metafile */
8146 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8147 if(dwSize != sizeof(pData->dwMetaFileWidth))
8149 hRes = CONVERT10_E_OLESTREAM_GET;
8151 if(hRes == S_OK)
8153 /* Get the Height of the Metafile */
8154 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8155 if(dwSize != sizeof(pData->dwMetaFileHeight))
8157 hRes = CONVERT10_E_OLESTREAM_GET;
8161 if(hRes == S_OK)
8163 /* Get the Length of the Data */
8164 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8165 if(dwSize != sizeof(pData->dwDataLength))
8167 hRes = CONVERT10_E_OLESTREAM_GET;
8171 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8173 if(!bStrem1) /* if it is a second OLE stream data */
8175 pData->dwDataLength -= 8;
8176 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8177 if(dwSize != sizeof(pData->strUnknown))
8179 hRes = CONVERT10_E_OLESTREAM_GET;
8183 if(hRes == S_OK)
8185 if(pData->dwDataLength > 0)
8187 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8189 /* Get Data (ex. IStorage, Metafile, or BMP) */
8190 if(pData->pData)
8192 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8193 if(dwSize != pData->dwDataLength)
8195 hRes = CONVERT10_E_OLESTREAM_GET;
8198 else
8200 hRes = CONVERT10_E_OLESTREAM_GET;
8206 return hRes;
8209 /*************************************************************************
8210 * OLECONVERT_SaveOLE10 [Internal]
8212 * Saves the OLE10 STREAM From memory
8214 * PARAMS
8215 * pData [I] Data Structure for the OLESTREAM Data
8216 * pOleStream [I] The OLESTREAM to save
8218 * RETURNS
8219 * Success: S_OK
8220 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8222 * NOTES
8223 * This function is used by OleConvertIStorageToOLESTREAM only.
8226 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8228 DWORD dwSize;
8229 HRESULT hRes = S_OK;
8232 /* Set the OleID */
8233 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8234 if(dwSize != sizeof(pData->dwOleID))
8236 hRes = CONVERT10_E_OLESTREAM_PUT;
8239 if(hRes == S_OK)
8241 /* Set the TypeID */
8242 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8243 if(dwSize != sizeof(pData->dwTypeID))
8245 hRes = CONVERT10_E_OLESTREAM_PUT;
8249 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8251 /* Set the Length of the OleTypeName */
8252 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8253 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8255 hRes = CONVERT10_E_OLESTREAM_PUT;
8258 if(hRes == S_OK)
8260 if(pData->dwOleTypeNameLength > 0)
8262 /* Set the OleTypeName */
8263 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8264 if(dwSize != pData->dwOleTypeNameLength)
8266 hRes = CONVERT10_E_OLESTREAM_PUT;
8271 if(hRes == S_OK)
8273 /* Set the width of the Metafile */
8274 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8275 if(dwSize != sizeof(pData->dwMetaFileWidth))
8277 hRes = CONVERT10_E_OLESTREAM_PUT;
8281 if(hRes == S_OK)
8283 /* Set the height of the Metafile */
8284 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8285 if(dwSize != sizeof(pData->dwMetaFileHeight))
8287 hRes = CONVERT10_E_OLESTREAM_PUT;
8291 if(hRes == S_OK)
8293 /* Set the length of the Data */
8294 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8295 if(dwSize != sizeof(pData->dwDataLength))
8297 hRes = CONVERT10_E_OLESTREAM_PUT;
8301 if(hRes == S_OK)
8303 if(pData->dwDataLength > 0)
8305 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8306 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8307 if(dwSize != pData->dwDataLength)
8309 hRes = CONVERT10_E_OLESTREAM_PUT;
8314 return hRes;
8317 /*************************************************************************
8318 * OLECONVERT_GetOLE20FromOLE10[Internal]
8320 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8321 * opens it, and copies the content to the dest IStorage for
8322 * OleConvertOLESTREAMToIStorage
8325 * PARAMS
8326 * pDestStorage [I] The IStorage to copy the data to
8327 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8328 * nBufferLength [I] The size of the buffer
8330 * RETURNS
8331 * Nothing
8333 * NOTES
8337 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8339 HRESULT hRes;
8340 HANDLE hFile;
8341 IStorage *pTempStorage;
8342 DWORD dwNumOfBytesWritten;
8343 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8344 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8346 /* Create a temp File */
8347 GetTempPathW(MAX_PATH, wstrTempDir);
8348 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8349 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8351 if(hFile != INVALID_HANDLE_VALUE)
8353 /* Write IStorage Data to File */
8354 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8355 CloseHandle(hFile);
8357 /* Open and copy temp storage to the Dest Storage */
8358 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8359 if(hRes == S_OK)
8361 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8362 IStorage_Release(pTempStorage);
8364 DeleteFileW(wstrTempFile);
8369 /*************************************************************************
8370 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8372 * Saves the OLE10 STREAM From memory
8374 * PARAMS
8375 * pStorage [I] The Src IStorage to copy
8376 * pData [I] The Dest Memory to write to.
8378 * RETURNS
8379 * The size in bytes allocated for pData
8381 * NOTES
8382 * Memory allocated for pData must be freed by the caller
8384 * Used by OleConvertIStorageToOLESTREAM only.
8387 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8389 HANDLE hFile;
8390 HRESULT hRes;
8391 DWORD nDataLength = 0;
8392 IStorage *pTempStorage;
8393 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8394 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8396 *pData = NULL;
8398 /* Create temp Storage */
8399 GetTempPathW(MAX_PATH, wstrTempDir);
8400 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8401 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8403 if(hRes == S_OK)
8405 /* Copy Src Storage to the Temp Storage */
8406 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8407 IStorage_Release(pTempStorage);
8409 /* Open Temp Storage as a file and copy to memory */
8410 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8411 if(hFile != INVALID_HANDLE_VALUE)
8413 nDataLength = GetFileSize(hFile, NULL);
8414 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8415 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8416 CloseHandle(hFile);
8418 DeleteFileW(wstrTempFile);
8420 return nDataLength;
8423 /*************************************************************************
8424 * OLECONVERT_CreateOleStream [Internal]
8426 * Creates the "\001OLE" stream in the IStorage if necessary.
8428 * PARAMS
8429 * pStorage [I] Dest storage to create the stream in
8431 * RETURNS
8432 * Nothing
8434 * NOTES
8435 * This function is used by OleConvertOLESTREAMToIStorage only.
8437 * This stream is still unknown, MS Word seems to have extra data
8438 * but since the data is stored in the OLESTREAM there should be
8439 * no need to recreate the stream. If the stream is manually
8440 * deleted it will create it with this default data.
8443 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8445 HRESULT hRes;
8446 IStream *pStream;
8447 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8448 BYTE pOleStreamHeader [] =
8450 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8451 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8452 0x00, 0x00, 0x00, 0x00
8455 /* Create stream if not present */
8456 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8457 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8459 if(hRes == S_OK)
8461 /* Write default Data */
8462 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8463 IStream_Release(pStream);
8467 /* write a string to a stream, preceded by its length */
8468 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8470 HRESULT r;
8471 LPSTR str;
8472 DWORD len = 0;
8474 if( string )
8475 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8476 r = IStream_Write( stm, &len, sizeof(len), NULL);
8477 if( FAILED( r ) )
8478 return r;
8479 if(len == 0)
8480 return r;
8481 str = CoTaskMemAlloc( len );
8482 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8483 r = IStream_Write( stm, str, len, NULL);
8484 CoTaskMemFree( str );
8485 return r;
8488 /* read a string preceded by its length from a stream */
8489 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8491 HRESULT r;
8492 DWORD len, count = 0;
8493 LPSTR str;
8494 LPWSTR wstr;
8496 r = IStream_Read( stm, &len, sizeof(len), &count );
8497 if( FAILED( r ) )
8498 return r;
8499 if( count != sizeof(len) )
8500 return E_OUTOFMEMORY;
8502 TRACE("%d bytes\n",len);
8504 str = CoTaskMemAlloc( len );
8505 if( !str )
8506 return E_OUTOFMEMORY;
8507 count = 0;
8508 r = IStream_Read( stm, str, len, &count );
8509 if( FAILED( r ) )
8510 return r;
8511 if( count != len )
8513 CoTaskMemFree( str );
8514 return E_OUTOFMEMORY;
8517 TRACE("Read string %s\n",debugstr_an(str,len));
8519 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8520 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8521 if( wstr )
8522 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8523 CoTaskMemFree( str );
8525 *string = wstr;
8527 return r;
8531 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8532 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8534 IStream *pstm;
8535 HRESULT r = S_OK;
8536 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8538 static const BYTE unknown1[12] =
8539 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8540 0xFF, 0xFF, 0xFF, 0xFF};
8541 static const BYTE unknown2[16] =
8542 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8543 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8545 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8546 debugstr_w(lpszUserType), debugstr_w(szClipName),
8547 debugstr_w(szProgIDName));
8549 /* Create a CompObj stream */
8550 r = IStorage_CreateStream(pstg, szwStreamName,
8551 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8552 if( FAILED (r) )
8553 return r;
8555 /* Write CompObj Structure to stream */
8556 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8558 if( SUCCEEDED( r ) )
8559 r = WriteClassStm( pstm, clsid );
8561 if( SUCCEEDED( r ) )
8562 r = STREAM_WriteString( pstm, lpszUserType );
8563 if( SUCCEEDED( r ) )
8564 r = STREAM_WriteString( pstm, szClipName );
8565 if( SUCCEEDED( r ) )
8566 r = STREAM_WriteString( pstm, szProgIDName );
8567 if( SUCCEEDED( r ) )
8568 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8570 IStream_Release( pstm );
8572 return r;
8575 /***********************************************************************
8576 * WriteFmtUserTypeStg (OLE32.@)
8578 HRESULT WINAPI WriteFmtUserTypeStg(
8579 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8581 HRESULT r;
8582 WCHAR szwClipName[0x40];
8583 CLSID clsid = CLSID_NULL;
8584 LPWSTR wstrProgID = NULL;
8585 DWORD n;
8587 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8589 /* get the clipboard format name */
8590 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8591 szwClipName[n]=0;
8593 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8595 /* FIXME: There's room to save a CLSID and its ProgID, but
8596 the CLSID is not looked up in the registry and in all the
8597 tests I wrote it was CLSID_NULL. Where does it come from?
8600 /* get the real program ID. This may fail, but that's fine */
8601 ProgIDFromCLSID(&clsid, &wstrProgID);
8603 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8605 r = STORAGE_WriteCompObj( pstg, &clsid,
8606 lpszUserType, szwClipName, wstrProgID );
8608 CoTaskMemFree(wstrProgID);
8610 return r;
8614 /******************************************************************************
8615 * ReadFmtUserTypeStg [OLE32.@]
8617 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8619 HRESULT r;
8620 IStream *stm = 0;
8621 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8622 unsigned char unknown1[12];
8623 unsigned char unknown2[16];
8624 DWORD count;
8625 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8626 CLSID clsid;
8628 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8630 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8631 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8632 if( FAILED ( r ) )
8634 WARN("Failed to open stream r = %08x\n", r);
8635 return r;
8638 /* read the various parts of the structure */
8639 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8640 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8641 goto end;
8642 r = ReadClassStm( stm, &clsid );
8643 if( FAILED( r ) )
8644 goto end;
8646 r = STREAM_ReadString( stm, &szCLSIDName );
8647 if( FAILED( r ) )
8648 goto end;
8650 r = STREAM_ReadString( stm, &szOleTypeName );
8651 if( FAILED( r ) )
8652 goto end;
8654 r = STREAM_ReadString( stm, &szProgIDName );
8655 if( FAILED( r ) )
8656 goto end;
8658 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8659 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8660 goto end;
8662 /* ok, success... now we just need to store what we found */
8663 if( pcf )
8664 *pcf = RegisterClipboardFormatW( szOleTypeName );
8665 CoTaskMemFree( szOleTypeName );
8667 if( lplpszUserType )
8668 *lplpszUserType = szCLSIDName;
8669 CoTaskMemFree( szProgIDName );
8671 end:
8672 IStream_Release( stm );
8674 return r;
8678 /*************************************************************************
8679 * OLECONVERT_CreateCompObjStream [Internal]
8681 * Creates a "\001CompObj" is the destination IStorage if necessary.
8683 * PARAMS
8684 * pStorage [I] The dest IStorage to create the CompObj Stream
8685 * if necessary.
8686 * strOleTypeName [I] The ProgID
8688 * RETURNS
8689 * Success: S_OK
8690 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8692 * NOTES
8693 * This function is used by OleConvertOLESTREAMToIStorage only.
8695 * The stream data is stored in the OLESTREAM and there should be
8696 * no need to recreate the stream. If the stream is manually
8697 * deleted it will attempt to create it by querying the registry.
8701 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8703 IStream *pStream;
8704 HRESULT hStorageRes, hRes = S_OK;
8705 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8706 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8707 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8709 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8710 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8712 /* Initialize the CompObj structure */
8713 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8714 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8715 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8718 /* Create a CompObj stream if it doesn't exist */
8719 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8720 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8721 if(hStorageRes == S_OK)
8723 /* copy the OleTypeName to the compobj struct */
8724 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8725 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8727 /* copy the OleTypeName to the compobj struct */
8728 /* Note: in the test made, these were Identical */
8729 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8730 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8732 /* Get the CLSID */
8733 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8734 bufferW, OLESTREAM_MAX_STR_LEN );
8735 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8737 if(hRes == S_OK)
8739 HKEY hKey;
8740 LONG hErr;
8741 /* Get the CLSID Default Name from the Registry */
8742 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8743 if(hErr == ERROR_SUCCESS)
8745 char strTemp[OLESTREAM_MAX_STR_LEN];
8746 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8747 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8748 if(hErr == ERROR_SUCCESS)
8750 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8752 RegCloseKey(hKey);
8756 /* Write CompObj Structure to stream */
8757 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8759 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8761 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8762 if(IStorageCompObj.dwCLSIDNameLength > 0)
8764 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8766 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8767 if(IStorageCompObj.dwOleTypeNameLength > 0)
8769 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8771 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8772 if(IStorageCompObj.dwProgIDNameLength > 0)
8774 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8776 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8777 IStream_Release(pStream);
8779 return hRes;
8783 /*************************************************************************
8784 * OLECONVERT_CreateOlePresStream[Internal]
8786 * Creates the "\002OlePres000" Stream with the Metafile data
8788 * PARAMS
8789 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8790 * dwExtentX [I] Width of the Metafile
8791 * dwExtentY [I] Height of the Metafile
8792 * pData [I] Metafile data
8793 * dwDataLength [I] Size of the Metafile data
8795 * RETURNS
8796 * Success: S_OK
8797 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8799 * NOTES
8800 * This function is used by OleConvertOLESTREAMToIStorage only.
8803 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8805 HRESULT hRes;
8806 IStream *pStream;
8807 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8808 BYTE pOlePresStreamHeader [] =
8810 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8811 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8812 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8813 0x00, 0x00, 0x00, 0x00
8816 BYTE pOlePresStreamHeaderEmpty [] =
8818 0x00, 0x00, 0x00, 0x00,
8819 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8820 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8821 0x00, 0x00, 0x00, 0x00
8824 /* Create the OlePres000 Stream */
8825 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8826 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8828 if(hRes == S_OK)
8830 DWORD nHeaderSize;
8831 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8833 memset(&OlePres, 0, sizeof(OlePres));
8834 /* Do we have any metafile data to save */
8835 if(dwDataLength > 0)
8837 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8838 nHeaderSize = sizeof(pOlePresStreamHeader);
8840 else
8842 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8843 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8845 /* Set width and height of the metafile */
8846 OlePres.dwExtentX = dwExtentX;
8847 OlePres.dwExtentY = -dwExtentY;
8849 /* Set Data and Length */
8850 if(dwDataLength > sizeof(METAFILEPICT16))
8852 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8853 OlePres.pData = &(pData[8]);
8855 /* Save OlePres000 Data to Stream */
8856 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8857 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8858 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8859 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8860 if(OlePres.dwSize > 0)
8862 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8864 IStream_Release(pStream);
8868 /*************************************************************************
8869 * OLECONVERT_CreateOle10NativeStream [Internal]
8871 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8873 * PARAMS
8874 * pStorage [I] Dest storage to create the stream in
8875 * pData [I] Ole10 Native Data (ex. bmp)
8876 * dwDataLength [I] Size of the Ole10 Native Data
8878 * RETURNS
8879 * Nothing
8881 * NOTES
8882 * This function is used by OleConvertOLESTREAMToIStorage only.
8884 * Might need to verify the data and return appropriate error message
8887 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8889 HRESULT hRes;
8890 IStream *pStream;
8891 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8893 /* Create the Ole10Native Stream */
8894 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8895 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8897 if(hRes == S_OK)
8899 /* Write info to stream */
8900 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8901 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8902 IStream_Release(pStream);
8907 /*************************************************************************
8908 * OLECONVERT_GetOLE10ProgID [Internal]
8910 * Finds the ProgID (or OleTypeID) from the IStorage
8912 * PARAMS
8913 * pStorage [I] The Src IStorage to get the ProgID
8914 * strProgID [I] the ProgID string to get
8915 * dwSize [I] the size of the string
8917 * RETURNS
8918 * Success: S_OK
8919 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8921 * NOTES
8922 * This function is used by OleConvertIStorageToOLESTREAM only.
8926 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8928 HRESULT hRes;
8929 IStream *pStream;
8930 LARGE_INTEGER iSeekPos;
8931 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8932 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8934 /* Open the CompObj Stream */
8935 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8936 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8937 if(hRes == S_OK)
8940 /*Get the OleType from the CompObj Stream */
8941 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8942 iSeekPos.u.HighPart = 0;
8944 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8945 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8946 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8947 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8948 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8949 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8950 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8952 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8953 if(*dwSize > 0)
8955 IStream_Read(pStream, strProgID, *dwSize, NULL);
8957 IStream_Release(pStream);
8959 else
8961 STATSTG stat;
8962 LPOLESTR wstrProgID;
8964 /* Get the OleType from the registry */
8965 REFCLSID clsid = &(stat.clsid);
8966 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8967 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8968 if(hRes == S_OK)
8970 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8974 return hRes;
8977 /*************************************************************************
8978 * OLECONVERT_GetOle10PresData [Internal]
8980 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8982 * PARAMS
8983 * pStorage [I] Src IStroage
8984 * pOleStream [I] Dest OleStream Mem Struct
8986 * RETURNS
8987 * Nothing
8989 * NOTES
8990 * This function is used by OleConvertIStorageToOLESTREAM only.
8992 * Memory allocated for pData must be freed by the caller
8996 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8999 HRESULT hRes;
9000 IStream *pStream;
9001 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9003 /* Initialize Default data for OLESTREAM */
9004 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9005 pOleStreamData[0].dwTypeID = 2;
9006 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9007 pOleStreamData[1].dwTypeID = 0;
9008 pOleStreamData[0].dwMetaFileWidth = 0;
9009 pOleStreamData[0].dwMetaFileHeight = 0;
9010 pOleStreamData[0].pData = NULL;
9011 pOleStreamData[1].pData = NULL;
9013 /* Open Ole10Native Stream */
9014 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9015 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9016 if(hRes == S_OK)
9019 /* Read Size and Data */
9020 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9021 if(pOleStreamData->dwDataLength > 0)
9023 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9024 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9026 IStream_Release(pStream);
9032 /*************************************************************************
9033 * OLECONVERT_GetOle20PresData[Internal]
9035 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9037 * PARAMS
9038 * pStorage [I] Src IStroage
9039 * pOleStreamData [I] Dest OleStream Mem Struct
9041 * RETURNS
9042 * Nothing
9044 * NOTES
9045 * This function is used by OleConvertIStorageToOLESTREAM only.
9047 * Memory allocated for pData must be freed by the caller
9049 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9051 HRESULT hRes;
9052 IStream *pStream;
9053 OLECONVERT_ISTORAGE_OLEPRES olePress;
9054 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9056 /* Initialize Default data for OLESTREAM */
9057 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9058 pOleStreamData[0].dwTypeID = 2;
9059 pOleStreamData[0].dwMetaFileWidth = 0;
9060 pOleStreamData[0].dwMetaFileHeight = 0;
9061 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9062 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9063 pOleStreamData[1].dwTypeID = 0;
9064 pOleStreamData[1].dwOleTypeNameLength = 0;
9065 pOleStreamData[1].strOleTypeName[0] = 0;
9066 pOleStreamData[1].dwMetaFileWidth = 0;
9067 pOleStreamData[1].dwMetaFileHeight = 0;
9068 pOleStreamData[1].pData = NULL;
9069 pOleStreamData[1].dwDataLength = 0;
9072 /* Open OlePress000 stream */
9073 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9074 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9075 if(hRes == S_OK)
9077 LARGE_INTEGER iSeekPos;
9078 METAFILEPICT16 MetaFilePict;
9079 static const char strMetafilePictName[] = "METAFILEPICT";
9081 /* Set the TypeID for a Metafile */
9082 pOleStreamData[1].dwTypeID = 5;
9084 /* Set the OleTypeName to Metafile */
9085 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9086 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9088 iSeekPos.u.HighPart = 0;
9089 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9091 /* Get Presentation Data */
9092 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9093 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9094 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9095 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9097 /*Set width and Height */
9098 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9099 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9100 if(olePress.dwSize > 0)
9102 /* Set Length */
9103 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9105 /* Set MetaFilePict struct */
9106 MetaFilePict.mm = 8;
9107 MetaFilePict.xExt = olePress.dwExtentX;
9108 MetaFilePict.yExt = olePress.dwExtentY;
9109 MetaFilePict.hMF = 0;
9111 /* Get Metafile Data */
9112 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9113 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9114 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9116 IStream_Release(pStream);
9120 /*************************************************************************
9121 * OleConvertOLESTREAMToIStorage [OLE32.@]
9123 * Read info on MSDN
9125 * TODO
9126 * DVTARGETDEVICE parameter is not handled
9127 * Still unsure of some mem fields for OLE 10 Stream
9128 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9129 * and "\001OLE" streams
9132 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9133 LPOLESTREAM pOleStream,
9134 LPSTORAGE pstg,
9135 const DVTARGETDEVICE* ptd)
9137 int i;
9138 HRESULT hRes=S_OK;
9139 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9141 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9143 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9145 if(ptd != NULL)
9147 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9150 if(pstg == NULL || pOleStream == NULL)
9152 hRes = E_INVALIDARG;
9155 if(hRes == S_OK)
9157 /* Load the OLESTREAM to Memory */
9158 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9161 if(hRes == S_OK)
9163 /* Load the OLESTREAM to Memory (part 2)*/
9164 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9167 if(hRes == S_OK)
9170 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9172 /* Do we have the IStorage Data in the OLESTREAM */
9173 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9175 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9176 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9178 else
9180 /* It must be an original OLE 1.0 source */
9181 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9184 else
9186 /* It must be an original OLE 1.0 source */
9187 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9190 /* Create CompObj Stream if necessary */
9191 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9192 if(hRes == S_OK)
9194 /*Create the Ole Stream if necessary */
9195 OLECONVERT_CreateOleStream(pstg);
9200 /* Free allocated memory */
9201 for(i=0; i < 2; i++)
9203 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9204 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9205 pOleStreamData[i].pstrOleObjFileName = NULL;
9207 return hRes;
9210 /*************************************************************************
9211 * OleConvertIStorageToOLESTREAM [OLE32.@]
9213 * Read info on MSDN
9215 * Read info on MSDN
9217 * TODO
9218 * Still unsure of some mem fields for OLE 10 Stream
9219 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9220 * and "\001OLE" streams.
9223 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9224 LPSTORAGE pstg,
9225 LPOLESTREAM pOleStream)
9227 int i;
9228 HRESULT hRes = S_OK;
9229 IStream *pStream;
9230 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9231 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9233 TRACE("%p %p\n", pstg, pOleStream);
9235 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9237 if(pstg == NULL || pOleStream == NULL)
9239 hRes = E_INVALIDARG;
9241 if(hRes == S_OK)
9243 /* Get the ProgID */
9244 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9245 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9247 if(hRes == S_OK)
9249 /* Was it originally Ole10 */
9250 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9251 if(hRes == S_OK)
9253 IStream_Release(pStream);
9254 /* Get Presentation Data for Ole10Native */
9255 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9257 else
9259 /* Get Presentation Data (OLE20) */
9260 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9263 /* Save OLESTREAM */
9264 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9265 if(hRes == S_OK)
9267 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9272 /* Free allocated memory */
9273 for(i=0; i < 2; i++)
9275 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9278 return hRes;
9281 /***********************************************************************
9282 * GetConvertStg (OLE32.@)
9284 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9285 FIXME("unimplemented stub!\n");
9286 return E_FAIL;
9289 /******************************************************************************
9290 * StgIsStorageFile [OLE32.@]
9291 * Verify if the file contains a storage object
9293 * PARAMS
9294 * fn [ I] Filename
9296 * RETURNS
9297 * S_OK if file has magic bytes as a storage object
9298 * S_FALSE if file is not storage
9300 HRESULT WINAPI
9301 StgIsStorageFile(LPCOLESTR fn)
9303 HANDLE hf;
9304 BYTE magic[8];
9305 DWORD bytes_read;
9307 TRACE("%s\n", debugstr_w(fn));
9308 hf = CreateFileW(fn, GENERIC_READ,
9309 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9310 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9312 if (hf == INVALID_HANDLE_VALUE)
9313 return STG_E_FILENOTFOUND;
9315 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9317 WARN(" unable to read file\n");
9318 CloseHandle(hf);
9319 return S_FALSE;
9322 CloseHandle(hf);
9324 if (bytes_read != 8) {
9325 TRACE(" too short\n");
9326 return S_FALSE;
9329 if (!memcmp(magic,STORAGE_magic,8)) {
9330 TRACE(" -> YES\n");
9331 return S_OK;
9334 TRACE(" -> Invalid header.\n");
9335 return S_FALSE;
9338 /***********************************************************************
9339 * WriteClassStm (OLE32.@)
9341 * Writes a CLSID to a stream.
9343 * PARAMS
9344 * pStm [I] Stream to write to.
9345 * rclsid [I] CLSID to write.
9347 * RETURNS
9348 * Success: S_OK.
9349 * Failure: HRESULT code.
9351 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9353 TRACE("(%p,%p)\n",pStm,rclsid);
9355 if (!pStm || !rclsid)
9356 return E_INVALIDARG;
9358 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9361 /***********************************************************************
9362 * ReadClassStm (OLE32.@)
9364 * Reads a CLSID from a stream.
9366 * PARAMS
9367 * pStm [I] Stream to read from.
9368 * rclsid [O] CLSID to read.
9370 * RETURNS
9371 * Success: S_OK.
9372 * Failure: HRESULT code.
9374 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9376 ULONG nbByte;
9377 HRESULT res;
9379 TRACE("(%p,%p)\n",pStm,pclsid);
9381 if (!pStm || !pclsid)
9382 return E_INVALIDARG;
9384 /* clear the output args */
9385 *pclsid = CLSID_NULL;
9387 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9389 if (FAILED(res))
9390 return res;
9392 if (nbByte != sizeof(CLSID))
9393 return STG_E_READFAULT;
9394 else
9395 return S_OK;