Release 1.3.7.
[wine/gsoc-2012-control.git] / dlls / ole32 / storage32.c
blob311f2aea43d09b5d2dfc4ca45f8a784c21ac23b8
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;
2771 /* Read the extended big block depot locations. */
2772 if (This->extBigBlockDepotCount != 0)
2774 ULONG current_block = This->extBigBlockDepotStart;
2775 ULONG cache_size = This->extBigBlockDepotCount * 2;
2776 int i;
2778 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2779 if (!This->extBigBlockDepotLocations)
2781 hr = E_OUTOFMEMORY;
2782 goto end;
2785 This->extBigBlockDepotLocationsSize = cache_size;
2787 for (i=0; i<This->extBigBlockDepotCount; i++)
2789 if (current_block == BLOCK_END_OF_CHAIN)
2791 WARN("File has too few extended big block depot blocks.\n");
2792 hr = STG_E_DOCFILECORRUPT;
2793 goto end;
2795 This->extBigBlockDepotLocations[i] = current_block;
2796 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2799 else
2801 This->extBigBlockDepotLocations = NULL;
2802 This->extBigBlockDepotLocationsSize = 0;
2806 * Create the block chain abstractions.
2808 if(!(This->rootBlockChain =
2809 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2811 hr = STG_E_READFAULT;
2812 goto end;
2815 if(!(This->smallBlockDepotChain =
2816 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2817 DIRENTRY_NULL)))
2819 hr = STG_E_READFAULT;
2820 goto end;
2824 * Write the root storage entry (memory only)
2826 if (create)
2828 DirEntry rootEntry;
2830 * Initialize the directory table
2832 memset(&rootEntry, 0, sizeof(rootEntry));
2833 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2834 sizeof(rootEntry.name)/sizeof(WCHAR) );
2835 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2836 rootEntry.stgType = STGTY_ROOT;
2837 rootEntry.leftChild = DIRENTRY_NULL;
2838 rootEntry.rightChild = DIRENTRY_NULL;
2839 rootEntry.dirRootEntry = DIRENTRY_NULL;
2840 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2841 rootEntry.size.u.HighPart = 0;
2842 rootEntry.size.u.LowPart = 0;
2844 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2848 * Find the ID of the root storage.
2850 currentEntryRef = 0;
2854 hr = StorageImpl_ReadDirEntry(
2855 This,
2856 currentEntryRef,
2857 &currentEntry);
2859 if (SUCCEEDED(hr))
2861 if ( (currentEntry.sizeOfNameString != 0 ) &&
2862 (currentEntry.stgType == STGTY_ROOT) )
2864 This->base.storageDirEntry = currentEntryRef;
2868 currentEntryRef++;
2870 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2872 if (FAILED(hr))
2874 hr = STG_E_READFAULT;
2875 goto end;
2879 * Create the block chain abstraction for the small block root chain.
2881 if(!(This->smallBlockRootChain =
2882 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2884 hr = STG_E_READFAULT;
2887 end:
2888 if (FAILED(hr))
2890 IStorage_Release((IStorage*)This);
2891 *result = NULL;
2893 else
2895 StorageImpl_Flush((StorageBaseImpl*)This);
2896 *result = This;
2899 return hr;
2902 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2904 StorageImpl *This = (StorageImpl*) iface;
2906 StorageBaseImpl_DeleteAll(&This->base);
2908 This->base.reverted = 1;
2911 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2913 StorageImpl *This = (StorageImpl*) iface;
2914 int i;
2915 TRACE("(%p)\n", This);
2917 StorageImpl_Flush(iface);
2919 StorageImpl_Invalidate(iface);
2921 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
2923 BlockChainStream_Destroy(This->smallBlockRootChain);
2924 BlockChainStream_Destroy(This->rootBlockChain);
2925 BlockChainStream_Destroy(This->smallBlockDepotChain);
2927 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2928 BlockChainStream_Destroy(This->blockChainCache[i]);
2930 if (This->lockBytes)
2931 ILockBytes_Release(This->lockBytes);
2932 HeapFree(GetProcessHeap(), 0, This);
2935 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2937 StorageImpl *This = (StorageImpl*) iface;
2938 int i;
2939 HRESULT hr;
2940 TRACE("(%p)\n", This);
2942 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2944 if (SUCCEEDED(hr))
2945 hr = BlockChainStream_Flush(This->rootBlockChain);
2947 if (SUCCEEDED(hr))
2948 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2950 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
2951 if (This->blockChainCache[i])
2952 hr = BlockChainStream_Flush(This->blockChainCache[i]);
2954 if (SUCCEEDED(hr))
2955 hr = ILockBytes_Flush(This->lockBytes);
2957 return hr;
2960 /******************************************************************************
2961 * Storage32Impl_GetNextFreeBigBlock
2963 * Returns the index of the next free big block.
2964 * If the big block depot is filled, this method will enlarge it.
2967 static ULONG StorageImpl_GetNextFreeBigBlock(
2968 StorageImpl* This)
2970 ULONG depotBlockIndexPos;
2971 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2972 BOOL success;
2973 ULONG depotBlockOffset;
2974 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2975 ULONG nextBlockIndex = BLOCK_SPECIAL;
2976 int depotIndex = 0;
2977 ULONG freeBlock = BLOCK_UNUSED;
2978 ULARGE_INTEGER neededSize;
2979 STATSTG statstg;
2981 depotIndex = This->prevFreeBlock / blocksPerDepot;
2982 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2985 * Scan the entire big block depot until we find a block marked free
2987 while (nextBlockIndex != BLOCK_UNUSED)
2989 if (depotIndex < COUNT_BBDEPOTINHEADER)
2991 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2994 * Grow the primary depot.
2996 if (depotBlockIndexPos == BLOCK_UNUSED)
2998 depotBlockIndexPos = depotIndex*blocksPerDepot;
3001 * Add a block depot.
3003 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3004 This->bigBlockDepotCount++;
3005 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3008 * Flag it as a block depot.
3010 StorageImpl_SetNextBlockInChain(This,
3011 depotBlockIndexPos,
3012 BLOCK_SPECIAL);
3014 /* Save new header information.
3016 StorageImpl_SaveFileHeader(This);
3019 else
3021 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3023 if (depotBlockIndexPos == BLOCK_UNUSED)
3026 * Grow the extended depot.
3028 ULONG extIndex = BLOCK_UNUSED;
3029 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3030 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3032 if (extBlockOffset == 0)
3034 /* We need an extended block.
3036 extIndex = Storage32Impl_AddExtBlockDepot(This);
3037 This->extBigBlockDepotCount++;
3038 depotBlockIndexPos = extIndex + 1;
3040 else
3041 depotBlockIndexPos = depotIndex * blocksPerDepot;
3044 * Add a block depot and mark it in the extended block.
3046 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3047 This->bigBlockDepotCount++;
3048 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3050 /* Flag the block depot.
3052 StorageImpl_SetNextBlockInChain(This,
3053 depotBlockIndexPos,
3054 BLOCK_SPECIAL);
3056 /* If necessary, flag the extended depot block.
3058 if (extIndex != BLOCK_UNUSED)
3059 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3061 /* Save header information.
3063 StorageImpl_SaveFileHeader(This);
3067 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3069 if (success)
3071 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3072 ( nextBlockIndex != BLOCK_UNUSED))
3074 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3076 if (nextBlockIndex == BLOCK_UNUSED)
3078 freeBlock = (depotIndex * blocksPerDepot) +
3079 (depotBlockOffset/sizeof(ULONG));
3082 depotBlockOffset += sizeof(ULONG);
3086 depotIndex++;
3087 depotBlockOffset = 0;
3091 * make sure that the block physically exists before using it
3093 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3095 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3097 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3098 ILockBytes_SetSize(This->lockBytes, neededSize);
3100 This->prevFreeBlock = freeBlock;
3102 return freeBlock;
3105 /******************************************************************************
3106 * Storage32Impl_AddBlockDepot
3108 * This will create a depot block, essentially it is a block initialized
3109 * to BLOCK_UNUSEDs.
3111 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3113 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3116 * Initialize blocks as free
3118 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3119 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3122 /******************************************************************************
3123 * Storage32Impl_GetExtDepotBlock
3125 * Returns the index of the block that corresponds to the specified depot
3126 * index. This method is only for depot indexes equal or greater than
3127 * COUNT_BBDEPOTINHEADER.
3129 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3131 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3132 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3133 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3134 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3135 ULONG blockIndex = BLOCK_UNUSED;
3136 ULONG extBlockIndex;
3138 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3140 if (extBlockCount >= This->extBigBlockDepotCount)
3141 return BLOCK_UNUSED;
3143 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3145 if (extBlockIndex != BLOCK_UNUSED)
3146 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3147 extBlockOffset * sizeof(ULONG), &blockIndex);
3149 return blockIndex;
3152 /******************************************************************************
3153 * Storage32Impl_SetExtDepotBlock
3155 * Associates the specified block index to the specified depot index.
3156 * This method is only for depot indexes equal or greater than
3157 * COUNT_BBDEPOTINHEADER.
3159 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3161 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3162 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3163 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3164 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3165 ULONG extBlockIndex;
3167 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3169 assert(extBlockCount < This->extBigBlockDepotCount);
3171 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3173 if (extBlockIndex != BLOCK_UNUSED)
3175 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3176 extBlockOffset * sizeof(ULONG),
3177 blockIndex);
3181 /******************************************************************************
3182 * Storage32Impl_AddExtBlockDepot
3184 * Creates an extended depot block.
3186 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3188 ULONG numExtBlocks = This->extBigBlockDepotCount;
3189 ULONG nextExtBlock = This->extBigBlockDepotStart;
3190 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3191 ULONG index = BLOCK_UNUSED;
3192 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3193 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3194 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3196 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3197 blocksPerDepotBlock;
3199 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3202 * The first extended block.
3204 This->extBigBlockDepotStart = index;
3206 else
3209 * Find the last existing extended block.
3211 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3214 * Add the new extended block to the chain.
3216 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3217 index);
3221 * Initialize this block.
3223 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3224 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3226 /* Add the block to our cache. */
3227 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3229 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3230 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3232 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3233 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3235 This->extBigBlockDepotLocations = new_cache;
3236 This->extBigBlockDepotLocationsSize = new_cache_size;
3238 This->extBigBlockDepotLocations[numExtBlocks] = index;
3240 return index;
3243 /******************************************************************************
3244 * Storage32Impl_FreeBigBlock
3246 * This method will flag the specified block as free in the big block depot.
3248 static void StorageImpl_FreeBigBlock(
3249 StorageImpl* This,
3250 ULONG blockIndex)
3252 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3254 if (blockIndex < This->prevFreeBlock)
3255 This->prevFreeBlock = blockIndex;
3258 /************************************************************************
3259 * Storage32Impl_GetNextBlockInChain
3261 * This method will retrieve the block index of the next big block in
3262 * in the chain.
3264 * Params: This - Pointer to the Storage object.
3265 * blockIndex - Index of the block to retrieve the chain
3266 * for.
3267 * nextBlockIndex - receives the return value.
3269 * Returns: This method returns the index of the next block in the chain.
3270 * It will return the constants:
3271 * BLOCK_SPECIAL - If the block given was not part of a
3272 * chain.
3273 * BLOCK_END_OF_CHAIN - If the block given was the last in
3274 * a chain.
3275 * BLOCK_UNUSED - If the block given was not past of a chain
3276 * and is available.
3277 * BLOCK_EXTBBDEPOT - This block is part of the extended
3278 * big block depot.
3280 * See Windows documentation for more details on IStorage methods.
3282 static HRESULT StorageImpl_GetNextBlockInChain(
3283 StorageImpl* This,
3284 ULONG blockIndex,
3285 ULONG* nextBlockIndex)
3287 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3288 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3289 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3290 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3291 BOOL success;
3292 ULONG depotBlockIndexPos;
3293 int index, num_blocks;
3295 *nextBlockIndex = BLOCK_SPECIAL;
3297 if(depotBlockCount >= This->bigBlockDepotCount)
3299 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3300 This->bigBlockDepotCount);
3301 return STG_E_READFAULT;
3305 * Cache the currently accessed depot block.
3307 if (depotBlockCount != This->indexBlockDepotCached)
3309 This->indexBlockDepotCached = depotBlockCount;
3311 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3313 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3315 else
3318 * We have to look in the extended depot.
3320 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3323 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3325 if (!success)
3326 return STG_E_READFAULT;
3328 num_blocks = This->bigBlockSize / 4;
3330 for (index = 0; index < num_blocks; index++)
3332 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3333 This->blockDepotCached[index] = *nextBlockIndex;
3337 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3339 return S_OK;
3342 /******************************************************************************
3343 * Storage32Impl_GetNextExtendedBlock
3345 * Given an extended block this method will return the next extended block.
3347 * NOTES:
3348 * The last ULONG of an extended block is the block index of the next
3349 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3350 * depot.
3352 * Return values:
3353 * - The index of the next extended block
3354 * - BLOCK_UNUSED: there is no next extended block.
3355 * - Any other return values denotes failure.
3357 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3359 ULONG nextBlockIndex = BLOCK_SPECIAL;
3360 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3362 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3363 &nextBlockIndex);
3365 return nextBlockIndex;
3368 /******************************************************************************
3369 * Storage32Impl_SetNextBlockInChain
3371 * This method will write the index of the specified block's next block
3372 * in the big block depot.
3374 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3375 * do the following
3377 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3378 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3379 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3382 static void StorageImpl_SetNextBlockInChain(
3383 StorageImpl* This,
3384 ULONG blockIndex,
3385 ULONG nextBlock)
3387 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3388 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3389 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3390 ULONG depotBlockIndexPos;
3392 assert(depotBlockCount < This->bigBlockDepotCount);
3393 assert(blockIndex != nextBlock);
3395 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3397 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3399 else
3402 * We have to look in the extended depot.
3404 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3407 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3408 nextBlock);
3410 * Update the cached block depot, if necessary.
3412 if (depotBlockCount == This->indexBlockDepotCached)
3414 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3418 /******************************************************************************
3419 * Storage32Impl_LoadFileHeader
3421 * This method will read in the file header
3423 static HRESULT StorageImpl_LoadFileHeader(
3424 StorageImpl* This)
3426 HRESULT hr;
3427 BYTE headerBigBlock[HEADER_SIZE];
3428 int index;
3429 ULARGE_INTEGER offset;
3430 DWORD bytes_read;
3432 TRACE("\n");
3434 * Get a pointer to the big block of data containing the header.
3436 offset.u.HighPart = 0;
3437 offset.u.LowPart = 0;
3438 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3439 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3440 hr = STG_E_FILENOTFOUND;
3443 * Extract the information from the header.
3445 if (SUCCEEDED(hr))
3448 * Check for the "magic number" signature and return an error if it is not
3449 * found.
3451 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3453 return STG_E_OLDFORMAT;
3456 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3458 return STG_E_INVALIDHEADER;
3461 StorageUtl_ReadWord(
3462 headerBigBlock,
3463 OFFSET_BIGBLOCKSIZEBITS,
3464 &This->bigBlockSizeBits);
3466 StorageUtl_ReadWord(
3467 headerBigBlock,
3468 OFFSET_SMALLBLOCKSIZEBITS,
3469 &This->smallBlockSizeBits);
3471 StorageUtl_ReadDWord(
3472 headerBigBlock,
3473 OFFSET_BBDEPOTCOUNT,
3474 &This->bigBlockDepotCount);
3476 StorageUtl_ReadDWord(
3477 headerBigBlock,
3478 OFFSET_ROOTSTARTBLOCK,
3479 &This->rootStartBlock);
3481 StorageUtl_ReadDWord(
3482 headerBigBlock,
3483 OFFSET_SMALLBLOCKLIMIT,
3484 &This->smallBlockLimit);
3486 StorageUtl_ReadDWord(
3487 headerBigBlock,
3488 OFFSET_SBDEPOTSTART,
3489 &This->smallBlockDepotStart);
3491 StorageUtl_ReadDWord(
3492 headerBigBlock,
3493 OFFSET_EXTBBDEPOTSTART,
3494 &This->extBigBlockDepotStart);
3496 StorageUtl_ReadDWord(
3497 headerBigBlock,
3498 OFFSET_EXTBBDEPOTCOUNT,
3499 &This->extBigBlockDepotCount);
3501 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3503 StorageUtl_ReadDWord(
3504 headerBigBlock,
3505 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3506 &(This->bigBlockDepotStart[index]));
3510 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3512 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3513 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3516 * Right now, the code is making some assumptions about the size of the
3517 * blocks, just make sure they are what we're expecting.
3519 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3520 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3521 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3523 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3524 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3525 hr = STG_E_INVALIDHEADER;
3527 else
3528 hr = S_OK;
3531 return hr;
3534 /******************************************************************************
3535 * Storage32Impl_SaveFileHeader
3537 * This method will save to the file the header
3539 static void StorageImpl_SaveFileHeader(
3540 StorageImpl* This)
3542 BYTE headerBigBlock[HEADER_SIZE];
3543 int index;
3544 HRESULT hr;
3545 ULARGE_INTEGER offset;
3546 DWORD bytes_read, bytes_written;
3547 DWORD major_version, dirsectorcount;
3550 * Get a pointer to the big block of data containing the header.
3552 offset.u.HighPart = 0;
3553 offset.u.LowPart = 0;
3554 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3555 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3556 hr = STG_E_FILENOTFOUND;
3558 if (This->bigBlockSizeBits == 0x9)
3559 major_version = 3;
3560 else if (This->bigBlockSizeBits == 0xc)
3561 major_version = 4;
3562 else
3564 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3565 major_version = 4;
3569 * If the block read failed, the file is probably new.
3571 if (FAILED(hr))
3574 * Initialize for all unknown fields.
3576 memset(headerBigBlock, 0, HEADER_SIZE);
3579 * Initialize the magic number.
3581 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3585 * Write the information to the header.
3587 StorageUtl_WriteWord(
3588 headerBigBlock,
3589 OFFSET_MINORVERSION,
3590 0x3e);
3592 StorageUtl_WriteWord(
3593 headerBigBlock,
3594 OFFSET_MAJORVERSION,
3595 major_version);
3597 StorageUtl_WriteWord(
3598 headerBigBlock,
3599 OFFSET_BYTEORDERMARKER,
3600 (WORD)-2);
3602 StorageUtl_WriteWord(
3603 headerBigBlock,
3604 OFFSET_BIGBLOCKSIZEBITS,
3605 This->bigBlockSizeBits);
3607 StorageUtl_WriteWord(
3608 headerBigBlock,
3609 OFFSET_SMALLBLOCKSIZEBITS,
3610 This->smallBlockSizeBits);
3612 if (major_version >= 4)
3614 if (This->rootBlockChain)
3615 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3616 else
3617 /* This file is being created, and it will start out with one block. */
3618 dirsectorcount = 1;
3620 else
3621 /* This field must be 0 in versions older than 4 */
3622 dirsectorcount = 0;
3624 StorageUtl_WriteDWord(
3625 headerBigBlock,
3626 OFFSET_DIRSECTORCOUNT,
3627 dirsectorcount);
3629 StorageUtl_WriteDWord(
3630 headerBigBlock,
3631 OFFSET_BBDEPOTCOUNT,
3632 This->bigBlockDepotCount);
3634 StorageUtl_WriteDWord(
3635 headerBigBlock,
3636 OFFSET_ROOTSTARTBLOCK,
3637 This->rootStartBlock);
3639 StorageUtl_WriteDWord(
3640 headerBigBlock,
3641 OFFSET_SMALLBLOCKLIMIT,
3642 This->smallBlockLimit);
3644 StorageUtl_WriteDWord(
3645 headerBigBlock,
3646 OFFSET_SBDEPOTSTART,
3647 This->smallBlockDepotStart);
3649 StorageUtl_WriteDWord(
3650 headerBigBlock,
3651 OFFSET_SBDEPOTCOUNT,
3652 This->smallBlockDepotChain ?
3653 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3655 StorageUtl_WriteDWord(
3656 headerBigBlock,
3657 OFFSET_EXTBBDEPOTSTART,
3658 This->extBigBlockDepotStart);
3660 StorageUtl_WriteDWord(
3661 headerBigBlock,
3662 OFFSET_EXTBBDEPOTCOUNT,
3663 This->extBigBlockDepotCount);
3665 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3667 StorageUtl_WriteDWord(
3668 headerBigBlock,
3669 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3670 (This->bigBlockDepotStart[index]));
3674 * Write the big block back to the file.
3676 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3679 /******************************************************************************
3680 * StorageImpl_ReadRawDirEntry
3682 * This method will read the raw data from a directory entry in the file.
3684 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3686 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3688 ULARGE_INTEGER offset;
3689 HRESULT hr;
3690 ULONG bytesRead;
3692 offset.u.HighPart = 0;
3693 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3695 hr = BlockChainStream_ReadAt(
3696 This->rootBlockChain,
3697 offset,
3698 RAW_DIRENTRY_SIZE,
3699 buffer,
3700 &bytesRead);
3702 if (bytesRead != RAW_DIRENTRY_SIZE)
3703 return STG_E_READFAULT;
3705 return hr;
3708 /******************************************************************************
3709 * StorageImpl_WriteRawDirEntry
3711 * This method will write the raw data from a directory entry in the file.
3713 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3715 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3717 ULARGE_INTEGER offset;
3718 HRESULT hr;
3719 ULONG bytesRead;
3721 offset.u.HighPart = 0;
3722 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3724 hr = BlockChainStream_WriteAt(
3725 This->rootBlockChain,
3726 offset,
3727 RAW_DIRENTRY_SIZE,
3728 buffer,
3729 &bytesRead);
3731 return hr;
3734 /******************************************************************************
3735 * UpdateRawDirEntry
3737 * Update raw directory entry data from the fields in newData.
3739 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3741 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3743 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3745 memcpy(
3746 buffer + OFFSET_PS_NAME,
3747 newData->name,
3748 DIRENTRY_NAME_BUFFER_LEN );
3750 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3752 StorageUtl_WriteWord(
3753 buffer,
3754 OFFSET_PS_NAMELENGTH,
3755 newData->sizeOfNameString);
3757 StorageUtl_WriteDWord(
3758 buffer,
3759 OFFSET_PS_LEFTCHILD,
3760 newData->leftChild);
3762 StorageUtl_WriteDWord(
3763 buffer,
3764 OFFSET_PS_RIGHTCHILD,
3765 newData->rightChild);
3767 StorageUtl_WriteDWord(
3768 buffer,
3769 OFFSET_PS_DIRROOT,
3770 newData->dirRootEntry);
3772 StorageUtl_WriteGUID(
3773 buffer,
3774 OFFSET_PS_GUID,
3775 &newData->clsid);
3777 StorageUtl_WriteDWord(
3778 buffer,
3779 OFFSET_PS_CTIMELOW,
3780 newData->ctime.dwLowDateTime);
3782 StorageUtl_WriteDWord(
3783 buffer,
3784 OFFSET_PS_CTIMEHIGH,
3785 newData->ctime.dwHighDateTime);
3787 StorageUtl_WriteDWord(
3788 buffer,
3789 OFFSET_PS_MTIMELOW,
3790 newData->mtime.dwLowDateTime);
3792 StorageUtl_WriteDWord(
3793 buffer,
3794 OFFSET_PS_MTIMEHIGH,
3795 newData->ctime.dwHighDateTime);
3797 StorageUtl_WriteDWord(
3798 buffer,
3799 OFFSET_PS_STARTBLOCK,
3800 newData->startingBlock);
3802 StorageUtl_WriteDWord(
3803 buffer,
3804 OFFSET_PS_SIZE,
3805 newData->size.u.LowPart);
3808 /******************************************************************************
3809 * Storage32Impl_ReadDirEntry
3811 * This method will read the specified directory entry.
3813 HRESULT StorageImpl_ReadDirEntry(
3814 StorageImpl* This,
3815 DirRef index,
3816 DirEntry* buffer)
3818 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3819 HRESULT readRes;
3821 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3823 if (SUCCEEDED(readRes))
3825 memset(buffer->name, 0, sizeof(buffer->name));
3826 memcpy(
3827 buffer->name,
3828 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3829 DIRENTRY_NAME_BUFFER_LEN );
3830 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3832 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3834 StorageUtl_ReadWord(
3835 currentEntry,
3836 OFFSET_PS_NAMELENGTH,
3837 &buffer->sizeOfNameString);
3839 StorageUtl_ReadDWord(
3840 currentEntry,
3841 OFFSET_PS_LEFTCHILD,
3842 &buffer->leftChild);
3844 StorageUtl_ReadDWord(
3845 currentEntry,
3846 OFFSET_PS_RIGHTCHILD,
3847 &buffer->rightChild);
3849 StorageUtl_ReadDWord(
3850 currentEntry,
3851 OFFSET_PS_DIRROOT,
3852 &buffer->dirRootEntry);
3854 StorageUtl_ReadGUID(
3855 currentEntry,
3856 OFFSET_PS_GUID,
3857 &buffer->clsid);
3859 StorageUtl_ReadDWord(
3860 currentEntry,
3861 OFFSET_PS_CTIMELOW,
3862 &buffer->ctime.dwLowDateTime);
3864 StorageUtl_ReadDWord(
3865 currentEntry,
3866 OFFSET_PS_CTIMEHIGH,
3867 &buffer->ctime.dwHighDateTime);
3869 StorageUtl_ReadDWord(
3870 currentEntry,
3871 OFFSET_PS_MTIMELOW,
3872 &buffer->mtime.dwLowDateTime);
3874 StorageUtl_ReadDWord(
3875 currentEntry,
3876 OFFSET_PS_MTIMEHIGH,
3877 &buffer->mtime.dwHighDateTime);
3879 StorageUtl_ReadDWord(
3880 currentEntry,
3881 OFFSET_PS_STARTBLOCK,
3882 &buffer->startingBlock);
3884 StorageUtl_ReadDWord(
3885 currentEntry,
3886 OFFSET_PS_SIZE,
3887 &buffer->size.u.LowPart);
3889 buffer->size.u.HighPart = 0;
3892 return readRes;
3895 /*********************************************************************
3896 * Write the specified directory entry to the file
3898 HRESULT StorageImpl_WriteDirEntry(
3899 StorageImpl* This,
3900 DirRef index,
3901 const DirEntry* buffer)
3903 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3904 HRESULT writeRes;
3906 UpdateRawDirEntry(currentEntry, buffer);
3908 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3909 return writeRes;
3912 static BOOL StorageImpl_ReadBigBlock(
3913 StorageImpl* This,
3914 ULONG blockIndex,
3915 void* buffer)
3917 ULARGE_INTEGER ulOffset;
3918 DWORD read=0;
3920 ulOffset.u.HighPart = 0;
3921 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3923 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3925 if (read && read < This->bigBlockSize)
3927 /* File ends during this block; fill the rest with 0's. */
3928 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3931 return (read != 0);
3934 static BOOL StorageImpl_ReadDWordFromBigBlock(
3935 StorageImpl* This,
3936 ULONG blockIndex,
3937 ULONG offset,
3938 DWORD* value)
3940 ULARGE_INTEGER ulOffset;
3941 DWORD read;
3942 DWORD tmp;
3944 ulOffset.u.HighPart = 0;
3945 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3946 ulOffset.u.LowPart += offset;
3948 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3949 *value = lendian32toh(tmp);
3950 return (read == sizeof(DWORD));
3953 static BOOL StorageImpl_WriteBigBlock(
3954 StorageImpl* This,
3955 ULONG blockIndex,
3956 const void* buffer)
3958 ULARGE_INTEGER ulOffset;
3959 DWORD wrote;
3961 ulOffset.u.HighPart = 0;
3962 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3964 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3965 return (wrote == This->bigBlockSize);
3968 static BOOL StorageImpl_WriteDWordToBigBlock(
3969 StorageImpl* This,
3970 ULONG blockIndex,
3971 ULONG offset,
3972 DWORD value)
3974 ULARGE_INTEGER ulOffset;
3975 DWORD wrote;
3977 ulOffset.u.HighPart = 0;
3978 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3979 ulOffset.u.LowPart += offset;
3981 value = htole32(value);
3982 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3983 return (wrote == sizeof(DWORD));
3986 /******************************************************************************
3987 * Storage32Impl_SmallBlocksToBigBlocks
3989 * This method will convert a small block chain to a big block chain.
3990 * The small block chain will be destroyed.
3992 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3993 StorageImpl* This,
3994 SmallBlockChainStream** ppsbChain)
3996 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3997 ULARGE_INTEGER size, offset;
3998 ULONG cbRead, cbWritten;
3999 ULARGE_INTEGER cbTotalRead;
4000 DirRef streamEntryRef;
4001 HRESULT resWrite = S_OK;
4002 HRESULT resRead;
4003 DirEntry streamEntry;
4004 BYTE *buffer;
4005 BlockChainStream *bbTempChain = NULL;
4006 BlockChainStream *bigBlockChain = NULL;
4009 * Create a temporary big block chain that doesn't have
4010 * an associated directory entry. This temporary chain will be
4011 * used to copy data from small blocks to big blocks.
4013 bbTempChain = BlockChainStream_Construct(This,
4014 &bbHeadOfChain,
4015 DIRENTRY_NULL);
4016 if(!bbTempChain) return NULL;
4018 * Grow the big block chain.
4020 size = SmallBlockChainStream_GetSize(*ppsbChain);
4021 BlockChainStream_SetSize(bbTempChain, size);
4024 * Copy the contents of the small block chain to the big block chain
4025 * by small block size increments.
4027 offset.u.LowPart = 0;
4028 offset.u.HighPart = 0;
4029 cbTotalRead.QuadPart = 0;
4031 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4034 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4035 offset,
4036 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4037 buffer,
4038 &cbRead);
4039 if (FAILED(resRead))
4040 break;
4042 if (cbRead > 0)
4044 cbTotalRead.QuadPart += cbRead;
4046 resWrite = BlockChainStream_WriteAt(bbTempChain,
4047 offset,
4048 cbRead,
4049 buffer,
4050 &cbWritten);
4052 if (FAILED(resWrite))
4053 break;
4055 offset.u.LowPart += cbRead;
4057 else
4059 resRead = STG_E_READFAULT;
4060 break;
4062 } while (cbTotalRead.QuadPart < size.QuadPart);
4063 HeapFree(GetProcessHeap(),0,buffer);
4065 size.u.HighPart = 0;
4066 size.u.LowPart = 0;
4068 if (FAILED(resRead) || FAILED(resWrite))
4070 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4071 BlockChainStream_SetSize(bbTempChain, size);
4072 BlockChainStream_Destroy(bbTempChain);
4073 return NULL;
4077 * Destroy the small block chain.
4079 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4080 SmallBlockChainStream_SetSize(*ppsbChain, size);
4081 SmallBlockChainStream_Destroy(*ppsbChain);
4082 *ppsbChain = 0;
4085 * Change the directory entry. This chain is now a big block chain
4086 * and it doesn't reside in the small blocks chain anymore.
4088 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4090 streamEntry.startingBlock = bbHeadOfChain;
4092 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4095 * Destroy the temporary entryless big block chain.
4096 * Create a new big block chain associated with this entry.
4098 BlockChainStream_Destroy(bbTempChain);
4099 bigBlockChain = BlockChainStream_Construct(This,
4100 NULL,
4101 streamEntryRef);
4103 return bigBlockChain;
4106 /******************************************************************************
4107 * Storage32Impl_BigBlocksToSmallBlocks
4109 * This method will convert a big block chain to a small block chain.
4110 * The big block chain will be destroyed on success.
4112 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4113 StorageImpl* This,
4114 BlockChainStream** ppbbChain)
4116 ULARGE_INTEGER size, offset, cbTotalRead;
4117 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4118 DirRef streamEntryRef;
4119 HRESULT resWrite = S_OK, resRead;
4120 DirEntry streamEntry;
4121 BYTE* buffer;
4122 SmallBlockChainStream* sbTempChain;
4124 TRACE("%p %p\n", This, ppbbChain);
4126 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4127 DIRENTRY_NULL);
4129 if(!sbTempChain)
4130 return NULL;
4132 size = BlockChainStream_GetSize(*ppbbChain);
4133 SmallBlockChainStream_SetSize(sbTempChain, size);
4135 offset.u.HighPart = 0;
4136 offset.u.LowPart = 0;
4137 cbTotalRead.QuadPart = 0;
4138 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4141 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4142 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4143 buffer, &cbRead);
4145 if(FAILED(resRead))
4146 break;
4148 if(cbRead > 0)
4150 cbTotalRead.QuadPart += cbRead;
4152 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4153 cbRead, buffer, &cbWritten);
4155 if(FAILED(resWrite))
4156 break;
4158 offset.u.LowPart += cbRead;
4160 else
4162 resRead = STG_E_READFAULT;
4163 break;
4165 }while(cbTotalRead.QuadPart < size.QuadPart);
4166 HeapFree(GetProcessHeap(), 0, buffer);
4168 size.u.HighPart = 0;
4169 size.u.LowPart = 0;
4171 if(FAILED(resRead) || FAILED(resWrite))
4173 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4174 SmallBlockChainStream_SetSize(sbTempChain, size);
4175 SmallBlockChainStream_Destroy(sbTempChain);
4176 return NULL;
4179 /* destroy the original big block chain */
4180 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4181 BlockChainStream_SetSize(*ppbbChain, size);
4182 BlockChainStream_Destroy(*ppbbChain);
4183 *ppbbChain = NULL;
4185 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4186 streamEntry.startingBlock = sbHeadOfChain;
4187 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4189 SmallBlockChainStream_Destroy(sbTempChain);
4190 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4193 static HRESULT StorageBaseImpl_CopyStream(
4194 StorageBaseImpl *dst, DirRef dst_entry,
4195 StorageBaseImpl *src, DirRef src_entry)
4197 HRESULT hr;
4198 BYTE data[4096];
4199 DirEntry srcdata;
4200 ULARGE_INTEGER bytes_copied;
4201 ULONG bytestocopy, bytesread, byteswritten;
4203 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4205 if (SUCCEEDED(hr))
4207 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4209 bytes_copied.QuadPart = 0;
4210 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4212 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4214 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4215 data, &bytesread);
4216 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4218 if (SUCCEEDED(hr))
4219 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4220 data, &byteswritten);
4221 if (SUCCEEDED(hr))
4223 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4224 bytes_copied.QuadPart += byteswritten;
4229 return hr;
4232 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4234 DirRef result=This->firstFreeEntry;
4236 while (result < This->entries_size && This->entries[result].inuse)
4237 result++;
4239 if (result == This->entries_size)
4241 ULONG new_size = This->entries_size * 2;
4242 TransactedDirEntry *new_entries;
4244 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4245 if (!new_entries) return DIRENTRY_NULL;
4247 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4248 HeapFree(GetProcessHeap(), 0, This->entries);
4250 This->entries = new_entries;
4251 This->entries_size = new_size;
4254 This->entries[result].inuse = 1;
4256 This->firstFreeEntry = result+1;
4258 return result;
4261 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4262 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4264 DirRef stubEntryRef;
4265 TransactedDirEntry *entry;
4267 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4269 if (stubEntryRef != DIRENTRY_NULL)
4271 entry = &This->entries[stubEntryRef];
4273 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4275 entry->read = 0;
4278 return stubEntryRef;
4281 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4282 TransactedSnapshotImpl *This, DirRef entry)
4284 HRESULT hr=S_OK;
4285 DirEntry data;
4287 if (!This->entries[entry].read)
4289 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4290 This->entries[entry].transactedParentEntry,
4291 &data);
4293 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4295 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4297 if (data.leftChild == DIRENTRY_NULL)
4298 hr = E_OUTOFMEMORY;
4301 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4303 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4305 if (data.rightChild == DIRENTRY_NULL)
4306 hr = E_OUTOFMEMORY;
4309 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4311 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4313 if (data.dirRootEntry == DIRENTRY_NULL)
4314 hr = E_OUTOFMEMORY;
4317 if (SUCCEEDED(hr))
4319 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4320 This->entries[entry].read = 1;
4324 return hr;
4327 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4328 TransactedSnapshotImpl *This, DirRef entry)
4330 HRESULT hr = S_OK;
4332 if (!This->entries[entry].stream_dirty)
4334 DirEntry new_entrydata;
4336 memset(&new_entrydata, 0, sizeof(DirEntry));
4337 new_entrydata.name[0] = 'S';
4338 new_entrydata.sizeOfNameString = 1;
4339 new_entrydata.stgType = STGTY_STREAM;
4340 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4341 new_entrydata.leftChild = DIRENTRY_NULL;
4342 new_entrydata.rightChild = DIRENTRY_NULL;
4343 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4345 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4346 &This->entries[entry].stream_entry);
4348 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4350 hr = StorageBaseImpl_CopyStream(
4351 This->scratch, This->entries[entry].stream_entry,
4352 This->transactedParent, This->entries[entry].transactedParentEntry);
4354 if (FAILED(hr))
4355 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4358 if (SUCCEEDED(hr))
4359 This->entries[entry].stream_dirty = 1;
4361 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4363 /* Since this entry is modified, and we aren't using its stream data, we
4364 * no longer care about the original entry. */
4365 DirRef delete_ref;
4366 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4368 if (delete_ref != DIRENTRY_NULL)
4369 This->entries[delete_ref].deleted = 1;
4371 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4375 return hr;
4378 /* Find the first entry in a depth-first traversal. */
4379 static DirRef TransactedSnapshotImpl_FindFirstChild(
4380 TransactedSnapshotImpl* This, DirRef parent)
4382 DirRef cursor, prev;
4383 TransactedDirEntry *entry;
4385 cursor = parent;
4386 entry = &This->entries[cursor];
4387 while (entry->read)
4389 if (entry->data.leftChild != DIRENTRY_NULL)
4391 prev = cursor;
4392 cursor = entry->data.leftChild;
4393 entry = &This->entries[cursor];
4394 entry->parent = prev;
4396 else if (entry->data.rightChild != DIRENTRY_NULL)
4398 prev = cursor;
4399 cursor = entry->data.rightChild;
4400 entry = &This->entries[cursor];
4401 entry->parent = prev;
4403 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4405 prev = cursor;
4406 cursor = entry->data.dirRootEntry;
4407 entry = &This->entries[cursor];
4408 entry->parent = prev;
4410 else
4411 break;
4414 return cursor;
4417 /* Find the next entry in a depth-first traversal. */
4418 static DirRef TransactedSnapshotImpl_FindNextChild(
4419 TransactedSnapshotImpl* This, DirRef current)
4421 DirRef parent;
4422 TransactedDirEntry *parent_entry;
4424 parent = This->entries[current].parent;
4425 parent_entry = &This->entries[parent];
4427 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4429 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4431 This->entries[parent_entry->data.rightChild].parent = parent;
4432 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4435 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4437 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4438 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4442 return parent;
4445 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4446 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4447 TransactedSnapshotImpl* This, DirRef entry)
4449 return entry != DIRENTRY_NULL &&
4450 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4453 /* Destroy the entries created by CopyTree. */
4454 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4455 TransactedSnapshotImpl* This, DirRef stop)
4457 DirRef cursor;
4458 TransactedDirEntry *entry;
4459 ULARGE_INTEGER zero;
4461 zero.QuadPart = 0;
4463 if (!This->entries[This->base.storageDirEntry].read)
4464 return;
4466 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4468 if (cursor == DIRENTRY_NULL)
4469 return;
4471 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4473 while (cursor != DIRENTRY_NULL && cursor != stop)
4475 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4477 entry = &This->entries[cursor];
4479 if (entry->stream_dirty)
4480 StorageBaseImpl_StreamSetSize(This->transactedParent,
4481 entry->newTransactedParentEntry, zero);
4483 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4484 entry->newTransactedParentEntry);
4486 entry->newTransactedParentEntry = entry->transactedParentEntry;
4489 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4493 /* Make a copy of our edited tree that we can use in the parent. */
4494 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4496 DirRef cursor;
4497 TransactedDirEntry *entry;
4498 HRESULT hr = S_OK;
4500 cursor = This->base.storageDirEntry;
4501 entry = &This->entries[cursor];
4502 entry->parent = DIRENTRY_NULL;
4503 entry->newTransactedParentEntry = entry->transactedParentEntry;
4505 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4506 return S_OK;
4508 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4510 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4511 entry = &This->entries[cursor];
4513 while (cursor != DIRENTRY_NULL)
4515 /* Make a copy of this entry in the transacted parent. */
4516 if (!entry->read ||
4517 (!entry->dirty && !entry->stream_dirty &&
4518 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4519 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4520 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4521 entry->newTransactedParentEntry = entry->transactedParentEntry;
4522 else
4524 DirEntry newData;
4526 memcpy(&newData, &entry->data, sizeof(DirEntry));
4528 newData.size.QuadPart = 0;
4529 newData.startingBlock = BLOCK_END_OF_CHAIN;
4531 if (newData.leftChild != DIRENTRY_NULL)
4532 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4534 if (newData.rightChild != DIRENTRY_NULL)
4535 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4537 if (newData.dirRootEntry != DIRENTRY_NULL)
4538 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4540 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4541 &entry->newTransactedParentEntry);
4542 if (FAILED(hr))
4544 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4545 return hr;
4548 if (entry->stream_dirty)
4550 hr = StorageBaseImpl_CopyStream(
4551 This->transactedParent, entry->newTransactedParentEntry,
4552 This->scratch, entry->stream_entry);
4554 else if (entry->data.size.QuadPart)
4556 hr = StorageBaseImpl_StreamLink(
4557 This->transactedParent, entry->newTransactedParentEntry,
4558 entry->transactedParentEntry);
4561 if (FAILED(hr))
4563 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4564 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4565 return hr;
4569 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4570 entry = &This->entries[cursor];
4573 return hr;
4576 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4577 IStorage* iface,
4578 DWORD grfCommitFlags) /* [in] */
4580 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4581 TransactedDirEntry *root_entry;
4582 DirRef i, dir_root_ref;
4583 DirEntry data;
4584 ULARGE_INTEGER zero;
4585 HRESULT hr;
4587 zero.QuadPart = 0;
4589 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4591 /* Cannot commit a read-only transacted storage */
4592 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4593 return STG_E_ACCESSDENIED;
4595 /* To prevent data loss, we create the new structure in the file before we
4596 * delete the old one, so that in case of errors the old data is intact. We
4597 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4598 * needed in the rare situation where we have just enough free disk space to
4599 * overwrite the existing data. */
4601 root_entry = &This->entries[This->base.storageDirEntry];
4603 if (!root_entry->read)
4604 return S_OK;
4606 hr = TransactedSnapshotImpl_CopyTree(This);
4607 if (FAILED(hr)) return hr;
4609 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4610 dir_root_ref = DIRENTRY_NULL;
4611 else
4612 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4614 hr = StorageBaseImpl_Flush(This->transactedParent);
4616 /* Update the storage to use the new data in one step. */
4617 if (SUCCEEDED(hr))
4618 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4619 root_entry->transactedParentEntry, &data);
4621 if (SUCCEEDED(hr))
4623 data.dirRootEntry = dir_root_ref;
4624 data.clsid = root_entry->data.clsid;
4625 data.ctime = root_entry->data.ctime;
4626 data.mtime = root_entry->data.mtime;
4628 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4629 root_entry->transactedParentEntry, &data);
4632 /* Try to flush after updating the root storage, but if the flush fails, keep
4633 * going, on the theory that it'll either succeed later or the subsequent
4634 * writes will fail. */
4635 StorageBaseImpl_Flush(This->transactedParent);
4637 if (SUCCEEDED(hr))
4639 /* Destroy the old now-orphaned data. */
4640 for (i=0; i<This->entries_size; i++)
4642 TransactedDirEntry *entry = &This->entries[i];
4643 if (entry->inuse)
4645 if (entry->deleted)
4647 StorageBaseImpl_StreamSetSize(This->transactedParent,
4648 entry->transactedParentEntry, zero);
4649 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4650 entry->transactedParentEntry);
4651 memset(entry, 0, sizeof(TransactedDirEntry));
4652 This->firstFreeEntry = min(i, This->firstFreeEntry);
4654 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4656 if (entry->transactedParentEntry != DIRENTRY_NULL)
4657 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4658 entry->transactedParentEntry);
4659 if (entry->stream_dirty)
4661 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4662 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4663 entry->stream_dirty = 0;
4665 entry->dirty = 0;
4666 entry->transactedParentEntry = entry->newTransactedParentEntry;
4671 else
4673 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4676 if (SUCCEEDED(hr))
4677 hr = StorageBaseImpl_Flush(This->transactedParent);
4679 return hr;
4682 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4683 IStorage* iface)
4685 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4686 ULARGE_INTEGER zero;
4687 ULONG i;
4689 TRACE("(%p)\n", iface);
4691 /* Destroy the open objects. */
4692 StorageBaseImpl_DeleteAll(&This->base);
4694 /* Clear out the scratch file. */
4695 zero.QuadPart = 0;
4696 for (i=0; i<This->entries_size; i++)
4698 if (This->entries[i].stream_dirty)
4700 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4701 zero);
4703 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4707 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4709 This->firstFreeEntry = 0;
4710 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4712 return S_OK;
4715 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4717 if (!This->reverted)
4719 TRACE("Storage invalidated (stg=%p)\n", This);
4721 This->reverted = 1;
4723 StorageBaseImpl_DeleteAll(This);
4727 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4729 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4731 TransactedSnapshotImpl_Revert((IStorage*)iface);
4733 IStorage_Release((IStorage*)This->transactedParent);
4735 IStorage_Release((IStorage*)This->scratch);
4737 HeapFree(GetProcessHeap(), 0, This->entries);
4739 HeapFree(GetProcessHeap(), 0, This);
4742 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4744 /* We only need to flush when committing. */
4745 return S_OK;
4748 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4750 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4752 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4755 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4756 const DirEntry *newData, DirRef *index)
4758 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4759 DirRef new_ref;
4760 TransactedDirEntry *new_entry;
4762 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4763 if (new_ref == DIRENTRY_NULL)
4764 return E_OUTOFMEMORY;
4766 new_entry = &This->entries[new_ref];
4768 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4769 new_entry->read = 1;
4770 new_entry->dirty = 1;
4771 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4773 *index = new_ref;
4775 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4777 return S_OK;
4780 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4781 DirRef index, const DirEntry *data)
4783 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4784 HRESULT hr;
4786 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4788 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4789 if (FAILED(hr)) return hr;
4791 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4793 if (index != This->base.storageDirEntry)
4795 This->entries[index].dirty = 1;
4797 if (data->size.QuadPart == 0 &&
4798 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4800 /* Since this entry is modified, and we aren't using its stream data, we
4801 * no longer care about the original entry. */
4802 DirRef delete_ref;
4803 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4805 if (delete_ref != DIRENTRY_NULL)
4806 This->entries[delete_ref].deleted = 1;
4808 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4812 return S_OK;
4815 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4816 DirRef index, DirEntry *data)
4818 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4819 HRESULT hr;
4821 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4822 if (FAILED(hr)) return hr;
4824 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4826 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4828 return S_OK;
4831 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4832 DirRef index)
4834 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4836 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4837 This->entries[index].data.size.QuadPart != 0)
4839 /* If we deleted this entry while it has stream data. We must have left the
4840 * data because some other entry is using it, and we need to leave the
4841 * original entry alone. */
4842 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4843 This->firstFreeEntry = min(index, This->firstFreeEntry);
4845 else
4847 This->entries[index].deleted = 1;
4850 return S_OK;
4853 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4854 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4856 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4858 if (This->entries[index].stream_dirty)
4860 return StorageBaseImpl_StreamReadAt(This->scratch,
4861 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4863 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4865 /* This stream doesn't live in the parent, and we haven't allocated storage
4866 * for it yet */
4867 *bytesRead = 0;
4868 return S_OK;
4870 else
4872 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4873 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4877 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4878 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4880 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4881 HRESULT hr;
4883 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4884 if (FAILED(hr)) return hr;
4886 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4887 if (FAILED(hr)) return hr;
4889 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4890 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4892 if (SUCCEEDED(hr) && size != 0)
4893 This->entries[index].data.size.QuadPart = max(
4894 This->entries[index].data.size.QuadPart,
4895 offset.QuadPart + size);
4897 return hr;
4900 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4901 DirRef index, ULARGE_INTEGER newsize)
4903 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4904 HRESULT hr;
4906 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4907 if (FAILED(hr)) return hr;
4909 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4910 return S_OK;
4912 if (newsize.QuadPart == 0)
4914 /* Destroy any parent references or entries in the scratch file. */
4915 if (This->entries[index].stream_dirty)
4917 ULARGE_INTEGER zero;
4918 zero.QuadPart = 0;
4919 StorageBaseImpl_StreamSetSize(This->scratch,
4920 This->entries[index].stream_entry, zero);
4921 StorageBaseImpl_DestroyDirEntry(This->scratch,
4922 This->entries[index].stream_entry);
4923 This->entries[index].stream_dirty = 0;
4925 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4927 DirRef delete_ref;
4928 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4930 if (delete_ref != DIRENTRY_NULL)
4931 This->entries[delete_ref].deleted = 1;
4933 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4936 else
4938 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4939 if (FAILED(hr)) return hr;
4941 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4942 This->entries[index].stream_entry, newsize);
4945 if (SUCCEEDED(hr))
4946 This->entries[index].data.size = newsize;
4948 return hr;
4951 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
4952 DirRef dst, DirRef src)
4954 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4955 HRESULT hr;
4956 TransactedDirEntry *dst_entry, *src_entry;
4958 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
4959 if (FAILED(hr)) return hr;
4961 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
4962 if (FAILED(hr)) return hr;
4964 dst_entry = &This->entries[dst];
4965 src_entry = &This->entries[src];
4967 dst_entry->stream_dirty = src_entry->stream_dirty;
4968 dst_entry->stream_entry = src_entry->stream_entry;
4969 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
4970 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
4971 dst_entry->data.size = src_entry->data.size;
4973 return S_OK;
4976 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4978 StorageBaseImpl_QueryInterface,
4979 StorageBaseImpl_AddRef,
4980 StorageBaseImpl_Release,
4981 StorageBaseImpl_CreateStream,
4982 StorageBaseImpl_OpenStream,
4983 StorageBaseImpl_CreateStorage,
4984 StorageBaseImpl_OpenStorage,
4985 StorageBaseImpl_CopyTo,
4986 StorageBaseImpl_MoveElementTo,
4987 TransactedSnapshotImpl_Commit,
4988 TransactedSnapshotImpl_Revert,
4989 StorageBaseImpl_EnumElements,
4990 StorageBaseImpl_DestroyElement,
4991 StorageBaseImpl_RenameElement,
4992 StorageBaseImpl_SetElementTimes,
4993 StorageBaseImpl_SetClass,
4994 StorageBaseImpl_SetStateBits,
4995 StorageBaseImpl_Stat
4998 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5000 TransactedSnapshotImpl_Destroy,
5001 TransactedSnapshotImpl_Invalidate,
5002 TransactedSnapshotImpl_Flush,
5003 TransactedSnapshotImpl_GetFilename,
5004 TransactedSnapshotImpl_CreateDirEntry,
5005 TransactedSnapshotImpl_WriteDirEntry,
5006 TransactedSnapshotImpl_ReadDirEntry,
5007 TransactedSnapshotImpl_DestroyDirEntry,
5008 TransactedSnapshotImpl_StreamReadAt,
5009 TransactedSnapshotImpl_StreamWriteAt,
5010 TransactedSnapshotImpl_StreamSetSize,
5011 TransactedSnapshotImpl_StreamLink
5014 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5015 TransactedSnapshotImpl** result)
5017 HRESULT hr;
5019 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5020 if (*result)
5022 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5024 /* This is OK because the property set storage functions use the IStorage functions. */
5025 (*result)->base.pssVtbl = parentStorage->pssVtbl;
5027 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5029 list_init(&(*result)->base.strmHead);
5031 list_init(&(*result)->base.storageHead);
5033 (*result)->base.ref = 1;
5035 (*result)->base.openFlags = parentStorage->openFlags;
5037 /* Create a new temporary storage to act as the scratch file. */
5038 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
5039 0, (IStorage**)&(*result)->scratch);
5041 if (SUCCEEDED(hr))
5043 ULONG num_entries = 20;
5045 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5047 (*result)->entries_size = num_entries;
5049 (*result)->firstFreeEntry = 0;
5051 if ((*result)->entries)
5053 /* parentStorage already has 1 reference, which we take over here. */
5054 (*result)->transactedParent = parentStorage;
5056 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5058 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5060 else
5062 IStorage_Release((IStorage*)(*result)->scratch);
5064 hr = E_OUTOFMEMORY;
5068 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5070 return hr;
5072 else
5073 return E_OUTOFMEMORY;
5076 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5077 StorageBaseImpl** result)
5079 static int fixme=0;
5081 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5083 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5086 return TransactedSnapshotImpl_Construct(parentStorage,
5087 (TransactedSnapshotImpl**)result);
5090 static HRESULT Storage_Construct(
5091 HANDLE hFile,
5092 LPCOLESTR pwcsName,
5093 ILockBytes* pLkbyt,
5094 DWORD openFlags,
5095 BOOL fileBased,
5096 BOOL create,
5097 ULONG sector_size,
5098 StorageBaseImpl** result)
5100 StorageImpl *newStorage;
5101 StorageBaseImpl *newTransactedStorage;
5102 HRESULT hr;
5104 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5105 if (FAILED(hr)) goto end;
5107 if (openFlags & STGM_TRANSACTED)
5109 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5110 if (FAILED(hr))
5111 IStorage_Release((IStorage*)newStorage);
5112 else
5113 *result = newTransactedStorage;
5115 else
5116 *result = &newStorage->base;
5118 end:
5119 return hr;
5122 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5124 StorageInternalImpl* This = (StorageInternalImpl*) base;
5126 if (!This->base.reverted)
5128 TRACE("Storage invalidated (stg=%p)\n", This);
5130 This->base.reverted = 1;
5132 This->parentStorage = NULL;
5134 StorageBaseImpl_DeleteAll(&This->base);
5136 list_remove(&This->ParentListEntry);
5140 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5142 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5144 StorageInternalImpl_Invalidate(&This->base);
5146 HeapFree(GetProcessHeap(), 0, This);
5149 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5151 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5153 return StorageBaseImpl_Flush(This->parentStorage);
5156 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5158 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5160 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5163 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5164 const DirEntry *newData, DirRef *index)
5166 StorageInternalImpl* This = (StorageInternalImpl*) base;
5168 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5169 newData, index);
5172 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5173 DirRef index, const DirEntry *data)
5175 StorageInternalImpl* This = (StorageInternalImpl*) base;
5177 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5178 index, data);
5181 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5182 DirRef index, DirEntry *data)
5184 StorageInternalImpl* This = (StorageInternalImpl*) base;
5186 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5187 index, data);
5190 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5191 DirRef index)
5193 StorageInternalImpl* This = (StorageInternalImpl*) base;
5195 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5196 index);
5199 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5200 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5202 StorageInternalImpl* This = (StorageInternalImpl*) base;
5204 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5205 index, offset, size, buffer, bytesRead);
5208 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5209 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5211 StorageInternalImpl* This = (StorageInternalImpl*) base;
5213 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5214 index, offset, size, buffer, bytesWritten);
5217 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5218 DirRef index, ULARGE_INTEGER newsize)
5220 StorageInternalImpl* This = (StorageInternalImpl*) base;
5222 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5223 index, newsize);
5226 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5227 DirRef dst, DirRef src)
5229 StorageInternalImpl* This = (StorageInternalImpl*) base;
5231 return StorageBaseImpl_StreamLink(This->parentStorage,
5232 dst, src);
5235 /******************************************************************************
5237 ** Storage32InternalImpl_Commit
5240 static HRESULT WINAPI StorageInternalImpl_Commit(
5241 IStorage* iface,
5242 DWORD grfCommitFlags) /* [in] */
5244 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5245 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5246 return StorageBaseImpl_Flush(base);
5249 /******************************************************************************
5251 ** Storage32InternalImpl_Revert
5254 static HRESULT WINAPI StorageInternalImpl_Revert(
5255 IStorage* iface)
5257 FIXME("(%p): stub\n", iface);
5258 return S_OK;
5261 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5263 IStorage_Release((IStorage*)This->parentStorage);
5264 HeapFree(GetProcessHeap(), 0, This);
5267 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5268 IEnumSTATSTG* iface,
5269 REFIID riid,
5270 void** ppvObject)
5272 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5274 if (ppvObject==0)
5275 return E_INVALIDARG;
5277 *ppvObject = 0;
5279 if (IsEqualGUID(&IID_IUnknown, riid) ||
5280 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5282 *ppvObject = This;
5283 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
5284 return S_OK;
5287 return E_NOINTERFACE;
5290 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5291 IEnumSTATSTG* iface)
5293 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5294 return InterlockedIncrement(&This->ref);
5297 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5298 IEnumSTATSTG* iface)
5300 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5302 ULONG newRef;
5304 newRef = InterlockedDecrement(&This->ref);
5306 if (newRef==0)
5308 IEnumSTATSTGImpl_Destroy(This);
5311 return newRef;
5314 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5315 IEnumSTATSTGImpl* This,
5316 DirRef *ref)
5318 DirRef result = DIRENTRY_NULL;
5319 DirRef searchNode;
5320 DirEntry entry;
5321 HRESULT hr;
5322 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5324 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5325 This->parentStorage->storageDirEntry, &entry);
5326 searchNode = entry.dirRootEntry;
5328 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5330 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5332 if (SUCCEEDED(hr))
5334 LONG diff = entryNameCmp( entry.name, This->name);
5336 if (diff <= 0)
5338 searchNode = entry.rightChild;
5340 else
5342 result = searchNode;
5343 memcpy(result_name, entry.name, sizeof(result_name));
5344 searchNode = entry.leftChild;
5349 if (SUCCEEDED(hr))
5351 *ref = result;
5352 if (result != DIRENTRY_NULL)
5353 memcpy(This->name, result_name, sizeof(result_name));
5356 return hr;
5359 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5360 IEnumSTATSTG* iface,
5361 ULONG celt,
5362 STATSTG* rgelt,
5363 ULONG* pceltFetched)
5365 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5367 DirEntry currentEntry;
5368 STATSTG* currentReturnStruct = rgelt;
5369 ULONG objectFetched = 0;
5370 DirRef currentSearchNode;
5371 HRESULT hr=S_OK;
5373 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5374 return E_INVALIDARG;
5376 if (This->parentStorage->reverted)
5377 return STG_E_REVERTED;
5380 * To avoid the special case, get another pointer to a ULONG value if
5381 * the caller didn't supply one.
5383 if (pceltFetched==0)
5384 pceltFetched = &objectFetched;
5387 * Start the iteration, we will iterate until we hit the end of the
5388 * linked list or until we hit the number of items to iterate through
5390 *pceltFetched = 0;
5392 while ( *pceltFetched < celt )
5394 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5396 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5397 break;
5400 * Read the entry from the storage.
5402 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5403 currentSearchNode,
5404 &currentEntry);
5407 * Copy the information to the return buffer.
5409 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5410 currentReturnStruct,
5411 &currentEntry,
5412 STATFLAG_DEFAULT);
5415 * Step to the next item in the iteration
5417 (*pceltFetched)++;
5418 currentReturnStruct++;
5421 if (SUCCEEDED(hr) && *pceltFetched != celt)
5422 hr = S_FALSE;
5424 return hr;
5428 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5429 IEnumSTATSTG* iface,
5430 ULONG celt)
5432 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5434 ULONG objectFetched = 0;
5435 DirRef currentSearchNode;
5436 HRESULT hr=S_OK;
5438 if (This->parentStorage->reverted)
5439 return STG_E_REVERTED;
5441 while ( (objectFetched < celt) )
5443 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5445 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5446 break;
5448 objectFetched++;
5451 if (SUCCEEDED(hr) && objectFetched != celt)
5452 return S_FALSE;
5454 return hr;
5457 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5458 IEnumSTATSTG* iface)
5460 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5462 if (This->parentStorage->reverted)
5463 return STG_E_REVERTED;
5465 This->name[0] = 0;
5467 return S_OK;
5470 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5471 IEnumSTATSTG* iface,
5472 IEnumSTATSTG** ppenum)
5474 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5476 IEnumSTATSTGImpl* newClone;
5478 if (This->parentStorage->reverted)
5479 return STG_E_REVERTED;
5482 * Perform a sanity check on the parameters.
5484 if (ppenum==0)
5485 return E_INVALIDARG;
5487 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5488 This->storageDirEntry);
5492 * The new clone enumeration must point to the same current node as
5493 * the ole one.
5495 memcpy(newClone->name, This->name, sizeof(newClone->name));
5497 *ppenum = (IEnumSTATSTG*)newClone;
5500 * Don't forget to nail down a reference to the clone before
5501 * returning it.
5503 IEnumSTATSTGImpl_AddRef(*ppenum);
5505 return S_OK;
5509 * Virtual function table for the IEnumSTATSTGImpl class.
5511 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5513 IEnumSTATSTGImpl_QueryInterface,
5514 IEnumSTATSTGImpl_AddRef,
5515 IEnumSTATSTGImpl_Release,
5516 IEnumSTATSTGImpl_Next,
5517 IEnumSTATSTGImpl_Skip,
5518 IEnumSTATSTGImpl_Reset,
5519 IEnumSTATSTGImpl_Clone
5522 /******************************************************************************
5523 ** IEnumSTATSTGImpl implementation
5526 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5527 StorageBaseImpl* parentStorage,
5528 DirRef storageDirEntry)
5530 IEnumSTATSTGImpl* newEnumeration;
5532 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5534 if (newEnumeration!=0)
5537 * Set-up the virtual function table and reference count.
5539 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5540 newEnumeration->ref = 0;
5543 * We want to nail-down the reference to the storage in case the
5544 * enumeration out-lives the storage in the client application.
5546 newEnumeration->parentStorage = parentStorage;
5547 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5549 newEnumeration->storageDirEntry = storageDirEntry;
5552 * Make sure the current node of the iterator is the first one.
5554 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
5557 return newEnumeration;
5561 * Virtual function table for the Storage32InternalImpl class.
5563 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5565 StorageBaseImpl_QueryInterface,
5566 StorageBaseImpl_AddRef,
5567 StorageBaseImpl_Release,
5568 StorageBaseImpl_CreateStream,
5569 StorageBaseImpl_OpenStream,
5570 StorageBaseImpl_CreateStorage,
5571 StorageBaseImpl_OpenStorage,
5572 StorageBaseImpl_CopyTo,
5573 StorageBaseImpl_MoveElementTo,
5574 StorageInternalImpl_Commit,
5575 StorageInternalImpl_Revert,
5576 StorageBaseImpl_EnumElements,
5577 StorageBaseImpl_DestroyElement,
5578 StorageBaseImpl_RenameElement,
5579 StorageBaseImpl_SetElementTimes,
5580 StorageBaseImpl_SetClass,
5581 StorageBaseImpl_SetStateBits,
5582 StorageBaseImpl_Stat
5585 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5587 StorageInternalImpl_Destroy,
5588 StorageInternalImpl_Invalidate,
5589 StorageInternalImpl_Flush,
5590 StorageInternalImpl_GetFilename,
5591 StorageInternalImpl_CreateDirEntry,
5592 StorageInternalImpl_WriteDirEntry,
5593 StorageInternalImpl_ReadDirEntry,
5594 StorageInternalImpl_DestroyDirEntry,
5595 StorageInternalImpl_StreamReadAt,
5596 StorageInternalImpl_StreamWriteAt,
5597 StorageInternalImpl_StreamSetSize,
5598 StorageInternalImpl_StreamLink
5601 /******************************************************************************
5602 ** Storage32InternalImpl implementation
5605 static StorageInternalImpl* StorageInternalImpl_Construct(
5606 StorageBaseImpl* parentStorage,
5607 DWORD openFlags,
5608 DirRef storageDirEntry)
5610 StorageInternalImpl* newStorage;
5612 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5614 if (newStorage!=0)
5616 list_init(&newStorage->base.strmHead);
5618 list_init(&newStorage->base.storageHead);
5621 * Initialize the virtual function table.
5623 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5624 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5625 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5626 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5628 newStorage->base.reverted = 0;
5630 newStorage->base.ref = 1;
5632 newStorage->parentStorage = parentStorage;
5635 * Keep a reference to the directory entry of this storage
5637 newStorage->base.storageDirEntry = storageDirEntry;
5639 newStorage->base.create = 0;
5641 return newStorage;
5644 return 0;
5647 /******************************************************************************
5648 ** StorageUtl implementation
5651 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5653 WORD tmp;
5655 memcpy(&tmp, buffer+offset, sizeof(WORD));
5656 *value = lendian16toh(tmp);
5659 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5661 value = htole16(value);
5662 memcpy(buffer+offset, &value, sizeof(WORD));
5665 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5667 DWORD tmp;
5669 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5670 *value = lendian32toh(tmp);
5673 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5675 value = htole32(value);
5676 memcpy(buffer+offset, &value, sizeof(DWORD));
5679 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5680 ULARGE_INTEGER* value)
5682 #ifdef WORDS_BIGENDIAN
5683 ULARGE_INTEGER tmp;
5685 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5686 value->u.LowPart = htole32(tmp.u.HighPart);
5687 value->u.HighPart = htole32(tmp.u.LowPart);
5688 #else
5689 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5690 #endif
5693 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5694 const ULARGE_INTEGER *value)
5696 #ifdef WORDS_BIGENDIAN
5697 ULARGE_INTEGER tmp;
5699 tmp.u.LowPart = htole32(value->u.HighPart);
5700 tmp.u.HighPart = htole32(value->u.LowPart);
5701 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5702 #else
5703 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5704 #endif
5707 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5709 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5710 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5711 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5713 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5716 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5718 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5719 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5720 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5722 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5725 void StorageUtl_CopyDirEntryToSTATSTG(
5726 StorageBaseImpl* storage,
5727 STATSTG* destination,
5728 const DirEntry* source,
5729 int statFlags)
5732 * The copy of the string occurs only when the flag is not set
5734 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5736 /* Use the filename for the root storage. */
5737 destination->pwcsName = 0;
5738 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5740 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5741 (source->name[0] == 0) )
5743 destination->pwcsName = 0;
5745 else
5747 destination->pwcsName =
5748 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5750 strcpyW(destination->pwcsName, source->name);
5753 switch (source->stgType)
5755 case STGTY_STORAGE:
5756 case STGTY_ROOT:
5757 destination->type = STGTY_STORAGE;
5758 break;
5759 case STGTY_STREAM:
5760 destination->type = STGTY_STREAM;
5761 break;
5762 default:
5763 destination->type = STGTY_STREAM;
5764 break;
5767 destination->cbSize = source->size;
5769 currentReturnStruct->mtime = {0}; TODO
5770 currentReturnStruct->ctime = {0};
5771 currentReturnStruct->atime = {0};
5773 destination->grfMode = 0;
5774 destination->grfLocksSupported = 0;
5775 destination->clsid = source->clsid;
5776 destination->grfStateBits = 0;
5777 destination->reserved = 0;
5780 /******************************************************************************
5781 ** BlockChainStream implementation
5784 /* Read and save the index of all blocks in this stream. */
5785 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5787 ULONG next_sector, next_offset;
5788 HRESULT hr;
5789 struct BlockChainRun *last_run;
5791 if (This->indexCacheLen == 0)
5793 last_run = NULL;
5794 next_offset = 0;
5795 next_sector = BlockChainStream_GetHeadOfChain(This);
5797 else
5799 last_run = &This->indexCache[This->indexCacheLen-1];
5800 next_offset = last_run->lastOffset+1;
5801 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5802 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5803 &next_sector);
5804 if (FAILED(hr)) return hr;
5807 while (next_sector != BLOCK_END_OF_CHAIN)
5809 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5811 /* Add the current block to the cache. */
5812 if (This->indexCacheSize == 0)
5814 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5815 if (!This->indexCache) return E_OUTOFMEMORY;
5816 This->indexCacheSize = 16;
5818 else if (This->indexCacheSize == This->indexCacheLen)
5820 struct BlockChainRun *new_cache;
5821 ULONG new_size;
5823 new_size = This->indexCacheSize * 2;
5824 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5825 if (!new_cache) return E_OUTOFMEMORY;
5826 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5828 HeapFree(GetProcessHeap(), 0, This->indexCache);
5829 This->indexCache = new_cache;
5830 This->indexCacheSize = new_size;
5833 This->indexCacheLen++;
5834 last_run = &This->indexCache[This->indexCacheLen-1];
5835 last_run->firstSector = next_sector;
5836 last_run->firstOffset = next_offset;
5839 last_run->lastOffset = next_offset;
5841 /* Find the next block. */
5842 next_offset++;
5843 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5844 if (FAILED(hr)) return hr;
5847 if (This->indexCacheLen)
5849 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5850 This->numBlocks = last_run->lastOffset+1;
5852 else
5854 This->tailIndex = BLOCK_END_OF_CHAIN;
5855 This->numBlocks = 0;
5858 return S_OK;
5861 /* Locate the nth block in this stream. */
5862 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5864 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5865 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5867 if (offset >= This->numBlocks)
5868 return BLOCK_END_OF_CHAIN;
5870 while (min_run < max_run)
5872 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5873 if (offset < This->indexCache[run_to_check].firstOffset)
5875 max_offset = This->indexCache[run_to_check].firstOffset-1;
5876 max_run = run_to_check-1;
5878 else if (offset > This->indexCache[run_to_check].lastOffset)
5880 min_offset = This->indexCache[run_to_check].lastOffset+1;
5881 min_run = run_to_check+1;
5883 else
5884 /* Block is in this run. */
5885 min_run = max_run = run_to_check;
5888 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5891 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5892 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5894 BlockChainBlock *result=NULL;
5895 int i;
5897 for (i=0; i<2; i++)
5898 if (This->cachedBlocks[i].index == index)
5900 *sector = This->cachedBlocks[i].sector;
5901 *block = &This->cachedBlocks[i];
5902 return S_OK;
5905 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5906 if (*sector == BLOCK_END_OF_CHAIN)
5907 return STG_E_DOCFILECORRUPT;
5909 if (create)
5911 if (This->cachedBlocks[0].index == 0xffffffff)
5912 result = &This->cachedBlocks[0];
5913 else if (This->cachedBlocks[1].index == 0xffffffff)
5914 result = &This->cachedBlocks[1];
5915 else
5917 result = &This->cachedBlocks[This->blockToEvict++];
5918 if (This->blockToEvict == 2)
5919 This->blockToEvict = 0;
5922 if (result->dirty)
5924 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5925 return STG_E_WRITEFAULT;
5926 result->dirty = 0;
5929 result->read = 0;
5930 result->index = index;
5931 result->sector = *sector;
5934 *block = result;
5935 return S_OK;
5938 BlockChainStream* BlockChainStream_Construct(
5939 StorageImpl* parentStorage,
5940 ULONG* headOfStreamPlaceHolder,
5941 DirRef dirEntry)
5943 BlockChainStream* newStream;
5945 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5947 newStream->parentStorage = parentStorage;
5948 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5949 newStream->ownerDirEntry = dirEntry;
5950 newStream->indexCache = NULL;
5951 newStream->indexCacheLen = 0;
5952 newStream->indexCacheSize = 0;
5953 newStream->cachedBlocks[0].index = 0xffffffff;
5954 newStream->cachedBlocks[0].dirty = 0;
5955 newStream->cachedBlocks[1].index = 0xffffffff;
5956 newStream->cachedBlocks[1].dirty = 0;
5957 newStream->blockToEvict = 0;
5959 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5961 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5962 HeapFree(GetProcessHeap(), 0, newStream);
5963 return NULL;
5966 return newStream;
5969 HRESULT BlockChainStream_Flush(BlockChainStream* This)
5971 int i;
5972 if (!This) return S_OK;
5973 for (i=0; i<2; i++)
5975 if (This->cachedBlocks[i].dirty)
5977 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
5978 This->cachedBlocks[i].dirty = 0;
5979 else
5980 return STG_E_WRITEFAULT;
5983 return S_OK;
5986 void BlockChainStream_Destroy(BlockChainStream* This)
5988 if (This)
5990 BlockChainStream_Flush(This);
5991 HeapFree(GetProcessHeap(), 0, This->indexCache);
5993 HeapFree(GetProcessHeap(), 0, This);
5996 /******************************************************************************
5997 * BlockChainStream_GetHeadOfChain
5999 * Returns the head of this stream chain.
6000 * Some special chains don't have directory entries, their heads are kept in
6001 * This->headOfStreamPlaceHolder.
6004 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6006 DirEntry chainEntry;
6007 HRESULT hr;
6009 if (This->headOfStreamPlaceHolder != 0)
6010 return *(This->headOfStreamPlaceHolder);
6012 if (This->ownerDirEntry != DIRENTRY_NULL)
6014 hr = StorageImpl_ReadDirEntry(
6015 This->parentStorage,
6016 This->ownerDirEntry,
6017 &chainEntry);
6019 if (SUCCEEDED(hr))
6021 return chainEntry.startingBlock;
6025 return BLOCK_END_OF_CHAIN;
6028 /******************************************************************************
6029 * BlockChainStream_GetCount
6031 * Returns the number of blocks that comprises this chain.
6032 * This is not the size of the stream as the last block may not be full!
6034 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6036 return This->numBlocks;
6039 /******************************************************************************
6040 * BlockChainStream_ReadAt
6042 * Reads a specified number of bytes from this chain at the specified offset.
6043 * bytesRead may be NULL.
6044 * Failure will be returned if the specified number of bytes has not been read.
6046 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6047 ULARGE_INTEGER offset,
6048 ULONG size,
6049 void* buffer,
6050 ULONG* bytesRead)
6052 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6053 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6054 ULONG bytesToReadInBuffer;
6055 ULONG blockIndex;
6056 BYTE* bufferWalker;
6057 ULARGE_INTEGER stream_size;
6058 HRESULT hr;
6059 BlockChainBlock *cachedBlock;
6061 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6064 * Find the first block in the stream that contains part of the buffer.
6066 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6068 *bytesRead = 0;
6070 stream_size = BlockChainStream_GetSize(This);
6071 if (stream_size.QuadPart > offset.QuadPart)
6072 size = min(stream_size.QuadPart - offset.QuadPart, size);
6073 else
6074 return S_OK;
6077 * Start reading the buffer.
6079 bufferWalker = buffer;
6081 while (size > 0)
6083 ULARGE_INTEGER ulOffset;
6084 DWORD bytesReadAt;
6087 * Calculate how many bytes we can copy from this big block.
6089 bytesToReadInBuffer =
6090 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6092 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6094 if (FAILED(hr))
6095 return hr;
6097 if (!cachedBlock)
6099 /* Not in cache, and we're going to read past the end of the block. */
6100 ulOffset.u.HighPart = 0;
6101 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6102 offsetInBlock;
6104 StorageImpl_ReadAt(This->parentStorage,
6105 ulOffset,
6106 bufferWalker,
6107 bytesToReadInBuffer,
6108 &bytesReadAt);
6110 else
6112 if (!cachedBlock->read)
6114 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6115 return STG_E_READFAULT;
6117 cachedBlock->read = 1;
6120 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6121 bytesReadAt = bytesToReadInBuffer;
6124 blockNoInSequence++;
6125 bufferWalker += bytesReadAt;
6126 size -= bytesReadAt;
6127 *bytesRead += bytesReadAt;
6128 offsetInBlock = 0; /* There is no offset on the next block */
6130 if (bytesToReadInBuffer != bytesReadAt)
6131 break;
6134 return S_OK;
6137 /******************************************************************************
6138 * BlockChainStream_WriteAt
6140 * Writes the specified number of bytes to this chain at the specified offset.
6141 * Will fail if not all specified number of bytes have been written.
6143 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6144 ULARGE_INTEGER offset,
6145 ULONG size,
6146 const void* buffer,
6147 ULONG* bytesWritten)
6149 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6150 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6151 ULONG bytesToWrite;
6152 ULONG blockIndex;
6153 const BYTE* bufferWalker;
6154 HRESULT hr;
6155 BlockChainBlock *cachedBlock;
6157 *bytesWritten = 0;
6158 bufferWalker = buffer;
6160 while (size > 0)
6162 ULARGE_INTEGER ulOffset;
6163 DWORD bytesWrittenAt;
6166 * Calculate how many bytes we can copy to this big block.
6168 bytesToWrite =
6169 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6171 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6173 /* BlockChainStream_SetSize should have already been called to ensure we have
6174 * enough blocks in the chain to write into */
6175 if (FAILED(hr))
6177 ERR("not enough blocks in chain to write data\n");
6178 return hr;
6181 if (!cachedBlock)
6183 /* Not in cache, and we're going to write past the end of the block. */
6184 ulOffset.u.HighPart = 0;
6185 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6186 offsetInBlock;
6188 StorageImpl_WriteAt(This->parentStorage,
6189 ulOffset,
6190 bufferWalker,
6191 bytesToWrite,
6192 &bytesWrittenAt);
6194 else
6196 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6198 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6199 return STG_E_READFAULT;
6202 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6203 bytesWrittenAt = bytesToWrite;
6204 cachedBlock->read = 1;
6205 cachedBlock->dirty = 1;
6208 blockNoInSequence++;
6209 bufferWalker += bytesWrittenAt;
6210 size -= bytesWrittenAt;
6211 *bytesWritten += bytesWrittenAt;
6212 offsetInBlock = 0; /* There is no offset on the next block */
6214 if (bytesWrittenAt != bytesToWrite)
6215 break;
6218 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6221 /******************************************************************************
6222 * BlockChainStream_Shrink
6224 * Shrinks this chain in the big block depot.
6226 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6227 ULARGE_INTEGER newSize)
6229 ULONG blockIndex;
6230 ULONG numBlocks;
6231 int i;
6234 * Figure out how many blocks are needed to contain the new size
6236 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6238 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6239 numBlocks++;
6241 if (numBlocks)
6244 * Go to the new end of chain
6246 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6248 /* Mark the new end of chain */
6249 StorageImpl_SetNextBlockInChain(
6250 This->parentStorage,
6251 blockIndex,
6252 BLOCK_END_OF_CHAIN);
6254 This->tailIndex = blockIndex;
6256 else
6258 if (This->headOfStreamPlaceHolder != 0)
6260 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6262 else
6264 DirEntry chainEntry;
6265 assert(This->ownerDirEntry != DIRENTRY_NULL);
6267 StorageImpl_ReadDirEntry(
6268 This->parentStorage,
6269 This->ownerDirEntry,
6270 &chainEntry);
6272 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6274 StorageImpl_WriteDirEntry(
6275 This->parentStorage,
6276 This->ownerDirEntry,
6277 &chainEntry);
6280 This->tailIndex = BLOCK_END_OF_CHAIN;
6283 This->numBlocks = numBlocks;
6286 * Mark the extra blocks as free
6288 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6290 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6291 StorageImpl_FreeBigBlock(This->parentStorage,
6292 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6293 if (last_run->lastOffset == last_run->firstOffset)
6294 This->indexCacheLen--;
6295 else
6296 last_run->lastOffset--;
6300 * Reset the last accessed block cache.
6302 for (i=0; i<2; i++)
6304 if (This->cachedBlocks[i].index >= numBlocks)
6306 This->cachedBlocks[i].index = 0xffffffff;
6307 This->cachedBlocks[i].dirty = 0;
6311 return TRUE;
6314 /******************************************************************************
6315 * BlockChainStream_Enlarge
6317 * Grows this chain in the big block depot.
6319 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6320 ULARGE_INTEGER newSize)
6322 ULONG blockIndex, currentBlock;
6323 ULONG newNumBlocks;
6324 ULONG oldNumBlocks = 0;
6326 blockIndex = BlockChainStream_GetHeadOfChain(This);
6329 * Empty chain. Create the head.
6331 if (blockIndex == BLOCK_END_OF_CHAIN)
6333 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6334 StorageImpl_SetNextBlockInChain(This->parentStorage,
6335 blockIndex,
6336 BLOCK_END_OF_CHAIN);
6338 if (This->headOfStreamPlaceHolder != 0)
6340 *(This->headOfStreamPlaceHolder) = blockIndex;
6342 else
6344 DirEntry chainEntry;
6345 assert(This->ownerDirEntry != DIRENTRY_NULL);
6347 StorageImpl_ReadDirEntry(
6348 This->parentStorage,
6349 This->ownerDirEntry,
6350 &chainEntry);
6352 chainEntry.startingBlock = blockIndex;
6354 StorageImpl_WriteDirEntry(
6355 This->parentStorage,
6356 This->ownerDirEntry,
6357 &chainEntry);
6360 This->tailIndex = blockIndex;
6361 This->numBlocks = 1;
6365 * Figure out how many blocks are needed to contain this stream
6367 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6369 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6370 newNumBlocks++;
6373 * Go to the current end of chain
6375 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6377 currentBlock = blockIndex;
6379 while (blockIndex != BLOCK_END_OF_CHAIN)
6381 This->numBlocks++;
6382 currentBlock = blockIndex;
6384 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6385 &blockIndex)))
6386 return FALSE;
6389 This->tailIndex = currentBlock;
6392 currentBlock = This->tailIndex;
6393 oldNumBlocks = This->numBlocks;
6396 * Add new blocks to the chain
6398 if (oldNumBlocks < newNumBlocks)
6400 while (oldNumBlocks < newNumBlocks)
6402 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6404 StorageImpl_SetNextBlockInChain(
6405 This->parentStorage,
6406 currentBlock,
6407 blockIndex);
6409 StorageImpl_SetNextBlockInChain(
6410 This->parentStorage,
6411 blockIndex,
6412 BLOCK_END_OF_CHAIN);
6414 currentBlock = blockIndex;
6415 oldNumBlocks++;
6418 This->tailIndex = blockIndex;
6419 This->numBlocks = newNumBlocks;
6422 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6423 return FALSE;
6425 return TRUE;
6428 /******************************************************************************
6429 * BlockChainStream_SetSize
6431 * Sets the size of this stream. The big block depot will be updated.
6432 * The file will grow if we grow the chain.
6434 * TODO: Free the actual blocks in the file when we shrink the chain.
6435 * Currently, the blocks are still in the file. So the file size
6436 * doesn't shrink even if we shrink streams.
6438 BOOL BlockChainStream_SetSize(
6439 BlockChainStream* This,
6440 ULARGE_INTEGER newSize)
6442 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6444 if (newSize.u.LowPart == size.u.LowPart)
6445 return TRUE;
6447 if (newSize.u.LowPart < size.u.LowPart)
6449 BlockChainStream_Shrink(This, newSize);
6451 else
6453 BlockChainStream_Enlarge(This, newSize);
6456 return TRUE;
6459 /******************************************************************************
6460 * BlockChainStream_GetSize
6462 * Returns the size of this chain.
6463 * Will return the block count if this chain doesn't have a directory entry.
6465 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6467 DirEntry chainEntry;
6469 if(This->headOfStreamPlaceHolder == NULL)
6472 * This chain has a directory entry so use the size value from there.
6474 StorageImpl_ReadDirEntry(
6475 This->parentStorage,
6476 This->ownerDirEntry,
6477 &chainEntry);
6479 return chainEntry.size;
6481 else
6484 * this chain is a chain that does not have a directory entry, figure out the
6485 * size by making the product number of used blocks times the
6486 * size of them
6488 ULARGE_INTEGER result;
6489 result.u.HighPart = 0;
6491 result.u.LowPart =
6492 BlockChainStream_GetCount(This) *
6493 This->parentStorage->bigBlockSize;
6495 return result;
6499 /******************************************************************************
6500 ** SmallBlockChainStream implementation
6503 SmallBlockChainStream* SmallBlockChainStream_Construct(
6504 StorageImpl* parentStorage,
6505 ULONG* headOfStreamPlaceHolder,
6506 DirRef dirEntry)
6508 SmallBlockChainStream* newStream;
6510 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6512 newStream->parentStorage = parentStorage;
6513 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6514 newStream->ownerDirEntry = dirEntry;
6516 return newStream;
6519 void SmallBlockChainStream_Destroy(
6520 SmallBlockChainStream* This)
6522 HeapFree(GetProcessHeap(), 0, This);
6525 /******************************************************************************
6526 * SmallBlockChainStream_GetHeadOfChain
6528 * Returns the head of this chain of small blocks.
6530 static ULONG SmallBlockChainStream_GetHeadOfChain(
6531 SmallBlockChainStream* This)
6533 DirEntry chainEntry;
6534 HRESULT hr;
6536 if (This->headOfStreamPlaceHolder != NULL)
6537 return *(This->headOfStreamPlaceHolder);
6539 if (This->ownerDirEntry)
6541 hr = StorageImpl_ReadDirEntry(
6542 This->parentStorage,
6543 This->ownerDirEntry,
6544 &chainEntry);
6546 if (SUCCEEDED(hr))
6548 return chainEntry.startingBlock;
6553 return BLOCK_END_OF_CHAIN;
6556 /******************************************************************************
6557 * SmallBlockChainStream_GetNextBlockInChain
6559 * Returns the index of the next small block in this chain.
6561 * Return Values:
6562 * - BLOCK_END_OF_CHAIN: end of this chain
6563 * - BLOCK_UNUSED: small block 'blockIndex' is free
6565 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6566 SmallBlockChainStream* This,
6567 ULONG blockIndex,
6568 ULONG* nextBlockInChain)
6570 ULARGE_INTEGER offsetOfBlockInDepot;
6571 DWORD buffer;
6572 ULONG bytesRead;
6573 HRESULT res;
6575 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6577 offsetOfBlockInDepot.u.HighPart = 0;
6578 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6581 * Read those bytes in the buffer from the small block file.
6583 res = BlockChainStream_ReadAt(
6584 This->parentStorage->smallBlockDepotChain,
6585 offsetOfBlockInDepot,
6586 sizeof(DWORD),
6587 &buffer,
6588 &bytesRead);
6590 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6591 res = STG_E_READFAULT;
6593 if (SUCCEEDED(res))
6595 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6596 return S_OK;
6599 return res;
6602 /******************************************************************************
6603 * SmallBlockChainStream_SetNextBlockInChain
6605 * Writes the index of the next block of the specified block in the small
6606 * block depot.
6607 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6608 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6610 static void SmallBlockChainStream_SetNextBlockInChain(
6611 SmallBlockChainStream* This,
6612 ULONG blockIndex,
6613 ULONG nextBlock)
6615 ULARGE_INTEGER offsetOfBlockInDepot;
6616 DWORD buffer;
6617 ULONG bytesWritten;
6619 offsetOfBlockInDepot.u.HighPart = 0;
6620 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6622 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6625 * Read those bytes in the buffer from the small block file.
6627 BlockChainStream_WriteAt(
6628 This->parentStorage->smallBlockDepotChain,
6629 offsetOfBlockInDepot,
6630 sizeof(DWORD),
6631 &buffer,
6632 &bytesWritten);
6635 /******************************************************************************
6636 * SmallBlockChainStream_FreeBlock
6638 * Flag small block 'blockIndex' as free in the small block depot.
6640 static void SmallBlockChainStream_FreeBlock(
6641 SmallBlockChainStream* This,
6642 ULONG blockIndex)
6644 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6647 /******************************************************************************
6648 * SmallBlockChainStream_GetNextFreeBlock
6650 * Returns the index of a free small block. The small block depot will be
6651 * enlarged if necessary. The small block chain will also be enlarged if
6652 * necessary.
6654 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6655 SmallBlockChainStream* This)
6657 ULARGE_INTEGER offsetOfBlockInDepot;
6658 DWORD buffer;
6659 ULONG bytesRead;
6660 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6661 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6662 HRESULT res = S_OK;
6663 ULONG smallBlocksPerBigBlock;
6664 DirEntry rootEntry;
6665 ULONG blocksRequired;
6666 ULARGE_INTEGER old_size, size_required;
6668 offsetOfBlockInDepot.u.HighPart = 0;
6671 * Scan the small block depot for a free block
6673 while (nextBlockIndex != BLOCK_UNUSED)
6675 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6677 res = BlockChainStream_ReadAt(
6678 This->parentStorage->smallBlockDepotChain,
6679 offsetOfBlockInDepot,
6680 sizeof(DWORD),
6681 &buffer,
6682 &bytesRead);
6685 * If we run out of space for the small block depot, enlarge it
6687 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6689 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6691 if (nextBlockIndex != BLOCK_UNUSED)
6692 blockIndex++;
6694 else
6696 ULONG count =
6697 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6699 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6700 ULARGE_INTEGER newSize, offset;
6701 ULONG bytesWritten;
6703 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6704 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6707 * Initialize all the small blocks to free
6709 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6710 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6711 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6712 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6714 StorageImpl_SaveFileHeader(This->parentStorage);
6718 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6720 smallBlocksPerBigBlock =
6721 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6724 * Verify if we have to allocate big blocks to contain small blocks
6726 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6728 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6730 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6732 if (size_required.QuadPart > old_size.QuadPart)
6734 BlockChainStream_SetSize(
6735 This->parentStorage->smallBlockRootChain,
6736 size_required);
6738 StorageImpl_ReadDirEntry(
6739 This->parentStorage,
6740 This->parentStorage->base.storageDirEntry,
6741 &rootEntry);
6743 rootEntry.size = size_required;
6745 StorageImpl_WriteDirEntry(
6746 This->parentStorage,
6747 This->parentStorage->base.storageDirEntry,
6748 &rootEntry);
6751 return blockIndex;
6754 /******************************************************************************
6755 * SmallBlockChainStream_ReadAt
6757 * Reads a specified number of bytes from this chain at the specified offset.
6758 * bytesRead may be NULL.
6759 * Failure will be returned if the specified number of bytes has not been read.
6761 HRESULT SmallBlockChainStream_ReadAt(
6762 SmallBlockChainStream* This,
6763 ULARGE_INTEGER offset,
6764 ULONG size,
6765 void* buffer,
6766 ULONG* bytesRead)
6768 HRESULT rc = S_OK;
6769 ULARGE_INTEGER offsetInBigBlockFile;
6770 ULONG blockNoInSequence =
6771 offset.u.LowPart / This->parentStorage->smallBlockSize;
6773 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6774 ULONG bytesToReadInBuffer;
6775 ULONG blockIndex;
6776 ULONG bytesReadFromBigBlockFile;
6777 BYTE* bufferWalker;
6778 ULARGE_INTEGER stream_size;
6781 * This should never happen on a small block file.
6783 assert(offset.u.HighPart==0);
6785 *bytesRead = 0;
6787 stream_size = SmallBlockChainStream_GetSize(This);
6788 if (stream_size.QuadPart > offset.QuadPart)
6789 size = min(stream_size.QuadPart - offset.QuadPart, size);
6790 else
6791 return S_OK;
6794 * Find the first block in the stream that contains part of the buffer.
6796 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6798 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6800 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6801 if(FAILED(rc))
6802 return rc;
6803 blockNoInSequence--;
6807 * Start reading the buffer.
6809 bufferWalker = buffer;
6811 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6814 * Calculate how many bytes we can copy from this small block.
6816 bytesToReadInBuffer =
6817 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6820 * Calculate the offset of the small block in the small block file.
6822 offsetInBigBlockFile.u.HighPart = 0;
6823 offsetInBigBlockFile.u.LowPart =
6824 blockIndex * This->parentStorage->smallBlockSize;
6826 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6829 * Read those bytes in the buffer from the small block file.
6830 * The small block has already been identified so it shouldn't fail
6831 * unless the file is corrupt.
6833 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6834 offsetInBigBlockFile,
6835 bytesToReadInBuffer,
6836 bufferWalker,
6837 &bytesReadFromBigBlockFile);
6839 if (FAILED(rc))
6840 return rc;
6842 if (!bytesReadFromBigBlockFile)
6843 return STG_E_DOCFILECORRUPT;
6846 * Step to the next big block.
6848 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6849 if(FAILED(rc))
6850 return STG_E_DOCFILECORRUPT;
6852 bufferWalker += bytesReadFromBigBlockFile;
6853 size -= bytesReadFromBigBlockFile;
6854 *bytesRead += bytesReadFromBigBlockFile;
6855 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6858 return S_OK;
6861 /******************************************************************************
6862 * SmallBlockChainStream_WriteAt
6864 * Writes the specified number of bytes to this chain at the specified offset.
6865 * Will fail if not all specified number of bytes have been written.
6867 HRESULT SmallBlockChainStream_WriteAt(
6868 SmallBlockChainStream* This,
6869 ULARGE_INTEGER offset,
6870 ULONG size,
6871 const void* buffer,
6872 ULONG* bytesWritten)
6874 ULARGE_INTEGER offsetInBigBlockFile;
6875 ULONG blockNoInSequence =
6876 offset.u.LowPart / This->parentStorage->smallBlockSize;
6878 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6879 ULONG bytesToWriteInBuffer;
6880 ULONG blockIndex;
6881 ULONG bytesWrittenToBigBlockFile;
6882 const BYTE* bufferWalker;
6883 HRESULT res;
6886 * This should never happen on a small block file.
6888 assert(offset.u.HighPart==0);
6891 * Find the first block in the stream that contains part of the buffer.
6893 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6895 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6897 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6898 return STG_E_DOCFILECORRUPT;
6899 blockNoInSequence--;
6903 * Start writing the buffer.
6905 *bytesWritten = 0;
6906 bufferWalker = buffer;
6907 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6910 * Calculate how many bytes we can copy to this small block.
6912 bytesToWriteInBuffer =
6913 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6916 * Calculate the offset of the small block in the small block file.
6918 offsetInBigBlockFile.u.HighPart = 0;
6919 offsetInBigBlockFile.u.LowPart =
6920 blockIndex * This->parentStorage->smallBlockSize;
6922 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6925 * Write those bytes in the buffer to the small block file.
6927 res = BlockChainStream_WriteAt(
6928 This->parentStorage->smallBlockRootChain,
6929 offsetInBigBlockFile,
6930 bytesToWriteInBuffer,
6931 bufferWalker,
6932 &bytesWrittenToBigBlockFile);
6933 if (FAILED(res))
6934 return res;
6937 * Step to the next big block.
6939 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6940 &blockIndex)))
6941 return FALSE;
6942 bufferWalker += bytesWrittenToBigBlockFile;
6943 size -= bytesWrittenToBigBlockFile;
6944 *bytesWritten += bytesWrittenToBigBlockFile;
6945 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6948 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6951 /******************************************************************************
6952 * SmallBlockChainStream_Shrink
6954 * Shrinks this chain in the small block depot.
6956 static BOOL SmallBlockChainStream_Shrink(
6957 SmallBlockChainStream* This,
6958 ULARGE_INTEGER newSize)
6960 ULONG blockIndex, extraBlock;
6961 ULONG numBlocks;
6962 ULONG count = 0;
6964 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6966 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6967 numBlocks++;
6969 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6972 * Go to the new end of chain
6974 while (count < numBlocks)
6976 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6977 &blockIndex)))
6978 return FALSE;
6979 count++;
6983 * If the count is 0, we have a special case, the head of the chain was
6984 * just freed.
6986 if (count == 0)
6988 DirEntry chainEntry;
6990 StorageImpl_ReadDirEntry(This->parentStorage,
6991 This->ownerDirEntry,
6992 &chainEntry);
6994 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6996 StorageImpl_WriteDirEntry(This->parentStorage,
6997 This->ownerDirEntry,
6998 &chainEntry);
7001 * We start freeing the chain at the head block.
7003 extraBlock = blockIndex;
7005 else
7007 /* Get the next block before marking the new end */
7008 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7009 &extraBlock)))
7010 return FALSE;
7012 /* Mark the new end of chain */
7013 SmallBlockChainStream_SetNextBlockInChain(
7014 This,
7015 blockIndex,
7016 BLOCK_END_OF_CHAIN);
7020 * Mark the extra blocks as free
7022 while (extraBlock != BLOCK_END_OF_CHAIN)
7024 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7025 &blockIndex)))
7026 return FALSE;
7027 SmallBlockChainStream_FreeBlock(This, extraBlock);
7028 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7029 extraBlock = blockIndex;
7032 return TRUE;
7035 /******************************************************************************
7036 * SmallBlockChainStream_Enlarge
7038 * Grows this chain in the small block depot.
7040 static BOOL SmallBlockChainStream_Enlarge(
7041 SmallBlockChainStream* This,
7042 ULARGE_INTEGER newSize)
7044 ULONG blockIndex, currentBlock;
7045 ULONG newNumBlocks;
7046 ULONG oldNumBlocks = 0;
7048 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7051 * Empty chain. Create the head.
7053 if (blockIndex == BLOCK_END_OF_CHAIN)
7055 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7056 SmallBlockChainStream_SetNextBlockInChain(
7057 This,
7058 blockIndex,
7059 BLOCK_END_OF_CHAIN);
7061 if (This->headOfStreamPlaceHolder != NULL)
7063 *(This->headOfStreamPlaceHolder) = blockIndex;
7065 else
7067 DirEntry chainEntry;
7069 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7070 &chainEntry);
7072 chainEntry.startingBlock = blockIndex;
7074 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7075 &chainEntry);
7079 currentBlock = blockIndex;
7082 * Figure out how many blocks are needed to contain this stream
7084 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7086 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7087 newNumBlocks++;
7090 * Go to the current end of chain
7092 while (blockIndex != BLOCK_END_OF_CHAIN)
7094 oldNumBlocks++;
7095 currentBlock = blockIndex;
7096 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7097 return FALSE;
7101 * Add new blocks to the chain
7103 while (oldNumBlocks < newNumBlocks)
7105 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7106 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7108 SmallBlockChainStream_SetNextBlockInChain(
7109 This,
7110 blockIndex,
7111 BLOCK_END_OF_CHAIN);
7113 currentBlock = blockIndex;
7114 oldNumBlocks++;
7117 return TRUE;
7120 /******************************************************************************
7121 * SmallBlockChainStream_SetSize
7123 * Sets the size of this stream.
7124 * The file will grow if we grow the chain.
7126 * TODO: Free the actual blocks in the file when we shrink the chain.
7127 * Currently, the blocks are still in the file. So the file size
7128 * doesn't shrink even if we shrink streams.
7130 BOOL SmallBlockChainStream_SetSize(
7131 SmallBlockChainStream* This,
7132 ULARGE_INTEGER newSize)
7134 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7136 if (newSize.u.LowPart == size.u.LowPart)
7137 return TRUE;
7139 if (newSize.u.LowPart < size.u.LowPart)
7141 SmallBlockChainStream_Shrink(This, newSize);
7143 else
7145 SmallBlockChainStream_Enlarge(This, newSize);
7148 return TRUE;
7151 /******************************************************************************
7152 * SmallBlockChainStream_GetCount
7154 * Returns the number of small blocks that comprises this chain.
7155 * This is not the size of the stream as the last block may not be full!
7158 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7160 ULONG blockIndex;
7161 ULONG count = 0;
7163 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7165 while(blockIndex != BLOCK_END_OF_CHAIN)
7167 count++;
7169 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7170 blockIndex, &blockIndex)))
7171 return 0;
7174 return count;
7177 /******************************************************************************
7178 * SmallBlockChainStream_GetSize
7180 * Returns the size of this chain.
7182 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7184 DirEntry chainEntry;
7186 if(This->headOfStreamPlaceHolder != NULL)
7188 ULARGE_INTEGER result;
7189 result.u.HighPart = 0;
7191 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7192 This->parentStorage->smallBlockSize;
7194 return result;
7197 StorageImpl_ReadDirEntry(
7198 This->parentStorage,
7199 This->ownerDirEntry,
7200 &chainEntry);
7202 return chainEntry.size;
7205 static HRESULT create_storagefile(
7206 LPCOLESTR pwcsName,
7207 DWORD grfMode,
7208 DWORD grfAttrs,
7209 STGOPTIONS* pStgOptions,
7210 REFIID riid,
7211 void** ppstgOpen)
7213 StorageBaseImpl* newStorage = 0;
7214 HANDLE hFile = INVALID_HANDLE_VALUE;
7215 HRESULT hr = STG_E_INVALIDFLAG;
7216 DWORD shareMode;
7217 DWORD accessMode;
7218 DWORD creationMode;
7219 DWORD fileAttributes;
7220 WCHAR tempFileName[MAX_PATH];
7222 if (ppstgOpen == 0)
7223 return STG_E_INVALIDPOINTER;
7225 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7226 return STG_E_INVALIDPARAMETER;
7228 /* if no share mode given then DENY_NONE is the default */
7229 if (STGM_SHARE_MODE(grfMode) == 0)
7230 grfMode |= STGM_SHARE_DENY_NONE;
7232 if ( FAILED( validateSTGM(grfMode) ))
7233 goto end;
7235 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7236 switch(STGM_ACCESS_MODE(grfMode))
7238 case STGM_WRITE:
7239 case STGM_READWRITE:
7240 break;
7241 default:
7242 goto end;
7245 /* in direct mode, can only use SHARE_EXCLUSIVE */
7246 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7247 goto end;
7249 /* but in transacted mode, any share mode is valid */
7252 * Generate a unique name.
7254 if (pwcsName == 0)
7256 WCHAR tempPath[MAX_PATH];
7257 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7259 memset(tempPath, 0, sizeof(tempPath));
7260 memset(tempFileName, 0, sizeof(tempFileName));
7262 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7263 tempPath[0] = '.';
7265 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7266 pwcsName = tempFileName;
7267 else
7269 hr = STG_E_INSUFFICIENTMEMORY;
7270 goto end;
7273 creationMode = TRUNCATE_EXISTING;
7275 else
7277 creationMode = GetCreationModeFromSTGM(grfMode);
7281 * Interpret the STGM value grfMode
7283 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7284 accessMode = GetAccessModeFromSTGM(grfMode);
7286 if (grfMode & STGM_DELETEONRELEASE)
7287 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7288 else
7289 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7291 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7293 static int fixme;
7294 if (!fixme++)
7295 FIXME("Storage share mode not implemented.\n");
7298 *ppstgOpen = 0;
7300 hFile = CreateFileW(pwcsName,
7301 accessMode,
7302 shareMode,
7303 NULL,
7304 creationMode,
7305 fileAttributes,
7308 if (hFile == INVALID_HANDLE_VALUE)
7310 if(GetLastError() == ERROR_FILE_EXISTS)
7311 hr = STG_E_FILEALREADYEXISTS;
7312 else
7313 hr = E_FAIL;
7314 goto end;
7318 * Allocate and initialize the new IStorage32object.
7320 hr = Storage_Construct(
7321 hFile,
7322 pwcsName,
7323 NULL,
7324 grfMode,
7325 TRUE,
7326 TRUE,
7327 pStgOptions->ulSectorSize,
7328 &newStorage);
7330 if (FAILED(hr))
7332 goto end;
7335 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7337 IStorage_Release((IStorage*)newStorage);
7339 end:
7340 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7342 return hr;
7345 /******************************************************************************
7346 * StgCreateDocfile [OLE32.@]
7347 * Creates a new compound file storage object
7349 * PARAMS
7350 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7351 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7352 * reserved [ ?] unused?, usually 0
7353 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7355 * RETURNS
7356 * S_OK if the file was successfully created
7357 * some STG_E_ value if error
7358 * NOTES
7359 * if pwcsName is NULL, create file with new unique name
7360 * the function can returns
7361 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7362 * (unrealized now)
7364 HRESULT WINAPI StgCreateDocfile(
7365 LPCOLESTR pwcsName,
7366 DWORD grfMode,
7367 DWORD reserved,
7368 IStorage **ppstgOpen)
7370 STGOPTIONS stgoptions = {1, 0, 512};
7372 TRACE("(%s, %x, %d, %p)\n",
7373 debugstr_w(pwcsName), grfMode,
7374 reserved, ppstgOpen);
7376 if (ppstgOpen == 0)
7377 return STG_E_INVALIDPOINTER;
7378 if (reserved != 0)
7379 return STG_E_INVALIDPARAMETER;
7381 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7384 /******************************************************************************
7385 * StgCreateStorageEx [OLE32.@]
7387 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7389 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7390 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7392 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7394 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7395 return STG_E_INVALIDPARAMETER;
7398 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7400 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7401 return STG_E_INVALIDPARAMETER;
7404 if (stgfmt == STGFMT_FILE)
7406 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7407 return STG_E_INVALIDPARAMETER;
7410 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7412 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7416 ERR("Invalid stgfmt argument\n");
7417 return STG_E_INVALIDPARAMETER;
7420 /******************************************************************************
7421 * StgCreatePropSetStg [OLE32.@]
7423 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7424 IPropertySetStorage **ppPropSetStg)
7426 HRESULT hr;
7428 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7429 if (reserved)
7430 hr = STG_E_INVALIDPARAMETER;
7431 else
7432 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7433 (void**)ppPropSetStg);
7434 return hr;
7437 /******************************************************************************
7438 * StgOpenStorageEx [OLE32.@]
7440 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7442 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7443 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7445 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7447 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7448 return STG_E_INVALIDPARAMETER;
7451 switch (stgfmt)
7453 case STGFMT_FILE:
7454 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7455 return STG_E_INVALIDPARAMETER;
7457 case STGFMT_STORAGE:
7458 break;
7460 case STGFMT_DOCFILE:
7461 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7463 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7464 return STG_E_INVALIDPARAMETER;
7466 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7467 break;
7469 case STGFMT_ANY:
7470 WARN("STGFMT_ANY assuming storage\n");
7471 break;
7473 default:
7474 return STG_E_INVALIDPARAMETER;
7477 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7481 /******************************************************************************
7482 * StgOpenStorage [OLE32.@]
7484 HRESULT WINAPI StgOpenStorage(
7485 const OLECHAR *pwcsName,
7486 IStorage *pstgPriority,
7487 DWORD grfMode,
7488 SNB snbExclude,
7489 DWORD reserved,
7490 IStorage **ppstgOpen)
7492 StorageBaseImpl* newStorage = 0;
7493 HRESULT hr = S_OK;
7494 HANDLE hFile = 0;
7495 DWORD shareMode;
7496 DWORD accessMode;
7498 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7499 debugstr_w(pwcsName), pstgPriority, grfMode,
7500 snbExclude, reserved, ppstgOpen);
7502 if (pwcsName == 0)
7504 hr = STG_E_INVALIDNAME;
7505 goto end;
7508 if (ppstgOpen == 0)
7510 hr = STG_E_INVALIDPOINTER;
7511 goto end;
7514 if (reserved)
7516 hr = STG_E_INVALIDPARAMETER;
7517 goto end;
7520 if (grfMode & STGM_PRIORITY)
7522 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7523 return STG_E_INVALIDFLAG;
7524 if (grfMode & STGM_DELETEONRELEASE)
7525 return STG_E_INVALIDFUNCTION;
7526 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7527 return STG_E_INVALIDFLAG;
7528 grfMode &= ~0xf0; /* remove the existing sharing mode */
7529 grfMode |= STGM_SHARE_DENY_NONE;
7531 /* STGM_PRIORITY stops other IStorage objects on the same file from
7532 * committing until the STGM_PRIORITY IStorage is closed. it also
7533 * stops non-transacted mode StgOpenStorage calls with write access from
7534 * succeeding. obviously, both of these cannot be achieved through just
7535 * file share flags */
7536 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7540 * Validate the sharing mode
7542 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7543 switch(STGM_SHARE_MODE(grfMode))
7545 case STGM_SHARE_EXCLUSIVE:
7546 case STGM_SHARE_DENY_WRITE:
7547 break;
7548 default:
7549 hr = STG_E_INVALIDFLAG;
7550 goto end;
7553 if ( FAILED( validateSTGM(grfMode) ) ||
7554 (grfMode&STGM_CREATE))
7556 hr = STG_E_INVALIDFLAG;
7557 goto end;
7560 /* shared reading requires transacted mode */
7561 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7562 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7563 !(grfMode&STGM_TRANSACTED) )
7565 hr = STG_E_INVALIDFLAG;
7566 goto end;
7570 * Interpret the STGM value grfMode
7572 shareMode = GetShareModeFromSTGM(grfMode);
7573 accessMode = GetAccessModeFromSTGM(grfMode);
7575 *ppstgOpen = 0;
7577 hFile = CreateFileW( pwcsName,
7578 accessMode,
7579 shareMode,
7580 NULL,
7581 OPEN_EXISTING,
7582 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7585 if (hFile==INVALID_HANDLE_VALUE)
7587 DWORD last_error = GetLastError();
7589 hr = E_FAIL;
7591 switch (last_error)
7593 case ERROR_FILE_NOT_FOUND:
7594 hr = STG_E_FILENOTFOUND;
7595 break;
7597 case ERROR_PATH_NOT_FOUND:
7598 hr = STG_E_PATHNOTFOUND;
7599 break;
7601 case ERROR_ACCESS_DENIED:
7602 case ERROR_WRITE_PROTECT:
7603 hr = STG_E_ACCESSDENIED;
7604 break;
7606 case ERROR_SHARING_VIOLATION:
7607 hr = STG_E_SHAREVIOLATION;
7608 break;
7610 default:
7611 hr = E_FAIL;
7614 goto end;
7618 * Refuse to open the file if it's too small to be a structured storage file
7619 * FIXME: verify the file when reading instead of here
7621 if (GetFileSize(hFile, NULL) < 0x100)
7623 CloseHandle(hFile);
7624 hr = STG_E_FILEALREADYEXISTS;
7625 goto end;
7629 * Allocate and initialize the new IStorage32object.
7631 hr = Storage_Construct(
7632 hFile,
7633 pwcsName,
7634 NULL,
7635 grfMode,
7636 TRUE,
7637 FALSE,
7638 512,
7639 &newStorage);
7641 if (FAILED(hr))
7644 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7646 if(hr == STG_E_INVALIDHEADER)
7647 hr = STG_E_FILEALREADYEXISTS;
7648 goto end;
7652 * Get an "out" pointer for the caller.
7654 *ppstgOpen = (IStorage*)newStorage;
7656 end:
7657 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7658 return hr;
7661 /******************************************************************************
7662 * StgCreateDocfileOnILockBytes [OLE32.@]
7664 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7665 ILockBytes *plkbyt,
7666 DWORD grfMode,
7667 DWORD reserved,
7668 IStorage** ppstgOpen)
7670 StorageBaseImpl* newStorage = 0;
7671 HRESULT hr = S_OK;
7673 if ((ppstgOpen == 0) || (plkbyt == 0))
7674 return STG_E_INVALIDPOINTER;
7677 * Allocate and initialize the new IStorage object.
7679 hr = Storage_Construct(
7682 plkbyt,
7683 grfMode,
7684 FALSE,
7685 TRUE,
7686 512,
7687 &newStorage);
7689 if (FAILED(hr))
7691 return hr;
7695 * Get an "out" pointer for the caller.
7697 *ppstgOpen = (IStorage*)newStorage;
7699 return hr;
7702 /******************************************************************************
7703 * StgOpenStorageOnILockBytes [OLE32.@]
7705 HRESULT WINAPI StgOpenStorageOnILockBytes(
7706 ILockBytes *plkbyt,
7707 IStorage *pstgPriority,
7708 DWORD grfMode,
7709 SNB snbExclude,
7710 DWORD reserved,
7711 IStorage **ppstgOpen)
7713 StorageBaseImpl* newStorage = 0;
7714 HRESULT hr = S_OK;
7716 if ((plkbyt == 0) || (ppstgOpen == 0))
7717 return STG_E_INVALIDPOINTER;
7719 if ( FAILED( validateSTGM(grfMode) ))
7720 return STG_E_INVALIDFLAG;
7722 *ppstgOpen = 0;
7725 * Allocate and initialize the new IStorage object.
7727 hr = Storage_Construct(
7730 plkbyt,
7731 grfMode,
7732 FALSE,
7733 FALSE,
7734 512,
7735 &newStorage);
7737 if (FAILED(hr))
7739 return hr;
7743 * Get an "out" pointer for the caller.
7745 *ppstgOpen = (IStorage*)newStorage;
7747 return hr;
7750 /******************************************************************************
7751 * StgSetTimes [ole32.@]
7752 * StgSetTimes [OLE32.@]
7756 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7757 FILETIME const *patime, FILETIME const *pmtime)
7759 IStorage *stg = NULL;
7760 HRESULT r;
7762 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7764 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7765 0, 0, &stg);
7766 if( SUCCEEDED(r) )
7768 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7769 IStorage_Release(stg);
7772 return r;
7775 /******************************************************************************
7776 * StgIsStorageILockBytes [OLE32.@]
7778 * Determines if the ILockBytes contains a storage object.
7780 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7782 BYTE sig[8];
7783 ULARGE_INTEGER offset;
7785 offset.u.HighPart = 0;
7786 offset.u.LowPart = 0;
7788 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7790 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7791 return S_OK;
7793 return S_FALSE;
7796 /******************************************************************************
7797 * WriteClassStg [OLE32.@]
7799 * This method will store the specified CLSID in the specified storage object
7801 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7803 HRESULT hRes;
7805 if(!pStg)
7806 return E_INVALIDARG;
7808 if(!rclsid)
7809 return STG_E_INVALIDPOINTER;
7811 hRes = IStorage_SetClass(pStg, rclsid);
7813 return hRes;
7816 /***********************************************************************
7817 * ReadClassStg (OLE32.@)
7819 * This method reads the CLSID previously written to a storage object with
7820 * the WriteClassStg.
7822 * PARAMS
7823 * pstg [I] IStorage pointer
7824 * pclsid [O] Pointer to where the CLSID is written
7826 * RETURNS
7827 * Success: S_OK.
7828 * Failure: HRESULT code.
7830 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7832 STATSTG pstatstg;
7833 HRESULT hRes;
7835 TRACE("(%p, %p)\n", pstg, pclsid);
7837 if(!pstg || !pclsid)
7838 return E_INVALIDARG;
7841 * read a STATSTG structure (contains the clsid) from the storage
7843 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7845 if(SUCCEEDED(hRes))
7846 *pclsid=pstatstg.clsid;
7848 return hRes;
7851 /***********************************************************************
7852 * OleLoadFromStream (OLE32.@)
7854 * This function loads an object from stream
7856 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7858 CLSID clsid;
7859 HRESULT res;
7860 LPPERSISTSTREAM xstm;
7862 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7864 res=ReadClassStm(pStm,&clsid);
7865 if (FAILED(res))
7866 return res;
7867 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7868 if (FAILED(res))
7869 return res;
7870 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7871 if (FAILED(res)) {
7872 IUnknown_Release((IUnknown*)*ppvObj);
7873 return res;
7875 res=IPersistStream_Load(xstm,pStm);
7876 IPersistStream_Release(xstm);
7877 /* FIXME: all refcounts ok at this point? I think they should be:
7878 * pStm : unchanged
7879 * ppvObj : 1
7880 * xstm : 0 (released)
7882 return res;
7885 /***********************************************************************
7886 * OleSaveToStream (OLE32.@)
7888 * This function saves an object with the IPersistStream interface on it
7889 * to the specified stream.
7891 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7894 CLSID clsid;
7895 HRESULT res;
7897 TRACE("(%p,%p)\n",pPStm,pStm);
7899 res=IPersistStream_GetClassID(pPStm,&clsid);
7901 if (SUCCEEDED(res)){
7903 res=WriteClassStm(pStm,&clsid);
7905 if (SUCCEEDED(res))
7907 res=IPersistStream_Save(pPStm,pStm,TRUE);
7910 TRACE("Finished Save\n");
7911 return res;
7914 /****************************************************************************
7915 * This method validate a STGM parameter that can contain the values below
7917 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7918 * The stgm values contained in 0xffff0000 are bitmasks.
7920 * STGM_DIRECT 0x00000000
7921 * STGM_TRANSACTED 0x00010000
7922 * STGM_SIMPLE 0x08000000
7924 * STGM_READ 0x00000000
7925 * STGM_WRITE 0x00000001
7926 * STGM_READWRITE 0x00000002
7928 * STGM_SHARE_DENY_NONE 0x00000040
7929 * STGM_SHARE_DENY_READ 0x00000030
7930 * STGM_SHARE_DENY_WRITE 0x00000020
7931 * STGM_SHARE_EXCLUSIVE 0x00000010
7933 * STGM_PRIORITY 0x00040000
7934 * STGM_DELETEONRELEASE 0x04000000
7936 * STGM_CREATE 0x00001000
7937 * STGM_CONVERT 0x00020000
7938 * STGM_FAILIFTHERE 0x00000000
7940 * STGM_NOSCRATCH 0x00100000
7941 * STGM_NOSNAPSHOT 0x00200000
7943 static HRESULT validateSTGM(DWORD stgm)
7945 DWORD access = STGM_ACCESS_MODE(stgm);
7946 DWORD share = STGM_SHARE_MODE(stgm);
7947 DWORD create = STGM_CREATE_MODE(stgm);
7949 if (stgm&~STGM_KNOWN_FLAGS)
7951 ERR("unknown flags %08x\n", stgm);
7952 return E_FAIL;
7955 switch (access)
7957 case STGM_READ:
7958 case STGM_WRITE:
7959 case STGM_READWRITE:
7960 break;
7961 default:
7962 return E_FAIL;
7965 switch (share)
7967 case STGM_SHARE_DENY_NONE:
7968 case STGM_SHARE_DENY_READ:
7969 case STGM_SHARE_DENY_WRITE:
7970 case STGM_SHARE_EXCLUSIVE:
7971 break;
7972 default:
7973 return E_FAIL;
7976 switch (create)
7978 case STGM_CREATE:
7979 case STGM_FAILIFTHERE:
7980 break;
7981 default:
7982 return E_FAIL;
7986 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7988 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7989 return E_FAIL;
7992 * STGM_CREATE | STGM_CONVERT
7993 * if both are false, STGM_FAILIFTHERE is set to TRUE
7995 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7996 return E_FAIL;
7999 * STGM_NOSCRATCH requires STGM_TRANSACTED
8001 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8002 return E_FAIL;
8005 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8006 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8008 if ( (stgm & STGM_NOSNAPSHOT) &&
8009 (!(stgm & STGM_TRANSACTED) ||
8010 share == STGM_SHARE_EXCLUSIVE ||
8011 share == STGM_SHARE_DENY_WRITE) )
8012 return E_FAIL;
8014 return S_OK;
8017 /****************************************************************************
8018 * GetShareModeFromSTGM
8020 * This method will return a share mode flag from a STGM value.
8021 * The STGM value is assumed valid.
8023 static DWORD GetShareModeFromSTGM(DWORD stgm)
8025 switch (STGM_SHARE_MODE(stgm))
8027 case STGM_SHARE_DENY_NONE:
8028 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8029 case STGM_SHARE_DENY_READ:
8030 return FILE_SHARE_WRITE;
8031 case STGM_SHARE_DENY_WRITE:
8032 return FILE_SHARE_READ;
8033 case STGM_SHARE_EXCLUSIVE:
8034 return 0;
8036 ERR("Invalid share mode!\n");
8037 assert(0);
8038 return 0;
8041 /****************************************************************************
8042 * GetAccessModeFromSTGM
8044 * This method will return an access mode flag from a STGM value.
8045 * The STGM value is assumed valid.
8047 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8049 switch (STGM_ACCESS_MODE(stgm))
8051 case STGM_READ:
8052 return GENERIC_READ;
8053 case STGM_WRITE:
8054 case STGM_READWRITE:
8055 return GENERIC_READ | GENERIC_WRITE;
8057 ERR("Invalid access mode!\n");
8058 assert(0);
8059 return 0;
8062 /****************************************************************************
8063 * GetCreationModeFromSTGM
8065 * This method will return a creation mode flag from a STGM value.
8066 * The STGM value is assumed valid.
8068 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8070 switch(STGM_CREATE_MODE(stgm))
8072 case STGM_CREATE:
8073 return CREATE_ALWAYS;
8074 case STGM_CONVERT:
8075 FIXME("STGM_CONVERT not implemented!\n");
8076 return CREATE_NEW;
8077 case STGM_FAILIFTHERE:
8078 return CREATE_NEW;
8080 ERR("Invalid create mode!\n");
8081 assert(0);
8082 return 0;
8086 /*************************************************************************
8087 * OLECONVERT_LoadOLE10 [Internal]
8089 * Loads the OLE10 STREAM to memory
8091 * PARAMS
8092 * pOleStream [I] The OLESTREAM
8093 * pData [I] Data Structure for the OLESTREAM Data
8095 * RETURNS
8096 * Success: S_OK
8097 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8098 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8100 * NOTES
8101 * This function is used by OleConvertOLESTREAMToIStorage only.
8103 * Memory allocated for pData must be freed by the caller
8105 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8107 DWORD dwSize;
8108 HRESULT hRes = S_OK;
8109 int nTryCnt=0;
8110 int max_try = 6;
8112 pData->pData = NULL;
8113 pData->pstrOleObjFileName = NULL;
8115 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8117 /* Get the OleID */
8118 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8119 if(dwSize != sizeof(pData->dwOleID))
8121 hRes = CONVERT10_E_OLESTREAM_GET;
8123 else if(pData->dwOleID != OLESTREAM_ID)
8125 hRes = CONVERT10_E_OLESTREAM_FMT;
8127 else
8129 hRes = S_OK;
8130 break;
8134 if(hRes == S_OK)
8136 /* Get the TypeID... more info needed for this field */
8137 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8138 if(dwSize != sizeof(pData->dwTypeID))
8140 hRes = CONVERT10_E_OLESTREAM_GET;
8143 if(hRes == S_OK)
8145 if(pData->dwTypeID != 0)
8147 /* Get the length of the OleTypeName */
8148 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8149 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8151 hRes = CONVERT10_E_OLESTREAM_GET;
8154 if(hRes == S_OK)
8156 if(pData->dwOleTypeNameLength > 0)
8158 /* Get the OleTypeName */
8159 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8160 if(dwSize != pData->dwOleTypeNameLength)
8162 hRes = CONVERT10_E_OLESTREAM_GET;
8166 if(bStrem1)
8168 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8169 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8171 hRes = CONVERT10_E_OLESTREAM_GET;
8173 if(hRes == S_OK)
8175 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8176 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8177 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8178 if(pData->pstrOleObjFileName)
8180 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8181 if(dwSize != pData->dwOleObjFileNameLength)
8183 hRes = CONVERT10_E_OLESTREAM_GET;
8186 else
8187 hRes = CONVERT10_E_OLESTREAM_GET;
8190 else
8192 /* Get the Width of the Metafile */
8193 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8194 if(dwSize != sizeof(pData->dwMetaFileWidth))
8196 hRes = CONVERT10_E_OLESTREAM_GET;
8198 if(hRes == S_OK)
8200 /* Get the Height of the Metafile */
8201 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8202 if(dwSize != sizeof(pData->dwMetaFileHeight))
8204 hRes = CONVERT10_E_OLESTREAM_GET;
8208 if(hRes == S_OK)
8210 /* Get the Length of the Data */
8211 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8212 if(dwSize != sizeof(pData->dwDataLength))
8214 hRes = CONVERT10_E_OLESTREAM_GET;
8218 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8220 if(!bStrem1) /* if it is a second OLE stream data */
8222 pData->dwDataLength -= 8;
8223 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8224 if(dwSize != sizeof(pData->strUnknown))
8226 hRes = CONVERT10_E_OLESTREAM_GET;
8230 if(hRes == S_OK)
8232 if(pData->dwDataLength > 0)
8234 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8236 /* Get Data (ex. IStorage, Metafile, or BMP) */
8237 if(pData->pData)
8239 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8240 if(dwSize != pData->dwDataLength)
8242 hRes = CONVERT10_E_OLESTREAM_GET;
8245 else
8247 hRes = CONVERT10_E_OLESTREAM_GET;
8253 return hRes;
8256 /*************************************************************************
8257 * OLECONVERT_SaveOLE10 [Internal]
8259 * Saves the OLE10 STREAM From memory
8261 * PARAMS
8262 * pData [I] Data Structure for the OLESTREAM Data
8263 * pOleStream [I] The OLESTREAM to save
8265 * RETURNS
8266 * Success: S_OK
8267 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8269 * NOTES
8270 * This function is used by OleConvertIStorageToOLESTREAM only.
8273 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8275 DWORD dwSize;
8276 HRESULT hRes = S_OK;
8279 /* Set the OleID */
8280 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8281 if(dwSize != sizeof(pData->dwOleID))
8283 hRes = CONVERT10_E_OLESTREAM_PUT;
8286 if(hRes == S_OK)
8288 /* Set the TypeID */
8289 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8290 if(dwSize != sizeof(pData->dwTypeID))
8292 hRes = CONVERT10_E_OLESTREAM_PUT;
8296 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8298 /* Set the Length of the OleTypeName */
8299 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8300 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8302 hRes = CONVERT10_E_OLESTREAM_PUT;
8305 if(hRes == S_OK)
8307 if(pData->dwOleTypeNameLength > 0)
8309 /* Set the OleTypeName */
8310 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8311 if(dwSize != pData->dwOleTypeNameLength)
8313 hRes = CONVERT10_E_OLESTREAM_PUT;
8318 if(hRes == S_OK)
8320 /* Set the width of the Metafile */
8321 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8322 if(dwSize != sizeof(pData->dwMetaFileWidth))
8324 hRes = CONVERT10_E_OLESTREAM_PUT;
8328 if(hRes == S_OK)
8330 /* Set the height of the Metafile */
8331 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8332 if(dwSize != sizeof(pData->dwMetaFileHeight))
8334 hRes = CONVERT10_E_OLESTREAM_PUT;
8338 if(hRes == S_OK)
8340 /* Set the length of the Data */
8341 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8342 if(dwSize != sizeof(pData->dwDataLength))
8344 hRes = CONVERT10_E_OLESTREAM_PUT;
8348 if(hRes == S_OK)
8350 if(pData->dwDataLength > 0)
8352 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8353 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8354 if(dwSize != pData->dwDataLength)
8356 hRes = CONVERT10_E_OLESTREAM_PUT;
8361 return hRes;
8364 /*************************************************************************
8365 * OLECONVERT_GetOLE20FromOLE10[Internal]
8367 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8368 * opens it, and copies the content to the dest IStorage for
8369 * OleConvertOLESTREAMToIStorage
8372 * PARAMS
8373 * pDestStorage [I] The IStorage to copy the data to
8374 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8375 * nBufferLength [I] The size of the buffer
8377 * RETURNS
8378 * Nothing
8380 * NOTES
8384 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8386 HRESULT hRes;
8387 HANDLE hFile;
8388 IStorage *pTempStorage;
8389 DWORD dwNumOfBytesWritten;
8390 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8391 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8393 /* Create a temp File */
8394 GetTempPathW(MAX_PATH, wstrTempDir);
8395 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8396 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8398 if(hFile != INVALID_HANDLE_VALUE)
8400 /* Write IStorage Data to File */
8401 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8402 CloseHandle(hFile);
8404 /* Open and copy temp storage to the Dest Storage */
8405 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8406 if(hRes == S_OK)
8408 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8409 IStorage_Release(pTempStorage);
8411 DeleteFileW(wstrTempFile);
8416 /*************************************************************************
8417 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8419 * Saves the OLE10 STREAM From memory
8421 * PARAMS
8422 * pStorage [I] The Src IStorage to copy
8423 * pData [I] The Dest Memory to write to.
8425 * RETURNS
8426 * The size in bytes allocated for pData
8428 * NOTES
8429 * Memory allocated for pData must be freed by the caller
8431 * Used by OleConvertIStorageToOLESTREAM only.
8434 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8436 HANDLE hFile;
8437 HRESULT hRes;
8438 DWORD nDataLength = 0;
8439 IStorage *pTempStorage;
8440 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8441 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8443 *pData = NULL;
8445 /* Create temp Storage */
8446 GetTempPathW(MAX_PATH, wstrTempDir);
8447 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8448 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8450 if(hRes == S_OK)
8452 /* Copy Src Storage to the Temp Storage */
8453 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8454 IStorage_Release(pTempStorage);
8456 /* Open Temp Storage as a file and copy to memory */
8457 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8458 if(hFile != INVALID_HANDLE_VALUE)
8460 nDataLength = GetFileSize(hFile, NULL);
8461 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8462 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8463 CloseHandle(hFile);
8465 DeleteFileW(wstrTempFile);
8467 return nDataLength;
8470 /*************************************************************************
8471 * OLECONVERT_CreateOleStream [Internal]
8473 * Creates the "\001OLE" stream in the IStorage if necessary.
8475 * PARAMS
8476 * pStorage [I] Dest storage to create the stream in
8478 * RETURNS
8479 * Nothing
8481 * NOTES
8482 * This function is used by OleConvertOLESTREAMToIStorage only.
8484 * This stream is still unknown, MS Word seems to have extra data
8485 * but since the data is stored in the OLESTREAM there should be
8486 * no need to recreate the stream. If the stream is manually
8487 * deleted it will create it with this default data.
8490 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8492 HRESULT hRes;
8493 IStream *pStream;
8494 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8495 BYTE pOleStreamHeader [] =
8497 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8499 0x00, 0x00, 0x00, 0x00
8502 /* Create stream if not present */
8503 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8504 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8506 if(hRes == S_OK)
8508 /* Write default Data */
8509 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8510 IStream_Release(pStream);
8514 /* write a string to a stream, preceded by its length */
8515 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8517 HRESULT r;
8518 LPSTR str;
8519 DWORD len = 0;
8521 if( string )
8522 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8523 r = IStream_Write( stm, &len, sizeof(len), NULL);
8524 if( FAILED( r ) )
8525 return r;
8526 if(len == 0)
8527 return r;
8528 str = CoTaskMemAlloc( len );
8529 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8530 r = IStream_Write( stm, str, len, NULL);
8531 CoTaskMemFree( str );
8532 return r;
8535 /* read a string preceded by its length from a stream */
8536 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8538 HRESULT r;
8539 DWORD len, count = 0;
8540 LPSTR str;
8541 LPWSTR wstr;
8543 r = IStream_Read( stm, &len, sizeof(len), &count );
8544 if( FAILED( r ) )
8545 return r;
8546 if( count != sizeof(len) )
8547 return E_OUTOFMEMORY;
8549 TRACE("%d bytes\n",len);
8551 str = CoTaskMemAlloc( len );
8552 if( !str )
8553 return E_OUTOFMEMORY;
8554 count = 0;
8555 r = IStream_Read( stm, str, len, &count );
8556 if( FAILED( r ) )
8557 return r;
8558 if( count != len )
8560 CoTaskMemFree( str );
8561 return E_OUTOFMEMORY;
8564 TRACE("Read string %s\n",debugstr_an(str,len));
8566 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8567 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8568 if( wstr )
8569 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8570 CoTaskMemFree( str );
8572 *string = wstr;
8574 return r;
8578 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8579 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8581 IStream *pstm;
8582 HRESULT r = S_OK;
8583 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8585 static const BYTE unknown1[12] =
8586 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8587 0xFF, 0xFF, 0xFF, 0xFF};
8588 static const BYTE unknown2[16] =
8589 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8590 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8592 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8593 debugstr_w(lpszUserType), debugstr_w(szClipName),
8594 debugstr_w(szProgIDName));
8596 /* Create a CompObj stream */
8597 r = IStorage_CreateStream(pstg, szwStreamName,
8598 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8599 if( FAILED (r) )
8600 return r;
8602 /* Write CompObj Structure to stream */
8603 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8605 if( SUCCEEDED( r ) )
8606 r = WriteClassStm( pstm, clsid );
8608 if( SUCCEEDED( r ) )
8609 r = STREAM_WriteString( pstm, lpszUserType );
8610 if( SUCCEEDED( r ) )
8611 r = STREAM_WriteString( pstm, szClipName );
8612 if( SUCCEEDED( r ) )
8613 r = STREAM_WriteString( pstm, szProgIDName );
8614 if( SUCCEEDED( r ) )
8615 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8617 IStream_Release( pstm );
8619 return r;
8622 /***********************************************************************
8623 * WriteFmtUserTypeStg (OLE32.@)
8625 HRESULT WINAPI WriteFmtUserTypeStg(
8626 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8628 HRESULT r;
8629 WCHAR szwClipName[0x40];
8630 CLSID clsid = CLSID_NULL;
8631 LPWSTR wstrProgID = NULL;
8632 DWORD n;
8634 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8636 /* get the clipboard format name */
8637 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8638 szwClipName[n]=0;
8640 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8642 /* FIXME: There's room to save a CLSID and its ProgID, but
8643 the CLSID is not looked up in the registry and in all the
8644 tests I wrote it was CLSID_NULL. Where does it come from?
8647 /* get the real program ID. This may fail, but that's fine */
8648 ProgIDFromCLSID(&clsid, &wstrProgID);
8650 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8652 r = STORAGE_WriteCompObj( pstg, &clsid,
8653 lpszUserType, szwClipName, wstrProgID );
8655 CoTaskMemFree(wstrProgID);
8657 return r;
8661 /******************************************************************************
8662 * ReadFmtUserTypeStg [OLE32.@]
8664 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8666 HRESULT r;
8667 IStream *stm = 0;
8668 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8669 unsigned char unknown1[12];
8670 unsigned char unknown2[16];
8671 DWORD count;
8672 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8673 CLSID clsid;
8675 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8677 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8678 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8679 if( FAILED ( r ) )
8681 WARN("Failed to open stream r = %08x\n", r);
8682 return r;
8685 /* read the various parts of the structure */
8686 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8687 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8688 goto end;
8689 r = ReadClassStm( stm, &clsid );
8690 if( FAILED( r ) )
8691 goto end;
8693 r = STREAM_ReadString( stm, &szCLSIDName );
8694 if( FAILED( r ) )
8695 goto end;
8697 r = STREAM_ReadString( stm, &szOleTypeName );
8698 if( FAILED( r ) )
8699 goto end;
8701 r = STREAM_ReadString( stm, &szProgIDName );
8702 if( FAILED( r ) )
8703 goto end;
8705 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8706 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8707 goto end;
8709 /* ok, success... now we just need to store what we found */
8710 if( pcf )
8711 *pcf = RegisterClipboardFormatW( szOleTypeName );
8712 CoTaskMemFree( szOleTypeName );
8714 if( lplpszUserType )
8715 *lplpszUserType = szCLSIDName;
8716 CoTaskMemFree( szProgIDName );
8718 end:
8719 IStream_Release( stm );
8721 return r;
8725 /*************************************************************************
8726 * OLECONVERT_CreateCompObjStream [Internal]
8728 * Creates a "\001CompObj" is the destination IStorage if necessary.
8730 * PARAMS
8731 * pStorage [I] The dest IStorage to create the CompObj Stream
8732 * if necessary.
8733 * strOleTypeName [I] The ProgID
8735 * RETURNS
8736 * Success: S_OK
8737 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8739 * NOTES
8740 * This function is used by OleConvertOLESTREAMToIStorage only.
8742 * The stream data is stored in the OLESTREAM and there should be
8743 * no need to recreate the stream. If the stream is manually
8744 * deleted it will attempt to create it by querying the registry.
8748 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8750 IStream *pStream;
8751 HRESULT hStorageRes, hRes = S_OK;
8752 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8753 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8754 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8756 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8757 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8759 /* Initialize the CompObj structure */
8760 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8761 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8762 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8765 /* Create a CompObj stream if it doesn't exist */
8766 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8767 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8768 if(hStorageRes == S_OK)
8770 /* copy the OleTypeName to the compobj struct */
8771 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8772 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8774 /* copy the OleTypeName to the compobj struct */
8775 /* Note: in the test made, these were Identical */
8776 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8777 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8779 /* Get the CLSID */
8780 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8781 bufferW, OLESTREAM_MAX_STR_LEN );
8782 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8784 if(hRes == S_OK)
8786 HKEY hKey;
8787 LONG hErr;
8788 /* Get the CLSID Default Name from the Registry */
8789 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8790 if(hErr == ERROR_SUCCESS)
8792 char strTemp[OLESTREAM_MAX_STR_LEN];
8793 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8794 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8795 if(hErr == ERROR_SUCCESS)
8797 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8799 RegCloseKey(hKey);
8803 /* Write CompObj Structure to stream */
8804 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8806 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8808 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8809 if(IStorageCompObj.dwCLSIDNameLength > 0)
8811 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8813 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8814 if(IStorageCompObj.dwOleTypeNameLength > 0)
8816 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8818 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8819 if(IStorageCompObj.dwProgIDNameLength > 0)
8821 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8823 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8824 IStream_Release(pStream);
8826 return hRes;
8830 /*************************************************************************
8831 * OLECONVERT_CreateOlePresStream[Internal]
8833 * Creates the "\002OlePres000" Stream with the Metafile data
8835 * PARAMS
8836 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8837 * dwExtentX [I] Width of the Metafile
8838 * dwExtentY [I] Height of the Metafile
8839 * pData [I] Metafile data
8840 * dwDataLength [I] Size of the Metafile data
8842 * RETURNS
8843 * Success: S_OK
8844 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8846 * NOTES
8847 * This function is used by OleConvertOLESTREAMToIStorage only.
8850 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8852 HRESULT hRes;
8853 IStream *pStream;
8854 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8855 BYTE pOlePresStreamHeader [] =
8857 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8858 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8859 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8860 0x00, 0x00, 0x00, 0x00
8863 BYTE pOlePresStreamHeaderEmpty [] =
8865 0x00, 0x00, 0x00, 0x00,
8866 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8867 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8868 0x00, 0x00, 0x00, 0x00
8871 /* Create the OlePres000 Stream */
8872 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8873 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8875 if(hRes == S_OK)
8877 DWORD nHeaderSize;
8878 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8880 memset(&OlePres, 0, sizeof(OlePres));
8881 /* Do we have any metafile data to save */
8882 if(dwDataLength > 0)
8884 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8885 nHeaderSize = sizeof(pOlePresStreamHeader);
8887 else
8889 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8890 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8892 /* Set width and height of the metafile */
8893 OlePres.dwExtentX = dwExtentX;
8894 OlePres.dwExtentY = -dwExtentY;
8896 /* Set Data and Length */
8897 if(dwDataLength > sizeof(METAFILEPICT16))
8899 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8900 OlePres.pData = &(pData[8]);
8902 /* Save OlePres000 Data to Stream */
8903 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8904 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8905 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8906 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8907 if(OlePres.dwSize > 0)
8909 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8911 IStream_Release(pStream);
8915 /*************************************************************************
8916 * OLECONVERT_CreateOle10NativeStream [Internal]
8918 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8920 * PARAMS
8921 * pStorage [I] Dest storage to create the stream in
8922 * pData [I] Ole10 Native Data (ex. bmp)
8923 * dwDataLength [I] Size of the Ole10 Native Data
8925 * RETURNS
8926 * Nothing
8928 * NOTES
8929 * This function is used by OleConvertOLESTREAMToIStorage only.
8931 * Might need to verify the data and return appropriate error message
8934 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8936 HRESULT hRes;
8937 IStream *pStream;
8938 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8940 /* Create the Ole10Native Stream */
8941 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8942 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8944 if(hRes == S_OK)
8946 /* Write info to stream */
8947 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8948 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8949 IStream_Release(pStream);
8954 /*************************************************************************
8955 * OLECONVERT_GetOLE10ProgID [Internal]
8957 * Finds the ProgID (or OleTypeID) from the IStorage
8959 * PARAMS
8960 * pStorage [I] The Src IStorage to get the ProgID
8961 * strProgID [I] the ProgID string to get
8962 * dwSize [I] the size of the string
8964 * RETURNS
8965 * Success: S_OK
8966 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8968 * NOTES
8969 * This function is used by OleConvertIStorageToOLESTREAM only.
8973 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8975 HRESULT hRes;
8976 IStream *pStream;
8977 LARGE_INTEGER iSeekPos;
8978 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8979 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8981 /* Open the CompObj Stream */
8982 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8983 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8984 if(hRes == S_OK)
8987 /*Get the OleType from the CompObj Stream */
8988 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8989 iSeekPos.u.HighPart = 0;
8991 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8992 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8993 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8994 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8995 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8996 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8997 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8999 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9000 if(*dwSize > 0)
9002 IStream_Read(pStream, strProgID, *dwSize, NULL);
9004 IStream_Release(pStream);
9006 else
9008 STATSTG stat;
9009 LPOLESTR wstrProgID;
9011 /* Get the OleType from the registry */
9012 REFCLSID clsid = &(stat.clsid);
9013 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9014 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9015 if(hRes == S_OK)
9017 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9021 return hRes;
9024 /*************************************************************************
9025 * OLECONVERT_GetOle10PresData [Internal]
9027 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9029 * PARAMS
9030 * pStorage [I] Src IStroage
9031 * pOleStream [I] Dest OleStream Mem Struct
9033 * RETURNS
9034 * Nothing
9036 * NOTES
9037 * This function is used by OleConvertIStorageToOLESTREAM only.
9039 * Memory allocated for pData must be freed by the caller
9043 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9046 HRESULT hRes;
9047 IStream *pStream;
9048 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9050 /* Initialize Default data for OLESTREAM */
9051 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9052 pOleStreamData[0].dwTypeID = 2;
9053 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9054 pOleStreamData[1].dwTypeID = 0;
9055 pOleStreamData[0].dwMetaFileWidth = 0;
9056 pOleStreamData[0].dwMetaFileHeight = 0;
9057 pOleStreamData[0].pData = NULL;
9058 pOleStreamData[1].pData = NULL;
9060 /* Open Ole10Native Stream */
9061 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9062 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9063 if(hRes == S_OK)
9066 /* Read Size and Data */
9067 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9068 if(pOleStreamData->dwDataLength > 0)
9070 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9071 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9073 IStream_Release(pStream);
9079 /*************************************************************************
9080 * OLECONVERT_GetOle20PresData[Internal]
9082 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9084 * PARAMS
9085 * pStorage [I] Src IStroage
9086 * pOleStreamData [I] Dest OleStream Mem Struct
9088 * RETURNS
9089 * Nothing
9091 * NOTES
9092 * This function is used by OleConvertIStorageToOLESTREAM only.
9094 * Memory allocated for pData must be freed by the caller
9096 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9098 HRESULT hRes;
9099 IStream *pStream;
9100 OLECONVERT_ISTORAGE_OLEPRES olePress;
9101 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9103 /* Initialize Default data for OLESTREAM */
9104 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9105 pOleStreamData[0].dwTypeID = 2;
9106 pOleStreamData[0].dwMetaFileWidth = 0;
9107 pOleStreamData[0].dwMetaFileHeight = 0;
9108 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9109 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9110 pOleStreamData[1].dwTypeID = 0;
9111 pOleStreamData[1].dwOleTypeNameLength = 0;
9112 pOleStreamData[1].strOleTypeName[0] = 0;
9113 pOleStreamData[1].dwMetaFileWidth = 0;
9114 pOleStreamData[1].dwMetaFileHeight = 0;
9115 pOleStreamData[1].pData = NULL;
9116 pOleStreamData[1].dwDataLength = 0;
9119 /* Open OlePress000 stream */
9120 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9121 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9122 if(hRes == S_OK)
9124 LARGE_INTEGER iSeekPos;
9125 METAFILEPICT16 MetaFilePict;
9126 static const char strMetafilePictName[] = "METAFILEPICT";
9128 /* Set the TypeID for a Metafile */
9129 pOleStreamData[1].dwTypeID = 5;
9131 /* Set the OleTypeName to Metafile */
9132 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9133 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9135 iSeekPos.u.HighPart = 0;
9136 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9138 /* Get Presentation Data */
9139 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9140 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9141 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9142 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9144 /*Set width and Height */
9145 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9146 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9147 if(olePress.dwSize > 0)
9149 /* Set Length */
9150 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9152 /* Set MetaFilePict struct */
9153 MetaFilePict.mm = 8;
9154 MetaFilePict.xExt = olePress.dwExtentX;
9155 MetaFilePict.yExt = olePress.dwExtentY;
9156 MetaFilePict.hMF = 0;
9158 /* Get Metafile Data */
9159 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9160 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9161 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9163 IStream_Release(pStream);
9167 /*************************************************************************
9168 * OleConvertOLESTREAMToIStorage [OLE32.@]
9170 * Read info on MSDN
9172 * TODO
9173 * DVTARGETDEVICE parameter is not handled
9174 * Still unsure of some mem fields for OLE 10 Stream
9175 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9176 * and "\001OLE" streams
9179 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9180 LPOLESTREAM pOleStream,
9181 LPSTORAGE pstg,
9182 const DVTARGETDEVICE* ptd)
9184 int i;
9185 HRESULT hRes=S_OK;
9186 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9188 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9190 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9192 if(ptd != NULL)
9194 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9197 if(pstg == NULL || pOleStream == NULL)
9199 hRes = E_INVALIDARG;
9202 if(hRes == S_OK)
9204 /* Load the OLESTREAM to Memory */
9205 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9208 if(hRes == S_OK)
9210 /* Load the OLESTREAM to Memory (part 2)*/
9211 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9214 if(hRes == S_OK)
9217 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9219 /* Do we have the IStorage Data in the OLESTREAM */
9220 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9222 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9223 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9225 else
9227 /* It must be an original OLE 1.0 source */
9228 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9231 else
9233 /* It must be an original OLE 1.0 source */
9234 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9237 /* Create CompObj Stream if necessary */
9238 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9239 if(hRes == S_OK)
9241 /*Create the Ole Stream if necessary */
9242 OLECONVERT_CreateOleStream(pstg);
9247 /* Free allocated memory */
9248 for(i=0; i < 2; i++)
9250 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9251 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9252 pOleStreamData[i].pstrOleObjFileName = NULL;
9254 return hRes;
9257 /*************************************************************************
9258 * OleConvertIStorageToOLESTREAM [OLE32.@]
9260 * Read info on MSDN
9262 * Read info on MSDN
9264 * TODO
9265 * Still unsure of some mem fields for OLE 10 Stream
9266 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9267 * and "\001OLE" streams.
9270 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9271 LPSTORAGE pstg,
9272 LPOLESTREAM pOleStream)
9274 int i;
9275 HRESULT hRes = S_OK;
9276 IStream *pStream;
9277 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9278 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9280 TRACE("%p %p\n", pstg, pOleStream);
9282 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9284 if(pstg == NULL || pOleStream == NULL)
9286 hRes = E_INVALIDARG;
9288 if(hRes == S_OK)
9290 /* Get the ProgID */
9291 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9292 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9294 if(hRes == S_OK)
9296 /* Was it originally Ole10 */
9297 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9298 if(hRes == S_OK)
9300 IStream_Release(pStream);
9301 /* Get Presentation Data for Ole10Native */
9302 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9304 else
9306 /* Get Presentation Data (OLE20) */
9307 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9310 /* Save OLESTREAM */
9311 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9312 if(hRes == S_OK)
9314 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9319 /* Free allocated memory */
9320 for(i=0; i < 2; i++)
9322 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9325 return hRes;
9328 /***********************************************************************
9329 * GetConvertStg (OLE32.@)
9331 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9332 FIXME("unimplemented stub!\n");
9333 return E_FAIL;
9336 /******************************************************************************
9337 * StgIsStorageFile [OLE32.@]
9338 * Verify if the file contains a storage object
9340 * PARAMS
9341 * fn [ I] Filename
9343 * RETURNS
9344 * S_OK if file has magic bytes as a storage object
9345 * S_FALSE if file is not storage
9347 HRESULT WINAPI
9348 StgIsStorageFile(LPCOLESTR fn)
9350 HANDLE hf;
9351 BYTE magic[8];
9352 DWORD bytes_read;
9354 TRACE("%s\n", debugstr_w(fn));
9355 hf = CreateFileW(fn, GENERIC_READ,
9356 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9357 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9359 if (hf == INVALID_HANDLE_VALUE)
9360 return STG_E_FILENOTFOUND;
9362 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9364 WARN(" unable to read file\n");
9365 CloseHandle(hf);
9366 return S_FALSE;
9369 CloseHandle(hf);
9371 if (bytes_read != 8) {
9372 TRACE(" too short\n");
9373 return S_FALSE;
9376 if (!memcmp(magic,STORAGE_magic,8)) {
9377 TRACE(" -> YES\n");
9378 return S_OK;
9381 TRACE(" -> Invalid header.\n");
9382 return S_FALSE;
9385 /***********************************************************************
9386 * WriteClassStm (OLE32.@)
9388 * Writes a CLSID to a stream.
9390 * PARAMS
9391 * pStm [I] Stream to write to.
9392 * rclsid [I] CLSID to write.
9394 * RETURNS
9395 * Success: S_OK.
9396 * Failure: HRESULT code.
9398 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9400 TRACE("(%p,%p)\n",pStm,rclsid);
9402 if (!pStm || !rclsid)
9403 return E_INVALIDARG;
9405 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9408 /***********************************************************************
9409 * ReadClassStm (OLE32.@)
9411 * Reads a CLSID from a stream.
9413 * PARAMS
9414 * pStm [I] Stream to read from.
9415 * rclsid [O] CLSID to read.
9417 * RETURNS
9418 * Success: S_OK.
9419 * Failure: HRESULT code.
9421 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9423 ULONG nbByte;
9424 HRESULT res;
9426 TRACE("(%p,%p)\n",pStm,pclsid);
9428 if (!pStm || !pclsid)
9429 return E_INVALIDARG;
9431 /* clear the output args */
9432 *pclsid = CLSID_NULL;
9434 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9436 if (FAILED(res))
9437 return res;
9439 if (nbByte != sizeof(CLSID))
9440 return STG_E_READFAULT;
9441 else
9442 return S_OK;