cabinet/tests: Make some variables static.
[wine.git] / dlls / ole32 / storage32.c
blob5c582019f4c0b4c0513be55751b013bf5ebe0140
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry;
86 StorageBaseImpl *parentStorage;
88 typedef struct StorageInternalImpl StorageInternalImpl;
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
92 DWORD openFlags, DirRef storageDirEntry);
93 static void StorageImpl_Destroy(StorageBaseImpl* iface);
94 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
95 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
96 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
97 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
98 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
99 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
100 static void StorageImpl_SaveFileHeader(StorageImpl* This);
102 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
103 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
104 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
105 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
106 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
108 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
109 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
110 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
112 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
113 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
114 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
115 ULONG blockIndex, ULONG offset, DWORD value);
116 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
117 ULONG blockIndex, ULONG offset, DWORD* value);
119 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
120 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
122 typedef struct TransactedDirEntry
124 /* If applicable, a reference to the original DirEntry in the transacted
125 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
126 DirRef transactedParentEntry;
128 /* True if this entry is being used. */
129 int inuse;
131 /* True if data is up to date. */
132 int read;
134 /* True if this entry has been modified. */
135 int dirty;
137 /* True if this entry's stream has been modified. */
138 int stream_dirty;
140 /* True if this entry has been deleted in the transacted storage, but the
141 * delete has not yet been committed. */
142 int deleted;
144 /* If this entry's stream has been modified, a reference to where the stream
145 * is stored in the snapshot file. */
146 DirRef stream_entry;
148 /* This directory entry's data, including any changes that have been made. */
149 DirEntry data;
151 /* A reference to the parent of this node. This is only valid while we are
152 * committing changes. */
153 DirRef parent;
155 /* A reference to a newly-created entry in the transacted parent. This is
156 * always equal to transactedParentEntry except when committing changes. */
157 DirRef newTransactedParentEntry;
158 } TransactedDirEntry;
160 /****************************************************************************
161 * Transacted storage object.
163 typedef struct TransactedSnapshotImpl
165 struct StorageBaseImpl base;
168 * Modified streams are temporarily saved to the scratch file.
170 StorageBaseImpl *scratch;
172 /* The directory structure is kept here, so that we can track how these
173 * entries relate to those in the parent storage. */
174 TransactedDirEntry *entries;
175 ULONG entries_size;
176 ULONG firstFreeEntry;
179 * Changes are committed to the transacted parent.
181 StorageBaseImpl *transactedParent;
182 } TransactedSnapshotImpl;
184 /* Generic function to create a transacted wrapper for a direct storage object. */
185 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
187 /* OLESTREAM memory structure to use for Get and Put Routines */
188 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
189 typedef struct
191 DWORD dwOleID;
192 DWORD dwTypeID;
193 DWORD dwOleTypeNameLength;
194 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
195 CHAR *pstrOleObjFileName;
196 DWORD dwOleObjFileNameLength;
197 DWORD dwMetaFileWidth;
198 DWORD dwMetaFileHeight;
199 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
200 DWORD dwDataLength;
201 BYTE *pData;
202 }OLECONVERT_OLESTREAM_DATA;
204 /* CompObj Stream structure */
205 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
206 typedef struct
208 BYTE byUnknown1[12];
209 CLSID clsid;
210 DWORD dwCLSIDNameLength;
211 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
212 DWORD dwOleTypeNameLength;
213 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
214 DWORD dwProgIDNameLength;
215 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
216 BYTE byUnknown2[16];
217 }OLECONVERT_ISTORAGE_COMPOBJ;
220 /* Ole Presentation Stream structure */
221 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
222 typedef struct
224 BYTE byUnknown1[28];
225 DWORD dwExtentX;
226 DWORD dwExtentY;
227 DWORD dwSize;
228 BYTE *pData;
229 }OLECONVERT_ISTORAGE_OLEPRES;
233 /***********************************************************************
234 * Forward declaration of internal functions used by the method DestroyElement
236 static HRESULT deleteStorageContents(
237 StorageBaseImpl *parentStorage,
238 DirRef indexToDelete,
239 DirEntry entryDataToDelete);
241 static HRESULT deleteStreamContents(
242 StorageBaseImpl *parentStorage,
243 DirRef indexToDelete,
244 DirEntry entryDataToDelete);
246 static HRESULT removeFromTree(
247 StorageBaseImpl *This,
248 DirRef parentStorageIndex,
249 DirRef deletedIndex);
251 /***********************************************************************
252 * Declaration of the functions used to manipulate DirEntry
255 static HRESULT insertIntoTree(
256 StorageBaseImpl *This,
257 DirRef parentStorageIndex,
258 DirRef newEntryIndex);
260 static LONG entryNameCmp(
261 const OLECHAR *name1,
262 const OLECHAR *name2);
264 static DirRef findElement(
265 StorageBaseImpl *storage,
266 DirRef storageEntry,
267 const OLECHAR *name,
268 DirEntry *data);
270 static HRESULT findTreeParent(
271 StorageBaseImpl *storage,
272 DirRef storageEntry,
273 const OLECHAR *childName,
274 DirEntry *parentData,
275 DirRef *parentEntry,
276 ULONG *relation);
278 /***********************************************************************
279 * Declaration of miscellaneous functions...
281 static HRESULT validateSTGM(DWORD stgmValue);
283 static DWORD GetShareModeFromSTGM(DWORD stgm);
284 static DWORD GetAccessModeFromSTGM(DWORD stgm);
285 static DWORD GetCreationModeFromSTGM(DWORD stgm);
287 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
290 /****************************************************************************
291 * IEnumSTATSTGImpl definitions.
293 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
294 * This class allows iterating through the content of a storage and to find
295 * specific items inside it.
297 struct IEnumSTATSTGImpl
299 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
300 * since we want to cast this in an IEnumSTATSTG pointer */
302 LONG ref; /* Reference count */
303 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
304 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
306 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
310 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
311 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
313 /************************************************************************
314 ** Block Functions
317 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
319 return (index+1) * This->bigBlockSize;
322 /************************************************************************
323 ** Storage32BaseImpl implementation
325 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
326 ULARGE_INTEGER offset,
327 void* buffer,
328 ULONG size,
329 ULONG* bytesRead)
331 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
334 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
335 ULARGE_INTEGER offset,
336 const void* buffer,
337 const ULONG size,
338 ULONG* bytesWritten)
340 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
343 /************************************************************************
344 * Storage32BaseImpl_QueryInterface (IUnknown)
346 * This method implements the common QueryInterface for all IStorage32
347 * implementations contained in this file.
349 * See Windows documentation for more details on IUnknown methods.
351 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
352 IStorage* iface,
353 REFIID riid,
354 void** ppvObject)
356 StorageBaseImpl *This = (StorageBaseImpl *)iface;
358 if ( (This==0) || (ppvObject==0) )
359 return E_INVALIDARG;
361 *ppvObject = 0;
363 if (IsEqualGUID(&IID_IUnknown, riid) ||
364 IsEqualGUID(&IID_IStorage, riid))
366 *ppvObject = This;
368 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
370 *ppvObject = &This->pssVtbl;
373 if ((*ppvObject)==0)
374 return E_NOINTERFACE;
376 IStorage_AddRef(iface);
378 return S_OK;
381 /************************************************************************
382 * Storage32BaseImpl_AddRef (IUnknown)
384 * This method implements the common AddRef for all IStorage32
385 * implementations contained in this file.
387 * See Windows documentation for more details on IUnknown methods.
389 static ULONG WINAPI StorageBaseImpl_AddRef(
390 IStorage* iface)
392 StorageBaseImpl *This = (StorageBaseImpl *)iface;
393 ULONG ref = InterlockedIncrement(&This->ref);
395 TRACE("(%p) AddRef to %d\n", This, ref);
397 return ref;
400 /************************************************************************
401 * Storage32BaseImpl_Release (IUnknown)
403 * This method implements the common Release for all IStorage32
404 * implementations contained in this file.
406 * See Windows documentation for more details on IUnknown methods.
408 static ULONG WINAPI StorageBaseImpl_Release(
409 IStorage* iface)
411 StorageBaseImpl *This = (StorageBaseImpl *)iface;
413 ULONG ref = InterlockedDecrement(&This->ref);
415 TRACE("(%p) ReleaseRef to %d\n", This, ref);
417 if (ref == 0)
420 * Since we are using a system of base-classes, we want to call the
421 * destructor of the appropriate derived class. To do this, we are
422 * using virtual functions to implement the destructor.
424 StorageBaseImpl_Destroy(This);
427 return ref;
430 /************************************************************************
431 * Storage32BaseImpl_OpenStream (IStorage)
433 * This method will open the specified stream object from the current storage.
435 * See Windows documentation for more details on IStorage methods.
437 static HRESULT WINAPI StorageBaseImpl_OpenStream(
438 IStorage* iface,
439 const OLECHAR* pwcsName, /* [string][in] */
440 void* reserved1, /* [unique][in] */
441 DWORD grfMode, /* [in] */
442 DWORD reserved2, /* [in] */
443 IStream** ppstm) /* [out] */
445 StorageBaseImpl *This = (StorageBaseImpl *)iface;
446 StgStreamImpl* newStream;
447 DirEntry currentEntry;
448 DirRef streamEntryRef;
449 HRESULT res = STG_E_UNKNOWN;
451 TRACE("(%p, %s, %p, %x, %d, %p)\n",
452 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
454 if ( (pwcsName==NULL) || (ppstm==0) )
456 res = E_INVALIDARG;
457 goto end;
460 *ppstm = NULL;
462 if ( FAILED( validateSTGM(grfMode) ) ||
463 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
465 res = STG_E_INVALIDFLAG;
466 goto end;
470 * As documented.
472 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
474 res = STG_E_INVALIDFUNCTION;
475 goto end;
478 if (This->reverted)
480 res = STG_E_REVERTED;
481 goto end;
485 * Check that we're compatible with the parent's storage mode, but
486 * only if we are not in transacted mode
488 if(!(This->openFlags & STGM_TRANSACTED)) {
489 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
491 res = STG_E_INVALIDFLAG;
492 goto end;
497 * Search for the element with the given name
499 streamEntryRef = findElement(
500 This,
501 This->storageDirEntry,
502 pwcsName,
503 &currentEntry);
506 * If it was found, construct the stream object and return a pointer to it.
508 if ( (streamEntryRef!=DIRENTRY_NULL) &&
509 (currentEntry.stgType==STGTY_STREAM) )
511 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
513 /* A single stream cannot be opened a second time. */
514 res = STG_E_ACCESSDENIED;
515 goto end;
518 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
520 if (newStream!=0)
522 newStream->grfMode = grfMode;
523 *ppstm = (IStream*)newStream;
525 IStream_AddRef(*ppstm);
527 res = S_OK;
528 goto end;
531 res = E_OUTOFMEMORY;
532 goto end;
535 res = STG_E_FILENOTFOUND;
537 end:
538 if (res == S_OK)
539 TRACE("<-- IStream %p\n", *ppstm);
540 TRACE("<-- %08x\n", res);
541 return res;
544 /************************************************************************
545 * Storage32BaseImpl_OpenStorage (IStorage)
547 * This method will open a new storage object from the current storage.
549 * See Windows documentation for more details on IStorage methods.
551 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
552 IStorage* iface,
553 const OLECHAR* pwcsName, /* [string][unique][in] */
554 IStorage* pstgPriority, /* [unique][in] */
555 DWORD grfMode, /* [in] */
556 SNB snbExclude, /* [unique][in] */
557 DWORD reserved, /* [in] */
558 IStorage** ppstg) /* [out] */
560 StorageBaseImpl *This = (StorageBaseImpl *)iface;
561 StorageInternalImpl* newStorage;
562 StorageBaseImpl* newTransactedStorage;
563 DirEntry currentEntry;
564 DirRef storageEntryRef;
565 HRESULT res = STG_E_UNKNOWN;
567 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
568 iface, debugstr_w(pwcsName), pstgPriority,
569 grfMode, snbExclude, reserved, ppstg);
571 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
573 res = E_INVALIDARG;
574 goto end;
577 if (This->openFlags & STGM_SIMPLE)
579 res = STG_E_INVALIDFUNCTION;
580 goto end;
583 /* as documented */
584 if (snbExclude != NULL)
586 res = STG_E_INVALIDPARAMETER;
587 goto end;
590 if ( FAILED( validateSTGM(grfMode) ))
592 res = STG_E_INVALIDFLAG;
593 goto end;
597 * As documented.
599 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
600 (grfMode & STGM_DELETEONRELEASE) ||
601 (grfMode & STGM_PRIORITY) )
603 res = STG_E_INVALIDFUNCTION;
604 goto end;
607 if (This->reverted)
608 return STG_E_REVERTED;
611 * Check that we're compatible with the parent's storage mode,
612 * but only if we are not transacted
614 if(!(This->openFlags & STGM_TRANSACTED)) {
615 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
617 res = STG_E_ACCESSDENIED;
618 goto end;
622 *ppstg = NULL;
624 storageEntryRef = findElement(
625 This,
626 This->storageDirEntry,
627 pwcsName,
628 &currentEntry);
630 if ( (storageEntryRef!=DIRENTRY_NULL) &&
631 (currentEntry.stgType==STGTY_STORAGE) )
633 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
635 /* A single storage cannot be opened a second time. */
636 res = STG_E_ACCESSDENIED;
637 goto end;
640 newStorage = StorageInternalImpl_Construct(
641 This,
642 grfMode,
643 storageEntryRef);
645 if (newStorage != 0)
647 if (grfMode & STGM_TRANSACTED)
649 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
651 if (FAILED(res))
653 HeapFree(GetProcessHeap(), 0, newStorage);
654 goto end;
657 *ppstg = (IStorage*)newTransactedStorage;
659 else
661 *ppstg = (IStorage*)newStorage;
664 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
666 res = S_OK;
667 goto end;
670 res = STG_E_INSUFFICIENTMEMORY;
671 goto end;
674 res = STG_E_FILENOTFOUND;
676 end:
677 TRACE("<-- %08x\n", res);
678 return res;
681 /************************************************************************
682 * Storage32BaseImpl_EnumElements (IStorage)
684 * This method will create an enumerator object that can be used to
685 * retrieve information about all the elements in the storage object.
687 * See Windows documentation for more details on IStorage methods.
689 static HRESULT WINAPI StorageBaseImpl_EnumElements(
690 IStorage* iface,
691 DWORD reserved1, /* [in] */
692 void* reserved2, /* [size_is][unique][in] */
693 DWORD reserved3, /* [in] */
694 IEnumSTATSTG** ppenum) /* [out] */
696 StorageBaseImpl *This = (StorageBaseImpl *)iface;
697 IEnumSTATSTGImpl* newEnum;
699 TRACE("(%p, %d, %p, %d, %p)\n",
700 iface, reserved1, reserved2, reserved3, ppenum);
702 if ( (This==0) || (ppenum==0))
703 return E_INVALIDARG;
705 if (This->reverted)
706 return STG_E_REVERTED;
708 newEnum = IEnumSTATSTGImpl_Construct(
709 This,
710 This->storageDirEntry);
712 if (newEnum!=0)
714 *ppenum = (IEnumSTATSTG*)newEnum;
716 IEnumSTATSTG_AddRef(*ppenum);
718 return S_OK;
721 return E_OUTOFMEMORY;
724 /************************************************************************
725 * Storage32BaseImpl_Stat (IStorage)
727 * This method will retrieve information about this storage object.
729 * See Windows documentation for more details on IStorage methods.
731 static HRESULT WINAPI StorageBaseImpl_Stat(
732 IStorage* iface,
733 STATSTG* pstatstg, /* [out] */
734 DWORD grfStatFlag) /* [in] */
736 StorageBaseImpl *This = (StorageBaseImpl *)iface;
737 DirEntry currentEntry;
738 HRESULT res = STG_E_UNKNOWN;
740 TRACE("(%p, %p, %x)\n",
741 iface, pstatstg, grfStatFlag);
743 if ( (This==0) || (pstatstg==0))
745 res = E_INVALIDARG;
746 goto end;
749 if (This->reverted)
751 res = STG_E_REVERTED;
752 goto end;
755 res = StorageBaseImpl_ReadDirEntry(
756 This,
757 This->storageDirEntry,
758 &currentEntry);
760 if (SUCCEEDED(res))
762 StorageUtl_CopyDirEntryToSTATSTG(
763 This,
764 pstatstg,
765 &currentEntry,
766 grfStatFlag);
768 pstatstg->grfMode = This->openFlags;
769 pstatstg->grfStateBits = This->stateBits;
772 end:
773 if (res == S_OK)
775 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
777 TRACE("<-- %08x\n", res);
778 return res;
781 /************************************************************************
782 * Storage32BaseImpl_RenameElement (IStorage)
784 * This method will rename the specified element.
786 * See Windows documentation for more details on IStorage methods.
788 static HRESULT WINAPI StorageBaseImpl_RenameElement(
789 IStorage* iface,
790 const OLECHAR* pwcsOldName, /* [in] */
791 const OLECHAR* pwcsNewName) /* [in] */
793 StorageBaseImpl *This = (StorageBaseImpl *)iface;
794 DirEntry currentEntry;
795 DirRef currentEntryRef;
797 TRACE("(%p, %s, %s)\n",
798 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
800 if (This->reverted)
801 return STG_E_REVERTED;
803 currentEntryRef = findElement(This,
804 This->storageDirEntry,
805 pwcsNewName,
806 &currentEntry);
808 if (currentEntryRef != DIRENTRY_NULL)
811 * There is already an element with the new name
813 return STG_E_FILEALREADYEXISTS;
817 * Search for the old element name
819 currentEntryRef = findElement(This,
820 This->storageDirEntry,
821 pwcsOldName,
822 &currentEntry);
824 if (currentEntryRef != DIRENTRY_NULL)
826 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
827 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
829 WARN("Element is already open; cannot rename.\n");
830 return STG_E_ACCESSDENIED;
833 /* Remove the element from its current position in the tree */
834 removeFromTree(This, This->storageDirEntry,
835 currentEntryRef);
837 /* Change the name of the element */
838 strcpyW(currentEntry.name, pwcsNewName);
840 /* Delete any sibling links */
841 currentEntry.leftChild = DIRENTRY_NULL;
842 currentEntry.rightChild = DIRENTRY_NULL;
844 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
845 &currentEntry);
847 /* Insert the element in a new position in the tree */
848 insertIntoTree(This, This->storageDirEntry,
849 currentEntryRef);
851 else
854 * There is no element with the old name
856 return STG_E_FILENOTFOUND;
859 return StorageBaseImpl_Flush(This);
862 /************************************************************************
863 * Storage32BaseImpl_CreateStream (IStorage)
865 * This method will create a stream object within this storage
867 * See Windows documentation for more details on IStorage methods.
869 static HRESULT WINAPI StorageBaseImpl_CreateStream(
870 IStorage* iface,
871 const OLECHAR* pwcsName, /* [string][in] */
872 DWORD grfMode, /* [in] */
873 DWORD reserved1, /* [in] */
874 DWORD reserved2, /* [in] */
875 IStream** ppstm) /* [out] */
877 StorageBaseImpl *This = (StorageBaseImpl *)iface;
878 StgStreamImpl* newStream;
879 DirEntry currentEntry, newStreamEntry;
880 DirRef currentEntryRef, newStreamEntryRef;
881 HRESULT hr;
883 TRACE("(%p, %s, %x, %d, %d, %p)\n",
884 iface, debugstr_w(pwcsName), grfMode,
885 reserved1, reserved2, ppstm);
887 if (ppstm == 0)
888 return STG_E_INVALIDPOINTER;
890 if (pwcsName == 0)
891 return STG_E_INVALIDNAME;
893 if (reserved1 || reserved2)
894 return STG_E_INVALIDPARAMETER;
896 if ( FAILED( validateSTGM(grfMode) ))
897 return STG_E_INVALIDFLAG;
899 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
900 return STG_E_INVALIDFLAG;
902 if (This->reverted)
903 return STG_E_REVERTED;
906 * As documented.
908 if ((grfMode & STGM_DELETEONRELEASE) ||
909 (grfMode & STGM_TRANSACTED))
910 return STG_E_INVALIDFUNCTION;
913 * Don't worry about permissions in transacted mode, as we can always write
914 * changes; we just can't always commit them.
916 if(!(This->openFlags & STGM_TRANSACTED)) {
917 /* Can't create a stream on read-only storage */
918 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
919 return STG_E_ACCESSDENIED;
921 /* Can't create a stream with greater access than the parent. */
922 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
923 return STG_E_ACCESSDENIED;
926 if(This->openFlags & STGM_SIMPLE)
927 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
929 *ppstm = 0;
931 currentEntryRef = findElement(This,
932 This->storageDirEntry,
933 pwcsName,
934 &currentEntry);
936 if (currentEntryRef != DIRENTRY_NULL)
939 * An element with this name already exists
941 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
943 IStorage_DestroyElement(iface, pwcsName);
945 else
946 return STG_E_FILEALREADYEXISTS;
950 * memset the empty entry
952 memset(&newStreamEntry, 0, sizeof(DirEntry));
954 newStreamEntry.sizeOfNameString =
955 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
957 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
958 return STG_E_INVALIDNAME;
960 strcpyW(newStreamEntry.name, pwcsName);
962 newStreamEntry.stgType = STGTY_STREAM;
963 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
964 newStreamEntry.size.u.LowPart = 0;
965 newStreamEntry.size.u.HighPart = 0;
967 newStreamEntry.leftChild = DIRENTRY_NULL;
968 newStreamEntry.rightChild = DIRENTRY_NULL;
969 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
971 /* call CoFileTime to get the current time
972 newStreamEntry.ctime
973 newStreamEntry.mtime
976 /* newStreamEntry.clsid */
979 * Create an entry with the new data
981 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
982 if (FAILED(hr))
983 return hr;
986 * Insert the new entry in the parent storage's tree.
988 hr = insertIntoTree(
989 This,
990 This->storageDirEntry,
991 newStreamEntryRef);
992 if (FAILED(hr))
994 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
995 return hr;
999 * Open the stream to return it.
1001 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1003 if (newStream != 0)
1005 *ppstm = (IStream*)newStream;
1007 IStream_AddRef(*ppstm);
1009 else
1011 return STG_E_INSUFFICIENTMEMORY;
1014 return StorageBaseImpl_Flush(This);
1017 /************************************************************************
1018 * Storage32BaseImpl_SetClass (IStorage)
1020 * This method will write the specified CLSID in the directory entry of this
1021 * storage.
1023 * See Windows documentation for more details on IStorage methods.
1025 static HRESULT WINAPI StorageBaseImpl_SetClass(
1026 IStorage* iface,
1027 REFCLSID clsid) /* [in] */
1029 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1030 HRESULT hRes;
1031 DirEntry currentEntry;
1033 TRACE("(%p, %p)\n", iface, clsid);
1035 if (This->reverted)
1036 return STG_E_REVERTED;
1038 hRes = StorageBaseImpl_ReadDirEntry(This,
1039 This->storageDirEntry,
1040 &currentEntry);
1041 if (SUCCEEDED(hRes))
1043 currentEntry.clsid = *clsid;
1045 hRes = StorageBaseImpl_WriteDirEntry(This,
1046 This->storageDirEntry,
1047 &currentEntry);
1050 if (SUCCEEDED(hRes))
1051 hRes = StorageBaseImpl_Flush(This);
1053 return hRes;
1056 /************************************************************************
1057 ** Storage32Impl implementation
1060 /************************************************************************
1061 * Storage32BaseImpl_CreateStorage (IStorage)
1063 * This method will create the storage object within the provided storage.
1065 * See Windows documentation for more details on IStorage methods.
1067 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1068 IStorage* iface,
1069 const OLECHAR *pwcsName, /* [string][in] */
1070 DWORD grfMode, /* [in] */
1071 DWORD reserved1, /* [in] */
1072 DWORD reserved2, /* [in] */
1073 IStorage **ppstg) /* [out] */
1075 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1077 DirEntry currentEntry;
1078 DirEntry newEntry;
1079 DirRef currentEntryRef;
1080 DirRef newEntryRef;
1081 HRESULT hr;
1083 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1084 iface, debugstr_w(pwcsName), grfMode,
1085 reserved1, reserved2, ppstg);
1087 if (ppstg == 0)
1088 return STG_E_INVALIDPOINTER;
1090 if (This->openFlags & STGM_SIMPLE)
1092 return STG_E_INVALIDFUNCTION;
1095 if (pwcsName == 0)
1096 return STG_E_INVALIDNAME;
1098 *ppstg = NULL;
1100 if ( FAILED( validateSTGM(grfMode) ) ||
1101 (grfMode & STGM_DELETEONRELEASE) )
1103 WARN("bad grfMode: 0x%x\n", grfMode);
1104 return STG_E_INVALIDFLAG;
1107 if (This->reverted)
1108 return STG_E_REVERTED;
1111 * Check that we're compatible with the parent's storage mode
1113 if ( !(This->openFlags & STGM_TRANSACTED) &&
1114 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1116 WARN("access denied\n");
1117 return STG_E_ACCESSDENIED;
1120 currentEntryRef = findElement(This,
1121 This->storageDirEntry,
1122 pwcsName,
1123 &currentEntry);
1125 if (currentEntryRef != DIRENTRY_NULL)
1128 * An element with this name already exists
1130 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1131 ((This->openFlags & STGM_TRANSACTED) ||
1132 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1134 hr = IStorage_DestroyElement(iface, pwcsName);
1135 if (FAILED(hr))
1136 return hr;
1138 else
1140 WARN("file already exists\n");
1141 return STG_E_FILEALREADYEXISTS;
1144 else if (!(This->openFlags & STGM_TRANSACTED) &&
1145 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1147 WARN("read-only storage\n");
1148 return STG_E_ACCESSDENIED;
1151 memset(&newEntry, 0, sizeof(DirEntry));
1153 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1155 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1157 FIXME("name too long\n");
1158 return STG_E_INVALIDNAME;
1161 strcpyW(newEntry.name, pwcsName);
1163 newEntry.stgType = STGTY_STORAGE;
1164 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1165 newEntry.size.u.LowPart = 0;
1166 newEntry.size.u.HighPart = 0;
1168 newEntry.leftChild = DIRENTRY_NULL;
1169 newEntry.rightChild = DIRENTRY_NULL;
1170 newEntry.dirRootEntry = DIRENTRY_NULL;
1172 /* call CoFileTime to get the current time
1173 newEntry.ctime
1174 newEntry.mtime
1177 /* newEntry.clsid */
1180 * Create a new directory entry for the storage
1182 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1183 if (FAILED(hr))
1184 return hr;
1187 * Insert the new directory entry into the parent storage's tree
1189 hr = insertIntoTree(
1190 This,
1191 This->storageDirEntry,
1192 newEntryRef);
1193 if (FAILED(hr))
1195 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1196 return hr;
1200 * Open it to get a pointer to return.
1202 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1204 if( (hr != S_OK) || (*ppstg == NULL))
1206 return hr;
1209 if (SUCCEEDED(hr))
1210 hr = StorageBaseImpl_Flush(This);
1212 return S_OK;
1216 /***************************************************************************
1218 * Internal Method
1220 * Reserve a directory entry in the file and initialize it.
1222 static HRESULT StorageImpl_CreateDirEntry(
1223 StorageBaseImpl *base,
1224 const DirEntry *newData,
1225 DirRef *index)
1227 StorageImpl *storage = (StorageImpl*)base;
1228 ULONG currentEntryIndex = 0;
1229 ULONG newEntryIndex = DIRENTRY_NULL;
1230 HRESULT hr = S_OK;
1231 BYTE currentData[RAW_DIRENTRY_SIZE];
1232 WORD sizeOfNameString;
1236 hr = StorageImpl_ReadRawDirEntry(storage,
1237 currentEntryIndex,
1238 currentData);
1240 if (SUCCEEDED(hr))
1242 StorageUtl_ReadWord(
1243 currentData,
1244 OFFSET_PS_NAMELENGTH,
1245 &sizeOfNameString);
1247 if (sizeOfNameString == 0)
1250 * The entry exists and is available, we found it.
1252 newEntryIndex = currentEntryIndex;
1255 else
1258 * We exhausted the directory entries, we will create more space below
1260 newEntryIndex = currentEntryIndex;
1262 currentEntryIndex++;
1264 } while (newEntryIndex == DIRENTRY_NULL);
1267 * grow the directory stream
1269 if (FAILED(hr))
1271 BYTE emptyData[RAW_DIRENTRY_SIZE];
1272 ULARGE_INTEGER newSize;
1273 ULONG entryIndex;
1274 ULONG lastEntry = 0;
1275 ULONG blockCount = 0;
1278 * obtain the new count of blocks in the directory stream
1280 blockCount = BlockChainStream_GetCount(
1281 storage->rootBlockChain)+1;
1284 * initialize the size used by the directory stream
1286 newSize.u.HighPart = 0;
1287 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1290 * add a block to the directory stream
1292 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1295 * memset the empty entry in order to initialize the unused newly
1296 * created entries
1298 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1301 * initialize them
1303 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1305 for(
1306 entryIndex = newEntryIndex + 1;
1307 entryIndex < lastEntry;
1308 entryIndex++)
1310 StorageImpl_WriteRawDirEntry(
1311 storage,
1312 entryIndex,
1313 emptyData);
1316 StorageImpl_SaveFileHeader(storage);
1319 UpdateRawDirEntry(currentData, newData);
1321 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1323 if (SUCCEEDED(hr))
1324 *index = newEntryIndex;
1326 return hr;
1329 /***************************************************************************
1331 * Internal Method
1333 * Mark a directory entry in the file as free.
1335 static HRESULT StorageImpl_DestroyDirEntry(
1336 StorageBaseImpl *base,
1337 DirRef index)
1339 HRESULT hr;
1340 BYTE emptyData[RAW_DIRENTRY_SIZE];
1341 StorageImpl *storage = (StorageImpl*)base;
1343 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1345 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1347 return hr;
1351 /****************************************************************************
1353 * Internal Method
1355 * Case insensitive comparison of DirEntry.name by first considering
1356 * their size.
1358 * Returns <0 when name1 < name2
1359 * >0 when name1 > name2
1360 * 0 when name1 == name2
1362 static LONG entryNameCmp(
1363 const OLECHAR *name1,
1364 const OLECHAR *name2)
1366 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1368 while (diff == 0 && *name1 != 0)
1371 * We compare the string themselves only when they are of the same length
1373 diff = toupperW(*name1++) - toupperW(*name2++);
1376 return diff;
1379 /****************************************************************************
1381 * Internal Method
1383 * Add a directory entry to a storage
1385 static HRESULT insertIntoTree(
1386 StorageBaseImpl *This,
1387 DirRef parentStorageIndex,
1388 DirRef newEntryIndex)
1390 DirEntry currentEntry;
1391 DirEntry newEntry;
1394 * Read the inserted entry
1396 StorageBaseImpl_ReadDirEntry(This,
1397 newEntryIndex,
1398 &newEntry);
1401 * Read the storage entry
1403 StorageBaseImpl_ReadDirEntry(This,
1404 parentStorageIndex,
1405 &currentEntry);
1407 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1410 * The root storage contains some element, therefore, start the research
1411 * for the appropriate location.
1413 BOOL found = 0;
1414 DirRef current, next, previous, currentEntryId;
1417 * Keep a reference to the root of the storage's element tree
1419 currentEntryId = currentEntry.dirRootEntry;
1422 * Read
1424 StorageBaseImpl_ReadDirEntry(This,
1425 currentEntry.dirRootEntry,
1426 &currentEntry);
1428 previous = currentEntry.leftChild;
1429 next = currentEntry.rightChild;
1430 current = currentEntryId;
1432 while (found == 0)
1434 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1436 if (diff < 0)
1438 if (previous != DIRENTRY_NULL)
1440 StorageBaseImpl_ReadDirEntry(This,
1441 previous,
1442 &currentEntry);
1443 current = previous;
1445 else
1447 currentEntry.leftChild = newEntryIndex;
1448 StorageBaseImpl_WriteDirEntry(This,
1449 current,
1450 &currentEntry);
1451 found = 1;
1454 else if (diff > 0)
1456 if (next != DIRENTRY_NULL)
1458 StorageBaseImpl_ReadDirEntry(This,
1459 next,
1460 &currentEntry);
1461 current = next;
1463 else
1465 currentEntry.rightChild = newEntryIndex;
1466 StorageBaseImpl_WriteDirEntry(This,
1467 current,
1468 &currentEntry);
1469 found = 1;
1472 else
1475 * Trying to insert an item with the same name in the
1476 * subtree structure.
1478 return STG_E_FILEALREADYEXISTS;
1481 previous = currentEntry.leftChild;
1482 next = currentEntry.rightChild;
1485 else
1488 * The storage is empty, make the new entry the root of its element tree
1490 currentEntry.dirRootEntry = newEntryIndex;
1491 StorageBaseImpl_WriteDirEntry(This,
1492 parentStorageIndex,
1493 &currentEntry);
1496 return S_OK;
1499 /****************************************************************************
1501 * Internal Method
1503 * Find and read the element of a storage with the given name.
1505 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1506 const OLECHAR *name, DirEntry *data)
1508 DirRef currentEntry;
1510 /* Read the storage entry to find the root of the tree. */
1511 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1513 currentEntry = data->dirRootEntry;
1515 while (currentEntry != DIRENTRY_NULL)
1517 LONG cmp;
1519 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1521 cmp = entryNameCmp(name, data->name);
1523 if (cmp == 0)
1524 /* found it */
1525 break;
1527 else if (cmp < 0)
1528 currentEntry = data->leftChild;
1530 else if (cmp > 0)
1531 currentEntry = data->rightChild;
1534 return currentEntry;
1537 /****************************************************************************
1539 * Internal Method
1541 * Find and read the binary tree parent of the element with the given name.
1543 * If there is no such element, find a place where it could be inserted and
1544 * return STG_E_FILENOTFOUND.
1546 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1547 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1548 ULONG *relation)
1550 DirRef childEntry;
1551 DirEntry childData;
1553 /* Read the storage entry to find the root of the tree. */
1554 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1556 *parentEntry = storageEntry;
1557 *relation = DIRENTRY_RELATION_DIR;
1559 childEntry = parentData->dirRootEntry;
1561 while (childEntry != DIRENTRY_NULL)
1563 LONG cmp;
1565 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1567 cmp = entryNameCmp(childName, childData.name);
1569 if (cmp == 0)
1570 /* found it */
1571 break;
1573 else if (cmp < 0)
1575 *parentData = childData;
1576 *parentEntry = childEntry;
1577 *relation = DIRENTRY_RELATION_PREVIOUS;
1579 childEntry = parentData->leftChild;
1582 else if (cmp > 0)
1584 *parentData = childData;
1585 *parentEntry = childEntry;
1586 *relation = DIRENTRY_RELATION_NEXT;
1588 childEntry = parentData->rightChild;
1592 if (childEntry == DIRENTRY_NULL)
1593 return STG_E_FILENOTFOUND;
1594 else
1595 return S_OK;
1599 /*************************************************************************
1600 * CopyTo (IStorage)
1602 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1603 IStorage* iface,
1604 DWORD ciidExclude, /* [in] */
1605 const IID* rgiidExclude, /* [size_is][unique][in] */
1606 SNB snbExclude, /* [unique][in] */
1607 IStorage* pstgDest) /* [unique][in] */
1609 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1611 IEnumSTATSTG *elements = 0;
1612 STATSTG curElement, strStat;
1613 HRESULT hr;
1614 IStorage *pstgTmp, *pstgChild;
1615 IStream *pstrTmp, *pstrChild;
1616 DirRef srcEntryRef;
1617 DirEntry srcEntry;
1618 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1619 int i;
1621 TRACE("(%p, %d, %p, %p, %p)\n",
1622 iface, ciidExclude, rgiidExclude,
1623 snbExclude, pstgDest);
1625 if ( pstgDest == 0 )
1626 return STG_E_INVALIDPOINTER;
1629 * Enumerate the elements
1631 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1633 if ( hr != S_OK )
1634 return hr;
1637 * set the class ID
1639 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1640 IStorage_SetClass( pstgDest, &curElement.clsid );
1642 for(i = 0; i < ciidExclude; ++i)
1644 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1645 skip_storage = TRUE;
1646 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1647 skip_stream = TRUE;
1648 else
1649 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1655 * Obtain the next element
1657 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1659 if ( hr == S_FALSE )
1661 hr = S_OK; /* done, every element has been copied */
1662 break;
1665 if ( snbExclude )
1667 WCHAR **snb = snbExclude;
1668 skip = FALSE;
1669 while ( *snb != NULL && !skip )
1671 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1672 skip = TRUE;
1673 ++snb;
1677 if ( skip )
1678 goto cleanup;
1680 if (curElement.type == STGTY_STORAGE)
1682 if(skip_storage)
1683 goto cleanup;
1686 * open child source storage
1688 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1689 STGM_READ|STGM_SHARE_EXCLUSIVE,
1690 NULL, 0, &pstgChild );
1692 if (hr != S_OK)
1693 goto cleanup;
1696 * create a new storage in destination storage
1698 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1699 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1700 0, 0,
1701 &pstgTmp );
1703 * if it already exist, don't create a new one use this one
1705 if (hr == STG_E_FILEALREADYEXISTS)
1707 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1708 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1709 NULL, 0, &pstgTmp );
1712 if (hr == S_OK)
1715 * do the copy recursively
1717 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1718 NULL, pstgTmp );
1720 IStorage_Release( pstgTmp );
1723 IStorage_Release( pstgChild );
1725 else if (curElement.type == STGTY_STREAM)
1727 if(skip_stream)
1728 goto cleanup;
1731 * create a new stream in destination storage. If the stream already
1732 * exist, it will be deleted and a new one will be created.
1734 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1735 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1736 0, 0, &pstrTmp );
1738 if (hr != S_OK)
1739 goto cleanup;
1742 * open child stream storage. This operation must succeed even if the
1743 * stream is already open, so we use internal functions to do it.
1745 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1746 &srcEntry);
1747 if (!srcEntryRef)
1749 ERR("source stream not found\n");
1750 hr = STG_E_DOCFILECORRUPT;
1753 if (hr == S_OK)
1755 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1756 if (pstrChild)
1757 IStream_AddRef(pstrChild);
1758 else
1759 hr = E_OUTOFMEMORY;
1762 if (hr == S_OK)
1765 * Get the size of the source stream
1767 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1770 * Set the size of the destination stream.
1772 IStream_SetSize(pstrTmp, strStat.cbSize);
1775 * do the copy
1777 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1778 NULL, NULL );
1780 IStream_Release( pstrChild );
1783 IStream_Release( pstrTmp );
1785 else
1787 WARN("unknown element type: %d\n", curElement.type);
1790 cleanup:
1791 CoTaskMemFree(curElement.pwcsName);
1792 } while (hr == S_OK);
1795 * Clean-up
1797 IEnumSTATSTG_Release(elements);
1799 return hr;
1802 /*************************************************************************
1803 * MoveElementTo (IStorage)
1805 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1806 IStorage* iface,
1807 const OLECHAR *pwcsName, /* [string][in] */
1808 IStorage *pstgDest, /* [unique][in] */
1809 const OLECHAR *pwcsNewName,/* [string][in] */
1810 DWORD grfFlags) /* [in] */
1812 FIXME("(%p %s %p %s %u): stub\n", iface,
1813 debugstr_w(pwcsName), pstgDest,
1814 debugstr_w(pwcsNewName), grfFlags);
1815 return E_NOTIMPL;
1818 /*************************************************************************
1819 * Commit (IStorage)
1821 * Ensures that any changes made to a storage object open in transacted mode
1822 * are reflected in the parent storage
1824 * In a non-transacted mode, this ensures all cached writes are completed.
1826 static HRESULT WINAPI StorageImpl_Commit(
1827 IStorage* iface,
1828 DWORD grfCommitFlags)/* [in] */
1830 StorageBaseImpl* const base=(StorageBaseImpl*)iface;
1831 TRACE("(%p %d)\n", iface, grfCommitFlags);
1832 return StorageBaseImpl_Flush(base);
1835 /*************************************************************************
1836 * Revert (IStorage)
1838 * Discard all changes that have been made since the last commit operation
1840 static HRESULT WINAPI StorageImpl_Revert(
1841 IStorage* iface)
1843 TRACE("(%p)\n", iface);
1844 return S_OK;
1847 /*************************************************************************
1848 * DestroyElement (IStorage)
1850 * Strategy: This implementation is built this way for simplicity not for speed.
1851 * I always delete the topmost element of the enumeration and adjust
1852 * the deleted element pointer all the time. This takes longer to
1853 * do but allow to reinvoke DestroyElement whenever we encounter a
1854 * storage object. The optimisation resides in the usage of another
1855 * enumeration strategy that would give all the leaves of a storage
1856 * first. (postfix order)
1858 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1859 IStorage* iface,
1860 const OLECHAR *pwcsName)/* [string][in] */
1862 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1864 HRESULT hr = S_OK;
1865 DirEntry entryToDelete;
1866 DirRef entryToDeleteRef;
1868 TRACE("(%p, %s)\n",
1869 iface, debugstr_w(pwcsName));
1871 if (pwcsName==NULL)
1872 return STG_E_INVALIDPOINTER;
1874 if (This->reverted)
1875 return STG_E_REVERTED;
1877 if ( !(This->openFlags & STGM_TRANSACTED) &&
1878 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1879 return STG_E_ACCESSDENIED;
1881 entryToDeleteRef = findElement(
1882 This,
1883 This->storageDirEntry,
1884 pwcsName,
1885 &entryToDelete);
1887 if ( entryToDeleteRef == DIRENTRY_NULL )
1889 return STG_E_FILENOTFOUND;
1892 if ( entryToDelete.stgType == STGTY_STORAGE )
1894 hr = deleteStorageContents(
1895 This,
1896 entryToDeleteRef,
1897 entryToDelete);
1899 else if ( entryToDelete.stgType == STGTY_STREAM )
1901 hr = deleteStreamContents(
1902 This,
1903 entryToDeleteRef,
1904 entryToDelete);
1907 if (hr!=S_OK)
1908 return hr;
1911 * Remove the entry from its parent storage
1913 hr = removeFromTree(
1914 This,
1915 This->storageDirEntry,
1916 entryToDeleteRef);
1919 * Invalidate the entry
1921 if (SUCCEEDED(hr))
1922 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1924 if (SUCCEEDED(hr))
1925 hr = StorageBaseImpl_Flush(This);
1927 return hr;
1931 /******************************************************************************
1932 * Internal stream list handlers
1935 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1937 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1938 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1941 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1943 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1944 list_remove(&(strm->StrmListEntry));
1947 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1949 StgStreamImpl *strm;
1951 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1953 if (strm->dirEntry == streamEntry)
1955 return TRUE;
1959 return FALSE;
1962 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1964 StorageInternalImpl *childstg;
1966 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1968 if (childstg->base.storageDirEntry == storageEntry)
1970 return TRUE;
1974 return FALSE;
1977 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1979 struct list *cur, *cur2;
1980 StgStreamImpl *strm=NULL;
1981 StorageInternalImpl *childstg=NULL;
1983 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1984 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1985 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1986 strm->parentStorage = NULL;
1987 list_remove(cur);
1990 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1991 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1992 StorageBaseImpl_Invalidate( &childstg->base );
1995 if (stg->transactedChild)
1997 StorageBaseImpl_Invalidate(stg->transactedChild);
1999 stg->transactedChild = NULL;
2004 /*********************************************************************
2006 * Internal Method
2008 * Delete the contents of a storage entry.
2011 static HRESULT deleteStorageContents(
2012 StorageBaseImpl *parentStorage,
2013 DirRef indexToDelete,
2014 DirEntry entryDataToDelete)
2016 IEnumSTATSTG *elements = 0;
2017 IStorage *childStorage = 0;
2018 STATSTG currentElement;
2019 HRESULT hr;
2020 HRESULT destroyHr = S_OK;
2021 StorageInternalImpl *stg, *stg2;
2023 /* Invalidate any open storage objects. */
2024 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2026 if (stg->base.storageDirEntry == indexToDelete)
2028 StorageBaseImpl_Invalidate(&stg->base);
2033 * Open the storage and enumerate it
2035 hr = StorageBaseImpl_OpenStorage(
2036 (IStorage*)parentStorage,
2037 entryDataToDelete.name,
2039 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2042 &childStorage);
2044 if (hr != S_OK)
2046 return hr;
2050 * Enumerate the elements
2052 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2057 * Obtain the next element
2059 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2060 if (hr==S_OK)
2062 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2064 CoTaskMemFree(currentElement.pwcsName);
2068 * We need to Reset the enumeration every time because we delete elements
2069 * and the enumeration could be invalid
2071 IEnumSTATSTG_Reset(elements);
2073 } while ((hr == S_OK) && (destroyHr == S_OK));
2075 IStorage_Release(childStorage);
2076 IEnumSTATSTG_Release(elements);
2078 return destroyHr;
2081 /*********************************************************************
2083 * Internal Method
2085 * Perform the deletion of a stream's data
2088 static HRESULT deleteStreamContents(
2089 StorageBaseImpl *parentStorage,
2090 DirRef indexToDelete,
2091 DirEntry entryDataToDelete)
2093 IStream *pis;
2094 HRESULT hr;
2095 ULARGE_INTEGER size;
2096 StgStreamImpl *strm, *strm2;
2098 /* Invalidate any open stream objects. */
2099 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2101 if (strm->dirEntry == indexToDelete)
2103 TRACE("Stream deleted %p\n", strm);
2104 strm->parentStorage = NULL;
2105 list_remove(&strm->StrmListEntry);
2109 size.u.HighPart = 0;
2110 size.u.LowPart = 0;
2112 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2113 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2115 if (hr!=S_OK)
2117 return(hr);
2121 * Zap the stream
2123 hr = IStream_SetSize(pis, size);
2125 if(hr != S_OK)
2127 return hr;
2131 * Release the stream object.
2133 IStream_Release(pis);
2135 return S_OK;
2138 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2140 switch (relation)
2142 case DIRENTRY_RELATION_PREVIOUS:
2143 entry->leftChild = new_target;
2144 break;
2145 case DIRENTRY_RELATION_NEXT:
2146 entry->rightChild = new_target;
2147 break;
2148 case DIRENTRY_RELATION_DIR:
2149 entry->dirRootEntry = new_target;
2150 break;
2151 default:
2152 assert(0);
2156 /*************************************************************************
2158 * Internal Method
2160 * This method removes a directory entry from its parent storage tree without
2161 * freeing any resources attached to it.
2163 static HRESULT removeFromTree(
2164 StorageBaseImpl *This,
2165 DirRef parentStorageIndex,
2166 DirRef deletedIndex)
2168 HRESULT hr = S_OK;
2169 DirEntry entryToDelete;
2170 DirEntry parentEntry;
2171 DirRef parentEntryRef;
2172 ULONG typeOfRelation;
2174 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2176 if (hr != S_OK)
2177 return hr;
2180 * Find the element that links to the one we want to delete.
2182 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2183 &parentEntry, &parentEntryRef, &typeOfRelation);
2185 if (hr != S_OK)
2186 return hr;
2188 if (entryToDelete.leftChild != DIRENTRY_NULL)
2191 * Replace the deleted entry with its left child
2193 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2195 hr = StorageBaseImpl_WriteDirEntry(
2196 This,
2197 parentEntryRef,
2198 &parentEntry);
2199 if(FAILED(hr))
2201 return hr;
2204 if (entryToDelete.rightChild != DIRENTRY_NULL)
2207 * We need to reinsert the right child somewhere. We already know it and
2208 * its children are greater than everything in the left tree, so we
2209 * insert it at the rightmost point in the left tree.
2211 DirRef newRightChildParent = entryToDelete.leftChild;
2212 DirEntry newRightChildParentEntry;
2216 hr = StorageBaseImpl_ReadDirEntry(
2217 This,
2218 newRightChildParent,
2219 &newRightChildParentEntry);
2220 if (FAILED(hr))
2222 return hr;
2225 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2226 newRightChildParent = newRightChildParentEntry.rightChild;
2227 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2229 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2231 hr = StorageBaseImpl_WriteDirEntry(
2232 This,
2233 newRightChildParent,
2234 &newRightChildParentEntry);
2235 if (FAILED(hr))
2237 return hr;
2241 else
2244 * Replace the deleted entry with its right child
2246 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2248 hr = StorageBaseImpl_WriteDirEntry(
2249 This,
2250 parentEntryRef,
2251 &parentEntry);
2252 if(FAILED(hr))
2254 return hr;
2258 return hr;
2262 /******************************************************************************
2263 * SetElementTimes (IStorage)
2265 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2266 IStorage* iface,
2267 const OLECHAR *pwcsName,/* [string][in] */
2268 const FILETIME *pctime, /* [in] */
2269 const FILETIME *patime, /* [in] */
2270 const FILETIME *pmtime) /* [in] */
2272 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2273 return S_OK;
2276 /******************************************************************************
2277 * SetStateBits (IStorage)
2279 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2280 IStorage* iface,
2281 DWORD grfStateBits,/* [in] */
2282 DWORD grfMask) /* [in] */
2284 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2286 if (This->reverted)
2287 return STG_E_REVERTED;
2289 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2290 return S_OK;
2293 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2294 DirRef index, const DirEntry *data)
2296 StorageImpl *This = (StorageImpl*)base;
2297 return StorageImpl_WriteDirEntry(This, index, data);
2300 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2301 DirRef index, DirEntry *data)
2303 StorageImpl *This = (StorageImpl*)base;
2304 return StorageImpl_ReadDirEntry(This, index, data);
2307 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2309 int i;
2311 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2313 if (!This->blockChainCache[i])
2315 return &This->blockChainCache[i];
2319 i = This->blockChainToEvict;
2321 BlockChainStream_Destroy(This->blockChainCache[i]);
2322 This->blockChainCache[i] = NULL;
2324 This->blockChainToEvict++;
2325 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2326 This->blockChainToEvict = 0;
2328 return &This->blockChainCache[i];
2331 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2332 DirRef index)
2334 int i, free_index=-1;
2336 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2338 if (!This->blockChainCache[i])
2340 if (free_index == -1) free_index = i;
2342 else if (This->blockChainCache[i]->ownerDirEntry == index)
2344 return &This->blockChainCache[i];
2348 if (free_index == -1)
2350 free_index = This->blockChainToEvict;
2352 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2353 This->blockChainCache[free_index] = NULL;
2355 This->blockChainToEvict++;
2356 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2357 This->blockChainToEvict = 0;
2360 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2361 return &This->blockChainCache[free_index];
2364 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2366 int i;
2368 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2370 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2372 BlockChainStream_Destroy(This->blockChainCache[i]);
2373 This->blockChainCache[i] = NULL;
2374 return;
2379 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2380 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2382 StorageImpl *This = (StorageImpl*)base;
2383 DirEntry data;
2384 HRESULT hr;
2385 ULONG bytesToRead;
2387 hr = StorageImpl_ReadDirEntry(This, index, &data);
2388 if (FAILED(hr)) return hr;
2390 if (data.size.QuadPart == 0)
2392 *bytesRead = 0;
2393 return S_OK;
2396 if (offset.QuadPart + size > data.size.QuadPart)
2398 bytesToRead = data.size.QuadPart - offset.QuadPart;
2400 else
2402 bytesToRead = size;
2405 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2407 SmallBlockChainStream *stream;
2409 stream = SmallBlockChainStream_Construct(This, NULL, index);
2410 if (!stream) return E_OUTOFMEMORY;
2412 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2414 SmallBlockChainStream_Destroy(stream);
2416 return hr;
2418 else
2420 BlockChainStream *stream = NULL;
2422 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2423 if (!stream) return E_OUTOFMEMORY;
2425 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2427 return hr;
2431 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2432 ULARGE_INTEGER newsize)
2434 StorageImpl *This = (StorageImpl*)base;
2435 DirEntry data;
2436 HRESULT hr;
2437 SmallBlockChainStream *smallblock=NULL;
2438 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2440 hr = StorageImpl_ReadDirEntry(This, index, &data);
2441 if (FAILED(hr)) return hr;
2443 /* In simple mode keep the stream size above the small block limit */
2444 if (This->base.openFlags & STGM_SIMPLE)
2445 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2447 if (data.size.QuadPart == newsize.QuadPart)
2448 return S_OK;
2450 /* Create a block chain object of the appropriate type */
2451 if (data.size.QuadPart == 0)
2453 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2455 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2456 if (!smallblock) return E_OUTOFMEMORY;
2458 else
2460 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2461 bigblock = *pbigblock;
2462 if (!bigblock) return E_OUTOFMEMORY;
2465 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2467 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2468 if (!smallblock) return E_OUTOFMEMORY;
2470 else
2472 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2473 bigblock = *pbigblock;
2474 if (!bigblock) return E_OUTOFMEMORY;
2477 /* Change the block chain type if necessary. */
2478 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2480 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2481 if (!bigblock)
2483 SmallBlockChainStream_Destroy(smallblock);
2484 return E_FAIL;
2487 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2488 *pbigblock = bigblock;
2490 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2492 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2493 if (!smallblock)
2494 return E_FAIL;
2497 /* Set the size of the block chain. */
2498 if (smallblock)
2500 SmallBlockChainStream_SetSize(smallblock, newsize);
2501 SmallBlockChainStream_Destroy(smallblock);
2503 else
2505 BlockChainStream_SetSize(bigblock, newsize);
2508 /* Set the size in the directory entry. */
2509 hr = StorageImpl_ReadDirEntry(This, index, &data);
2510 if (SUCCEEDED(hr))
2512 data.size = newsize;
2514 hr = StorageImpl_WriteDirEntry(This, index, &data);
2516 return hr;
2519 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2520 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2522 StorageImpl *This = (StorageImpl*)base;
2523 DirEntry data;
2524 HRESULT hr;
2525 ULARGE_INTEGER newSize;
2527 hr = StorageImpl_ReadDirEntry(This, index, &data);
2528 if (FAILED(hr)) return hr;
2530 /* Grow the stream if necessary */
2531 newSize.QuadPart = 0;
2532 newSize.QuadPart = offset.QuadPart + size;
2534 if (newSize.QuadPart > data.size.QuadPart)
2536 hr = StorageImpl_StreamSetSize(base, index, newSize);
2537 if (FAILED(hr))
2538 return hr;
2540 hr = StorageImpl_ReadDirEntry(This, index, &data);
2541 if (FAILED(hr)) return hr;
2544 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2546 SmallBlockChainStream *stream;
2548 stream = SmallBlockChainStream_Construct(This, NULL, index);
2549 if (!stream) return E_OUTOFMEMORY;
2551 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2553 SmallBlockChainStream_Destroy(stream);
2555 return hr;
2557 else
2559 BlockChainStream *stream;
2561 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2562 if (!stream) return E_OUTOFMEMORY;
2564 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2566 return hr;
2570 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2571 DirRef src)
2573 StorageImpl *This = (StorageImpl*)base;
2574 DirEntry dst_data, src_data;
2575 HRESULT hr;
2577 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2579 if (SUCCEEDED(hr))
2580 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2582 if (SUCCEEDED(hr))
2584 StorageImpl_DeleteCachedBlockChainStream(This, src);
2585 dst_data.startingBlock = src_data.startingBlock;
2586 dst_data.size = src_data.size;
2588 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2591 return hr;
2594 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2596 StorageImpl *This = (StorageImpl*) iface;
2597 STATSTG statstg;
2598 HRESULT hr;
2600 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2602 *result = statstg.pwcsName;
2604 return hr;
2608 * Virtual function table for the IStorage32Impl class.
2610 static const IStorageVtbl Storage32Impl_Vtbl =
2612 StorageBaseImpl_QueryInterface,
2613 StorageBaseImpl_AddRef,
2614 StorageBaseImpl_Release,
2615 StorageBaseImpl_CreateStream,
2616 StorageBaseImpl_OpenStream,
2617 StorageBaseImpl_CreateStorage,
2618 StorageBaseImpl_OpenStorage,
2619 StorageBaseImpl_CopyTo,
2620 StorageBaseImpl_MoveElementTo,
2621 StorageImpl_Commit,
2622 StorageImpl_Revert,
2623 StorageBaseImpl_EnumElements,
2624 StorageBaseImpl_DestroyElement,
2625 StorageBaseImpl_RenameElement,
2626 StorageBaseImpl_SetElementTimes,
2627 StorageBaseImpl_SetClass,
2628 StorageBaseImpl_SetStateBits,
2629 StorageBaseImpl_Stat
2632 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2634 StorageImpl_Destroy,
2635 StorageImpl_Invalidate,
2636 StorageImpl_Flush,
2637 StorageImpl_GetFilename,
2638 StorageImpl_CreateDirEntry,
2639 StorageImpl_BaseWriteDirEntry,
2640 StorageImpl_BaseReadDirEntry,
2641 StorageImpl_DestroyDirEntry,
2642 StorageImpl_StreamReadAt,
2643 StorageImpl_StreamWriteAt,
2644 StorageImpl_StreamSetSize,
2645 StorageImpl_StreamLink
2648 static HRESULT StorageImpl_Construct(
2649 HANDLE hFile,
2650 LPCOLESTR pwcsName,
2651 ILockBytes* pLkbyt,
2652 DWORD openFlags,
2653 BOOL fileBased,
2654 BOOL create,
2655 ULONG sector_size,
2656 StorageImpl** result)
2658 StorageImpl* This;
2659 HRESULT hr = S_OK;
2660 DirEntry currentEntry;
2661 DirRef currentEntryRef;
2663 if ( FAILED( validateSTGM(openFlags) ))
2664 return STG_E_INVALIDFLAG;
2666 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2667 if (!This)
2668 return E_OUTOFMEMORY;
2670 memset(This, 0, sizeof(StorageImpl));
2672 list_init(&This->base.strmHead);
2674 list_init(&This->base.storageHead);
2676 This->base.lpVtbl = &Storage32Impl_Vtbl;
2677 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2678 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2679 This->base.openFlags = (openFlags & ~STGM_CREATE);
2680 This->base.ref = 1;
2681 This->base.create = create;
2683 This->base.reverted = 0;
2686 * Initialize the big block cache.
2688 This->bigBlockSize = sector_size;
2689 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2690 if (hFile)
2691 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2692 else
2694 This->lockBytes = pLkbyt;
2695 ILockBytes_AddRef(pLkbyt);
2698 if (FAILED(hr))
2699 goto end;
2701 if (create)
2703 ULARGE_INTEGER size;
2704 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2707 * Initialize all header variables:
2708 * - The big block depot consists of one block and it is at block 0
2709 * - The directory table starts at block 1
2710 * - There is no small block depot
2712 memset( This->bigBlockDepotStart,
2713 BLOCK_UNUSED,
2714 sizeof(This->bigBlockDepotStart));
2716 This->bigBlockDepotCount = 1;
2717 This->bigBlockDepotStart[0] = 0;
2718 This->rootStartBlock = 1;
2719 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2720 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2721 if (sector_size == 4096)
2722 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2723 else
2724 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2725 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2726 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2727 This->extBigBlockDepotCount = 0;
2729 StorageImpl_SaveFileHeader(This);
2732 * Add one block for the big block depot and one block for the directory table
2734 size.u.HighPart = 0;
2735 size.u.LowPart = This->bigBlockSize * 3;
2736 ILockBytes_SetSize(This->lockBytes, size);
2739 * Initialize the big block depot
2741 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2742 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2743 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2744 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2746 else
2749 * Load the header for the file.
2751 hr = StorageImpl_LoadFileHeader(This);
2753 if (FAILED(hr))
2755 goto end;
2760 * There is no block depot cached yet.
2762 This->indexBlockDepotCached = 0xFFFFFFFF;
2765 * Start searching for free blocks with block 0.
2767 This->prevFreeBlock = 0;
2769 This->firstFreeSmallBlock = 0;
2772 * Create the block chain abstractions.
2774 if(!(This->rootBlockChain =
2775 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2777 hr = STG_E_READFAULT;
2778 goto end;
2781 if(!(This->smallBlockDepotChain =
2782 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2783 DIRENTRY_NULL)))
2785 hr = STG_E_READFAULT;
2786 goto end;
2790 * Write the root storage entry (memory only)
2792 if (create)
2794 DirEntry rootEntry;
2796 * Initialize the directory table
2798 memset(&rootEntry, 0, sizeof(rootEntry));
2799 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2800 sizeof(rootEntry.name)/sizeof(WCHAR) );
2801 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2802 rootEntry.stgType = STGTY_ROOT;
2803 rootEntry.leftChild = DIRENTRY_NULL;
2804 rootEntry.rightChild = DIRENTRY_NULL;
2805 rootEntry.dirRootEntry = DIRENTRY_NULL;
2806 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2807 rootEntry.size.u.HighPart = 0;
2808 rootEntry.size.u.LowPart = 0;
2810 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2814 * Find the ID of the root storage.
2816 currentEntryRef = 0;
2820 hr = StorageImpl_ReadDirEntry(
2821 This,
2822 currentEntryRef,
2823 &currentEntry);
2825 if (SUCCEEDED(hr))
2827 if ( (currentEntry.sizeOfNameString != 0 ) &&
2828 (currentEntry.stgType == STGTY_ROOT) )
2830 This->base.storageDirEntry = currentEntryRef;
2834 currentEntryRef++;
2836 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2838 if (FAILED(hr))
2840 hr = STG_E_READFAULT;
2841 goto end;
2845 * Create the block chain abstraction for the small block root chain.
2847 if(!(This->smallBlockRootChain =
2848 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2850 hr = STG_E_READFAULT;
2853 end:
2854 if (FAILED(hr))
2856 IStorage_Release((IStorage*)This);
2857 *result = NULL;
2859 else
2861 StorageImpl_Flush((StorageBaseImpl*)This);
2862 *result = This;
2865 return hr;
2868 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2870 StorageImpl *This = (StorageImpl*) iface;
2872 StorageBaseImpl_DeleteAll(&This->base);
2874 This->base.reverted = 1;
2877 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2879 StorageImpl *This = (StorageImpl*) iface;
2880 int i;
2881 TRACE("(%p)\n", This);
2883 StorageImpl_Flush(iface);
2885 StorageImpl_Invalidate(iface);
2887 BlockChainStream_Destroy(This->smallBlockRootChain);
2888 BlockChainStream_Destroy(This->rootBlockChain);
2889 BlockChainStream_Destroy(This->smallBlockDepotChain);
2891 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2892 BlockChainStream_Destroy(This->blockChainCache[i]);
2894 if (This->lockBytes)
2895 ILockBytes_Release(This->lockBytes);
2896 HeapFree(GetProcessHeap(), 0, This);
2899 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2901 StorageImpl *This = (StorageImpl*) iface;
2902 int i;
2903 HRESULT hr;
2904 TRACE("(%p)\n", This);
2906 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2908 if (SUCCEEDED(hr))
2909 hr = BlockChainStream_Flush(This->rootBlockChain);
2911 if (SUCCEEDED(hr))
2912 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2914 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
2915 if (This->blockChainCache[i])
2916 hr = BlockChainStream_Flush(This->blockChainCache[i]);
2918 if (SUCCEEDED(hr))
2919 hr = ILockBytes_Flush(This->lockBytes);
2921 return hr;
2924 /******************************************************************************
2925 * Storage32Impl_GetNextFreeBigBlock
2927 * Returns the index of the next free big block.
2928 * If the big block depot is filled, this method will enlarge it.
2931 static ULONG StorageImpl_GetNextFreeBigBlock(
2932 StorageImpl* This)
2934 ULONG depotBlockIndexPos;
2935 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2936 BOOL success;
2937 ULONG depotBlockOffset;
2938 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2939 ULONG nextBlockIndex = BLOCK_SPECIAL;
2940 int depotIndex = 0;
2941 ULONG freeBlock = BLOCK_UNUSED;
2942 ULARGE_INTEGER neededSize;
2943 STATSTG statstg;
2945 depotIndex = This->prevFreeBlock / blocksPerDepot;
2946 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2949 * Scan the entire big block depot until we find a block marked free
2951 while (nextBlockIndex != BLOCK_UNUSED)
2953 if (depotIndex < COUNT_BBDEPOTINHEADER)
2955 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2958 * Grow the primary depot.
2960 if (depotBlockIndexPos == BLOCK_UNUSED)
2962 depotBlockIndexPos = depotIndex*blocksPerDepot;
2965 * Add a block depot.
2967 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2968 This->bigBlockDepotCount++;
2969 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2972 * Flag it as a block depot.
2974 StorageImpl_SetNextBlockInChain(This,
2975 depotBlockIndexPos,
2976 BLOCK_SPECIAL);
2978 /* Save new header information.
2980 StorageImpl_SaveFileHeader(This);
2983 else
2985 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2987 if (depotBlockIndexPos == BLOCK_UNUSED)
2990 * Grow the extended depot.
2992 ULONG extIndex = BLOCK_UNUSED;
2993 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2994 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2996 if (extBlockOffset == 0)
2998 /* We need an extended block.
3000 extIndex = Storage32Impl_AddExtBlockDepot(This);
3001 This->extBigBlockDepotCount++;
3002 depotBlockIndexPos = extIndex + 1;
3004 else
3005 depotBlockIndexPos = depotIndex * blocksPerDepot;
3008 * Add a block depot and mark it in the extended block.
3010 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3011 This->bigBlockDepotCount++;
3012 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3014 /* Flag the block depot.
3016 StorageImpl_SetNextBlockInChain(This,
3017 depotBlockIndexPos,
3018 BLOCK_SPECIAL);
3020 /* If necessary, flag the extended depot block.
3022 if (extIndex != BLOCK_UNUSED)
3023 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3025 /* Save header information.
3027 StorageImpl_SaveFileHeader(This);
3031 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3033 if (success)
3035 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3036 ( nextBlockIndex != BLOCK_UNUSED))
3038 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3040 if (nextBlockIndex == BLOCK_UNUSED)
3042 freeBlock = (depotIndex * blocksPerDepot) +
3043 (depotBlockOffset/sizeof(ULONG));
3046 depotBlockOffset += sizeof(ULONG);
3050 depotIndex++;
3051 depotBlockOffset = 0;
3055 * make sure that the block physically exists before using it
3057 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3059 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3061 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3062 ILockBytes_SetSize(This->lockBytes, neededSize);
3064 This->prevFreeBlock = freeBlock;
3066 return freeBlock;
3069 /******************************************************************************
3070 * Storage32Impl_AddBlockDepot
3072 * This will create a depot block, essentially it is a block initialized
3073 * to BLOCK_UNUSEDs.
3075 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3077 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3080 * Initialize blocks as free
3082 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3083 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3086 /******************************************************************************
3087 * Storage32Impl_GetExtDepotBlock
3089 * Returns the index of the block that corresponds to the specified depot
3090 * index. This method is only for depot indexes equal or greater than
3091 * COUNT_BBDEPOTINHEADER.
3093 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3095 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3096 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3097 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3098 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3099 ULONG blockIndex = BLOCK_UNUSED;
3100 ULONG extBlockIndex = This->extBigBlockDepotStart;
3102 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3104 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3105 return BLOCK_UNUSED;
3107 while (extBlockCount > 0)
3109 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3110 extBlockCount--;
3113 if (extBlockIndex != BLOCK_UNUSED)
3114 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3115 extBlockOffset * sizeof(ULONG), &blockIndex);
3117 return blockIndex;
3120 /******************************************************************************
3121 * Storage32Impl_SetExtDepotBlock
3123 * Associates the specified block index to the specified depot index.
3124 * This method is only for depot indexes equal or greater than
3125 * COUNT_BBDEPOTINHEADER.
3127 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3129 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3130 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3131 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3132 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3133 ULONG extBlockIndex = This->extBigBlockDepotStart;
3135 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3137 while (extBlockCount > 0)
3139 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3140 extBlockCount--;
3143 if (extBlockIndex != BLOCK_UNUSED)
3145 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3146 extBlockOffset * sizeof(ULONG),
3147 blockIndex);
3151 /******************************************************************************
3152 * Storage32Impl_AddExtBlockDepot
3154 * Creates an extended depot block.
3156 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3158 ULONG numExtBlocks = This->extBigBlockDepotCount;
3159 ULONG nextExtBlock = This->extBigBlockDepotStart;
3160 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3161 ULONG index = BLOCK_UNUSED;
3162 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3163 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3164 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3166 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3167 blocksPerDepotBlock;
3169 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3172 * The first extended block.
3174 This->extBigBlockDepotStart = index;
3176 else
3178 unsigned int i;
3180 * Follow the chain to the last one.
3182 for (i = 0; i < (numExtBlocks - 1); i++)
3184 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3188 * Add the new extended block to the chain.
3190 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3191 index);
3195 * Initialize this block.
3197 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3198 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3200 return index;
3203 /******************************************************************************
3204 * Storage32Impl_FreeBigBlock
3206 * This method will flag the specified block as free in the big block depot.
3208 static void StorageImpl_FreeBigBlock(
3209 StorageImpl* This,
3210 ULONG blockIndex)
3212 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3214 if (blockIndex < This->prevFreeBlock)
3215 This->prevFreeBlock = blockIndex;
3218 /************************************************************************
3219 * Storage32Impl_GetNextBlockInChain
3221 * This method will retrieve the block index of the next big block in
3222 * in the chain.
3224 * Params: This - Pointer to the Storage object.
3225 * blockIndex - Index of the block to retrieve the chain
3226 * for.
3227 * nextBlockIndex - receives the return value.
3229 * Returns: This method returns the index of the next block in the chain.
3230 * It will return the constants:
3231 * BLOCK_SPECIAL - If the block given was not part of a
3232 * chain.
3233 * BLOCK_END_OF_CHAIN - If the block given was the last in
3234 * a chain.
3235 * BLOCK_UNUSED - If the block given was not past of a chain
3236 * and is available.
3237 * BLOCK_EXTBBDEPOT - This block is part of the extended
3238 * big block depot.
3240 * See Windows documentation for more details on IStorage methods.
3242 static HRESULT StorageImpl_GetNextBlockInChain(
3243 StorageImpl* This,
3244 ULONG blockIndex,
3245 ULONG* nextBlockIndex)
3247 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3248 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3249 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3250 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3251 BOOL success;
3252 ULONG depotBlockIndexPos;
3253 int index, num_blocks;
3255 *nextBlockIndex = BLOCK_SPECIAL;
3257 if(depotBlockCount >= This->bigBlockDepotCount)
3259 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3260 This->bigBlockDepotCount);
3261 return STG_E_READFAULT;
3265 * Cache the currently accessed depot block.
3267 if (depotBlockCount != This->indexBlockDepotCached)
3269 This->indexBlockDepotCached = depotBlockCount;
3271 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3273 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3275 else
3278 * We have to look in the extended depot.
3280 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3283 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3285 if (!success)
3286 return STG_E_READFAULT;
3288 num_blocks = This->bigBlockSize / 4;
3290 for (index = 0; index < num_blocks; index++)
3292 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3293 This->blockDepotCached[index] = *nextBlockIndex;
3297 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3299 return S_OK;
3302 /******************************************************************************
3303 * Storage32Impl_GetNextExtendedBlock
3305 * Given an extended block this method will return the next extended block.
3307 * NOTES:
3308 * The last ULONG of an extended block is the block index of the next
3309 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3310 * depot.
3312 * Return values:
3313 * - The index of the next extended block
3314 * - BLOCK_UNUSED: there is no next extended block.
3315 * - Any other return values denotes failure.
3317 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3319 ULONG nextBlockIndex = BLOCK_SPECIAL;
3320 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3322 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3323 &nextBlockIndex);
3325 return nextBlockIndex;
3328 /******************************************************************************
3329 * Storage32Impl_SetNextBlockInChain
3331 * This method will write the index of the specified block's next block
3332 * in the big block depot.
3334 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3335 * do the following
3337 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3338 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3339 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3342 static void StorageImpl_SetNextBlockInChain(
3343 StorageImpl* This,
3344 ULONG blockIndex,
3345 ULONG nextBlock)
3347 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3348 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3349 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3350 ULONG depotBlockIndexPos;
3352 assert(depotBlockCount < This->bigBlockDepotCount);
3353 assert(blockIndex != nextBlock);
3355 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3357 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3359 else
3362 * We have to look in the extended depot.
3364 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3367 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3368 nextBlock);
3370 * Update the cached block depot, if necessary.
3372 if (depotBlockCount == This->indexBlockDepotCached)
3374 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3378 /******************************************************************************
3379 * Storage32Impl_LoadFileHeader
3381 * This method will read in the file header
3383 static HRESULT StorageImpl_LoadFileHeader(
3384 StorageImpl* This)
3386 HRESULT hr;
3387 BYTE headerBigBlock[HEADER_SIZE];
3388 int index;
3389 ULARGE_INTEGER offset;
3390 DWORD bytes_read;
3392 TRACE("\n");
3394 * Get a pointer to the big block of data containing the header.
3396 offset.u.HighPart = 0;
3397 offset.u.LowPart = 0;
3398 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3399 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3400 hr = STG_E_FILENOTFOUND;
3403 * Extract the information from the header.
3405 if (SUCCEEDED(hr))
3408 * Check for the "magic number" signature and return an error if it is not
3409 * found.
3411 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3413 return STG_E_OLDFORMAT;
3416 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3418 return STG_E_INVALIDHEADER;
3421 StorageUtl_ReadWord(
3422 headerBigBlock,
3423 OFFSET_BIGBLOCKSIZEBITS,
3424 &This->bigBlockSizeBits);
3426 StorageUtl_ReadWord(
3427 headerBigBlock,
3428 OFFSET_SMALLBLOCKSIZEBITS,
3429 &This->smallBlockSizeBits);
3431 StorageUtl_ReadDWord(
3432 headerBigBlock,
3433 OFFSET_BBDEPOTCOUNT,
3434 &This->bigBlockDepotCount);
3436 StorageUtl_ReadDWord(
3437 headerBigBlock,
3438 OFFSET_ROOTSTARTBLOCK,
3439 &This->rootStartBlock);
3441 StorageUtl_ReadDWord(
3442 headerBigBlock,
3443 OFFSET_SMALLBLOCKLIMIT,
3444 &This->smallBlockLimit);
3446 StorageUtl_ReadDWord(
3447 headerBigBlock,
3448 OFFSET_SBDEPOTSTART,
3449 &This->smallBlockDepotStart);
3451 StorageUtl_ReadDWord(
3452 headerBigBlock,
3453 OFFSET_EXTBBDEPOTSTART,
3454 &This->extBigBlockDepotStart);
3456 StorageUtl_ReadDWord(
3457 headerBigBlock,
3458 OFFSET_EXTBBDEPOTCOUNT,
3459 &This->extBigBlockDepotCount);
3461 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3463 StorageUtl_ReadDWord(
3464 headerBigBlock,
3465 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3466 &(This->bigBlockDepotStart[index]));
3470 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3472 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3473 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3476 * Right now, the code is making some assumptions about the size of the
3477 * blocks, just make sure they are what we're expecting.
3479 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3480 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3481 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3483 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3484 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3485 hr = STG_E_INVALIDHEADER;
3487 else
3488 hr = S_OK;
3491 return hr;
3494 /******************************************************************************
3495 * Storage32Impl_SaveFileHeader
3497 * This method will save to the file the header
3499 static void StorageImpl_SaveFileHeader(
3500 StorageImpl* This)
3502 BYTE headerBigBlock[HEADER_SIZE];
3503 int index;
3504 HRESULT hr;
3505 ULARGE_INTEGER offset;
3506 DWORD bytes_read, bytes_written;
3507 DWORD major_version, dirsectorcount;
3510 * Get a pointer to the big block of data containing the header.
3512 offset.u.HighPart = 0;
3513 offset.u.LowPart = 0;
3514 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3515 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3516 hr = STG_E_FILENOTFOUND;
3518 if (This->bigBlockSizeBits == 0x9)
3519 major_version = 3;
3520 else if (This->bigBlockSizeBits == 0xc)
3521 major_version = 4;
3522 else
3524 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3525 major_version = 4;
3529 * If the block read failed, the file is probably new.
3531 if (FAILED(hr))
3534 * Initialize for all unknown fields.
3536 memset(headerBigBlock, 0, HEADER_SIZE);
3539 * Initialize the magic number.
3541 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3545 * Write the information to the header.
3547 StorageUtl_WriteWord(
3548 headerBigBlock,
3549 OFFSET_MINORVERSION,
3550 0x3e);
3552 StorageUtl_WriteWord(
3553 headerBigBlock,
3554 OFFSET_MAJORVERSION,
3555 major_version);
3557 StorageUtl_WriteWord(
3558 headerBigBlock,
3559 OFFSET_BYTEORDERMARKER,
3560 (WORD)-2);
3562 StorageUtl_WriteWord(
3563 headerBigBlock,
3564 OFFSET_BIGBLOCKSIZEBITS,
3565 This->bigBlockSizeBits);
3567 StorageUtl_WriteWord(
3568 headerBigBlock,
3569 OFFSET_SMALLBLOCKSIZEBITS,
3570 This->smallBlockSizeBits);
3572 if (major_version >= 4)
3574 if (This->rootBlockChain)
3575 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3576 else
3577 /* This file is being created, and it will start out with one block. */
3578 dirsectorcount = 1;
3580 else
3581 /* This field must be 0 in versions older than 4 */
3582 dirsectorcount = 0;
3584 StorageUtl_WriteDWord(
3585 headerBigBlock,
3586 OFFSET_DIRSECTORCOUNT,
3587 dirsectorcount);
3589 StorageUtl_WriteDWord(
3590 headerBigBlock,
3591 OFFSET_BBDEPOTCOUNT,
3592 This->bigBlockDepotCount);
3594 StorageUtl_WriteDWord(
3595 headerBigBlock,
3596 OFFSET_ROOTSTARTBLOCK,
3597 This->rootStartBlock);
3599 StorageUtl_WriteDWord(
3600 headerBigBlock,
3601 OFFSET_SMALLBLOCKLIMIT,
3602 This->smallBlockLimit);
3604 StorageUtl_WriteDWord(
3605 headerBigBlock,
3606 OFFSET_SBDEPOTSTART,
3607 This->smallBlockDepotStart);
3609 StorageUtl_WriteDWord(
3610 headerBigBlock,
3611 OFFSET_SBDEPOTCOUNT,
3612 This->smallBlockDepotChain ?
3613 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3615 StorageUtl_WriteDWord(
3616 headerBigBlock,
3617 OFFSET_EXTBBDEPOTSTART,
3618 This->extBigBlockDepotStart);
3620 StorageUtl_WriteDWord(
3621 headerBigBlock,
3622 OFFSET_EXTBBDEPOTCOUNT,
3623 This->extBigBlockDepotCount);
3625 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3627 StorageUtl_WriteDWord(
3628 headerBigBlock,
3629 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3630 (This->bigBlockDepotStart[index]));
3634 * Write the big block back to the file.
3636 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3639 /******************************************************************************
3640 * StorageImpl_ReadRawDirEntry
3642 * This method will read the raw data from a directory entry in the file.
3644 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3646 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3648 ULARGE_INTEGER offset;
3649 HRESULT hr;
3650 ULONG bytesRead;
3652 offset.u.HighPart = 0;
3653 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3655 hr = BlockChainStream_ReadAt(
3656 This->rootBlockChain,
3657 offset,
3658 RAW_DIRENTRY_SIZE,
3659 buffer,
3660 &bytesRead);
3662 if (bytesRead != RAW_DIRENTRY_SIZE)
3663 return STG_E_READFAULT;
3665 return hr;
3668 /******************************************************************************
3669 * StorageImpl_WriteRawDirEntry
3671 * This method will write the raw data from a directory entry in the file.
3673 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3675 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3677 ULARGE_INTEGER offset;
3678 HRESULT hr;
3679 ULONG bytesRead;
3681 offset.u.HighPart = 0;
3682 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3684 hr = BlockChainStream_WriteAt(
3685 This->rootBlockChain,
3686 offset,
3687 RAW_DIRENTRY_SIZE,
3688 buffer,
3689 &bytesRead);
3691 return hr;
3694 /******************************************************************************
3695 * UpdateRawDirEntry
3697 * Update raw directory entry data from the fields in newData.
3699 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3701 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3703 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3705 memcpy(
3706 buffer + OFFSET_PS_NAME,
3707 newData->name,
3708 DIRENTRY_NAME_BUFFER_LEN );
3710 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3712 StorageUtl_WriteWord(
3713 buffer,
3714 OFFSET_PS_NAMELENGTH,
3715 newData->sizeOfNameString);
3717 StorageUtl_WriteDWord(
3718 buffer,
3719 OFFSET_PS_LEFTCHILD,
3720 newData->leftChild);
3722 StorageUtl_WriteDWord(
3723 buffer,
3724 OFFSET_PS_RIGHTCHILD,
3725 newData->rightChild);
3727 StorageUtl_WriteDWord(
3728 buffer,
3729 OFFSET_PS_DIRROOT,
3730 newData->dirRootEntry);
3732 StorageUtl_WriteGUID(
3733 buffer,
3734 OFFSET_PS_GUID,
3735 &newData->clsid);
3737 StorageUtl_WriteDWord(
3738 buffer,
3739 OFFSET_PS_CTIMELOW,
3740 newData->ctime.dwLowDateTime);
3742 StorageUtl_WriteDWord(
3743 buffer,
3744 OFFSET_PS_CTIMEHIGH,
3745 newData->ctime.dwHighDateTime);
3747 StorageUtl_WriteDWord(
3748 buffer,
3749 OFFSET_PS_MTIMELOW,
3750 newData->mtime.dwLowDateTime);
3752 StorageUtl_WriteDWord(
3753 buffer,
3754 OFFSET_PS_MTIMEHIGH,
3755 newData->ctime.dwHighDateTime);
3757 StorageUtl_WriteDWord(
3758 buffer,
3759 OFFSET_PS_STARTBLOCK,
3760 newData->startingBlock);
3762 StorageUtl_WriteDWord(
3763 buffer,
3764 OFFSET_PS_SIZE,
3765 newData->size.u.LowPart);
3768 /******************************************************************************
3769 * Storage32Impl_ReadDirEntry
3771 * This method will read the specified directory entry.
3773 HRESULT StorageImpl_ReadDirEntry(
3774 StorageImpl* This,
3775 DirRef index,
3776 DirEntry* buffer)
3778 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3779 HRESULT readRes;
3781 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3783 if (SUCCEEDED(readRes))
3785 memset(buffer->name, 0, sizeof(buffer->name));
3786 memcpy(
3787 buffer->name,
3788 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3789 DIRENTRY_NAME_BUFFER_LEN );
3790 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3792 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3794 StorageUtl_ReadWord(
3795 currentEntry,
3796 OFFSET_PS_NAMELENGTH,
3797 &buffer->sizeOfNameString);
3799 StorageUtl_ReadDWord(
3800 currentEntry,
3801 OFFSET_PS_LEFTCHILD,
3802 &buffer->leftChild);
3804 StorageUtl_ReadDWord(
3805 currentEntry,
3806 OFFSET_PS_RIGHTCHILD,
3807 &buffer->rightChild);
3809 StorageUtl_ReadDWord(
3810 currentEntry,
3811 OFFSET_PS_DIRROOT,
3812 &buffer->dirRootEntry);
3814 StorageUtl_ReadGUID(
3815 currentEntry,
3816 OFFSET_PS_GUID,
3817 &buffer->clsid);
3819 StorageUtl_ReadDWord(
3820 currentEntry,
3821 OFFSET_PS_CTIMELOW,
3822 &buffer->ctime.dwLowDateTime);
3824 StorageUtl_ReadDWord(
3825 currentEntry,
3826 OFFSET_PS_CTIMEHIGH,
3827 &buffer->ctime.dwHighDateTime);
3829 StorageUtl_ReadDWord(
3830 currentEntry,
3831 OFFSET_PS_MTIMELOW,
3832 &buffer->mtime.dwLowDateTime);
3834 StorageUtl_ReadDWord(
3835 currentEntry,
3836 OFFSET_PS_MTIMEHIGH,
3837 &buffer->mtime.dwHighDateTime);
3839 StorageUtl_ReadDWord(
3840 currentEntry,
3841 OFFSET_PS_STARTBLOCK,
3842 &buffer->startingBlock);
3844 StorageUtl_ReadDWord(
3845 currentEntry,
3846 OFFSET_PS_SIZE,
3847 &buffer->size.u.LowPart);
3849 buffer->size.u.HighPart = 0;
3852 return readRes;
3855 /*********************************************************************
3856 * Write the specified directory entry to the file
3858 HRESULT StorageImpl_WriteDirEntry(
3859 StorageImpl* This,
3860 DirRef index,
3861 const DirEntry* buffer)
3863 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3864 HRESULT writeRes;
3866 UpdateRawDirEntry(currentEntry, buffer);
3868 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3869 return writeRes;
3872 static BOOL StorageImpl_ReadBigBlock(
3873 StorageImpl* This,
3874 ULONG blockIndex,
3875 void* buffer)
3877 ULARGE_INTEGER ulOffset;
3878 DWORD read=0;
3880 ulOffset.u.HighPart = 0;
3881 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3883 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3885 if (read && read < This->bigBlockSize)
3887 /* File ends during this block; fill the rest with 0's. */
3888 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3891 return (read != 0);
3894 static BOOL StorageImpl_ReadDWordFromBigBlock(
3895 StorageImpl* This,
3896 ULONG blockIndex,
3897 ULONG offset,
3898 DWORD* value)
3900 ULARGE_INTEGER ulOffset;
3901 DWORD read;
3902 DWORD tmp;
3904 ulOffset.u.HighPart = 0;
3905 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3906 ulOffset.u.LowPart += offset;
3908 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3909 *value = lendian32toh(tmp);
3910 return (read == sizeof(DWORD));
3913 static BOOL StorageImpl_WriteBigBlock(
3914 StorageImpl* This,
3915 ULONG blockIndex,
3916 const void* buffer)
3918 ULARGE_INTEGER ulOffset;
3919 DWORD wrote;
3921 ulOffset.u.HighPart = 0;
3922 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3924 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3925 return (wrote == This->bigBlockSize);
3928 static BOOL StorageImpl_WriteDWordToBigBlock(
3929 StorageImpl* This,
3930 ULONG blockIndex,
3931 ULONG offset,
3932 DWORD value)
3934 ULARGE_INTEGER ulOffset;
3935 DWORD wrote;
3937 ulOffset.u.HighPart = 0;
3938 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3939 ulOffset.u.LowPart += offset;
3941 value = htole32(value);
3942 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3943 return (wrote == sizeof(DWORD));
3946 /******************************************************************************
3947 * Storage32Impl_SmallBlocksToBigBlocks
3949 * This method will convert a small block chain to a big block chain.
3950 * The small block chain will be destroyed.
3952 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3953 StorageImpl* This,
3954 SmallBlockChainStream** ppsbChain)
3956 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3957 ULARGE_INTEGER size, offset;
3958 ULONG cbRead, cbWritten;
3959 ULARGE_INTEGER cbTotalRead;
3960 DirRef streamEntryRef;
3961 HRESULT resWrite = S_OK;
3962 HRESULT resRead;
3963 DirEntry streamEntry;
3964 BYTE *buffer;
3965 BlockChainStream *bbTempChain = NULL;
3966 BlockChainStream *bigBlockChain = NULL;
3969 * Create a temporary big block chain that doesn't have
3970 * an associated directory entry. This temporary chain will be
3971 * used to copy data from small blocks to big blocks.
3973 bbTempChain = BlockChainStream_Construct(This,
3974 &bbHeadOfChain,
3975 DIRENTRY_NULL);
3976 if(!bbTempChain) return NULL;
3978 * Grow the big block chain.
3980 size = SmallBlockChainStream_GetSize(*ppsbChain);
3981 BlockChainStream_SetSize(bbTempChain, size);
3984 * Copy the contents of the small block chain to the big block chain
3985 * by small block size increments.
3987 offset.u.LowPart = 0;
3988 offset.u.HighPart = 0;
3989 cbTotalRead.QuadPart = 0;
3991 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3994 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3995 offset,
3996 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3997 buffer,
3998 &cbRead);
3999 if (FAILED(resRead))
4000 break;
4002 if (cbRead > 0)
4004 cbTotalRead.QuadPart += cbRead;
4006 resWrite = BlockChainStream_WriteAt(bbTempChain,
4007 offset,
4008 cbRead,
4009 buffer,
4010 &cbWritten);
4012 if (FAILED(resWrite))
4013 break;
4015 offset.u.LowPart += cbRead;
4017 else
4019 resRead = STG_E_READFAULT;
4020 break;
4022 } while (cbTotalRead.QuadPart < size.QuadPart);
4023 HeapFree(GetProcessHeap(),0,buffer);
4025 size.u.HighPart = 0;
4026 size.u.LowPart = 0;
4028 if (FAILED(resRead) || FAILED(resWrite))
4030 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4031 BlockChainStream_SetSize(bbTempChain, size);
4032 BlockChainStream_Destroy(bbTempChain);
4033 return NULL;
4037 * Destroy the small block chain.
4039 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4040 SmallBlockChainStream_SetSize(*ppsbChain, size);
4041 SmallBlockChainStream_Destroy(*ppsbChain);
4042 *ppsbChain = 0;
4045 * Change the directory entry. This chain is now a big block chain
4046 * and it doesn't reside in the small blocks chain anymore.
4048 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4050 streamEntry.startingBlock = bbHeadOfChain;
4052 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4055 * Destroy the temporary entryless big block chain.
4056 * Create a new big block chain associated with this entry.
4058 BlockChainStream_Destroy(bbTempChain);
4059 bigBlockChain = BlockChainStream_Construct(This,
4060 NULL,
4061 streamEntryRef);
4063 return bigBlockChain;
4066 /******************************************************************************
4067 * Storage32Impl_BigBlocksToSmallBlocks
4069 * This method will convert a big block chain to a small block chain.
4070 * The big block chain will be destroyed on success.
4072 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4073 StorageImpl* This,
4074 BlockChainStream** ppbbChain)
4076 ULARGE_INTEGER size, offset, cbTotalRead;
4077 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4078 DirRef streamEntryRef;
4079 HRESULT resWrite = S_OK, resRead;
4080 DirEntry streamEntry;
4081 BYTE* buffer;
4082 SmallBlockChainStream* sbTempChain;
4084 TRACE("%p %p\n", This, ppbbChain);
4086 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4087 DIRENTRY_NULL);
4089 if(!sbTempChain)
4090 return NULL;
4092 size = BlockChainStream_GetSize(*ppbbChain);
4093 SmallBlockChainStream_SetSize(sbTempChain, size);
4095 offset.u.HighPart = 0;
4096 offset.u.LowPart = 0;
4097 cbTotalRead.QuadPart = 0;
4098 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4101 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4102 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4103 buffer, &cbRead);
4105 if(FAILED(resRead))
4106 break;
4108 if(cbRead > 0)
4110 cbTotalRead.QuadPart += cbRead;
4112 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4113 cbRead, buffer, &cbWritten);
4115 if(FAILED(resWrite))
4116 break;
4118 offset.u.LowPart += cbRead;
4120 else
4122 resRead = STG_E_READFAULT;
4123 break;
4125 }while(cbTotalRead.QuadPart < size.QuadPart);
4126 HeapFree(GetProcessHeap(), 0, buffer);
4128 size.u.HighPart = 0;
4129 size.u.LowPart = 0;
4131 if(FAILED(resRead) || FAILED(resWrite))
4133 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4134 SmallBlockChainStream_SetSize(sbTempChain, size);
4135 SmallBlockChainStream_Destroy(sbTempChain);
4136 return NULL;
4139 /* destroy the original big block chain */
4140 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4141 BlockChainStream_SetSize(*ppbbChain, size);
4142 BlockChainStream_Destroy(*ppbbChain);
4143 *ppbbChain = NULL;
4145 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4146 streamEntry.startingBlock = sbHeadOfChain;
4147 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4149 SmallBlockChainStream_Destroy(sbTempChain);
4150 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4153 static HRESULT StorageBaseImpl_CopyStream(
4154 StorageBaseImpl *dst, DirRef dst_entry,
4155 StorageBaseImpl *src, DirRef src_entry)
4157 HRESULT hr;
4158 BYTE data[4096];
4159 DirEntry srcdata;
4160 ULARGE_INTEGER bytes_copied;
4161 ULONG bytestocopy, bytesread, byteswritten;
4163 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4165 if (SUCCEEDED(hr))
4167 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4169 bytes_copied.QuadPart = 0;
4170 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4172 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4174 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4175 data, &bytesread);
4176 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4178 if (SUCCEEDED(hr))
4179 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4180 data, &byteswritten);
4181 if (SUCCEEDED(hr))
4183 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4184 bytes_copied.QuadPart += byteswritten;
4189 return hr;
4192 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4194 DirRef result=This->firstFreeEntry;
4196 while (result < This->entries_size && This->entries[result].inuse)
4197 result++;
4199 if (result == This->entries_size)
4201 ULONG new_size = This->entries_size * 2;
4202 TransactedDirEntry *new_entries;
4204 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4205 if (!new_entries) return DIRENTRY_NULL;
4207 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4208 HeapFree(GetProcessHeap(), 0, This->entries);
4210 This->entries = new_entries;
4211 This->entries_size = new_size;
4214 This->entries[result].inuse = 1;
4216 This->firstFreeEntry = result+1;
4218 return result;
4221 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4222 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4224 DirRef stubEntryRef;
4225 TransactedDirEntry *entry;
4227 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4229 if (stubEntryRef != DIRENTRY_NULL)
4231 entry = &This->entries[stubEntryRef];
4233 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4235 entry->read = 0;
4238 return stubEntryRef;
4241 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4242 TransactedSnapshotImpl *This, DirRef entry)
4244 HRESULT hr=S_OK;
4245 DirEntry data;
4247 if (!This->entries[entry].read)
4249 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4250 This->entries[entry].transactedParentEntry,
4251 &data);
4253 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4255 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4257 if (data.leftChild == DIRENTRY_NULL)
4258 hr = E_OUTOFMEMORY;
4261 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4263 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4265 if (data.rightChild == DIRENTRY_NULL)
4266 hr = E_OUTOFMEMORY;
4269 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4271 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4273 if (data.dirRootEntry == DIRENTRY_NULL)
4274 hr = E_OUTOFMEMORY;
4277 if (SUCCEEDED(hr))
4279 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4280 This->entries[entry].read = 1;
4284 return hr;
4287 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4288 TransactedSnapshotImpl *This, DirRef entry)
4290 HRESULT hr = S_OK;
4292 if (!This->entries[entry].stream_dirty)
4294 DirEntry new_entrydata;
4296 memset(&new_entrydata, 0, sizeof(DirEntry));
4297 new_entrydata.name[0] = 'S';
4298 new_entrydata.sizeOfNameString = 1;
4299 new_entrydata.stgType = STGTY_STREAM;
4300 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4301 new_entrydata.leftChild = DIRENTRY_NULL;
4302 new_entrydata.rightChild = DIRENTRY_NULL;
4303 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4305 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4306 &This->entries[entry].stream_entry);
4308 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4310 hr = StorageBaseImpl_CopyStream(
4311 This->scratch, This->entries[entry].stream_entry,
4312 This->transactedParent, This->entries[entry].transactedParentEntry);
4314 if (FAILED(hr))
4315 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4318 if (SUCCEEDED(hr))
4319 This->entries[entry].stream_dirty = 1;
4321 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4323 /* Since this entry is modified, and we aren't using its stream data, we
4324 * no longer care about the original entry. */
4325 DirRef delete_ref;
4326 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4328 if (delete_ref != DIRENTRY_NULL)
4329 This->entries[delete_ref].deleted = 1;
4331 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4335 return hr;
4338 /* Find the first entry in a depth-first traversal. */
4339 static DirRef TransactedSnapshotImpl_FindFirstChild(
4340 TransactedSnapshotImpl* This, DirRef parent)
4342 DirRef cursor, prev;
4343 TransactedDirEntry *entry;
4345 cursor = parent;
4346 entry = &This->entries[cursor];
4347 while (entry->read)
4349 if (entry->data.leftChild != DIRENTRY_NULL)
4351 prev = cursor;
4352 cursor = entry->data.leftChild;
4353 entry = &This->entries[cursor];
4354 entry->parent = prev;
4356 else if (entry->data.rightChild != DIRENTRY_NULL)
4358 prev = cursor;
4359 cursor = entry->data.rightChild;
4360 entry = &This->entries[cursor];
4361 entry->parent = prev;
4363 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4365 prev = cursor;
4366 cursor = entry->data.dirRootEntry;
4367 entry = &This->entries[cursor];
4368 entry->parent = prev;
4370 else
4371 break;
4374 return cursor;
4377 /* Find the next entry in a depth-first traversal. */
4378 static DirRef TransactedSnapshotImpl_FindNextChild(
4379 TransactedSnapshotImpl* This, DirRef current)
4381 DirRef parent;
4382 TransactedDirEntry *parent_entry;
4384 parent = This->entries[current].parent;
4385 parent_entry = &This->entries[parent];
4387 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4389 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4391 This->entries[parent_entry->data.rightChild].parent = parent;
4392 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4395 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4397 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4398 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4402 return parent;
4405 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4406 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4407 TransactedSnapshotImpl* This, DirRef entry)
4409 return entry != DIRENTRY_NULL &&
4410 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4413 /* Destroy the entries created by CopyTree. */
4414 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4415 TransactedSnapshotImpl* This, DirRef stop)
4417 DirRef cursor;
4418 TransactedDirEntry *entry;
4419 ULARGE_INTEGER zero;
4421 zero.QuadPart = 0;
4423 if (!This->entries[This->base.storageDirEntry].read)
4424 return;
4426 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4428 if (cursor == DIRENTRY_NULL)
4429 return;
4431 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4433 while (cursor != DIRENTRY_NULL && cursor != stop)
4435 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4437 entry = &This->entries[cursor];
4439 if (entry->stream_dirty)
4440 StorageBaseImpl_StreamSetSize(This->transactedParent,
4441 entry->newTransactedParentEntry, zero);
4443 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4444 entry->newTransactedParentEntry);
4446 entry->newTransactedParentEntry = entry->transactedParentEntry;
4449 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4453 /* Make a copy of our edited tree that we can use in the parent. */
4454 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4456 DirRef cursor;
4457 TransactedDirEntry *entry;
4458 HRESULT hr = S_OK;
4460 cursor = This->base.storageDirEntry;
4461 entry = &This->entries[cursor];
4462 entry->parent = DIRENTRY_NULL;
4463 entry->newTransactedParentEntry = entry->transactedParentEntry;
4465 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4466 return S_OK;
4468 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4470 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4471 entry = &This->entries[cursor];
4473 while (cursor != DIRENTRY_NULL)
4475 /* Make a copy of this entry in the transacted parent. */
4476 if (!entry->read ||
4477 (!entry->dirty && !entry->stream_dirty &&
4478 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4479 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4480 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4481 entry->newTransactedParentEntry = entry->transactedParentEntry;
4482 else
4484 DirEntry newData;
4486 memcpy(&newData, &entry->data, sizeof(DirEntry));
4488 newData.size.QuadPart = 0;
4489 newData.startingBlock = BLOCK_END_OF_CHAIN;
4491 if (newData.leftChild != DIRENTRY_NULL)
4492 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4494 if (newData.rightChild != DIRENTRY_NULL)
4495 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4497 if (newData.dirRootEntry != DIRENTRY_NULL)
4498 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4500 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4501 &entry->newTransactedParentEntry);
4502 if (FAILED(hr))
4504 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4505 return hr;
4508 if (entry->stream_dirty)
4510 hr = StorageBaseImpl_CopyStream(
4511 This->transactedParent, entry->newTransactedParentEntry,
4512 This->scratch, entry->stream_entry);
4514 else if (entry->data.size.QuadPart)
4516 hr = StorageBaseImpl_StreamLink(
4517 This->transactedParent, entry->newTransactedParentEntry,
4518 entry->transactedParentEntry);
4521 if (FAILED(hr))
4523 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4524 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4525 return hr;
4529 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4530 entry = &This->entries[cursor];
4533 return hr;
4536 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4537 IStorage* iface,
4538 DWORD grfCommitFlags) /* [in] */
4540 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4541 TransactedDirEntry *root_entry;
4542 DirRef i, dir_root_ref;
4543 DirEntry data;
4544 ULARGE_INTEGER zero;
4545 HRESULT hr;
4547 zero.QuadPart = 0;
4549 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4551 /* Cannot commit a read-only transacted storage */
4552 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4553 return STG_E_ACCESSDENIED;
4555 /* To prevent data loss, we create the new structure in the file before we
4556 * delete the old one, so that in case of errors the old data is intact. We
4557 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4558 * needed in the rare situation where we have just enough free disk space to
4559 * overwrite the existing data. */
4561 root_entry = &This->entries[This->base.storageDirEntry];
4563 if (!root_entry->read)
4564 return S_OK;
4566 hr = TransactedSnapshotImpl_CopyTree(This);
4567 if (FAILED(hr)) return hr;
4569 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4570 dir_root_ref = DIRENTRY_NULL;
4571 else
4572 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4574 hr = StorageBaseImpl_Flush(This->transactedParent);
4576 /* Update the storage to use the new data in one step. */
4577 if (SUCCEEDED(hr))
4578 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4579 root_entry->transactedParentEntry, &data);
4581 if (SUCCEEDED(hr))
4583 data.dirRootEntry = dir_root_ref;
4584 data.clsid = root_entry->data.clsid;
4585 data.ctime = root_entry->data.ctime;
4586 data.mtime = root_entry->data.mtime;
4588 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4589 root_entry->transactedParentEntry, &data);
4592 /* Try to flush after updating the root storage, but if the flush fails, keep
4593 * going, on the theory that it'll either succeed later or the subsequent
4594 * writes will fail. */
4595 StorageBaseImpl_Flush(This->transactedParent);
4597 if (SUCCEEDED(hr))
4599 /* Destroy the old now-orphaned data. */
4600 for (i=0; i<This->entries_size; i++)
4602 TransactedDirEntry *entry = &This->entries[i];
4603 if (entry->inuse)
4605 if (entry->deleted)
4607 StorageBaseImpl_StreamSetSize(This->transactedParent,
4608 entry->transactedParentEntry, zero);
4609 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4610 entry->transactedParentEntry);
4611 memset(entry, 0, sizeof(TransactedDirEntry));
4612 This->firstFreeEntry = min(i, This->firstFreeEntry);
4614 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4616 if (entry->transactedParentEntry != DIRENTRY_NULL)
4617 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4618 entry->transactedParentEntry);
4619 if (entry->stream_dirty)
4621 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4622 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4623 entry->stream_dirty = 0;
4625 entry->dirty = 0;
4626 entry->transactedParentEntry = entry->newTransactedParentEntry;
4631 else
4633 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4636 if (SUCCEEDED(hr))
4637 hr = StorageBaseImpl_Flush(This->transactedParent);
4639 return hr;
4642 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4643 IStorage* iface)
4645 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4646 ULARGE_INTEGER zero;
4647 ULONG i;
4649 TRACE("(%p)\n", iface);
4651 /* Destroy the open objects. */
4652 StorageBaseImpl_DeleteAll(&This->base);
4654 /* Clear out the scratch file. */
4655 zero.QuadPart = 0;
4656 for (i=0; i<This->entries_size; i++)
4658 if (This->entries[i].stream_dirty)
4660 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4661 zero);
4663 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4667 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4669 This->firstFreeEntry = 0;
4670 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4672 return S_OK;
4675 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4677 if (!This->reverted)
4679 TRACE("Storage invalidated (stg=%p)\n", This);
4681 This->reverted = 1;
4683 StorageBaseImpl_DeleteAll(This);
4687 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4689 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4691 TransactedSnapshotImpl_Revert((IStorage*)iface);
4693 IStorage_Release((IStorage*)This->transactedParent);
4695 IStorage_Release((IStorage*)This->scratch);
4697 HeapFree(GetProcessHeap(), 0, This->entries);
4699 HeapFree(GetProcessHeap(), 0, This);
4702 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4704 /* We only need to flush when committing. */
4705 return S_OK;
4708 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4710 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4712 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4715 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4716 const DirEntry *newData, DirRef *index)
4718 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4719 DirRef new_ref;
4720 TransactedDirEntry *new_entry;
4722 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4723 if (new_ref == DIRENTRY_NULL)
4724 return E_OUTOFMEMORY;
4726 new_entry = &This->entries[new_ref];
4728 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4729 new_entry->read = 1;
4730 new_entry->dirty = 1;
4731 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4733 *index = new_ref;
4735 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4737 return S_OK;
4740 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4741 DirRef index, const DirEntry *data)
4743 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4744 HRESULT hr;
4746 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4748 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4749 if (FAILED(hr)) return hr;
4751 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4753 if (index != This->base.storageDirEntry)
4755 This->entries[index].dirty = 1;
4757 if (data->size.QuadPart == 0 &&
4758 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4760 /* Since this entry is modified, and we aren't using its stream data, we
4761 * no longer care about the original entry. */
4762 DirRef delete_ref;
4763 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4765 if (delete_ref != DIRENTRY_NULL)
4766 This->entries[delete_ref].deleted = 1;
4768 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4772 return S_OK;
4775 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4776 DirRef index, DirEntry *data)
4778 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4779 HRESULT hr;
4781 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4782 if (FAILED(hr)) return hr;
4784 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4786 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4788 return S_OK;
4791 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4792 DirRef index)
4794 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4796 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4797 This->entries[index].data.size.QuadPart != 0)
4799 /* If we deleted this entry while it has stream data. We must have left the
4800 * data because some other entry is using it, and we need to leave the
4801 * original entry alone. */
4802 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4803 This->firstFreeEntry = min(index, This->firstFreeEntry);
4805 else
4807 This->entries[index].deleted = 1;
4810 return S_OK;
4813 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4814 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4816 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4818 if (This->entries[index].stream_dirty)
4820 return StorageBaseImpl_StreamReadAt(This->scratch,
4821 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4823 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4825 /* This stream doesn't live in the parent, and we haven't allocated storage
4826 * for it yet */
4827 *bytesRead = 0;
4828 return S_OK;
4830 else
4832 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4833 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4837 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4838 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4840 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4841 HRESULT hr;
4843 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4844 if (FAILED(hr)) return hr;
4846 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4847 if (FAILED(hr)) return hr;
4849 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4850 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4852 if (SUCCEEDED(hr) && size != 0)
4853 This->entries[index].data.size.QuadPart = max(
4854 This->entries[index].data.size.QuadPart,
4855 offset.QuadPart + size);
4857 return hr;
4860 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4861 DirRef index, ULARGE_INTEGER newsize)
4863 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4864 HRESULT hr;
4866 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4867 if (FAILED(hr)) return hr;
4869 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4870 return S_OK;
4872 if (newsize.QuadPart == 0)
4874 /* Destroy any parent references or entries in the scratch file. */
4875 if (This->entries[index].stream_dirty)
4877 ULARGE_INTEGER zero;
4878 zero.QuadPart = 0;
4879 StorageBaseImpl_StreamSetSize(This->scratch,
4880 This->entries[index].stream_entry, zero);
4881 StorageBaseImpl_DestroyDirEntry(This->scratch,
4882 This->entries[index].stream_entry);
4883 This->entries[index].stream_dirty = 0;
4885 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4887 DirRef delete_ref;
4888 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4890 if (delete_ref != DIRENTRY_NULL)
4891 This->entries[delete_ref].deleted = 1;
4893 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4896 else
4898 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4899 if (FAILED(hr)) return hr;
4901 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4902 This->entries[index].stream_entry, newsize);
4905 if (SUCCEEDED(hr))
4906 This->entries[index].data.size = newsize;
4908 return hr;
4911 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
4912 DirRef dst, DirRef src)
4914 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4915 HRESULT hr;
4916 TransactedDirEntry *dst_entry, *src_entry;
4918 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
4919 if (FAILED(hr)) return hr;
4921 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
4922 if (FAILED(hr)) return hr;
4924 dst_entry = &This->entries[dst];
4925 src_entry = &This->entries[src];
4927 dst_entry->stream_dirty = src_entry->stream_dirty;
4928 dst_entry->stream_entry = src_entry->stream_entry;
4929 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
4930 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
4931 dst_entry->data.size = src_entry->data.size;
4933 return S_OK;
4936 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4938 StorageBaseImpl_QueryInterface,
4939 StorageBaseImpl_AddRef,
4940 StorageBaseImpl_Release,
4941 StorageBaseImpl_CreateStream,
4942 StorageBaseImpl_OpenStream,
4943 StorageBaseImpl_CreateStorage,
4944 StorageBaseImpl_OpenStorage,
4945 StorageBaseImpl_CopyTo,
4946 StorageBaseImpl_MoveElementTo,
4947 TransactedSnapshotImpl_Commit,
4948 TransactedSnapshotImpl_Revert,
4949 StorageBaseImpl_EnumElements,
4950 StorageBaseImpl_DestroyElement,
4951 StorageBaseImpl_RenameElement,
4952 StorageBaseImpl_SetElementTimes,
4953 StorageBaseImpl_SetClass,
4954 StorageBaseImpl_SetStateBits,
4955 StorageBaseImpl_Stat
4958 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4960 TransactedSnapshotImpl_Destroy,
4961 TransactedSnapshotImpl_Invalidate,
4962 TransactedSnapshotImpl_Flush,
4963 TransactedSnapshotImpl_GetFilename,
4964 TransactedSnapshotImpl_CreateDirEntry,
4965 TransactedSnapshotImpl_WriteDirEntry,
4966 TransactedSnapshotImpl_ReadDirEntry,
4967 TransactedSnapshotImpl_DestroyDirEntry,
4968 TransactedSnapshotImpl_StreamReadAt,
4969 TransactedSnapshotImpl_StreamWriteAt,
4970 TransactedSnapshotImpl_StreamSetSize,
4971 TransactedSnapshotImpl_StreamLink
4974 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4975 TransactedSnapshotImpl** result)
4977 HRESULT hr;
4979 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4980 if (*result)
4982 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4984 /* This is OK because the property set storage functions use the IStorage functions. */
4985 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4987 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4989 list_init(&(*result)->base.strmHead);
4991 list_init(&(*result)->base.storageHead);
4993 (*result)->base.ref = 1;
4995 (*result)->base.openFlags = parentStorage->openFlags;
4997 /* Create a new temporary storage to act as the scratch file. */
4998 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
4999 0, (IStorage**)&(*result)->scratch);
5001 if (SUCCEEDED(hr))
5003 ULONG num_entries = 20;
5005 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5007 (*result)->entries_size = num_entries;
5009 (*result)->firstFreeEntry = 0;
5011 if ((*result)->entries)
5013 /* parentStorage already has 1 reference, which we take over here. */
5014 (*result)->transactedParent = parentStorage;
5016 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5018 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5020 else
5022 IStorage_Release((IStorage*)(*result)->scratch);
5024 hr = E_OUTOFMEMORY;
5028 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5030 return hr;
5032 else
5033 return E_OUTOFMEMORY;
5036 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5037 StorageBaseImpl** result)
5039 static int fixme=0;
5041 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5043 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5046 return TransactedSnapshotImpl_Construct(parentStorage,
5047 (TransactedSnapshotImpl**)result);
5050 static HRESULT Storage_Construct(
5051 HANDLE hFile,
5052 LPCOLESTR pwcsName,
5053 ILockBytes* pLkbyt,
5054 DWORD openFlags,
5055 BOOL fileBased,
5056 BOOL create,
5057 ULONG sector_size,
5058 StorageBaseImpl** result)
5060 StorageImpl *newStorage;
5061 StorageBaseImpl *newTransactedStorage;
5062 HRESULT hr;
5064 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5065 if (FAILED(hr)) goto end;
5067 if (openFlags & STGM_TRANSACTED)
5069 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5070 if (FAILED(hr))
5071 IStorage_Release((IStorage*)newStorage);
5072 else
5073 *result = newTransactedStorage;
5075 else
5076 *result = &newStorage->base;
5078 end:
5079 return hr;
5082 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5084 StorageInternalImpl* This = (StorageInternalImpl*) base;
5086 if (!This->base.reverted)
5088 TRACE("Storage invalidated (stg=%p)\n", This);
5090 This->base.reverted = 1;
5092 This->parentStorage = NULL;
5094 StorageBaseImpl_DeleteAll(&This->base);
5096 list_remove(&This->ParentListEntry);
5100 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5102 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5104 StorageInternalImpl_Invalidate(&This->base);
5106 HeapFree(GetProcessHeap(), 0, This);
5109 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5111 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5113 return StorageBaseImpl_Flush(This->parentStorage);
5116 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5118 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5120 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5123 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5124 const DirEntry *newData, DirRef *index)
5126 StorageInternalImpl* This = (StorageInternalImpl*) base;
5128 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5129 newData, index);
5132 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5133 DirRef index, const DirEntry *data)
5135 StorageInternalImpl* This = (StorageInternalImpl*) base;
5137 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5138 index, data);
5141 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5142 DirRef index, DirEntry *data)
5144 StorageInternalImpl* This = (StorageInternalImpl*) base;
5146 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5147 index, data);
5150 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5151 DirRef index)
5153 StorageInternalImpl* This = (StorageInternalImpl*) base;
5155 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5156 index);
5159 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5160 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5162 StorageInternalImpl* This = (StorageInternalImpl*) base;
5164 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5165 index, offset, size, buffer, bytesRead);
5168 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5169 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5171 StorageInternalImpl* This = (StorageInternalImpl*) base;
5173 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5174 index, offset, size, buffer, bytesWritten);
5177 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5178 DirRef index, ULARGE_INTEGER newsize)
5180 StorageInternalImpl* This = (StorageInternalImpl*) base;
5182 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5183 index, newsize);
5186 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5187 DirRef dst, DirRef src)
5189 StorageInternalImpl* This = (StorageInternalImpl*) base;
5191 return StorageBaseImpl_StreamLink(This->parentStorage,
5192 dst, src);
5195 /******************************************************************************
5197 ** Storage32InternalImpl_Commit
5200 static HRESULT WINAPI StorageInternalImpl_Commit(
5201 IStorage* iface,
5202 DWORD grfCommitFlags) /* [in] */
5204 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5205 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5206 return StorageBaseImpl_Flush(base);
5209 /******************************************************************************
5211 ** Storage32InternalImpl_Revert
5214 static HRESULT WINAPI StorageInternalImpl_Revert(
5215 IStorage* iface)
5217 FIXME("(%p): stub\n", iface);
5218 return S_OK;
5221 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5223 IStorage_Release((IStorage*)This->parentStorage);
5224 HeapFree(GetProcessHeap(), 0, This);
5227 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5228 IEnumSTATSTG* iface,
5229 REFIID riid,
5230 void** ppvObject)
5232 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5234 if (ppvObject==0)
5235 return E_INVALIDARG;
5237 *ppvObject = 0;
5239 if (IsEqualGUID(&IID_IUnknown, riid) ||
5240 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5242 *ppvObject = This;
5243 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
5244 return S_OK;
5247 return E_NOINTERFACE;
5250 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5251 IEnumSTATSTG* iface)
5253 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5254 return InterlockedIncrement(&This->ref);
5257 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5258 IEnumSTATSTG* iface)
5260 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5262 ULONG newRef;
5264 newRef = InterlockedDecrement(&This->ref);
5266 if (newRef==0)
5268 IEnumSTATSTGImpl_Destroy(This);
5271 return newRef;
5274 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5275 IEnumSTATSTGImpl* This,
5276 DirRef *ref)
5278 DirRef result = DIRENTRY_NULL;
5279 DirRef searchNode;
5280 DirEntry entry;
5281 HRESULT hr;
5282 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5284 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5285 This->parentStorage->storageDirEntry, &entry);
5286 searchNode = entry.dirRootEntry;
5288 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5290 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5292 if (SUCCEEDED(hr))
5294 LONG diff = entryNameCmp( entry.name, This->name);
5296 if (diff <= 0)
5298 searchNode = entry.rightChild;
5300 else
5302 result = searchNode;
5303 memcpy(result_name, entry.name, sizeof(result_name));
5304 searchNode = entry.leftChild;
5309 if (SUCCEEDED(hr))
5311 *ref = result;
5312 if (result != DIRENTRY_NULL)
5313 memcpy(This->name, result_name, sizeof(result_name));
5316 return hr;
5319 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5320 IEnumSTATSTG* iface,
5321 ULONG celt,
5322 STATSTG* rgelt,
5323 ULONG* pceltFetched)
5325 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5327 DirEntry currentEntry;
5328 STATSTG* currentReturnStruct = rgelt;
5329 ULONG objectFetched = 0;
5330 DirRef currentSearchNode;
5331 HRESULT hr=S_OK;
5333 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5334 return E_INVALIDARG;
5336 if (This->parentStorage->reverted)
5337 return STG_E_REVERTED;
5340 * To avoid the special case, get another pointer to a ULONG value if
5341 * the caller didn't supply one.
5343 if (pceltFetched==0)
5344 pceltFetched = &objectFetched;
5347 * Start the iteration, we will iterate until we hit the end of the
5348 * linked list or until we hit the number of items to iterate through
5350 *pceltFetched = 0;
5352 while ( *pceltFetched < celt )
5354 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5356 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5357 break;
5360 * Read the entry from the storage.
5362 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5363 currentSearchNode,
5364 &currentEntry);
5367 * Copy the information to the return buffer.
5369 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5370 currentReturnStruct,
5371 &currentEntry,
5372 STATFLAG_DEFAULT);
5375 * Step to the next item in the iteration
5377 (*pceltFetched)++;
5378 currentReturnStruct++;
5381 if (SUCCEEDED(hr) && *pceltFetched != celt)
5382 hr = S_FALSE;
5384 return hr;
5388 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5389 IEnumSTATSTG* iface,
5390 ULONG celt)
5392 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5394 ULONG objectFetched = 0;
5395 DirRef currentSearchNode;
5396 HRESULT hr=S_OK;
5398 if (This->parentStorage->reverted)
5399 return STG_E_REVERTED;
5401 while ( (objectFetched < celt) )
5403 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5405 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5406 break;
5408 objectFetched++;
5411 if (SUCCEEDED(hr) && objectFetched != celt)
5412 return S_FALSE;
5414 return hr;
5417 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5418 IEnumSTATSTG* iface)
5420 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5422 if (This->parentStorage->reverted)
5423 return STG_E_REVERTED;
5425 This->name[0] = 0;
5427 return S_OK;
5430 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5431 IEnumSTATSTG* iface,
5432 IEnumSTATSTG** ppenum)
5434 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5436 IEnumSTATSTGImpl* newClone;
5438 if (This->parentStorage->reverted)
5439 return STG_E_REVERTED;
5442 * Perform a sanity check on the parameters.
5444 if (ppenum==0)
5445 return E_INVALIDARG;
5447 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5448 This->storageDirEntry);
5452 * The new clone enumeration must point to the same current node as
5453 * the ole one.
5455 memcpy(newClone->name, This->name, sizeof(newClone->name));
5457 *ppenum = (IEnumSTATSTG*)newClone;
5460 * Don't forget to nail down a reference to the clone before
5461 * returning it.
5463 IEnumSTATSTGImpl_AddRef(*ppenum);
5465 return S_OK;
5469 * Virtual function table for the IEnumSTATSTGImpl class.
5471 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5473 IEnumSTATSTGImpl_QueryInterface,
5474 IEnumSTATSTGImpl_AddRef,
5475 IEnumSTATSTGImpl_Release,
5476 IEnumSTATSTGImpl_Next,
5477 IEnumSTATSTGImpl_Skip,
5478 IEnumSTATSTGImpl_Reset,
5479 IEnumSTATSTGImpl_Clone
5482 /******************************************************************************
5483 ** IEnumSTATSTGImpl implementation
5486 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5487 StorageBaseImpl* parentStorage,
5488 DirRef storageDirEntry)
5490 IEnumSTATSTGImpl* newEnumeration;
5492 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5494 if (newEnumeration!=0)
5497 * Set-up the virtual function table and reference count.
5499 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5500 newEnumeration->ref = 0;
5503 * We want to nail-down the reference to the storage in case the
5504 * enumeration out-lives the storage in the client application.
5506 newEnumeration->parentStorage = parentStorage;
5507 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5509 newEnumeration->storageDirEntry = storageDirEntry;
5512 * Make sure the current node of the iterator is the first one.
5514 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
5517 return newEnumeration;
5521 * Virtual function table for the Storage32InternalImpl class.
5523 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5525 StorageBaseImpl_QueryInterface,
5526 StorageBaseImpl_AddRef,
5527 StorageBaseImpl_Release,
5528 StorageBaseImpl_CreateStream,
5529 StorageBaseImpl_OpenStream,
5530 StorageBaseImpl_CreateStorage,
5531 StorageBaseImpl_OpenStorage,
5532 StorageBaseImpl_CopyTo,
5533 StorageBaseImpl_MoveElementTo,
5534 StorageInternalImpl_Commit,
5535 StorageInternalImpl_Revert,
5536 StorageBaseImpl_EnumElements,
5537 StorageBaseImpl_DestroyElement,
5538 StorageBaseImpl_RenameElement,
5539 StorageBaseImpl_SetElementTimes,
5540 StorageBaseImpl_SetClass,
5541 StorageBaseImpl_SetStateBits,
5542 StorageBaseImpl_Stat
5545 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5547 StorageInternalImpl_Destroy,
5548 StorageInternalImpl_Invalidate,
5549 StorageInternalImpl_Flush,
5550 StorageInternalImpl_GetFilename,
5551 StorageInternalImpl_CreateDirEntry,
5552 StorageInternalImpl_WriteDirEntry,
5553 StorageInternalImpl_ReadDirEntry,
5554 StorageInternalImpl_DestroyDirEntry,
5555 StorageInternalImpl_StreamReadAt,
5556 StorageInternalImpl_StreamWriteAt,
5557 StorageInternalImpl_StreamSetSize,
5558 StorageInternalImpl_StreamLink
5561 /******************************************************************************
5562 ** Storage32InternalImpl implementation
5565 static StorageInternalImpl* StorageInternalImpl_Construct(
5566 StorageBaseImpl* parentStorage,
5567 DWORD openFlags,
5568 DirRef storageDirEntry)
5570 StorageInternalImpl* newStorage;
5572 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5574 if (newStorage!=0)
5576 list_init(&newStorage->base.strmHead);
5578 list_init(&newStorage->base.storageHead);
5581 * Initialize the virtual function table.
5583 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5584 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5585 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5586 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5588 newStorage->base.reverted = 0;
5590 newStorage->base.ref = 1;
5592 newStorage->parentStorage = parentStorage;
5595 * Keep a reference to the directory entry of this storage
5597 newStorage->base.storageDirEntry = storageDirEntry;
5599 newStorage->base.create = 0;
5601 return newStorage;
5604 return 0;
5607 /******************************************************************************
5608 ** StorageUtl implementation
5611 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5613 WORD tmp;
5615 memcpy(&tmp, buffer+offset, sizeof(WORD));
5616 *value = lendian16toh(tmp);
5619 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5621 value = htole16(value);
5622 memcpy(buffer+offset, &value, sizeof(WORD));
5625 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5627 DWORD tmp;
5629 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5630 *value = lendian32toh(tmp);
5633 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5635 value = htole32(value);
5636 memcpy(buffer+offset, &value, sizeof(DWORD));
5639 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5640 ULARGE_INTEGER* value)
5642 #ifdef WORDS_BIGENDIAN
5643 ULARGE_INTEGER tmp;
5645 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5646 value->u.LowPart = htole32(tmp.u.HighPart);
5647 value->u.HighPart = htole32(tmp.u.LowPart);
5648 #else
5649 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5650 #endif
5653 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5654 const ULARGE_INTEGER *value)
5656 #ifdef WORDS_BIGENDIAN
5657 ULARGE_INTEGER tmp;
5659 tmp.u.LowPart = htole32(value->u.HighPart);
5660 tmp.u.HighPart = htole32(value->u.LowPart);
5661 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5662 #else
5663 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5664 #endif
5667 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5669 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5670 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5671 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5673 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5676 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5678 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5679 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5680 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5682 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5685 void StorageUtl_CopyDirEntryToSTATSTG(
5686 StorageBaseImpl* storage,
5687 STATSTG* destination,
5688 const DirEntry* source,
5689 int statFlags)
5692 * The copy of the string occurs only when the flag is not set
5694 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5696 /* Use the filename for the root storage. */
5697 destination->pwcsName = 0;
5698 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5700 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5701 (source->name[0] == 0) )
5703 destination->pwcsName = 0;
5705 else
5707 destination->pwcsName =
5708 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5710 strcpyW(destination->pwcsName, source->name);
5713 switch (source->stgType)
5715 case STGTY_STORAGE:
5716 case STGTY_ROOT:
5717 destination->type = STGTY_STORAGE;
5718 break;
5719 case STGTY_STREAM:
5720 destination->type = STGTY_STREAM;
5721 break;
5722 default:
5723 destination->type = STGTY_STREAM;
5724 break;
5727 destination->cbSize = source->size;
5729 currentReturnStruct->mtime = {0}; TODO
5730 currentReturnStruct->ctime = {0};
5731 currentReturnStruct->atime = {0};
5733 destination->grfMode = 0;
5734 destination->grfLocksSupported = 0;
5735 destination->clsid = source->clsid;
5736 destination->grfStateBits = 0;
5737 destination->reserved = 0;
5740 /******************************************************************************
5741 ** BlockChainStream implementation
5744 /* Read and save the index of all blocks in this stream. */
5745 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5747 ULONG next_sector, next_offset;
5748 HRESULT hr;
5749 struct BlockChainRun *last_run;
5751 if (This->indexCacheLen == 0)
5753 last_run = NULL;
5754 next_offset = 0;
5755 next_sector = BlockChainStream_GetHeadOfChain(This);
5757 else
5759 last_run = &This->indexCache[This->indexCacheLen-1];
5760 next_offset = last_run->lastOffset+1;
5761 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5762 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5763 &next_sector);
5764 if (FAILED(hr)) return hr;
5767 while (next_sector != BLOCK_END_OF_CHAIN)
5769 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5771 /* Add the current block to the cache. */
5772 if (This->indexCacheSize == 0)
5774 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5775 if (!This->indexCache) return E_OUTOFMEMORY;
5776 This->indexCacheSize = 16;
5778 else if (This->indexCacheSize == This->indexCacheLen)
5780 struct BlockChainRun *new_cache;
5781 ULONG new_size;
5783 new_size = This->indexCacheSize * 2;
5784 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5785 if (!new_cache) return E_OUTOFMEMORY;
5786 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5788 HeapFree(GetProcessHeap(), 0, This->indexCache);
5789 This->indexCache = new_cache;
5790 This->indexCacheSize = new_size;
5793 This->indexCacheLen++;
5794 last_run = &This->indexCache[This->indexCacheLen-1];
5795 last_run->firstSector = next_sector;
5796 last_run->firstOffset = next_offset;
5799 last_run->lastOffset = next_offset;
5801 /* Find the next block. */
5802 next_offset++;
5803 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5804 if (FAILED(hr)) return hr;
5807 if (This->indexCacheLen)
5809 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5810 This->numBlocks = last_run->lastOffset+1;
5812 else
5814 This->tailIndex = BLOCK_END_OF_CHAIN;
5815 This->numBlocks = 0;
5818 return S_OK;
5821 /* Locate the nth block in this stream. */
5822 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5824 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5825 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5827 if (offset >= This->numBlocks)
5828 return BLOCK_END_OF_CHAIN;
5830 while (min_run < max_run)
5832 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5833 if (offset < This->indexCache[run_to_check].firstOffset)
5835 max_offset = This->indexCache[run_to_check].firstOffset-1;
5836 max_run = run_to_check-1;
5838 else if (offset > This->indexCache[run_to_check].lastOffset)
5840 min_offset = This->indexCache[run_to_check].lastOffset+1;
5841 min_run = run_to_check+1;
5843 else
5844 /* Block is in this run. */
5845 min_run = max_run = run_to_check;
5848 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5851 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5852 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5854 BlockChainBlock *result=NULL;
5855 int i;
5857 for (i=0; i<2; i++)
5858 if (This->cachedBlocks[i].index == index)
5860 *sector = This->cachedBlocks[i].sector;
5861 *block = &This->cachedBlocks[i];
5862 return S_OK;
5865 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5866 if (*sector == BLOCK_END_OF_CHAIN)
5867 return STG_E_DOCFILECORRUPT;
5869 if (create)
5871 if (This->cachedBlocks[0].index == 0xffffffff)
5872 result = &This->cachedBlocks[0];
5873 else if (This->cachedBlocks[1].index == 0xffffffff)
5874 result = &This->cachedBlocks[1];
5875 else
5877 result = &This->cachedBlocks[This->blockToEvict++];
5878 if (This->blockToEvict == 2)
5879 This->blockToEvict = 0;
5882 if (result->dirty)
5884 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5885 return STG_E_WRITEFAULT;
5886 result->dirty = 0;
5889 result->read = 0;
5890 result->index = index;
5891 result->sector = *sector;
5894 *block = result;
5895 return S_OK;
5898 BlockChainStream* BlockChainStream_Construct(
5899 StorageImpl* parentStorage,
5900 ULONG* headOfStreamPlaceHolder,
5901 DirRef dirEntry)
5903 BlockChainStream* newStream;
5905 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5907 newStream->parentStorage = parentStorage;
5908 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5909 newStream->ownerDirEntry = dirEntry;
5910 newStream->indexCache = NULL;
5911 newStream->indexCacheLen = 0;
5912 newStream->indexCacheSize = 0;
5913 newStream->cachedBlocks[0].index = 0xffffffff;
5914 newStream->cachedBlocks[0].dirty = 0;
5915 newStream->cachedBlocks[1].index = 0xffffffff;
5916 newStream->cachedBlocks[1].dirty = 0;
5917 newStream->blockToEvict = 0;
5919 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5921 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5922 HeapFree(GetProcessHeap(), 0, newStream);
5923 return NULL;
5926 return newStream;
5929 HRESULT BlockChainStream_Flush(BlockChainStream* This)
5931 int i;
5932 if (!This) return S_OK;
5933 for (i=0; i<2; i++)
5935 if (This->cachedBlocks[i].dirty)
5937 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
5938 This->cachedBlocks[i].dirty = 0;
5939 else
5940 return STG_E_WRITEFAULT;
5943 return S_OK;
5946 void BlockChainStream_Destroy(BlockChainStream* This)
5948 if (This)
5950 BlockChainStream_Flush(This);
5951 HeapFree(GetProcessHeap(), 0, This->indexCache);
5953 HeapFree(GetProcessHeap(), 0, This);
5956 /******************************************************************************
5957 * BlockChainStream_GetHeadOfChain
5959 * Returns the head of this stream chain.
5960 * Some special chains don't have directory entries, their heads are kept in
5961 * This->headOfStreamPlaceHolder.
5964 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5966 DirEntry chainEntry;
5967 HRESULT hr;
5969 if (This->headOfStreamPlaceHolder != 0)
5970 return *(This->headOfStreamPlaceHolder);
5972 if (This->ownerDirEntry != DIRENTRY_NULL)
5974 hr = StorageImpl_ReadDirEntry(
5975 This->parentStorage,
5976 This->ownerDirEntry,
5977 &chainEntry);
5979 if (SUCCEEDED(hr))
5981 return chainEntry.startingBlock;
5985 return BLOCK_END_OF_CHAIN;
5988 /******************************************************************************
5989 * BlockChainStream_GetCount
5991 * Returns the number of blocks that comprises this chain.
5992 * This is not the size of the stream as the last block may not be full!
5994 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5996 return This->numBlocks;
5999 /******************************************************************************
6000 * BlockChainStream_ReadAt
6002 * Reads a specified number of bytes from this chain at the specified offset.
6003 * bytesRead may be NULL.
6004 * Failure will be returned if the specified number of bytes has not been read.
6006 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6007 ULARGE_INTEGER offset,
6008 ULONG size,
6009 void* buffer,
6010 ULONG* bytesRead)
6012 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6013 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6014 ULONG bytesToReadInBuffer;
6015 ULONG blockIndex;
6016 BYTE* bufferWalker;
6017 ULARGE_INTEGER stream_size;
6018 HRESULT hr;
6019 BlockChainBlock *cachedBlock;
6021 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6024 * Find the first block in the stream that contains part of the buffer.
6026 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6028 *bytesRead = 0;
6030 stream_size = BlockChainStream_GetSize(This);
6031 if (stream_size.QuadPart > offset.QuadPart)
6032 size = min(stream_size.QuadPart - offset.QuadPart, size);
6033 else
6034 return S_OK;
6037 * Start reading the buffer.
6039 bufferWalker = buffer;
6041 while (size > 0)
6043 ULARGE_INTEGER ulOffset;
6044 DWORD bytesReadAt;
6047 * Calculate how many bytes we can copy from this big block.
6049 bytesToReadInBuffer =
6050 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6052 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6054 if (FAILED(hr))
6055 return hr;
6057 if (!cachedBlock)
6059 /* Not in cache, and we're going to read past the end of the block. */
6060 ulOffset.u.HighPart = 0;
6061 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6062 offsetInBlock;
6064 StorageImpl_ReadAt(This->parentStorage,
6065 ulOffset,
6066 bufferWalker,
6067 bytesToReadInBuffer,
6068 &bytesReadAt);
6070 else
6072 if (!cachedBlock->read)
6074 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6075 return STG_E_READFAULT;
6077 cachedBlock->read = 1;
6080 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6081 bytesReadAt = bytesToReadInBuffer;
6084 blockNoInSequence++;
6085 bufferWalker += bytesReadAt;
6086 size -= bytesReadAt;
6087 *bytesRead += bytesReadAt;
6088 offsetInBlock = 0; /* There is no offset on the next block */
6090 if (bytesToReadInBuffer != bytesReadAt)
6091 break;
6094 return S_OK;
6097 /******************************************************************************
6098 * BlockChainStream_WriteAt
6100 * Writes the specified number of bytes to this chain at the specified offset.
6101 * Will fail if not all specified number of bytes have been written.
6103 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6104 ULARGE_INTEGER offset,
6105 ULONG size,
6106 const void* buffer,
6107 ULONG* bytesWritten)
6109 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6110 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6111 ULONG bytesToWrite;
6112 ULONG blockIndex;
6113 const BYTE* bufferWalker;
6114 HRESULT hr;
6115 BlockChainBlock *cachedBlock;
6117 *bytesWritten = 0;
6118 bufferWalker = buffer;
6120 while (size > 0)
6122 ULARGE_INTEGER ulOffset;
6123 DWORD bytesWrittenAt;
6126 * Calculate how many bytes we can copy to this big block.
6128 bytesToWrite =
6129 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6131 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6133 /* BlockChainStream_SetSize should have already been called to ensure we have
6134 * enough blocks in the chain to write into */
6135 if (FAILED(hr))
6137 ERR("not enough blocks in chain to write data\n");
6138 return hr;
6141 if (!cachedBlock)
6143 /* Not in cache, and we're going to write past the end of the block. */
6144 ulOffset.u.HighPart = 0;
6145 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6146 offsetInBlock;
6148 StorageImpl_WriteAt(This->parentStorage,
6149 ulOffset,
6150 bufferWalker,
6151 bytesToWrite,
6152 &bytesWrittenAt);
6154 else
6156 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6158 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6159 return STG_E_READFAULT;
6162 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6163 bytesWrittenAt = bytesToWrite;
6164 cachedBlock->read = 1;
6165 cachedBlock->dirty = 1;
6168 blockNoInSequence++;
6169 bufferWalker += bytesWrittenAt;
6170 size -= bytesWrittenAt;
6171 *bytesWritten += bytesWrittenAt;
6172 offsetInBlock = 0; /* There is no offset on the next block */
6174 if (bytesWrittenAt != bytesToWrite)
6175 break;
6178 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6181 /******************************************************************************
6182 * BlockChainStream_Shrink
6184 * Shrinks this chain in the big block depot.
6186 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6187 ULARGE_INTEGER newSize)
6189 ULONG blockIndex;
6190 ULONG numBlocks;
6191 int i;
6194 * Figure out how many blocks are needed to contain the new size
6196 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6198 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6199 numBlocks++;
6201 if (numBlocks)
6204 * Go to the new end of chain
6206 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6208 /* Mark the new end of chain */
6209 StorageImpl_SetNextBlockInChain(
6210 This->parentStorage,
6211 blockIndex,
6212 BLOCK_END_OF_CHAIN);
6214 This->tailIndex = blockIndex;
6216 else
6218 if (This->headOfStreamPlaceHolder != 0)
6220 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6222 else
6224 DirEntry chainEntry;
6225 assert(This->ownerDirEntry != DIRENTRY_NULL);
6227 StorageImpl_ReadDirEntry(
6228 This->parentStorage,
6229 This->ownerDirEntry,
6230 &chainEntry);
6232 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6234 StorageImpl_WriteDirEntry(
6235 This->parentStorage,
6236 This->ownerDirEntry,
6237 &chainEntry);
6240 This->tailIndex = BLOCK_END_OF_CHAIN;
6243 This->numBlocks = numBlocks;
6246 * Mark the extra blocks as free
6248 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6250 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6251 StorageImpl_FreeBigBlock(This->parentStorage,
6252 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6253 if (last_run->lastOffset == last_run->firstOffset)
6254 This->indexCacheLen--;
6255 else
6256 last_run->lastOffset--;
6260 * Reset the last accessed block cache.
6262 for (i=0; i<2; i++)
6264 if (This->cachedBlocks[i].index >= numBlocks)
6266 This->cachedBlocks[i].index = 0xffffffff;
6267 This->cachedBlocks[i].dirty = 0;
6271 return TRUE;
6274 /******************************************************************************
6275 * BlockChainStream_Enlarge
6277 * Grows this chain in the big block depot.
6279 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6280 ULARGE_INTEGER newSize)
6282 ULONG blockIndex, currentBlock;
6283 ULONG newNumBlocks;
6284 ULONG oldNumBlocks = 0;
6286 blockIndex = BlockChainStream_GetHeadOfChain(This);
6289 * Empty chain. Create the head.
6291 if (blockIndex == BLOCK_END_OF_CHAIN)
6293 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6294 StorageImpl_SetNextBlockInChain(This->parentStorage,
6295 blockIndex,
6296 BLOCK_END_OF_CHAIN);
6298 if (This->headOfStreamPlaceHolder != 0)
6300 *(This->headOfStreamPlaceHolder) = blockIndex;
6302 else
6304 DirEntry chainEntry;
6305 assert(This->ownerDirEntry != DIRENTRY_NULL);
6307 StorageImpl_ReadDirEntry(
6308 This->parentStorage,
6309 This->ownerDirEntry,
6310 &chainEntry);
6312 chainEntry.startingBlock = blockIndex;
6314 StorageImpl_WriteDirEntry(
6315 This->parentStorage,
6316 This->ownerDirEntry,
6317 &chainEntry);
6320 This->tailIndex = blockIndex;
6321 This->numBlocks = 1;
6325 * Figure out how many blocks are needed to contain this stream
6327 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6329 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6330 newNumBlocks++;
6333 * Go to the current end of chain
6335 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6337 currentBlock = blockIndex;
6339 while (blockIndex != BLOCK_END_OF_CHAIN)
6341 This->numBlocks++;
6342 currentBlock = blockIndex;
6344 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6345 &blockIndex)))
6346 return FALSE;
6349 This->tailIndex = currentBlock;
6352 currentBlock = This->tailIndex;
6353 oldNumBlocks = This->numBlocks;
6356 * Add new blocks to the chain
6358 if (oldNumBlocks < newNumBlocks)
6360 while (oldNumBlocks < newNumBlocks)
6362 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6364 StorageImpl_SetNextBlockInChain(
6365 This->parentStorage,
6366 currentBlock,
6367 blockIndex);
6369 StorageImpl_SetNextBlockInChain(
6370 This->parentStorage,
6371 blockIndex,
6372 BLOCK_END_OF_CHAIN);
6374 currentBlock = blockIndex;
6375 oldNumBlocks++;
6378 This->tailIndex = blockIndex;
6379 This->numBlocks = newNumBlocks;
6382 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6383 return FALSE;
6385 return TRUE;
6388 /******************************************************************************
6389 * BlockChainStream_SetSize
6391 * Sets the size of this stream. The big block depot will be updated.
6392 * The file will grow if we grow the chain.
6394 * TODO: Free the actual blocks in the file when we shrink the chain.
6395 * Currently, the blocks are still in the file. So the file size
6396 * doesn't shrink even if we shrink streams.
6398 BOOL BlockChainStream_SetSize(
6399 BlockChainStream* This,
6400 ULARGE_INTEGER newSize)
6402 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6404 if (newSize.u.LowPart == size.u.LowPart)
6405 return TRUE;
6407 if (newSize.u.LowPart < size.u.LowPart)
6409 BlockChainStream_Shrink(This, newSize);
6411 else
6413 BlockChainStream_Enlarge(This, newSize);
6416 return TRUE;
6419 /******************************************************************************
6420 * BlockChainStream_GetSize
6422 * Returns the size of this chain.
6423 * Will return the block count if this chain doesn't have a directory entry.
6425 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6427 DirEntry chainEntry;
6429 if(This->headOfStreamPlaceHolder == NULL)
6432 * This chain has a directory entry so use the size value from there.
6434 StorageImpl_ReadDirEntry(
6435 This->parentStorage,
6436 This->ownerDirEntry,
6437 &chainEntry);
6439 return chainEntry.size;
6441 else
6444 * this chain is a chain that does not have a directory entry, figure out the
6445 * size by making the product number of used blocks times the
6446 * size of them
6448 ULARGE_INTEGER result;
6449 result.u.HighPart = 0;
6451 result.u.LowPart =
6452 BlockChainStream_GetCount(This) *
6453 This->parentStorage->bigBlockSize;
6455 return result;
6459 /******************************************************************************
6460 ** SmallBlockChainStream implementation
6463 SmallBlockChainStream* SmallBlockChainStream_Construct(
6464 StorageImpl* parentStorage,
6465 ULONG* headOfStreamPlaceHolder,
6466 DirRef dirEntry)
6468 SmallBlockChainStream* newStream;
6470 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6472 newStream->parentStorage = parentStorage;
6473 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6474 newStream->ownerDirEntry = dirEntry;
6476 return newStream;
6479 void SmallBlockChainStream_Destroy(
6480 SmallBlockChainStream* This)
6482 HeapFree(GetProcessHeap(), 0, This);
6485 /******************************************************************************
6486 * SmallBlockChainStream_GetHeadOfChain
6488 * Returns the head of this chain of small blocks.
6490 static ULONG SmallBlockChainStream_GetHeadOfChain(
6491 SmallBlockChainStream* This)
6493 DirEntry chainEntry;
6494 HRESULT hr;
6496 if (This->headOfStreamPlaceHolder != NULL)
6497 return *(This->headOfStreamPlaceHolder);
6499 if (This->ownerDirEntry)
6501 hr = StorageImpl_ReadDirEntry(
6502 This->parentStorage,
6503 This->ownerDirEntry,
6504 &chainEntry);
6506 if (SUCCEEDED(hr))
6508 return chainEntry.startingBlock;
6513 return BLOCK_END_OF_CHAIN;
6516 /******************************************************************************
6517 * SmallBlockChainStream_GetNextBlockInChain
6519 * Returns the index of the next small block in this chain.
6521 * Return Values:
6522 * - BLOCK_END_OF_CHAIN: end of this chain
6523 * - BLOCK_UNUSED: small block 'blockIndex' is free
6525 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6526 SmallBlockChainStream* This,
6527 ULONG blockIndex,
6528 ULONG* nextBlockInChain)
6530 ULARGE_INTEGER offsetOfBlockInDepot;
6531 DWORD buffer;
6532 ULONG bytesRead;
6533 HRESULT res;
6535 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6537 offsetOfBlockInDepot.u.HighPart = 0;
6538 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6541 * Read those bytes in the buffer from the small block file.
6543 res = BlockChainStream_ReadAt(
6544 This->parentStorage->smallBlockDepotChain,
6545 offsetOfBlockInDepot,
6546 sizeof(DWORD),
6547 &buffer,
6548 &bytesRead);
6550 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6551 res = STG_E_READFAULT;
6553 if (SUCCEEDED(res))
6555 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6556 return S_OK;
6559 return res;
6562 /******************************************************************************
6563 * SmallBlockChainStream_SetNextBlockInChain
6565 * Writes the index of the next block of the specified block in the small
6566 * block depot.
6567 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6568 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6570 static void SmallBlockChainStream_SetNextBlockInChain(
6571 SmallBlockChainStream* This,
6572 ULONG blockIndex,
6573 ULONG nextBlock)
6575 ULARGE_INTEGER offsetOfBlockInDepot;
6576 DWORD buffer;
6577 ULONG bytesWritten;
6579 offsetOfBlockInDepot.u.HighPart = 0;
6580 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6582 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6585 * Read those bytes in the buffer from the small block file.
6587 BlockChainStream_WriteAt(
6588 This->parentStorage->smallBlockDepotChain,
6589 offsetOfBlockInDepot,
6590 sizeof(DWORD),
6591 &buffer,
6592 &bytesWritten);
6595 /******************************************************************************
6596 * SmallBlockChainStream_FreeBlock
6598 * Flag small block 'blockIndex' as free in the small block depot.
6600 static void SmallBlockChainStream_FreeBlock(
6601 SmallBlockChainStream* This,
6602 ULONG blockIndex)
6604 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6607 /******************************************************************************
6608 * SmallBlockChainStream_GetNextFreeBlock
6610 * Returns the index of a free small block. The small block depot will be
6611 * enlarged if necessary. The small block chain will also be enlarged if
6612 * necessary.
6614 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6615 SmallBlockChainStream* This)
6617 ULARGE_INTEGER offsetOfBlockInDepot;
6618 DWORD buffer;
6619 ULONG bytesRead;
6620 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6621 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6622 HRESULT res = S_OK;
6623 ULONG smallBlocksPerBigBlock;
6624 DirEntry rootEntry;
6625 ULONG blocksRequired;
6626 ULARGE_INTEGER old_size, size_required;
6628 offsetOfBlockInDepot.u.HighPart = 0;
6631 * Scan the small block depot for a free block
6633 while (nextBlockIndex != BLOCK_UNUSED)
6635 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6637 res = BlockChainStream_ReadAt(
6638 This->parentStorage->smallBlockDepotChain,
6639 offsetOfBlockInDepot,
6640 sizeof(DWORD),
6641 &buffer,
6642 &bytesRead);
6645 * If we run out of space for the small block depot, enlarge it
6647 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6649 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6651 if (nextBlockIndex != BLOCK_UNUSED)
6652 blockIndex++;
6654 else
6656 ULONG count =
6657 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6659 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6660 ULARGE_INTEGER newSize, offset;
6661 ULONG bytesWritten;
6663 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6664 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6667 * Initialize all the small blocks to free
6669 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6670 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6671 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6672 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6674 StorageImpl_SaveFileHeader(This->parentStorage);
6678 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6680 smallBlocksPerBigBlock =
6681 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6684 * Verify if we have to allocate big blocks to contain small blocks
6686 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6688 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6690 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6692 if (size_required.QuadPart > old_size.QuadPart)
6694 BlockChainStream_SetSize(
6695 This->parentStorage->smallBlockRootChain,
6696 size_required);
6698 StorageImpl_ReadDirEntry(
6699 This->parentStorage,
6700 This->parentStorage->base.storageDirEntry,
6701 &rootEntry);
6703 rootEntry.size = size_required;
6705 StorageImpl_WriteDirEntry(
6706 This->parentStorage,
6707 This->parentStorage->base.storageDirEntry,
6708 &rootEntry);
6711 return blockIndex;
6714 /******************************************************************************
6715 * SmallBlockChainStream_ReadAt
6717 * Reads a specified number of bytes from this chain at the specified offset.
6718 * bytesRead may be NULL.
6719 * Failure will be returned if the specified number of bytes has not been read.
6721 HRESULT SmallBlockChainStream_ReadAt(
6722 SmallBlockChainStream* This,
6723 ULARGE_INTEGER offset,
6724 ULONG size,
6725 void* buffer,
6726 ULONG* bytesRead)
6728 HRESULT rc = S_OK;
6729 ULARGE_INTEGER offsetInBigBlockFile;
6730 ULONG blockNoInSequence =
6731 offset.u.LowPart / This->parentStorage->smallBlockSize;
6733 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6734 ULONG bytesToReadInBuffer;
6735 ULONG blockIndex;
6736 ULONG bytesReadFromBigBlockFile;
6737 BYTE* bufferWalker;
6738 ULARGE_INTEGER stream_size;
6741 * This should never happen on a small block file.
6743 assert(offset.u.HighPart==0);
6745 *bytesRead = 0;
6747 stream_size = SmallBlockChainStream_GetSize(This);
6748 if (stream_size.QuadPart > offset.QuadPart)
6749 size = min(stream_size.QuadPart - offset.QuadPart, size);
6750 else
6751 return S_OK;
6754 * Find the first block in the stream that contains part of the buffer.
6756 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6758 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6760 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6761 if(FAILED(rc))
6762 return rc;
6763 blockNoInSequence--;
6767 * Start reading the buffer.
6769 bufferWalker = buffer;
6771 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6774 * Calculate how many bytes we can copy from this small block.
6776 bytesToReadInBuffer =
6777 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6780 * Calculate the offset of the small block in the small block file.
6782 offsetInBigBlockFile.u.HighPart = 0;
6783 offsetInBigBlockFile.u.LowPart =
6784 blockIndex * This->parentStorage->smallBlockSize;
6786 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6789 * Read those bytes in the buffer from the small block file.
6790 * The small block has already been identified so it shouldn't fail
6791 * unless the file is corrupt.
6793 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6794 offsetInBigBlockFile,
6795 bytesToReadInBuffer,
6796 bufferWalker,
6797 &bytesReadFromBigBlockFile);
6799 if (FAILED(rc))
6800 return rc;
6802 if (!bytesReadFromBigBlockFile)
6803 return STG_E_DOCFILECORRUPT;
6806 * Step to the next big block.
6808 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6809 if(FAILED(rc))
6810 return STG_E_DOCFILECORRUPT;
6812 bufferWalker += bytesReadFromBigBlockFile;
6813 size -= bytesReadFromBigBlockFile;
6814 *bytesRead += bytesReadFromBigBlockFile;
6815 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6818 return S_OK;
6821 /******************************************************************************
6822 * SmallBlockChainStream_WriteAt
6824 * Writes the specified number of bytes to this chain at the specified offset.
6825 * Will fail if not all specified number of bytes have been written.
6827 HRESULT SmallBlockChainStream_WriteAt(
6828 SmallBlockChainStream* This,
6829 ULARGE_INTEGER offset,
6830 ULONG size,
6831 const void* buffer,
6832 ULONG* bytesWritten)
6834 ULARGE_INTEGER offsetInBigBlockFile;
6835 ULONG blockNoInSequence =
6836 offset.u.LowPart / This->parentStorage->smallBlockSize;
6838 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6839 ULONG bytesToWriteInBuffer;
6840 ULONG blockIndex;
6841 ULONG bytesWrittenToBigBlockFile;
6842 const BYTE* bufferWalker;
6843 HRESULT res;
6846 * This should never happen on a small block file.
6848 assert(offset.u.HighPart==0);
6851 * Find the first block in the stream that contains part of the buffer.
6853 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6855 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6857 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6858 return STG_E_DOCFILECORRUPT;
6859 blockNoInSequence--;
6863 * Start writing the buffer.
6865 *bytesWritten = 0;
6866 bufferWalker = buffer;
6867 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6870 * Calculate how many bytes we can copy to this small block.
6872 bytesToWriteInBuffer =
6873 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6876 * Calculate the offset of the small block in the small block file.
6878 offsetInBigBlockFile.u.HighPart = 0;
6879 offsetInBigBlockFile.u.LowPart =
6880 blockIndex * This->parentStorage->smallBlockSize;
6882 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6885 * Write those bytes in the buffer to the small block file.
6887 res = BlockChainStream_WriteAt(
6888 This->parentStorage->smallBlockRootChain,
6889 offsetInBigBlockFile,
6890 bytesToWriteInBuffer,
6891 bufferWalker,
6892 &bytesWrittenToBigBlockFile);
6893 if (FAILED(res))
6894 return res;
6897 * Step to the next big block.
6899 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6900 &blockIndex)))
6901 return FALSE;
6902 bufferWalker += bytesWrittenToBigBlockFile;
6903 size -= bytesWrittenToBigBlockFile;
6904 *bytesWritten += bytesWrittenToBigBlockFile;
6905 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6908 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6911 /******************************************************************************
6912 * SmallBlockChainStream_Shrink
6914 * Shrinks this chain in the small block depot.
6916 static BOOL SmallBlockChainStream_Shrink(
6917 SmallBlockChainStream* This,
6918 ULARGE_INTEGER newSize)
6920 ULONG blockIndex, extraBlock;
6921 ULONG numBlocks;
6922 ULONG count = 0;
6924 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6926 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6927 numBlocks++;
6929 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6932 * Go to the new end of chain
6934 while (count < numBlocks)
6936 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6937 &blockIndex)))
6938 return FALSE;
6939 count++;
6943 * If the count is 0, we have a special case, the head of the chain was
6944 * just freed.
6946 if (count == 0)
6948 DirEntry chainEntry;
6950 StorageImpl_ReadDirEntry(This->parentStorage,
6951 This->ownerDirEntry,
6952 &chainEntry);
6954 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6956 StorageImpl_WriteDirEntry(This->parentStorage,
6957 This->ownerDirEntry,
6958 &chainEntry);
6961 * We start freeing the chain at the head block.
6963 extraBlock = blockIndex;
6965 else
6967 /* Get the next block before marking the new end */
6968 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6969 &extraBlock)))
6970 return FALSE;
6972 /* Mark the new end of chain */
6973 SmallBlockChainStream_SetNextBlockInChain(
6974 This,
6975 blockIndex,
6976 BLOCK_END_OF_CHAIN);
6980 * Mark the extra blocks as free
6982 while (extraBlock != BLOCK_END_OF_CHAIN)
6984 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6985 &blockIndex)))
6986 return FALSE;
6987 SmallBlockChainStream_FreeBlock(This, extraBlock);
6988 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
6989 extraBlock = blockIndex;
6992 return TRUE;
6995 /******************************************************************************
6996 * SmallBlockChainStream_Enlarge
6998 * Grows this chain in the small block depot.
7000 static BOOL SmallBlockChainStream_Enlarge(
7001 SmallBlockChainStream* This,
7002 ULARGE_INTEGER newSize)
7004 ULONG blockIndex, currentBlock;
7005 ULONG newNumBlocks;
7006 ULONG oldNumBlocks = 0;
7008 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7011 * Empty chain. Create the head.
7013 if (blockIndex == BLOCK_END_OF_CHAIN)
7015 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7016 SmallBlockChainStream_SetNextBlockInChain(
7017 This,
7018 blockIndex,
7019 BLOCK_END_OF_CHAIN);
7021 if (This->headOfStreamPlaceHolder != NULL)
7023 *(This->headOfStreamPlaceHolder) = blockIndex;
7025 else
7027 DirEntry chainEntry;
7029 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7030 &chainEntry);
7032 chainEntry.startingBlock = blockIndex;
7034 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7035 &chainEntry);
7039 currentBlock = blockIndex;
7042 * Figure out how many blocks are needed to contain this stream
7044 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7046 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7047 newNumBlocks++;
7050 * Go to the current end of chain
7052 while (blockIndex != BLOCK_END_OF_CHAIN)
7054 oldNumBlocks++;
7055 currentBlock = blockIndex;
7056 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7057 return FALSE;
7061 * Add new blocks to the chain
7063 while (oldNumBlocks < newNumBlocks)
7065 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7066 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7068 SmallBlockChainStream_SetNextBlockInChain(
7069 This,
7070 blockIndex,
7071 BLOCK_END_OF_CHAIN);
7073 currentBlock = blockIndex;
7074 oldNumBlocks++;
7077 return TRUE;
7080 /******************************************************************************
7081 * SmallBlockChainStream_SetSize
7083 * Sets the size of this stream.
7084 * The file will grow if we grow the chain.
7086 * TODO: Free the actual blocks in the file when we shrink the chain.
7087 * Currently, the blocks are still in the file. So the file size
7088 * doesn't shrink even if we shrink streams.
7090 BOOL SmallBlockChainStream_SetSize(
7091 SmallBlockChainStream* This,
7092 ULARGE_INTEGER newSize)
7094 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7096 if (newSize.u.LowPart == size.u.LowPart)
7097 return TRUE;
7099 if (newSize.u.LowPart < size.u.LowPart)
7101 SmallBlockChainStream_Shrink(This, newSize);
7103 else
7105 SmallBlockChainStream_Enlarge(This, newSize);
7108 return TRUE;
7111 /******************************************************************************
7112 * SmallBlockChainStream_GetCount
7114 * Returns the number of small blocks that comprises this chain.
7115 * This is not the size of the stream as the last block may not be full!
7118 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7120 ULONG blockIndex;
7121 ULONG count = 0;
7123 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7125 while(blockIndex != BLOCK_END_OF_CHAIN)
7127 count++;
7129 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7130 blockIndex, &blockIndex)))
7131 return 0;
7134 return count;
7137 /******************************************************************************
7138 * SmallBlockChainStream_GetSize
7140 * Returns the size of this chain.
7142 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7144 DirEntry chainEntry;
7146 if(This->headOfStreamPlaceHolder != NULL)
7148 ULARGE_INTEGER result;
7149 result.u.HighPart = 0;
7151 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7152 This->parentStorage->smallBlockSize;
7154 return result;
7157 StorageImpl_ReadDirEntry(
7158 This->parentStorage,
7159 This->ownerDirEntry,
7160 &chainEntry);
7162 return chainEntry.size;
7165 static HRESULT create_storagefile(
7166 LPCOLESTR pwcsName,
7167 DWORD grfMode,
7168 DWORD grfAttrs,
7169 STGOPTIONS* pStgOptions,
7170 REFIID riid,
7171 void** ppstgOpen)
7173 StorageBaseImpl* newStorage = 0;
7174 HANDLE hFile = INVALID_HANDLE_VALUE;
7175 HRESULT hr = STG_E_INVALIDFLAG;
7176 DWORD shareMode;
7177 DWORD accessMode;
7178 DWORD creationMode;
7179 DWORD fileAttributes;
7180 WCHAR tempFileName[MAX_PATH];
7182 if (ppstgOpen == 0)
7183 return STG_E_INVALIDPOINTER;
7185 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7186 return STG_E_INVALIDPARAMETER;
7188 /* if no share mode given then DENY_NONE is the default */
7189 if (STGM_SHARE_MODE(grfMode) == 0)
7190 grfMode |= STGM_SHARE_DENY_NONE;
7192 if ( FAILED( validateSTGM(grfMode) ))
7193 goto end;
7195 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7196 switch(STGM_ACCESS_MODE(grfMode))
7198 case STGM_WRITE:
7199 case STGM_READWRITE:
7200 break;
7201 default:
7202 goto end;
7205 /* in direct mode, can only use SHARE_EXCLUSIVE */
7206 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7207 goto end;
7209 /* but in transacted mode, any share mode is valid */
7212 * Generate a unique name.
7214 if (pwcsName == 0)
7216 WCHAR tempPath[MAX_PATH];
7217 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7219 memset(tempPath, 0, sizeof(tempPath));
7220 memset(tempFileName, 0, sizeof(tempFileName));
7222 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7223 tempPath[0] = '.';
7225 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7226 pwcsName = tempFileName;
7227 else
7229 hr = STG_E_INSUFFICIENTMEMORY;
7230 goto end;
7233 creationMode = TRUNCATE_EXISTING;
7235 else
7237 creationMode = GetCreationModeFromSTGM(grfMode);
7241 * Interpret the STGM value grfMode
7243 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7244 accessMode = GetAccessModeFromSTGM(grfMode);
7246 if (grfMode & STGM_DELETEONRELEASE)
7247 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7248 else
7249 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7251 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7253 static int fixme;
7254 if (!fixme++)
7255 FIXME("Storage share mode not implemented.\n");
7258 *ppstgOpen = 0;
7260 hFile = CreateFileW(pwcsName,
7261 accessMode,
7262 shareMode,
7263 NULL,
7264 creationMode,
7265 fileAttributes,
7268 if (hFile == INVALID_HANDLE_VALUE)
7270 if(GetLastError() == ERROR_FILE_EXISTS)
7271 hr = STG_E_FILEALREADYEXISTS;
7272 else
7273 hr = E_FAIL;
7274 goto end;
7278 * Allocate and initialize the new IStorage32object.
7280 hr = Storage_Construct(
7281 hFile,
7282 pwcsName,
7283 NULL,
7284 grfMode,
7285 TRUE,
7286 TRUE,
7287 pStgOptions->ulSectorSize,
7288 &newStorage);
7290 if (FAILED(hr))
7292 goto end;
7295 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7297 IStorage_Release((IStorage*)newStorage);
7299 end:
7300 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7302 return hr;
7305 /******************************************************************************
7306 * StgCreateDocfile [OLE32.@]
7307 * Creates a new compound file storage object
7309 * PARAMS
7310 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7311 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7312 * reserved [ ?] unused?, usually 0
7313 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7315 * RETURNS
7316 * S_OK if the file was successfully created
7317 * some STG_E_ value if error
7318 * NOTES
7319 * if pwcsName is NULL, create file with new unique name
7320 * the function can returns
7321 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7322 * (unrealized now)
7324 HRESULT WINAPI StgCreateDocfile(
7325 LPCOLESTR pwcsName,
7326 DWORD grfMode,
7327 DWORD reserved,
7328 IStorage **ppstgOpen)
7330 STGOPTIONS stgoptions = {1, 0, 512};
7332 TRACE("(%s, %x, %d, %p)\n",
7333 debugstr_w(pwcsName), grfMode,
7334 reserved, ppstgOpen);
7336 if (ppstgOpen == 0)
7337 return STG_E_INVALIDPOINTER;
7338 if (reserved != 0)
7339 return STG_E_INVALIDPARAMETER;
7341 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7344 /******************************************************************************
7345 * StgCreateStorageEx [OLE32.@]
7347 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7349 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7350 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7352 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7354 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7355 return STG_E_INVALIDPARAMETER;
7358 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7360 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7361 return STG_E_INVALIDPARAMETER;
7364 if (stgfmt == STGFMT_FILE)
7366 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7367 return STG_E_INVALIDPARAMETER;
7370 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7372 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7376 ERR("Invalid stgfmt argument\n");
7377 return STG_E_INVALIDPARAMETER;
7380 /******************************************************************************
7381 * StgCreatePropSetStg [OLE32.@]
7383 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7384 IPropertySetStorage **ppPropSetStg)
7386 HRESULT hr;
7388 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7389 if (reserved)
7390 hr = STG_E_INVALIDPARAMETER;
7391 else
7392 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7393 (void**)ppPropSetStg);
7394 return hr;
7397 /******************************************************************************
7398 * StgOpenStorageEx [OLE32.@]
7400 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7402 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7403 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7405 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7407 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7408 return STG_E_INVALIDPARAMETER;
7411 switch (stgfmt)
7413 case STGFMT_FILE:
7414 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7415 return STG_E_INVALIDPARAMETER;
7417 case STGFMT_STORAGE:
7418 break;
7420 case STGFMT_DOCFILE:
7421 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7423 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7424 return STG_E_INVALIDPARAMETER;
7426 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7427 break;
7429 case STGFMT_ANY:
7430 WARN("STGFMT_ANY assuming storage\n");
7431 break;
7433 default:
7434 return STG_E_INVALIDPARAMETER;
7437 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7441 /******************************************************************************
7442 * StgOpenStorage [OLE32.@]
7444 HRESULT WINAPI StgOpenStorage(
7445 const OLECHAR *pwcsName,
7446 IStorage *pstgPriority,
7447 DWORD grfMode,
7448 SNB snbExclude,
7449 DWORD reserved,
7450 IStorage **ppstgOpen)
7452 StorageBaseImpl* newStorage = 0;
7453 HRESULT hr = S_OK;
7454 HANDLE hFile = 0;
7455 DWORD shareMode;
7456 DWORD accessMode;
7458 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7459 debugstr_w(pwcsName), pstgPriority, grfMode,
7460 snbExclude, reserved, ppstgOpen);
7462 if (pwcsName == 0)
7464 hr = STG_E_INVALIDNAME;
7465 goto end;
7468 if (ppstgOpen == 0)
7470 hr = STG_E_INVALIDPOINTER;
7471 goto end;
7474 if (reserved)
7476 hr = STG_E_INVALIDPARAMETER;
7477 goto end;
7480 if (grfMode & STGM_PRIORITY)
7482 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7483 return STG_E_INVALIDFLAG;
7484 if (grfMode & STGM_DELETEONRELEASE)
7485 return STG_E_INVALIDFUNCTION;
7486 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7487 return STG_E_INVALIDFLAG;
7488 grfMode &= ~0xf0; /* remove the existing sharing mode */
7489 grfMode |= STGM_SHARE_DENY_NONE;
7491 /* STGM_PRIORITY stops other IStorage objects on the same file from
7492 * committing until the STGM_PRIORITY IStorage is closed. it also
7493 * stops non-transacted mode StgOpenStorage calls with write access from
7494 * succeeding. obviously, both of these cannot be achieved through just
7495 * file share flags */
7496 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7500 * Validate the sharing mode
7502 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7503 switch(STGM_SHARE_MODE(grfMode))
7505 case STGM_SHARE_EXCLUSIVE:
7506 case STGM_SHARE_DENY_WRITE:
7507 break;
7508 default:
7509 hr = STG_E_INVALIDFLAG;
7510 goto end;
7513 if ( FAILED( validateSTGM(grfMode) ) ||
7514 (grfMode&STGM_CREATE))
7516 hr = STG_E_INVALIDFLAG;
7517 goto end;
7520 /* shared reading requires transacted mode */
7521 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7522 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7523 !(grfMode&STGM_TRANSACTED) )
7525 hr = STG_E_INVALIDFLAG;
7526 goto end;
7530 * Interpret the STGM value grfMode
7532 shareMode = GetShareModeFromSTGM(grfMode);
7533 accessMode = GetAccessModeFromSTGM(grfMode);
7535 *ppstgOpen = 0;
7537 hFile = CreateFileW( pwcsName,
7538 accessMode,
7539 shareMode,
7540 NULL,
7541 OPEN_EXISTING,
7542 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7545 if (hFile==INVALID_HANDLE_VALUE)
7547 DWORD last_error = GetLastError();
7549 hr = E_FAIL;
7551 switch (last_error)
7553 case ERROR_FILE_NOT_FOUND:
7554 hr = STG_E_FILENOTFOUND;
7555 break;
7557 case ERROR_PATH_NOT_FOUND:
7558 hr = STG_E_PATHNOTFOUND;
7559 break;
7561 case ERROR_ACCESS_DENIED:
7562 case ERROR_WRITE_PROTECT:
7563 hr = STG_E_ACCESSDENIED;
7564 break;
7566 case ERROR_SHARING_VIOLATION:
7567 hr = STG_E_SHAREVIOLATION;
7568 break;
7570 default:
7571 hr = E_FAIL;
7574 goto end;
7578 * Refuse to open the file if it's too small to be a structured storage file
7579 * FIXME: verify the file when reading instead of here
7581 if (GetFileSize(hFile, NULL) < 0x100)
7583 CloseHandle(hFile);
7584 hr = STG_E_FILEALREADYEXISTS;
7585 goto end;
7589 * Allocate and initialize the new IStorage32object.
7591 hr = Storage_Construct(
7592 hFile,
7593 pwcsName,
7594 NULL,
7595 grfMode,
7596 TRUE,
7597 FALSE,
7598 512,
7599 &newStorage);
7601 if (FAILED(hr))
7604 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7606 if(hr == STG_E_INVALIDHEADER)
7607 hr = STG_E_FILEALREADYEXISTS;
7608 goto end;
7612 * Get an "out" pointer for the caller.
7614 *ppstgOpen = (IStorage*)newStorage;
7616 end:
7617 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7618 return hr;
7621 /******************************************************************************
7622 * StgCreateDocfileOnILockBytes [OLE32.@]
7624 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7625 ILockBytes *plkbyt,
7626 DWORD grfMode,
7627 DWORD reserved,
7628 IStorage** ppstgOpen)
7630 StorageBaseImpl* newStorage = 0;
7631 HRESULT hr = S_OK;
7633 if ((ppstgOpen == 0) || (plkbyt == 0))
7634 return STG_E_INVALIDPOINTER;
7637 * Allocate and initialize the new IStorage object.
7639 hr = Storage_Construct(
7642 plkbyt,
7643 grfMode,
7644 FALSE,
7645 TRUE,
7646 512,
7647 &newStorage);
7649 if (FAILED(hr))
7651 return hr;
7655 * Get an "out" pointer for the caller.
7657 *ppstgOpen = (IStorage*)newStorage;
7659 return hr;
7662 /******************************************************************************
7663 * StgOpenStorageOnILockBytes [OLE32.@]
7665 HRESULT WINAPI StgOpenStorageOnILockBytes(
7666 ILockBytes *plkbyt,
7667 IStorage *pstgPriority,
7668 DWORD grfMode,
7669 SNB snbExclude,
7670 DWORD reserved,
7671 IStorage **ppstgOpen)
7673 StorageBaseImpl* newStorage = 0;
7674 HRESULT hr = S_OK;
7676 if ((plkbyt == 0) || (ppstgOpen == 0))
7677 return STG_E_INVALIDPOINTER;
7679 if ( FAILED( validateSTGM(grfMode) ))
7680 return STG_E_INVALIDFLAG;
7682 *ppstgOpen = 0;
7685 * Allocate and initialize the new IStorage object.
7687 hr = Storage_Construct(
7690 plkbyt,
7691 grfMode,
7692 FALSE,
7693 FALSE,
7694 512,
7695 &newStorage);
7697 if (FAILED(hr))
7699 return hr;
7703 * Get an "out" pointer for the caller.
7705 *ppstgOpen = (IStorage*)newStorage;
7707 return hr;
7710 /******************************************************************************
7711 * StgSetTimes [ole32.@]
7712 * StgSetTimes [OLE32.@]
7716 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7717 FILETIME const *patime, FILETIME const *pmtime)
7719 IStorage *stg = NULL;
7720 HRESULT r;
7722 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7724 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7725 0, 0, &stg);
7726 if( SUCCEEDED(r) )
7728 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7729 IStorage_Release(stg);
7732 return r;
7735 /******************************************************************************
7736 * StgIsStorageILockBytes [OLE32.@]
7738 * Determines if the ILockBytes contains a storage object.
7740 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7742 BYTE sig[8];
7743 ULARGE_INTEGER offset;
7745 offset.u.HighPart = 0;
7746 offset.u.LowPart = 0;
7748 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7750 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7751 return S_OK;
7753 return S_FALSE;
7756 /******************************************************************************
7757 * WriteClassStg [OLE32.@]
7759 * This method will store the specified CLSID in the specified storage object
7761 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7763 HRESULT hRes;
7765 if(!pStg)
7766 return E_INVALIDARG;
7768 if(!rclsid)
7769 return STG_E_INVALIDPOINTER;
7771 hRes = IStorage_SetClass(pStg, rclsid);
7773 return hRes;
7776 /***********************************************************************
7777 * ReadClassStg (OLE32.@)
7779 * This method reads the CLSID previously written to a storage object with
7780 * the WriteClassStg.
7782 * PARAMS
7783 * pstg [I] IStorage pointer
7784 * pclsid [O] Pointer to where the CLSID is written
7786 * RETURNS
7787 * Success: S_OK.
7788 * Failure: HRESULT code.
7790 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7792 STATSTG pstatstg;
7793 HRESULT hRes;
7795 TRACE("(%p, %p)\n", pstg, pclsid);
7797 if(!pstg || !pclsid)
7798 return E_INVALIDARG;
7801 * read a STATSTG structure (contains the clsid) from the storage
7803 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7805 if(SUCCEEDED(hRes))
7806 *pclsid=pstatstg.clsid;
7808 return hRes;
7811 /***********************************************************************
7812 * OleLoadFromStream (OLE32.@)
7814 * This function loads an object from stream
7816 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7818 CLSID clsid;
7819 HRESULT res;
7820 LPPERSISTSTREAM xstm;
7822 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7824 res=ReadClassStm(pStm,&clsid);
7825 if (FAILED(res))
7826 return res;
7827 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7828 if (FAILED(res))
7829 return res;
7830 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7831 if (FAILED(res)) {
7832 IUnknown_Release((IUnknown*)*ppvObj);
7833 return res;
7835 res=IPersistStream_Load(xstm,pStm);
7836 IPersistStream_Release(xstm);
7837 /* FIXME: all refcounts ok at this point? I think they should be:
7838 * pStm : unchanged
7839 * ppvObj : 1
7840 * xstm : 0 (released)
7842 return res;
7845 /***********************************************************************
7846 * OleSaveToStream (OLE32.@)
7848 * This function saves an object with the IPersistStream interface on it
7849 * to the specified stream.
7851 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7854 CLSID clsid;
7855 HRESULT res;
7857 TRACE("(%p,%p)\n",pPStm,pStm);
7859 res=IPersistStream_GetClassID(pPStm,&clsid);
7861 if (SUCCEEDED(res)){
7863 res=WriteClassStm(pStm,&clsid);
7865 if (SUCCEEDED(res))
7867 res=IPersistStream_Save(pPStm,pStm,TRUE);
7870 TRACE("Finished Save\n");
7871 return res;
7874 /****************************************************************************
7875 * This method validate a STGM parameter that can contain the values below
7877 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7878 * The stgm values contained in 0xffff0000 are bitmasks.
7880 * STGM_DIRECT 0x00000000
7881 * STGM_TRANSACTED 0x00010000
7882 * STGM_SIMPLE 0x08000000
7884 * STGM_READ 0x00000000
7885 * STGM_WRITE 0x00000001
7886 * STGM_READWRITE 0x00000002
7888 * STGM_SHARE_DENY_NONE 0x00000040
7889 * STGM_SHARE_DENY_READ 0x00000030
7890 * STGM_SHARE_DENY_WRITE 0x00000020
7891 * STGM_SHARE_EXCLUSIVE 0x00000010
7893 * STGM_PRIORITY 0x00040000
7894 * STGM_DELETEONRELEASE 0x04000000
7896 * STGM_CREATE 0x00001000
7897 * STGM_CONVERT 0x00020000
7898 * STGM_FAILIFTHERE 0x00000000
7900 * STGM_NOSCRATCH 0x00100000
7901 * STGM_NOSNAPSHOT 0x00200000
7903 static HRESULT validateSTGM(DWORD stgm)
7905 DWORD access = STGM_ACCESS_MODE(stgm);
7906 DWORD share = STGM_SHARE_MODE(stgm);
7907 DWORD create = STGM_CREATE_MODE(stgm);
7909 if (stgm&~STGM_KNOWN_FLAGS)
7911 ERR("unknown flags %08x\n", stgm);
7912 return E_FAIL;
7915 switch (access)
7917 case STGM_READ:
7918 case STGM_WRITE:
7919 case STGM_READWRITE:
7920 break;
7921 default:
7922 return E_FAIL;
7925 switch (share)
7927 case STGM_SHARE_DENY_NONE:
7928 case STGM_SHARE_DENY_READ:
7929 case STGM_SHARE_DENY_WRITE:
7930 case STGM_SHARE_EXCLUSIVE:
7931 break;
7932 default:
7933 return E_FAIL;
7936 switch (create)
7938 case STGM_CREATE:
7939 case STGM_FAILIFTHERE:
7940 break;
7941 default:
7942 return E_FAIL;
7946 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7948 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7949 return E_FAIL;
7952 * STGM_CREATE | STGM_CONVERT
7953 * if both are false, STGM_FAILIFTHERE is set to TRUE
7955 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7956 return E_FAIL;
7959 * STGM_NOSCRATCH requires STGM_TRANSACTED
7961 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7962 return E_FAIL;
7965 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7966 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7968 if ( (stgm & STGM_NOSNAPSHOT) &&
7969 (!(stgm & STGM_TRANSACTED) ||
7970 share == STGM_SHARE_EXCLUSIVE ||
7971 share == STGM_SHARE_DENY_WRITE) )
7972 return E_FAIL;
7974 return S_OK;
7977 /****************************************************************************
7978 * GetShareModeFromSTGM
7980 * This method will return a share mode flag from a STGM value.
7981 * The STGM value is assumed valid.
7983 static DWORD GetShareModeFromSTGM(DWORD stgm)
7985 switch (STGM_SHARE_MODE(stgm))
7987 case STGM_SHARE_DENY_NONE:
7988 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7989 case STGM_SHARE_DENY_READ:
7990 return FILE_SHARE_WRITE;
7991 case STGM_SHARE_DENY_WRITE:
7992 return FILE_SHARE_READ;
7993 case STGM_SHARE_EXCLUSIVE:
7994 return 0;
7996 ERR("Invalid share mode!\n");
7997 assert(0);
7998 return 0;
8001 /****************************************************************************
8002 * GetAccessModeFromSTGM
8004 * This method will return an access mode flag from a STGM value.
8005 * The STGM value is assumed valid.
8007 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8009 switch (STGM_ACCESS_MODE(stgm))
8011 case STGM_READ:
8012 return GENERIC_READ;
8013 case STGM_WRITE:
8014 case STGM_READWRITE:
8015 return GENERIC_READ | GENERIC_WRITE;
8017 ERR("Invalid access mode!\n");
8018 assert(0);
8019 return 0;
8022 /****************************************************************************
8023 * GetCreationModeFromSTGM
8025 * This method will return a creation mode flag from a STGM value.
8026 * The STGM value is assumed valid.
8028 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8030 switch(STGM_CREATE_MODE(stgm))
8032 case STGM_CREATE:
8033 return CREATE_ALWAYS;
8034 case STGM_CONVERT:
8035 FIXME("STGM_CONVERT not implemented!\n");
8036 return CREATE_NEW;
8037 case STGM_FAILIFTHERE:
8038 return CREATE_NEW;
8040 ERR("Invalid create mode!\n");
8041 assert(0);
8042 return 0;
8046 /*************************************************************************
8047 * OLECONVERT_LoadOLE10 [Internal]
8049 * Loads the OLE10 STREAM to memory
8051 * PARAMS
8052 * pOleStream [I] The OLESTREAM
8053 * pData [I] Data Structure for the OLESTREAM Data
8055 * RETURNS
8056 * Success: S_OK
8057 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8058 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8060 * NOTES
8061 * This function is used by OleConvertOLESTREAMToIStorage only.
8063 * Memory allocated for pData must be freed by the caller
8065 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8067 DWORD dwSize;
8068 HRESULT hRes = S_OK;
8069 int nTryCnt=0;
8070 int max_try = 6;
8072 pData->pData = NULL;
8073 pData->pstrOleObjFileName = NULL;
8075 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8077 /* Get the OleID */
8078 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8079 if(dwSize != sizeof(pData->dwOleID))
8081 hRes = CONVERT10_E_OLESTREAM_GET;
8083 else if(pData->dwOleID != OLESTREAM_ID)
8085 hRes = CONVERT10_E_OLESTREAM_FMT;
8087 else
8089 hRes = S_OK;
8090 break;
8094 if(hRes == S_OK)
8096 /* Get the TypeID... more info needed for this field */
8097 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8098 if(dwSize != sizeof(pData->dwTypeID))
8100 hRes = CONVERT10_E_OLESTREAM_GET;
8103 if(hRes == S_OK)
8105 if(pData->dwTypeID != 0)
8107 /* Get the length of the OleTypeName */
8108 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8109 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8111 hRes = CONVERT10_E_OLESTREAM_GET;
8114 if(hRes == S_OK)
8116 if(pData->dwOleTypeNameLength > 0)
8118 /* Get the OleTypeName */
8119 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8120 if(dwSize != pData->dwOleTypeNameLength)
8122 hRes = CONVERT10_E_OLESTREAM_GET;
8126 if(bStrem1)
8128 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8129 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8131 hRes = CONVERT10_E_OLESTREAM_GET;
8133 if(hRes == S_OK)
8135 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8136 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8137 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8138 if(pData->pstrOleObjFileName)
8140 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8141 if(dwSize != pData->dwOleObjFileNameLength)
8143 hRes = CONVERT10_E_OLESTREAM_GET;
8146 else
8147 hRes = CONVERT10_E_OLESTREAM_GET;
8150 else
8152 /* Get the Width of the Metafile */
8153 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8154 if(dwSize != sizeof(pData->dwMetaFileWidth))
8156 hRes = CONVERT10_E_OLESTREAM_GET;
8158 if(hRes == S_OK)
8160 /* Get the Height of the Metafile */
8161 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8162 if(dwSize != sizeof(pData->dwMetaFileHeight))
8164 hRes = CONVERT10_E_OLESTREAM_GET;
8168 if(hRes == S_OK)
8170 /* Get the Length of the Data */
8171 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8172 if(dwSize != sizeof(pData->dwDataLength))
8174 hRes = CONVERT10_E_OLESTREAM_GET;
8178 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8180 if(!bStrem1) /* if it is a second OLE stream data */
8182 pData->dwDataLength -= 8;
8183 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8184 if(dwSize != sizeof(pData->strUnknown))
8186 hRes = CONVERT10_E_OLESTREAM_GET;
8190 if(hRes == S_OK)
8192 if(pData->dwDataLength > 0)
8194 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8196 /* Get Data (ex. IStorage, Metafile, or BMP) */
8197 if(pData->pData)
8199 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8200 if(dwSize != pData->dwDataLength)
8202 hRes = CONVERT10_E_OLESTREAM_GET;
8205 else
8207 hRes = CONVERT10_E_OLESTREAM_GET;
8213 return hRes;
8216 /*************************************************************************
8217 * OLECONVERT_SaveOLE10 [Internal]
8219 * Saves the OLE10 STREAM From memory
8221 * PARAMS
8222 * pData [I] Data Structure for the OLESTREAM Data
8223 * pOleStream [I] The OLESTREAM to save
8225 * RETURNS
8226 * Success: S_OK
8227 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8229 * NOTES
8230 * This function is used by OleConvertIStorageToOLESTREAM only.
8233 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8235 DWORD dwSize;
8236 HRESULT hRes = S_OK;
8239 /* Set the OleID */
8240 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8241 if(dwSize != sizeof(pData->dwOleID))
8243 hRes = CONVERT10_E_OLESTREAM_PUT;
8246 if(hRes == S_OK)
8248 /* Set the TypeID */
8249 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8250 if(dwSize != sizeof(pData->dwTypeID))
8252 hRes = CONVERT10_E_OLESTREAM_PUT;
8256 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8258 /* Set the Length of the OleTypeName */
8259 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8260 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8262 hRes = CONVERT10_E_OLESTREAM_PUT;
8265 if(hRes == S_OK)
8267 if(pData->dwOleTypeNameLength > 0)
8269 /* Set the OleTypeName */
8270 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8271 if(dwSize != pData->dwOleTypeNameLength)
8273 hRes = CONVERT10_E_OLESTREAM_PUT;
8278 if(hRes == S_OK)
8280 /* Set the width of the Metafile */
8281 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8282 if(dwSize != sizeof(pData->dwMetaFileWidth))
8284 hRes = CONVERT10_E_OLESTREAM_PUT;
8288 if(hRes == S_OK)
8290 /* Set the height of the Metafile */
8291 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8292 if(dwSize != sizeof(pData->dwMetaFileHeight))
8294 hRes = CONVERT10_E_OLESTREAM_PUT;
8298 if(hRes == S_OK)
8300 /* Set the length of the Data */
8301 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8302 if(dwSize != sizeof(pData->dwDataLength))
8304 hRes = CONVERT10_E_OLESTREAM_PUT;
8308 if(hRes == S_OK)
8310 if(pData->dwDataLength > 0)
8312 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8313 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8314 if(dwSize != pData->dwDataLength)
8316 hRes = CONVERT10_E_OLESTREAM_PUT;
8321 return hRes;
8324 /*************************************************************************
8325 * OLECONVERT_GetOLE20FromOLE10[Internal]
8327 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8328 * opens it, and copies the content to the dest IStorage for
8329 * OleConvertOLESTREAMToIStorage
8332 * PARAMS
8333 * pDestStorage [I] The IStorage to copy the data to
8334 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8335 * nBufferLength [I] The size of the buffer
8337 * RETURNS
8338 * Nothing
8340 * NOTES
8344 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8346 HRESULT hRes;
8347 HANDLE hFile;
8348 IStorage *pTempStorage;
8349 DWORD dwNumOfBytesWritten;
8350 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8351 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8353 /* Create a temp File */
8354 GetTempPathW(MAX_PATH, wstrTempDir);
8355 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8356 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8358 if(hFile != INVALID_HANDLE_VALUE)
8360 /* Write IStorage Data to File */
8361 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8362 CloseHandle(hFile);
8364 /* Open and copy temp storage to the Dest Storage */
8365 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8366 if(hRes == S_OK)
8368 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8369 IStorage_Release(pTempStorage);
8371 DeleteFileW(wstrTempFile);
8376 /*************************************************************************
8377 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8379 * Saves the OLE10 STREAM From memory
8381 * PARAMS
8382 * pStorage [I] The Src IStorage to copy
8383 * pData [I] The Dest Memory to write to.
8385 * RETURNS
8386 * The size in bytes allocated for pData
8388 * NOTES
8389 * Memory allocated for pData must be freed by the caller
8391 * Used by OleConvertIStorageToOLESTREAM only.
8394 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8396 HANDLE hFile;
8397 HRESULT hRes;
8398 DWORD nDataLength = 0;
8399 IStorage *pTempStorage;
8400 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8401 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8403 *pData = NULL;
8405 /* Create temp Storage */
8406 GetTempPathW(MAX_PATH, wstrTempDir);
8407 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8408 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8410 if(hRes == S_OK)
8412 /* Copy Src Storage to the Temp Storage */
8413 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8414 IStorage_Release(pTempStorage);
8416 /* Open Temp Storage as a file and copy to memory */
8417 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8418 if(hFile != INVALID_HANDLE_VALUE)
8420 nDataLength = GetFileSize(hFile, NULL);
8421 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8422 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8423 CloseHandle(hFile);
8425 DeleteFileW(wstrTempFile);
8427 return nDataLength;
8430 /*************************************************************************
8431 * OLECONVERT_CreateOleStream [Internal]
8433 * Creates the "\001OLE" stream in the IStorage if necessary.
8435 * PARAMS
8436 * pStorage [I] Dest storage to create the stream in
8438 * RETURNS
8439 * Nothing
8441 * NOTES
8442 * This function is used by OleConvertOLESTREAMToIStorage only.
8444 * This stream is still unknown, MS Word seems to have extra data
8445 * but since the data is stored in the OLESTREAM there should be
8446 * no need to recreate the stream. If the stream is manually
8447 * deleted it will create it with this default data.
8450 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8452 HRESULT hRes;
8453 IStream *pStream;
8454 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8455 BYTE pOleStreamHeader [] =
8457 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8458 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8459 0x00, 0x00, 0x00, 0x00
8462 /* Create stream if not present */
8463 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8464 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8466 if(hRes == S_OK)
8468 /* Write default Data */
8469 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8470 IStream_Release(pStream);
8474 /* write a string to a stream, preceded by its length */
8475 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8477 HRESULT r;
8478 LPSTR str;
8479 DWORD len = 0;
8481 if( string )
8482 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8483 r = IStream_Write( stm, &len, sizeof(len), NULL);
8484 if( FAILED( r ) )
8485 return r;
8486 if(len == 0)
8487 return r;
8488 str = CoTaskMemAlloc( len );
8489 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8490 r = IStream_Write( stm, str, len, NULL);
8491 CoTaskMemFree( str );
8492 return r;
8495 /* read a string preceded by its length from a stream */
8496 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8498 HRESULT r;
8499 DWORD len, count = 0;
8500 LPSTR str;
8501 LPWSTR wstr;
8503 r = IStream_Read( stm, &len, sizeof(len), &count );
8504 if( FAILED( r ) )
8505 return r;
8506 if( count != sizeof(len) )
8507 return E_OUTOFMEMORY;
8509 TRACE("%d bytes\n",len);
8511 str = CoTaskMemAlloc( len );
8512 if( !str )
8513 return E_OUTOFMEMORY;
8514 count = 0;
8515 r = IStream_Read( stm, str, len, &count );
8516 if( FAILED( r ) )
8517 return r;
8518 if( count != len )
8520 CoTaskMemFree( str );
8521 return E_OUTOFMEMORY;
8524 TRACE("Read string %s\n",debugstr_an(str,len));
8526 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8527 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8528 if( wstr )
8529 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8530 CoTaskMemFree( str );
8532 *string = wstr;
8534 return r;
8538 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8539 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8541 IStream *pstm;
8542 HRESULT r = S_OK;
8543 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8545 static const BYTE unknown1[12] =
8546 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8547 0xFF, 0xFF, 0xFF, 0xFF};
8548 static const BYTE unknown2[16] =
8549 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8550 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8552 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8553 debugstr_w(lpszUserType), debugstr_w(szClipName),
8554 debugstr_w(szProgIDName));
8556 /* Create a CompObj stream */
8557 r = IStorage_CreateStream(pstg, szwStreamName,
8558 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8559 if( FAILED (r) )
8560 return r;
8562 /* Write CompObj Structure to stream */
8563 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8565 if( SUCCEEDED( r ) )
8566 r = WriteClassStm( pstm, clsid );
8568 if( SUCCEEDED( r ) )
8569 r = STREAM_WriteString( pstm, lpszUserType );
8570 if( SUCCEEDED( r ) )
8571 r = STREAM_WriteString( pstm, szClipName );
8572 if( SUCCEEDED( r ) )
8573 r = STREAM_WriteString( pstm, szProgIDName );
8574 if( SUCCEEDED( r ) )
8575 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8577 IStream_Release( pstm );
8579 return r;
8582 /***********************************************************************
8583 * WriteFmtUserTypeStg (OLE32.@)
8585 HRESULT WINAPI WriteFmtUserTypeStg(
8586 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8588 HRESULT r;
8589 WCHAR szwClipName[0x40];
8590 CLSID clsid = CLSID_NULL;
8591 LPWSTR wstrProgID = NULL;
8592 DWORD n;
8594 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8596 /* get the clipboard format name */
8597 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8598 szwClipName[n]=0;
8600 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8602 /* FIXME: There's room to save a CLSID and its ProgID, but
8603 the CLSID is not looked up in the registry and in all the
8604 tests I wrote it was CLSID_NULL. Where does it come from?
8607 /* get the real program ID. This may fail, but that's fine */
8608 ProgIDFromCLSID(&clsid, &wstrProgID);
8610 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8612 r = STORAGE_WriteCompObj( pstg, &clsid,
8613 lpszUserType, szwClipName, wstrProgID );
8615 CoTaskMemFree(wstrProgID);
8617 return r;
8621 /******************************************************************************
8622 * ReadFmtUserTypeStg [OLE32.@]
8624 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8626 HRESULT r;
8627 IStream *stm = 0;
8628 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8629 unsigned char unknown1[12];
8630 unsigned char unknown2[16];
8631 DWORD count;
8632 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8633 CLSID clsid;
8635 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8637 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8638 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8639 if( FAILED ( r ) )
8641 WARN("Failed to open stream r = %08x\n", r);
8642 return r;
8645 /* read the various parts of the structure */
8646 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8647 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8648 goto end;
8649 r = ReadClassStm( stm, &clsid );
8650 if( FAILED( r ) )
8651 goto end;
8653 r = STREAM_ReadString( stm, &szCLSIDName );
8654 if( FAILED( r ) )
8655 goto end;
8657 r = STREAM_ReadString( stm, &szOleTypeName );
8658 if( FAILED( r ) )
8659 goto end;
8661 r = STREAM_ReadString( stm, &szProgIDName );
8662 if( FAILED( r ) )
8663 goto end;
8665 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8666 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8667 goto end;
8669 /* ok, success... now we just need to store what we found */
8670 if( pcf )
8671 *pcf = RegisterClipboardFormatW( szOleTypeName );
8672 CoTaskMemFree( szOleTypeName );
8674 if( lplpszUserType )
8675 *lplpszUserType = szCLSIDName;
8676 CoTaskMemFree( szProgIDName );
8678 end:
8679 IStream_Release( stm );
8681 return r;
8685 /*************************************************************************
8686 * OLECONVERT_CreateCompObjStream [Internal]
8688 * Creates a "\001CompObj" is the destination IStorage if necessary.
8690 * PARAMS
8691 * pStorage [I] The dest IStorage to create the CompObj Stream
8692 * if necessary.
8693 * strOleTypeName [I] The ProgID
8695 * RETURNS
8696 * Success: S_OK
8697 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8699 * NOTES
8700 * This function is used by OleConvertOLESTREAMToIStorage only.
8702 * The stream data is stored in the OLESTREAM and there should be
8703 * no need to recreate the stream. If the stream is manually
8704 * deleted it will attempt to create it by querying the registry.
8708 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8710 IStream *pStream;
8711 HRESULT hStorageRes, hRes = S_OK;
8712 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8713 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8714 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8716 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8717 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8719 /* Initialize the CompObj structure */
8720 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8721 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8722 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8725 /* Create a CompObj stream if it doesn't exist */
8726 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8727 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8728 if(hStorageRes == S_OK)
8730 /* copy the OleTypeName to the compobj struct */
8731 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8732 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8734 /* copy the OleTypeName to the compobj struct */
8735 /* Note: in the test made, these were Identical */
8736 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8737 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8739 /* Get the CLSID */
8740 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8741 bufferW, OLESTREAM_MAX_STR_LEN );
8742 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8744 if(hRes == S_OK)
8746 HKEY hKey;
8747 LONG hErr;
8748 /* Get the CLSID Default Name from the Registry */
8749 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8750 if(hErr == ERROR_SUCCESS)
8752 char strTemp[OLESTREAM_MAX_STR_LEN];
8753 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8754 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8755 if(hErr == ERROR_SUCCESS)
8757 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8759 RegCloseKey(hKey);
8763 /* Write CompObj Structure to stream */
8764 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8766 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8768 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8769 if(IStorageCompObj.dwCLSIDNameLength > 0)
8771 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8773 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8774 if(IStorageCompObj.dwOleTypeNameLength > 0)
8776 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8778 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8779 if(IStorageCompObj.dwProgIDNameLength > 0)
8781 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8783 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8784 IStream_Release(pStream);
8786 return hRes;
8790 /*************************************************************************
8791 * OLECONVERT_CreateOlePresStream[Internal]
8793 * Creates the "\002OlePres000" Stream with the Metafile data
8795 * PARAMS
8796 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8797 * dwExtentX [I] Width of the Metafile
8798 * dwExtentY [I] Height of the Metafile
8799 * pData [I] Metafile data
8800 * dwDataLength [I] Size of the Metafile data
8802 * RETURNS
8803 * Success: S_OK
8804 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8806 * NOTES
8807 * This function is used by OleConvertOLESTREAMToIStorage only.
8810 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8812 HRESULT hRes;
8813 IStream *pStream;
8814 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8815 BYTE pOlePresStreamHeader [] =
8817 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8818 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8819 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8820 0x00, 0x00, 0x00, 0x00
8823 BYTE pOlePresStreamHeaderEmpty [] =
8825 0x00, 0x00, 0x00, 0x00,
8826 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8827 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8828 0x00, 0x00, 0x00, 0x00
8831 /* Create the OlePres000 Stream */
8832 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8833 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8835 if(hRes == S_OK)
8837 DWORD nHeaderSize;
8838 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8840 memset(&OlePres, 0, sizeof(OlePres));
8841 /* Do we have any metafile data to save */
8842 if(dwDataLength > 0)
8844 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8845 nHeaderSize = sizeof(pOlePresStreamHeader);
8847 else
8849 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8850 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8852 /* Set width and height of the metafile */
8853 OlePres.dwExtentX = dwExtentX;
8854 OlePres.dwExtentY = -dwExtentY;
8856 /* Set Data and Length */
8857 if(dwDataLength > sizeof(METAFILEPICT16))
8859 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8860 OlePres.pData = &(pData[8]);
8862 /* Save OlePres000 Data to Stream */
8863 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8864 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8865 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8866 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8867 if(OlePres.dwSize > 0)
8869 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8871 IStream_Release(pStream);
8875 /*************************************************************************
8876 * OLECONVERT_CreateOle10NativeStream [Internal]
8878 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8880 * PARAMS
8881 * pStorage [I] Dest storage to create the stream in
8882 * pData [I] Ole10 Native Data (ex. bmp)
8883 * dwDataLength [I] Size of the Ole10 Native Data
8885 * RETURNS
8886 * Nothing
8888 * NOTES
8889 * This function is used by OleConvertOLESTREAMToIStorage only.
8891 * Might need to verify the data and return appropriate error message
8894 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8896 HRESULT hRes;
8897 IStream *pStream;
8898 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8900 /* Create the Ole10Native Stream */
8901 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8902 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8904 if(hRes == S_OK)
8906 /* Write info to stream */
8907 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8908 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8909 IStream_Release(pStream);
8914 /*************************************************************************
8915 * OLECONVERT_GetOLE10ProgID [Internal]
8917 * Finds the ProgID (or OleTypeID) from the IStorage
8919 * PARAMS
8920 * pStorage [I] The Src IStorage to get the ProgID
8921 * strProgID [I] the ProgID string to get
8922 * dwSize [I] the size of the string
8924 * RETURNS
8925 * Success: S_OK
8926 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8928 * NOTES
8929 * This function is used by OleConvertIStorageToOLESTREAM only.
8933 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8935 HRESULT hRes;
8936 IStream *pStream;
8937 LARGE_INTEGER iSeekPos;
8938 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8939 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8941 /* Open the CompObj Stream */
8942 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8943 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8944 if(hRes == S_OK)
8947 /*Get the OleType from the CompObj Stream */
8948 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8949 iSeekPos.u.HighPart = 0;
8951 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8952 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8953 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8954 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8955 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8956 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8957 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8959 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8960 if(*dwSize > 0)
8962 IStream_Read(pStream, strProgID, *dwSize, NULL);
8964 IStream_Release(pStream);
8966 else
8968 STATSTG stat;
8969 LPOLESTR wstrProgID;
8971 /* Get the OleType from the registry */
8972 REFCLSID clsid = &(stat.clsid);
8973 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8974 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8975 if(hRes == S_OK)
8977 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8981 return hRes;
8984 /*************************************************************************
8985 * OLECONVERT_GetOle10PresData [Internal]
8987 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8989 * PARAMS
8990 * pStorage [I] Src IStroage
8991 * pOleStream [I] Dest OleStream Mem Struct
8993 * RETURNS
8994 * Nothing
8996 * NOTES
8997 * This function is used by OleConvertIStorageToOLESTREAM only.
8999 * Memory allocated for pData must be freed by the caller
9003 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9006 HRESULT hRes;
9007 IStream *pStream;
9008 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9010 /* Initialize Default data for OLESTREAM */
9011 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9012 pOleStreamData[0].dwTypeID = 2;
9013 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9014 pOleStreamData[1].dwTypeID = 0;
9015 pOleStreamData[0].dwMetaFileWidth = 0;
9016 pOleStreamData[0].dwMetaFileHeight = 0;
9017 pOleStreamData[0].pData = NULL;
9018 pOleStreamData[1].pData = NULL;
9020 /* Open Ole10Native Stream */
9021 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9022 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9023 if(hRes == S_OK)
9026 /* Read Size and Data */
9027 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9028 if(pOleStreamData->dwDataLength > 0)
9030 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9031 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9033 IStream_Release(pStream);
9039 /*************************************************************************
9040 * OLECONVERT_GetOle20PresData[Internal]
9042 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9044 * PARAMS
9045 * pStorage [I] Src IStroage
9046 * pOleStreamData [I] Dest OleStream Mem Struct
9048 * RETURNS
9049 * Nothing
9051 * NOTES
9052 * This function is used by OleConvertIStorageToOLESTREAM only.
9054 * Memory allocated for pData must be freed by the caller
9056 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9058 HRESULT hRes;
9059 IStream *pStream;
9060 OLECONVERT_ISTORAGE_OLEPRES olePress;
9061 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9063 /* Initialize Default data for OLESTREAM */
9064 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9065 pOleStreamData[0].dwTypeID = 2;
9066 pOleStreamData[0].dwMetaFileWidth = 0;
9067 pOleStreamData[0].dwMetaFileHeight = 0;
9068 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9069 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9070 pOleStreamData[1].dwTypeID = 0;
9071 pOleStreamData[1].dwOleTypeNameLength = 0;
9072 pOleStreamData[1].strOleTypeName[0] = 0;
9073 pOleStreamData[1].dwMetaFileWidth = 0;
9074 pOleStreamData[1].dwMetaFileHeight = 0;
9075 pOleStreamData[1].pData = NULL;
9076 pOleStreamData[1].dwDataLength = 0;
9079 /* Open OlePress000 stream */
9080 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9081 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9082 if(hRes == S_OK)
9084 LARGE_INTEGER iSeekPos;
9085 METAFILEPICT16 MetaFilePict;
9086 static const char strMetafilePictName[] = "METAFILEPICT";
9088 /* Set the TypeID for a Metafile */
9089 pOleStreamData[1].dwTypeID = 5;
9091 /* Set the OleTypeName to Metafile */
9092 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9093 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9095 iSeekPos.u.HighPart = 0;
9096 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9098 /* Get Presentation Data */
9099 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9100 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9101 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9102 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9104 /*Set width and Height */
9105 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9106 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9107 if(olePress.dwSize > 0)
9109 /* Set Length */
9110 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9112 /* Set MetaFilePict struct */
9113 MetaFilePict.mm = 8;
9114 MetaFilePict.xExt = olePress.dwExtentX;
9115 MetaFilePict.yExt = olePress.dwExtentY;
9116 MetaFilePict.hMF = 0;
9118 /* Get Metafile Data */
9119 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9120 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9121 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9123 IStream_Release(pStream);
9127 /*************************************************************************
9128 * OleConvertOLESTREAMToIStorage [OLE32.@]
9130 * Read info on MSDN
9132 * TODO
9133 * DVTARGETDEVICE parameter is not handled
9134 * Still unsure of some mem fields for OLE 10 Stream
9135 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9136 * and "\001OLE" streams
9139 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9140 LPOLESTREAM pOleStream,
9141 LPSTORAGE pstg,
9142 const DVTARGETDEVICE* ptd)
9144 int i;
9145 HRESULT hRes=S_OK;
9146 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9148 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9150 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9152 if(ptd != NULL)
9154 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9157 if(pstg == NULL || pOleStream == NULL)
9159 hRes = E_INVALIDARG;
9162 if(hRes == S_OK)
9164 /* Load the OLESTREAM to Memory */
9165 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9168 if(hRes == S_OK)
9170 /* Load the OLESTREAM to Memory (part 2)*/
9171 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9174 if(hRes == S_OK)
9177 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9179 /* Do we have the IStorage Data in the OLESTREAM */
9180 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9182 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9183 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9185 else
9187 /* It must be an original OLE 1.0 source */
9188 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9191 else
9193 /* It must be an original OLE 1.0 source */
9194 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9197 /* Create CompObj Stream if necessary */
9198 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9199 if(hRes == S_OK)
9201 /*Create the Ole Stream if necessary */
9202 OLECONVERT_CreateOleStream(pstg);
9207 /* Free allocated memory */
9208 for(i=0; i < 2; i++)
9210 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9211 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9212 pOleStreamData[i].pstrOleObjFileName = NULL;
9214 return hRes;
9217 /*************************************************************************
9218 * OleConvertIStorageToOLESTREAM [OLE32.@]
9220 * Read info on MSDN
9222 * Read info on MSDN
9224 * TODO
9225 * Still unsure of some mem fields for OLE 10 Stream
9226 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9227 * and "\001OLE" streams.
9230 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9231 LPSTORAGE pstg,
9232 LPOLESTREAM pOleStream)
9234 int i;
9235 HRESULT hRes = S_OK;
9236 IStream *pStream;
9237 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9238 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9240 TRACE("%p %p\n", pstg, pOleStream);
9242 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9244 if(pstg == NULL || pOleStream == NULL)
9246 hRes = E_INVALIDARG;
9248 if(hRes == S_OK)
9250 /* Get the ProgID */
9251 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9252 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9254 if(hRes == S_OK)
9256 /* Was it originally Ole10 */
9257 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9258 if(hRes == S_OK)
9260 IStream_Release(pStream);
9261 /* Get Presentation Data for Ole10Native */
9262 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9264 else
9266 /* Get Presentation Data (OLE20) */
9267 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9270 /* Save OLESTREAM */
9271 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9272 if(hRes == S_OK)
9274 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9279 /* Free allocated memory */
9280 for(i=0; i < 2; i++)
9282 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9285 return hRes;
9288 /***********************************************************************
9289 * GetConvertStg (OLE32.@)
9291 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9292 FIXME("unimplemented stub!\n");
9293 return E_FAIL;
9296 /******************************************************************************
9297 * StgIsStorageFile [OLE32.@]
9298 * Verify if the file contains a storage object
9300 * PARAMS
9301 * fn [ I] Filename
9303 * RETURNS
9304 * S_OK if file has magic bytes as a storage object
9305 * S_FALSE if file is not storage
9307 HRESULT WINAPI
9308 StgIsStorageFile(LPCOLESTR fn)
9310 HANDLE hf;
9311 BYTE magic[8];
9312 DWORD bytes_read;
9314 TRACE("%s\n", debugstr_w(fn));
9315 hf = CreateFileW(fn, GENERIC_READ,
9316 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9317 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9319 if (hf == INVALID_HANDLE_VALUE)
9320 return STG_E_FILENOTFOUND;
9322 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9324 WARN(" unable to read file\n");
9325 CloseHandle(hf);
9326 return S_FALSE;
9329 CloseHandle(hf);
9331 if (bytes_read != 8) {
9332 TRACE(" too short\n");
9333 return S_FALSE;
9336 if (!memcmp(magic,STORAGE_magic,8)) {
9337 TRACE(" -> YES\n");
9338 return S_OK;
9341 TRACE(" -> Invalid header.\n");
9342 return S_FALSE;
9345 /***********************************************************************
9346 * WriteClassStm (OLE32.@)
9348 * Writes a CLSID to a stream.
9350 * PARAMS
9351 * pStm [I] Stream to write to.
9352 * rclsid [I] CLSID to write.
9354 * RETURNS
9355 * Success: S_OK.
9356 * Failure: HRESULT code.
9358 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9360 TRACE("(%p,%p)\n",pStm,rclsid);
9362 if (!pStm || !rclsid)
9363 return E_INVALIDARG;
9365 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9368 /***********************************************************************
9369 * ReadClassStm (OLE32.@)
9371 * Reads a CLSID from a stream.
9373 * PARAMS
9374 * pStm [I] Stream to read from.
9375 * rclsid [O] CLSID to read.
9377 * RETURNS
9378 * Success: S_OK.
9379 * Failure: HRESULT code.
9381 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9383 ULONG nbByte;
9384 HRESULT res;
9386 TRACE("(%p,%p)\n",pStm,pclsid);
9388 if (!pStm || !pclsid)
9389 return E_INVALIDARG;
9391 /* clear the output args */
9392 *pclsid = CLSID_NULL;
9394 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9396 if (FAILED(res))
9397 return res;
9399 if (nbByte != sizeof(CLSID))
9400 return STG_E_READFAULT;
9401 else
9402 return S_OK;