ole32: Create storage files with 4096-byte blocks when asked.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob434d8b2460629b9b89127f00550fdf33f9a95f1e
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 BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
96 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
97 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
98 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
99 static void StorageImpl_SaveFileHeader(StorageImpl* This);
101 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
102 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
103 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
104 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
105 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
107 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
108 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
109 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
111 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
112 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
113 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
114 ULONG blockIndex, ULONG offset, DWORD value);
115 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
116 ULONG blockIndex, ULONG offset, DWORD* value);
118 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
119 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
121 /****************************************************************************
122 * Transacted storage object that reads/writes a snapshot file.
124 typedef struct TransactedSnapshotImpl
126 struct StorageBaseImpl base;
129 * Changes are temporarily saved to the snapshot.
131 StorageBaseImpl *snapshot;
134 * Changes are committed to the transacted parent.
136 StorageBaseImpl *transactedParent;
137 } TransactedSnapshotImpl;
139 /* Generic function to create a transacted wrapper for a direct storage object. */
140 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
142 /* OLESTREAM memory structure to use for Get and Put Routines */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
144 typedef struct
146 DWORD dwOleID;
147 DWORD dwTypeID;
148 DWORD dwOleTypeNameLength;
149 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
150 CHAR *pstrOleObjFileName;
151 DWORD dwOleObjFileNameLength;
152 DWORD dwMetaFileWidth;
153 DWORD dwMetaFileHeight;
154 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
155 DWORD dwDataLength;
156 BYTE *pData;
157 }OLECONVERT_OLESTREAM_DATA;
159 /* CompObj Stream structure */
160 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
161 typedef struct
163 BYTE byUnknown1[12];
164 CLSID clsid;
165 DWORD dwCLSIDNameLength;
166 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
167 DWORD dwOleTypeNameLength;
168 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
169 DWORD dwProgIDNameLength;
170 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
171 BYTE byUnknown2[16];
172 }OLECONVERT_ISTORAGE_COMPOBJ;
175 /* Ole Presentation Stream structure */
176 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
177 typedef struct
179 BYTE byUnknown1[28];
180 DWORD dwExtentX;
181 DWORD dwExtentY;
182 DWORD dwSize;
183 BYTE *pData;
184 }OLECONVERT_ISTORAGE_OLEPRES;
188 /***********************************************************************
189 * Forward declaration of internal functions used by the method DestroyElement
191 static HRESULT deleteStorageContents(
192 StorageBaseImpl *parentStorage,
193 DirRef indexToDelete,
194 DirEntry entryDataToDelete);
196 static HRESULT deleteStreamContents(
197 StorageBaseImpl *parentStorage,
198 DirRef indexToDelete,
199 DirEntry entryDataToDelete);
201 static HRESULT removeFromTree(
202 StorageBaseImpl *This,
203 DirRef parentStorageIndex,
204 DirRef deletedIndex);
206 /***********************************************************************
207 * Declaration of the functions used to manipulate DirEntry
210 static HRESULT insertIntoTree(
211 StorageBaseImpl *This,
212 DirRef parentStorageIndex,
213 DirRef newEntryIndex);
215 static LONG entryNameCmp(
216 const OLECHAR *name1,
217 const OLECHAR *name2);
219 static DirRef findElement(
220 StorageBaseImpl *storage,
221 DirRef storageEntry,
222 const OLECHAR *name,
223 DirEntry *data);
225 static HRESULT findTreeParent(
226 StorageBaseImpl *storage,
227 DirRef storageEntry,
228 const OLECHAR *childName,
229 DirEntry *parentData,
230 DirRef *parentEntry,
231 ULONG *relation);
233 /***********************************************************************
234 * Declaration of miscellaneous functions...
236 static HRESULT validateSTGM(DWORD stgmValue);
238 static DWORD GetShareModeFromSTGM(DWORD stgm);
239 static DWORD GetAccessModeFromSTGM(DWORD stgm);
240 static DWORD GetCreationModeFromSTGM(DWORD stgm);
242 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
245 /****************************************************************************
246 * IEnumSTATSTGImpl definitions.
248 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
249 * This class allows iterating through the content of a storage and to find
250 * specific items inside it.
252 struct IEnumSTATSTGImpl
254 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
255 * since we want to cast this in an IEnumSTATSTG pointer */
257 LONG ref; /* Reference count */
258 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
259 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
261 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
265 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
266 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
268 /************************************************************************
269 ** Block Functions
272 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
274 return (index+1) * This->bigBlockSize;
277 /************************************************************************
278 ** Storage32BaseImpl implementation
280 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
281 ULARGE_INTEGER offset,
282 void* buffer,
283 ULONG size,
284 ULONG* bytesRead)
286 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
289 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
290 ULARGE_INTEGER offset,
291 const void* buffer,
292 const ULONG size,
293 ULONG* bytesWritten)
295 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
298 /************************************************************************
299 * Storage32BaseImpl_QueryInterface (IUnknown)
301 * This method implements the common QueryInterface for all IStorage32
302 * implementations contained in this file.
304 * See Windows documentation for more details on IUnknown methods.
306 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
307 IStorage* iface,
308 REFIID riid,
309 void** ppvObject)
311 StorageBaseImpl *This = (StorageBaseImpl *)iface;
313 if ( (This==0) || (ppvObject==0) )
314 return E_INVALIDARG;
316 *ppvObject = 0;
318 if (IsEqualGUID(&IID_IUnknown, riid) ||
319 IsEqualGUID(&IID_IStorage, riid))
321 *ppvObject = This;
323 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
325 *ppvObject = &This->pssVtbl;
328 if ((*ppvObject)==0)
329 return E_NOINTERFACE;
331 IStorage_AddRef(iface);
333 return S_OK;
336 /************************************************************************
337 * Storage32BaseImpl_AddRef (IUnknown)
339 * This method implements the common AddRef for all IStorage32
340 * implementations contained in this file.
342 * See Windows documentation for more details on IUnknown methods.
344 static ULONG WINAPI StorageBaseImpl_AddRef(
345 IStorage* iface)
347 StorageBaseImpl *This = (StorageBaseImpl *)iface;
348 ULONG ref = InterlockedIncrement(&This->ref);
350 TRACE("(%p) AddRef to %d\n", This, ref);
352 return ref;
355 /************************************************************************
356 * Storage32BaseImpl_Release (IUnknown)
358 * This method implements the common Release for all IStorage32
359 * implementations contained in this file.
361 * See Windows documentation for more details on IUnknown methods.
363 static ULONG WINAPI StorageBaseImpl_Release(
364 IStorage* iface)
366 StorageBaseImpl *This = (StorageBaseImpl *)iface;
368 ULONG ref = InterlockedDecrement(&This->ref);
370 TRACE("(%p) ReleaseRef to %d\n", This, ref);
372 if (ref == 0)
375 * Since we are using a system of base-classes, we want to call the
376 * destructor of the appropriate derived class. To do this, we are
377 * using virtual functions to implement the destructor.
379 StorageBaseImpl_Destroy(This);
382 return ref;
385 /************************************************************************
386 * Storage32BaseImpl_OpenStream (IStorage)
388 * This method will open the specified stream object from the current storage.
390 * See Windows documentation for more details on IStorage methods.
392 static HRESULT WINAPI StorageBaseImpl_OpenStream(
393 IStorage* iface,
394 const OLECHAR* pwcsName, /* [string][in] */
395 void* reserved1, /* [unique][in] */
396 DWORD grfMode, /* [in] */
397 DWORD reserved2, /* [in] */
398 IStream** ppstm) /* [out] */
400 StorageBaseImpl *This = (StorageBaseImpl *)iface;
401 StgStreamImpl* newStream;
402 DirEntry currentEntry;
403 DirRef streamEntryRef;
404 HRESULT res = STG_E_UNKNOWN;
406 TRACE("(%p, %s, %p, %x, %d, %p)\n",
407 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
409 if ( (pwcsName==NULL) || (ppstm==0) )
411 res = E_INVALIDARG;
412 goto end;
415 *ppstm = NULL;
417 if ( FAILED( validateSTGM(grfMode) ) ||
418 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
420 res = STG_E_INVALIDFLAG;
421 goto end;
425 * As documented.
427 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
429 res = STG_E_INVALIDFUNCTION;
430 goto end;
433 if (This->reverted)
435 res = STG_E_REVERTED;
436 goto end;
440 * Check that we're compatible with the parent's storage mode, but
441 * only if we are not in transacted mode
443 if(!(This->openFlags & STGM_TRANSACTED)) {
444 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
446 res = STG_E_INVALIDFLAG;
447 goto end;
452 * Search for the element with the given name
454 streamEntryRef = findElement(
455 This,
456 This->storageDirEntry,
457 pwcsName,
458 &currentEntry);
461 * If it was found, construct the stream object and return a pointer to it.
463 if ( (streamEntryRef!=DIRENTRY_NULL) &&
464 (currentEntry.stgType==STGTY_STREAM) )
466 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
468 /* A single stream cannot be opened a second time. */
469 res = STG_E_ACCESSDENIED;
470 goto end;
473 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
475 if (newStream!=0)
477 newStream->grfMode = grfMode;
478 *ppstm = (IStream*)newStream;
480 IStream_AddRef(*ppstm);
482 res = S_OK;
483 goto end;
486 res = E_OUTOFMEMORY;
487 goto end;
490 res = STG_E_FILENOTFOUND;
492 end:
493 if (res == S_OK)
494 TRACE("<-- IStream %p\n", *ppstm);
495 TRACE("<-- %08x\n", res);
496 return res;
499 /************************************************************************
500 * Storage32BaseImpl_OpenStorage (IStorage)
502 * This method will open a new storage object from the current storage.
504 * See Windows documentation for more details on IStorage methods.
506 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
507 IStorage* iface,
508 const OLECHAR* pwcsName, /* [string][unique][in] */
509 IStorage* pstgPriority, /* [unique][in] */
510 DWORD grfMode, /* [in] */
511 SNB snbExclude, /* [unique][in] */
512 DWORD reserved, /* [in] */
513 IStorage** ppstg) /* [out] */
515 StorageBaseImpl *This = (StorageBaseImpl *)iface;
516 StorageInternalImpl* newStorage;
517 StorageBaseImpl* newTransactedStorage;
518 DirEntry currentEntry;
519 DirRef storageEntryRef;
520 HRESULT res = STG_E_UNKNOWN;
522 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
523 iface, debugstr_w(pwcsName), pstgPriority,
524 grfMode, snbExclude, reserved, ppstg);
526 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
528 res = E_INVALIDARG;
529 goto end;
532 if (This->openFlags & STGM_SIMPLE)
534 res = STG_E_INVALIDFUNCTION;
535 goto end;
538 /* as documented */
539 if (snbExclude != NULL)
541 res = STG_E_INVALIDPARAMETER;
542 goto end;
545 if ( FAILED( validateSTGM(grfMode) ))
547 res = STG_E_INVALIDFLAG;
548 goto end;
552 * As documented.
554 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
555 (grfMode & STGM_DELETEONRELEASE) ||
556 (grfMode & STGM_PRIORITY) )
558 res = STG_E_INVALIDFUNCTION;
559 goto end;
562 if (This->reverted)
563 return STG_E_REVERTED;
566 * Check that we're compatible with the parent's storage mode,
567 * but only if we are not transacted
569 if(!(This->openFlags & STGM_TRANSACTED)) {
570 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
572 res = STG_E_ACCESSDENIED;
573 goto end;
577 *ppstg = NULL;
579 storageEntryRef = findElement(
580 This,
581 This->storageDirEntry,
582 pwcsName,
583 &currentEntry);
585 if ( (storageEntryRef!=DIRENTRY_NULL) &&
586 (currentEntry.stgType==STGTY_STORAGE) )
588 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
590 /* A single storage cannot be opened a second time. */
591 res = STG_E_ACCESSDENIED;
592 goto end;
595 newStorage = StorageInternalImpl_Construct(
596 This,
597 grfMode,
598 storageEntryRef);
600 if (newStorage != 0)
602 if (grfMode & STGM_TRANSACTED)
604 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
606 if (FAILED(res))
608 HeapFree(GetProcessHeap(), 0, newStorage);
609 goto end;
612 *ppstg = (IStorage*)newTransactedStorage;
614 else
616 *ppstg = (IStorage*)newStorage;
619 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
621 res = S_OK;
622 goto end;
625 res = STG_E_INSUFFICIENTMEMORY;
626 goto end;
629 res = STG_E_FILENOTFOUND;
631 end:
632 TRACE("<-- %08x\n", res);
633 return res;
636 /************************************************************************
637 * Storage32BaseImpl_EnumElements (IStorage)
639 * This method will create an enumerator object that can be used to
640 * retrieve information about all the elements in the storage object.
642 * See Windows documentation for more details on IStorage methods.
644 static HRESULT WINAPI StorageBaseImpl_EnumElements(
645 IStorage* iface,
646 DWORD reserved1, /* [in] */
647 void* reserved2, /* [size_is][unique][in] */
648 DWORD reserved3, /* [in] */
649 IEnumSTATSTG** ppenum) /* [out] */
651 StorageBaseImpl *This = (StorageBaseImpl *)iface;
652 IEnumSTATSTGImpl* newEnum;
654 TRACE("(%p, %d, %p, %d, %p)\n",
655 iface, reserved1, reserved2, reserved3, ppenum);
657 if ( (This==0) || (ppenum==0))
658 return E_INVALIDARG;
660 if (This->reverted)
661 return STG_E_REVERTED;
663 newEnum = IEnumSTATSTGImpl_Construct(
664 This,
665 This->storageDirEntry);
667 if (newEnum!=0)
669 *ppenum = (IEnumSTATSTG*)newEnum;
671 IEnumSTATSTG_AddRef(*ppenum);
673 return S_OK;
676 return E_OUTOFMEMORY;
679 /************************************************************************
680 * Storage32BaseImpl_Stat (IStorage)
682 * This method will retrieve information about this storage object.
684 * See Windows documentation for more details on IStorage methods.
686 static HRESULT WINAPI StorageBaseImpl_Stat(
687 IStorage* iface,
688 STATSTG* pstatstg, /* [out] */
689 DWORD grfStatFlag) /* [in] */
691 StorageBaseImpl *This = (StorageBaseImpl *)iface;
692 DirEntry currentEntry;
693 HRESULT res = STG_E_UNKNOWN;
695 TRACE("(%p, %p, %x)\n",
696 iface, pstatstg, grfStatFlag);
698 if ( (This==0) || (pstatstg==0))
700 res = E_INVALIDARG;
701 goto end;
704 if (This->reverted)
706 res = STG_E_REVERTED;
707 goto end;
710 res = StorageBaseImpl_ReadDirEntry(
711 This,
712 This->storageDirEntry,
713 &currentEntry);
715 if (SUCCEEDED(res))
717 StorageUtl_CopyDirEntryToSTATSTG(
718 This,
719 pstatstg,
720 &currentEntry,
721 grfStatFlag);
723 pstatstg->grfMode = This->openFlags;
724 pstatstg->grfStateBits = This->stateBits;
727 end:
728 if (res == S_OK)
730 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);
732 TRACE("<-- %08x\n", res);
733 return res;
736 /************************************************************************
737 * Storage32BaseImpl_RenameElement (IStorage)
739 * This method will rename the specified element.
741 * See Windows documentation for more details on IStorage methods.
743 static HRESULT WINAPI StorageBaseImpl_RenameElement(
744 IStorage* iface,
745 const OLECHAR* pwcsOldName, /* [in] */
746 const OLECHAR* pwcsNewName) /* [in] */
748 StorageBaseImpl *This = (StorageBaseImpl *)iface;
749 DirEntry currentEntry;
750 DirRef currentEntryRef;
752 TRACE("(%p, %s, %s)\n",
753 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
755 if (This->reverted)
756 return STG_E_REVERTED;
758 currentEntryRef = findElement(This,
759 This->storageDirEntry,
760 pwcsNewName,
761 &currentEntry);
763 if (currentEntryRef != DIRENTRY_NULL)
766 * There is already an element with the new name
768 return STG_E_FILEALREADYEXISTS;
772 * Search for the old element name
774 currentEntryRef = findElement(This,
775 This->storageDirEntry,
776 pwcsOldName,
777 &currentEntry);
779 if (currentEntryRef != DIRENTRY_NULL)
781 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
782 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
784 WARN("Element is already open; cannot rename.\n");
785 return STG_E_ACCESSDENIED;
788 /* Remove the element from its current position in the tree */
789 removeFromTree(This, This->storageDirEntry,
790 currentEntryRef);
792 /* Change the name of the element */
793 strcpyW(currentEntry.name, pwcsNewName);
795 /* Delete any sibling links */
796 currentEntry.leftChild = DIRENTRY_NULL;
797 currentEntry.rightChild = DIRENTRY_NULL;
799 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
800 &currentEntry);
802 /* Insert the element in a new position in the tree */
803 insertIntoTree(This, This->storageDirEntry,
804 currentEntryRef);
806 else
809 * There is no element with the old name
811 return STG_E_FILENOTFOUND;
814 return S_OK;
817 /************************************************************************
818 * Storage32BaseImpl_CreateStream (IStorage)
820 * This method will create a stream object within this storage
822 * See Windows documentation for more details on IStorage methods.
824 static HRESULT WINAPI StorageBaseImpl_CreateStream(
825 IStorage* iface,
826 const OLECHAR* pwcsName, /* [string][in] */
827 DWORD grfMode, /* [in] */
828 DWORD reserved1, /* [in] */
829 DWORD reserved2, /* [in] */
830 IStream** ppstm) /* [out] */
832 StorageBaseImpl *This = (StorageBaseImpl *)iface;
833 StgStreamImpl* newStream;
834 DirEntry currentEntry, newStreamEntry;
835 DirRef currentEntryRef, newStreamEntryRef;
836 HRESULT hr;
838 TRACE("(%p, %s, %x, %d, %d, %p)\n",
839 iface, debugstr_w(pwcsName), grfMode,
840 reserved1, reserved2, ppstm);
842 if (ppstm == 0)
843 return STG_E_INVALIDPOINTER;
845 if (pwcsName == 0)
846 return STG_E_INVALIDNAME;
848 if (reserved1 || reserved2)
849 return STG_E_INVALIDPARAMETER;
851 if ( FAILED( validateSTGM(grfMode) ))
852 return STG_E_INVALIDFLAG;
854 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
855 return STG_E_INVALIDFLAG;
857 if (This->reverted)
858 return STG_E_REVERTED;
861 * As documented.
863 if ((grfMode & STGM_DELETEONRELEASE) ||
864 (grfMode & STGM_TRANSACTED))
865 return STG_E_INVALIDFUNCTION;
868 * Don't worry about permissions in transacted mode, as we can always write
869 * changes; we just can't always commit them.
871 if(!(This->openFlags & STGM_TRANSACTED)) {
872 /* Can't create a stream on read-only storage */
873 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
874 return STG_E_ACCESSDENIED;
876 /* Can't create a stream with greater access than the parent. */
877 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
878 return STG_E_ACCESSDENIED;
881 if(This->openFlags & STGM_SIMPLE)
882 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
884 *ppstm = 0;
886 currentEntryRef = findElement(This,
887 This->storageDirEntry,
888 pwcsName,
889 &currentEntry);
891 if (currentEntryRef != DIRENTRY_NULL)
894 * An element with this name already exists
896 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
898 IStorage_DestroyElement(iface, pwcsName);
900 else
901 return STG_E_FILEALREADYEXISTS;
905 * memset the empty entry
907 memset(&newStreamEntry, 0, sizeof(DirEntry));
909 newStreamEntry.sizeOfNameString =
910 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
912 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
913 return STG_E_INVALIDNAME;
915 strcpyW(newStreamEntry.name, pwcsName);
917 newStreamEntry.stgType = STGTY_STREAM;
918 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
919 newStreamEntry.size.u.LowPart = 0;
920 newStreamEntry.size.u.HighPart = 0;
922 newStreamEntry.leftChild = DIRENTRY_NULL;
923 newStreamEntry.rightChild = DIRENTRY_NULL;
924 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
926 /* call CoFileTime to get the current time
927 newStreamEntry.ctime
928 newStreamEntry.mtime
931 /* newStreamEntry.clsid */
934 * Create an entry with the new data
936 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
937 if (FAILED(hr))
938 return hr;
941 * Insert the new entry in the parent storage's tree.
943 hr = insertIntoTree(
944 This,
945 This->storageDirEntry,
946 newStreamEntryRef);
947 if (FAILED(hr))
949 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
950 return hr;
954 * Open the stream to return it.
956 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
958 if (newStream != 0)
960 *ppstm = (IStream*)newStream;
962 IStream_AddRef(*ppstm);
964 else
966 return STG_E_INSUFFICIENTMEMORY;
969 return S_OK;
972 /************************************************************************
973 * Storage32BaseImpl_SetClass (IStorage)
975 * This method will write the specified CLSID in the directory entry of this
976 * storage.
978 * See Windows documentation for more details on IStorage methods.
980 static HRESULT WINAPI StorageBaseImpl_SetClass(
981 IStorage* iface,
982 REFCLSID clsid) /* [in] */
984 StorageBaseImpl *This = (StorageBaseImpl *)iface;
985 HRESULT hRes;
986 DirEntry currentEntry;
988 TRACE("(%p, %p)\n", iface, clsid);
990 if (This->reverted)
991 return STG_E_REVERTED;
993 hRes = StorageBaseImpl_ReadDirEntry(This,
994 This->storageDirEntry,
995 &currentEntry);
996 if (SUCCEEDED(hRes))
998 currentEntry.clsid = *clsid;
1000 hRes = StorageBaseImpl_WriteDirEntry(This,
1001 This->storageDirEntry,
1002 &currentEntry);
1005 return hRes;
1008 /************************************************************************
1009 ** Storage32Impl implementation
1012 /************************************************************************
1013 * Storage32BaseImpl_CreateStorage (IStorage)
1015 * This method will create the storage object within the provided storage.
1017 * See Windows documentation for more details on IStorage methods.
1019 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1020 IStorage* iface,
1021 const OLECHAR *pwcsName, /* [string][in] */
1022 DWORD grfMode, /* [in] */
1023 DWORD reserved1, /* [in] */
1024 DWORD reserved2, /* [in] */
1025 IStorage **ppstg) /* [out] */
1027 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1029 DirEntry currentEntry;
1030 DirEntry newEntry;
1031 DirRef currentEntryRef;
1032 DirRef newEntryRef;
1033 HRESULT hr;
1035 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1036 iface, debugstr_w(pwcsName), grfMode,
1037 reserved1, reserved2, ppstg);
1039 if (ppstg == 0)
1040 return STG_E_INVALIDPOINTER;
1042 if (This->openFlags & STGM_SIMPLE)
1044 return STG_E_INVALIDFUNCTION;
1047 if (pwcsName == 0)
1048 return STG_E_INVALIDNAME;
1050 *ppstg = NULL;
1052 if ( FAILED( validateSTGM(grfMode) ) ||
1053 (grfMode & STGM_DELETEONRELEASE) )
1055 WARN("bad grfMode: 0x%x\n", grfMode);
1056 return STG_E_INVALIDFLAG;
1059 if (This->reverted)
1060 return STG_E_REVERTED;
1063 * Check that we're compatible with the parent's storage mode
1065 if ( !(This->openFlags & STGM_TRANSACTED) &&
1066 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1068 WARN("access denied\n");
1069 return STG_E_ACCESSDENIED;
1072 currentEntryRef = findElement(This,
1073 This->storageDirEntry,
1074 pwcsName,
1075 &currentEntry);
1077 if (currentEntryRef != DIRENTRY_NULL)
1080 * An element with this name already exists
1082 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1083 ((This->openFlags & STGM_TRANSACTED) ||
1084 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1086 hr = IStorage_DestroyElement(iface, pwcsName);
1087 if (FAILED(hr))
1088 return hr;
1090 else
1092 WARN("file already exists\n");
1093 return STG_E_FILEALREADYEXISTS;
1096 else if (!(This->openFlags & STGM_TRANSACTED) &&
1097 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1099 WARN("read-only storage\n");
1100 return STG_E_ACCESSDENIED;
1103 memset(&newEntry, 0, sizeof(DirEntry));
1105 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1107 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1109 FIXME("name too long\n");
1110 return STG_E_INVALIDNAME;
1113 strcpyW(newEntry.name, pwcsName);
1115 newEntry.stgType = STGTY_STORAGE;
1116 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1117 newEntry.size.u.LowPart = 0;
1118 newEntry.size.u.HighPart = 0;
1120 newEntry.leftChild = DIRENTRY_NULL;
1121 newEntry.rightChild = DIRENTRY_NULL;
1122 newEntry.dirRootEntry = DIRENTRY_NULL;
1124 /* call CoFileTime to get the current time
1125 newEntry.ctime
1126 newEntry.mtime
1129 /* newEntry.clsid */
1132 * Create a new directory entry for the storage
1134 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1135 if (FAILED(hr))
1136 return hr;
1139 * Insert the new directory entry into the parent storage's tree
1141 hr = insertIntoTree(
1142 This,
1143 This->storageDirEntry,
1144 newEntryRef);
1145 if (FAILED(hr))
1147 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1148 return hr;
1152 * Open it to get a pointer to return.
1154 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1156 if( (hr != S_OK) || (*ppstg == NULL))
1158 return hr;
1162 return S_OK;
1166 /***************************************************************************
1168 * Internal Method
1170 * Reserve a directory entry in the file and initialize it.
1172 static HRESULT StorageImpl_CreateDirEntry(
1173 StorageBaseImpl *base,
1174 const DirEntry *newData,
1175 DirRef *index)
1177 StorageImpl *storage = (StorageImpl*)base;
1178 ULONG currentEntryIndex = 0;
1179 ULONG newEntryIndex = DIRENTRY_NULL;
1180 HRESULT hr = S_OK;
1181 BYTE currentData[RAW_DIRENTRY_SIZE];
1182 WORD sizeOfNameString;
1186 hr = StorageImpl_ReadRawDirEntry(storage,
1187 currentEntryIndex,
1188 currentData);
1190 if (SUCCEEDED(hr))
1192 StorageUtl_ReadWord(
1193 currentData,
1194 OFFSET_PS_NAMELENGTH,
1195 &sizeOfNameString);
1197 if (sizeOfNameString == 0)
1200 * The entry exists and is available, we found it.
1202 newEntryIndex = currentEntryIndex;
1205 else
1208 * We exhausted the directory entries, we will create more space below
1210 newEntryIndex = currentEntryIndex;
1212 currentEntryIndex++;
1214 } while (newEntryIndex == DIRENTRY_NULL);
1217 * grow the directory stream
1219 if (FAILED(hr))
1221 BYTE emptyData[RAW_DIRENTRY_SIZE];
1222 ULARGE_INTEGER newSize;
1223 ULONG entryIndex;
1224 ULONG lastEntry = 0;
1225 ULONG blockCount = 0;
1228 * obtain the new count of blocks in the directory stream
1230 blockCount = BlockChainStream_GetCount(
1231 storage->rootBlockChain)+1;
1234 * initialize the size used by the directory stream
1236 newSize.u.HighPart = 0;
1237 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1240 * add a block to the directory stream
1242 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1245 * memset the empty entry in order to initialize the unused newly
1246 * created entries
1248 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1251 * initialize them
1253 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1255 for(
1256 entryIndex = newEntryIndex + 1;
1257 entryIndex < lastEntry;
1258 entryIndex++)
1260 StorageImpl_WriteRawDirEntry(
1261 storage,
1262 entryIndex,
1263 emptyData);
1267 UpdateRawDirEntry(currentData, newData);
1269 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1271 if (SUCCEEDED(hr))
1272 *index = newEntryIndex;
1274 return hr;
1277 /***************************************************************************
1279 * Internal Method
1281 * Mark a directory entry in the file as free.
1283 static HRESULT StorageImpl_DestroyDirEntry(
1284 StorageBaseImpl *base,
1285 DirRef index)
1287 HRESULT hr;
1288 BYTE emptyData[RAW_DIRENTRY_SIZE];
1289 StorageImpl *storage = (StorageImpl*)base;
1291 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1293 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1295 return hr;
1299 /***************************************************************************
1301 * Internal Method
1303 * Destroy an entry, its attached data, and all entries reachable from it.
1305 static HRESULT DestroyReachableEntries(
1306 StorageBaseImpl *base,
1307 DirRef index)
1309 HRESULT hr = S_OK;
1310 DirEntry data;
1311 ULARGE_INTEGER zero;
1313 zero.QuadPart = 0;
1315 if (index != DIRENTRY_NULL)
1317 hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1319 if (SUCCEEDED(hr))
1320 hr = DestroyReachableEntries(base, data.dirRootEntry);
1322 if (SUCCEEDED(hr))
1323 hr = DestroyReachableEntries(base, data.leftChild);
1325 if (SUCCEEDED(hr))
1326 hr = DestroyReachableEntries(base, data.rightChild);
1328 if (SUCCEEDED(hr))
1329 hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1331 if (SUCCEEDED(hr))
1332 hr = StorageBaseImpl_DestroyDirEntry(base, index);
1335 return hr;
1339 /****************************************************************************
1341 * Internal Method
1343 * Case insensitive comparison of DirEntry.name by first considering
1344 * their size.
1346 * Returns <0 when name1 < name2
1347 * >0 when name1 > name2
1348 * 0 when name1 == name2
1350 static LONG entryNameCmp(
1351 const OLECHAR *name1,
1352 const OLECHAR *name2)
1354 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1356 while (diff == 0 && *name1 != 0)
1359 * We compare the string themselves only when they are of the same length
1361 diff = toupperW(*name1++) - toupperW(*name2++);
1364 return diff;
1367 /****************************************************************************
1369 * Internal Method
1371 * Add a directory entry to a storage
1373 static HRESULT insertIntoTree(
1374 StorageBaseImpl *This,
1375 DirRef parentStorageIndex,
1376 DirRef newEntryIndex)
1378 DirEntry currentEntry;
1379 DirEntry newEntry;
1382 * Read the inserted entry
1384 StorageBaseImpl_ReadDirEntry(This,
1385 newEntryIndex,
1386 &newEntry);
1389 * Read the storage entry
1391 StorageBaseImpl_ReadDirEntry(This,
1392 parentStorageIndex,
1393 &currentEntry);
1395 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1398 * The root storage contains some element, therefore, start the research
1399 * for the appropriate location.
1401 BOOL found = 0;
1402 DirRef current, next, previous, currentEntryId;
1405 * Keep a reference to the root of the storage's element tree
1407 currentEntryId = currentEntry.dirRootEntry;
1410 * Read
1412 StorageBaseImpl_ReadDirEntry(This,
1413 currentEntry.dirRootEntry,
1414 &currentEntry);
1416 previous = currentEntry.leftChild;
1417 next = currentEntry.rightChild;
1418 current = currentEntryId;
1420 while (found == 0)
1422 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1424 if (diff < 0)
1426 if (previous != DIRENTRY_NULL)
1428 StorageBaseImpl_ReadDirEntry(This,
1429 previous,
1430 &currentEntry);
1431 current = previous;
1433 else
1435 currentEntry.leftChild = newEntryIndex;
1436 StorageBaseImpl_WriteDirEntry(This,
1437 current,
1438 &currentEntry);
1439 found = 1;
1442 else if (diff > 0)
1444 if (next != DIRENTRY_NULL)
1446 StorageBaseImpl_ReadDirEntry(This,
1447 next,
1448 &currentEntry);
1449 current = next;
1451 else
1453 currentEntry.rightChild = newEntryIndex;
1454 StorageBaseImpl_WriteDirEntry(This,
1455 current,
1456 &currentEntry);
1457 found = 1;
1460 else
1463 * Trying to insert an item with the same name in the
1464 * subtree structure.
1466 return STG_E_FILEALREADYEXISTS;
1469 previous = currentEntry.leftChild;
1470 next = currentEntry.rightChild;
1473 else
1476 * The storage is empty, make the new entry the root of its element tree
1478 currentEntry.dirRootEntry = newEntryIndex;
1479 StorageBaseImpl_WriteDirEntry(This,
1480 parentStorageIndex,
1481 &currentEntry);
1484 return S_OK;
1487 /****************************************************************************
1489 * Internal Method
1491 * Find and read the element of a storage with the given name.
1493 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1494 const OLECHAR *name, DirEntry *data)
1496 DirRef currentEntry;
1498 /* Read the storage entry to find the root of the tree. */
1499 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1501 currentEntry = data->dirRootEntry;
1503 while (currentEntry != DIRENTRY_NULL)
1505 LONG cmp;
1507 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1509 cmp = entryNameCmp(name, data->name);
1511 if (cmp == 0)
1512 /* found it */
1513 break;
1515 else if (cmp < 0)
1516 currentEntry = data->leftChild;
1518 else if (cmp > 0)
1519 currentEntry = data->rightChild;
1522 return currentEntry;
1525 /****************************************************************************
1527 * Internal Method
1529 * Find and read the binary tree parent of the element with the given name.
1531 * If there is no such element, find a place where it could be inserted and
1532 * return STG_E_FILENOTFOUND.
1534 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1535 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1536 ULONG *relation)
1538 DirRef childEntry;
1539 DirEntry childData;
1541 /* Read the storage entry to find the root of the tree. */
1542 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1544 *parentEntry = storageEntry;
1545 *relation = DIRENTRY_RELATION_DIR;
1547 childEntry = parentData->dirRootEntry;
1549 while (childEntry != DIRENTRY_NULL)
1551 LONG cmp;
1553 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1555 cmp = entryNameCmp(childName, childData.name);
1557 if (cmp == 0)
1558 /* found it */
1559 break;
1561 else if (cmp < 0)
1563 *parentData = childData;
1564 *parentEntry = childEntry;
1565 *relation = DIRENTRY_RELATION_PREVIOUS;
1567 childEntry = parentData->leftChild;
1570 else if (cmp > 0)
1572 *parentData = childData;
1573 *parentEntry = childEntry;
1574 *relation = DIRENTRY_RELATION_NEXT;
1576 childEntry = parentData->rightChild;
1580 if (childEntry == DIRENTRY_NULL)
1581 return STG_E_FILENOTFOUND;
1582 else
1583 return S_OK;
1587 /*************************************************************************
1588 * CopyTo (IStorage)
1590 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1591 IStorage* iface,
1592 DWORD ciidExclude, /* [in] */
1593 const IID* rgiidExclude, /* [size_is][unique][in] */
1594 SNB snbExclude, /* [unique][in] */
1595 IStorage* pstgDest) /* [unique][in] */
1597 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1599 IEnumSTATSTG *elements = 0;
1600 STATSTG curElement, strStat;
1601 HRESULT hr;
1602 IStorage *pstgTmp, *pstgChild;
1603 IStream *pstrTmp, *pstrChild;
1604 DirRef srcEntryRef;
1605 DirEntry srcEntry;
1606 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1607 int i;
1609 TRACE("(%p, %d, %p, %p, %p)\n",
1610 iface, ciidExclude, rgiidExclude,
1611 snbExclude, pstgDest);
1613 if ( pstgDest == 0 )
1614 return STG_E_INVALIDPOINTER;
1617 * Enumerate the elements
1619 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1621 if ( hr != S_OK )
1622 return hr;
1625 * set the class ID
1627 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1628 IStorage_SetClass( pstgDest, &curElement.clsid );
1630 for(i = 0; i < ciidExclude; ++i)
1632 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1633 skip_storage = TRUE;
1634 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1635 skip_stream = TRUE;
1636 else
1637 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1643 * Obtain the next element
1645 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1647 if ( hr == S_FALSE )
1649 hr = S_OK; /* done, every element has been copied */
1650 break;
1653 if ( snbExclude )
1655 WCHAR **snb = snbExclude;
1656 skip = FALSE;
1657 while ( *snb != NULL && !skip )
1659 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1660 skip = TRUE;
1661 ++snb;
1665 if ( skip )
1666 goto cleanup;
1668 if (curElement.type == STGTY_STORAGE)
1670 if(skip_storage)
1671 goto cleanup;
1674 * open child source storage
1676 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1677 STGM_READ|STGM_SHARE_EXCLUSIVE,
1678 NULL, 0, &pstgChild );
1680 if (hr != S_OK)
1681 goto cleanup;
1684 * create a new storage in destination storage
1686 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1687 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1688 0, 0,
1689 &pstgTmp );
1691 * if it already exist, don't create a new one use this one
1693 if (hr == STG_E_FILEALREADYEXISTS)
1695 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1696 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1697 NULL, 0, &pstgTmp );
1700 if (hr == S_OK)
1703 * do the copy recursively
1705 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1706 NULL, pstgTmp );
1708 IStorage_Release( pstgTmp );
1711 IStorage_Release( pstgChild );
1713 else if (curElement.type == STGTY_STREAM)
1715 if(skip_stream)
1716 goto cleanup;
1719 * create a new stream in destination storage. If the stream already
1720 * exist, it will be deleted and a new one will be created.
1722 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1723 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1724 0, 0, &pstrTmp );
1726 if (hr != S_OK)
1727 goto cleanup;
1730 * open child stream storage. This operation must succeed even if the
1731 * stream is already open, so we use internal functions to do it.
1733 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1734 &srcEntry);
1735 if (!srcEntryRef)
1737 ERR("source stream not found\n");
1738 hr = STG_E_DOCFILECORRUPT;
1741 if (hr == S_OK)
1743 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1744 if (pstrChild)
1745 IStream_AddRef(pstrChild);
1746 else
1747 hr = E_OUTOFMEMORY;
1750 if (hr == S_OK)
1753 * Get the size of the source stream
1755 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1758 * Set the size of the destination stream.
1760 IStream_SetSize(pstrTmp, strStat.cbSize);
1763 * do the copy
1765 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1766 NULL, NULL );
1768 IStream_Release( pstrChild );
1771 IStream_Release( pstrTmp );
1773 else
1775 WARN("unknown element type: %d\n", curElement.type);
1778 cleanup:
1779 CoTaskMemFree(curElement.pwcsName);
1780 } while (hr == S_OK);
1783 * Clean-up
1785 IEnumSTATSTG_Release(elements);
1787 return hr;
1790 /*************************************************************************
1791 * MoveElementTo (IStorage)
1793 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1794 IStorage* iface,
1795 const OLECHAR *pwcsName, /* [string][in] */
1796 IStorage *pstgDest, /* [unique][in] */
1797 const OLECHAR *pwcsNewName,/* [string][in] */
1798 DWORD grfFlags) /* [in] */
1800 FIXME("(%p %s %p %s %u): stub\n", iface,
1801 debugstr_w(pwcsName), pstgDest,
1802 debugstr_w(pwcsNewName), grfFlags);
1803 return E_NOTIMPL;
1806 /*************************************************************************
1807 * Commit (IStorage)
1809 * Ensures that any changes made to a storage object open in transacted mode
1810 * are reflected in the parent storage
1812 * NOTES
1813 * Wine doesn't implement transacted mode, which seems to be a basic
1814 * optimization, so we can ignore this stub for now.
1816 static HRESULT WINAPI StorageImpl_Commit(
1817 IStorage* iface,
1818 DWORD grfCommitFlags)/* [in] */
1820 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1821 return S_OK;
1824 /*************************************************************************
1825 * Revert (IStorage)
1827 * Discard all changes that have been made since the last commit operation
1829 static HRESULT WINAPI StorageImpl_Revert(
1830 IStorage* iface)
1832 TRACE("(%p)\n", iface);
1833 return S_OK;
1836 /*************************************************************************
1837 * DestroyElement (IStorage)
1839 * Strategy: This implementation is built this way for simplicity not for speed.
1840 * I always delete the topmost element of the enumeration and adjust
1841 * the deleted element pointer all the time. This takes longer to
1842 * do but allow to reinvoke DestroyElement whenever we encounter a
1843 * storage object. The optimisation resides in the usage of another
1844 * enumeration strategy that would give all the leaves of a storage
1845 * first. (postfix order)
1847 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1848 IStorage* iface,
1849 const OLECHAR *pwcsName)/* [string][in] */
1851 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1853 HRESULT hr = S_OK;
1854 DirEntry entryToDelete;
1855 DirRef entryToDeleteRef;
1857 TRACE("(%p, %s)\n",
1858 iface, debugstr_w(pwcsName));
1860 if (pwcsName==NULL)
1861 return STG_E_INVALIDPOINTER;
1863 if (This->reverted)
1864 return STG_E_REVERTED;
1866 if ( !(This->openFlags & STGM_TRANSACTED) &&
1867 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1868 return STG_E_ACCESSDENIED;
1870 entryToDeleteRef = findElement(
1871 This,
1872 This->storageDirEntry,
1873 pwcsName,
1874 &entryToDelete);
1876 if ( entryToDeleteRef == DIRENTRY_NULL )
1878 return STG_E_FILENOTFOUND;
1881 if ( entryToDelete.stgType == STGTY_STORAGE )
1883 hr = deleteStorageContents(
1884 This,
1885 entryToDeleteRef,
1886 entryToDelete);
1888 else if ( entryToDelete.stgType == STGTY_STREAM )
1890 hr = deleteStreamContents(
1891 This,
1892 entryToDeleteRef,
1893 entryToDelete);
1896 if (hr!=S_OK)
1897 return hr;
1900 * Remove the entry from its parent storage
1902 hr = removeFromTree(
1903 This,
1904 This->storageDirEntry,
1905 entryToDeleteRef);
1908 * Invalidate the entry
1910 if (SUCCEEDED(hr))
1911 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1913 return hr;
1917 /******************************************************************************
1918 * Internal stream list handlers
1921 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1923 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1924 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1927 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1929 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1930 list_remove(&(strm->StrmListEntry));
1933 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1935 StgStreamImpl *strm;
1937 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1939 if (strm->dirEntry == streamEntry)
1941 return TRUE;
1945 return FALSE;
1948 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1950 StorageInternalImpl *childstg;
1952 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1954 if (childstg->base.storageDirEntry == storageEntry)
1956 return TRUE;
1960 return FALSE;
1963 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1965 struct list *cur, *cur2;
1966 StgStreamImpl *strm=NULL;
1967 StorageInternalImpl *childstg=NULL;
1969 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1970 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1971 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1972 strm->parentStorage = NULL;
1973 list_remove(cur);
1976 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1977 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1978 StorageBaseImpl_Invalidate( &childstg->base );
1981 if (stg->transactedChild)
1983 StorageBaseImpl_Invalidate(stg->transactedChild);
1985 stg->transactedChild = NULL;
1990 /*********************************************************************
1992 * Internal Method
1994 * Delete the contents of a storage entry.
1997 static HRESULT deleteStorageContents(
1998 StorageBaseImpl *parentStorage,
1999 DirRef indexToDelete,
2000 DirEntry entryDataToDelete)
2002 IEnumSTATSTG *elements = 0;
2003 IStorage *childStorage = 0;
2004 STATSTG currentElement;
2005 HRESULT hr;
2006 HRESULT destroyHr = S_OK;
2007 StorageInternalImpl *stg, *stg2;
2009 /* Invalidate any open storage objects. */
2010 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2012 if (stg->base.storageDirEntry == indexToDelete)
2014 StorageBaseImpl_Invalidate(&stg->base);
2019 * Open the storage and enumerate it
2021 hr = StorageBaseImpl_OpenStorage(
2022 (IStorage*)parentStorage,
2023 entryDataToDelete.name,
2025 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2028 &childStorage);
2030 if (hr != S_OK)
2032 return hr;
2036 * Enumerate the elements
2038 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2043 * Obtain the next element
2045 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2046 if (hr==S_OK)
2048 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2050 CoTaskMemFree(currentElement.pwcsName);
2054 * We need to Reset the enumeration every time because we delete elements
2055 * and the enumeration could be invalid
2057 IEnumSTATSTG_Reset(elements);
2059 } while ((hr == S_OK) && (destroyHr == S_OK));
2061 IStorage_Release(childStorage);
2062 IEnumSTATSTG_Release(elements);
2064 return destroyHr;
2067 /*********************************************************************
2069 * Internal Method
2071 * Perform the deletion of a stream's data
2074 static HRESULT deleteStreamContents(
2075 StorageBaseImpl *parentStorage,
2076 DirRef indexToDelete,
2077 DirEntry entryDataToDelete)
2079 IStream *pis;
2080 HRESULT hr;
2081 ULARGE_INTEGER size;
2082 StgStreamImpl *strm, *strm2;
2084 /* Invalidate any open stream objects. */
2085 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2087 if (strm->dirEntry == indexToDelete)
2089 TRACE("Stream deleted %p\n", strm);
2090 strm->parentStorage = NULL;
2091 list_remove(&strm->StrmListEntry);
2095 size.u.HighPart = 0;
2096 size.u.LowPart = 0;
2098 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2099 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2101 if (hr!=S_OK)
2103 return(hr);
2107 * Zap the stream
2109 hr = IStream_SetSize(pis, size);
2111 if(hr != S_OK)
2113 return hr;
2117 * Release the stream object.
2119 IStream_Release(pis);
2121 return S_OK;
2124 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2126 switch (relation)
2128 case DIRENTRY_RELATION_PREVIOUS:
2129 entry->leftChild = new_target;
2130 break;
2131 case DIRENTRY_RELATION_NEXT:
2132 entry->rightChild = new_target;
2133 break;
2134 case DIRENTRY_RELATION_DIR:
2135 entry->dirRootEntry = new_target;
2136 break;
2137 default:
2138 assert(0);
2142 /*************************************************************************
2144 * Internal Method
2146 * This method removes a directory entry from its parent storage tree without
2147 * freeing any resources attached to it.
2149 static HRESULT removeFromTree(
2150 StorageBaseImpl *This,
2151 DirRef parentStorageIndex,
2152 DirRef deletedIndex)
2154 HRESULT hr = S_OK;
2155 DirEntry entryToDelete;
2156 DirEntry parentEntry;
2157 DirRef parentEntryRef;
2158 ULONG typeOfRelation;
2160 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2162 if (hr != S_OK)
2163 return hr;
2166 * Find the element that links to the one we want to delete.
2168 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2169 &parentEntry, &parentEntryRef, &typeOfRelation);
2171 if (hr != S_OK)
2172 return hr;
2174 if (entryToDelete.leftChild != DIRENTRY_NULL)
2177 * Replace the deleted entry with its left child
2179 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2181 hr = StorageBaseImpl_WriteDirEntry(
2182 This,
2183 parentEntryRef,
2184 &parentEntry);
2185 if(FAILED(hr))
2187 return hr;
2190 if (entryToDelete.rightChild != DIRENTRY_NULL)
2193 * We need to reinsert the right child somewhere. We already know it and
2194 * its children are greater than everything in the left tree, so we
2195 * insert it at the rightmost point in the left tree.
2197 DirRef newRightChildParent = entryToDelete.leftChild;
2198 DirEntry newRightChildParentEntry;
2202 hr = StorageBaseImpl_ReadDirEntry(
2203 This,
2204 newRightChildParent,
2205 &newRightChildParentEntry);
2206 if (FAILED(hr))
2208 return hr;
2211 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2212 newRightChildParent = newRightChildParentEntry.rightChild;
2213 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2215 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2217 hr = StorageBaseImpl_WriteDirEntry(
2218 This,
2219 newRightChildParent,
2220 &newRightChildParentEntry);
2221 if (FAILED(hr))
2223 return hr;
2227 else
2230 * Replace the deleted entry with its right child
2232 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2234 hr = StorageBaseImpl_WriteDirEntry(
2235 This,
2236 parentEntryRef,
2237 &parentEntry);
2238 if(FAILED(hr))
2240 return hr;
2244 return hr;
2248 /******************************************************************************
2249 * SetElementTimes (IStorage)
2251 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2252 IStorage* iface,
2253 const OLECHAR *pwcsName,/* [string][in] */
2254 const FILETIME *pctime, /* [in] */
2255 const FILETIME *patime, /* [in] */
2256 const FILETIME *pmtime) /* [in] */
2258 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2259 return S_OK;
2262 /******************************************************************************
2263 * SetStateBits (IStorage)
2265 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2266 IStorage* iface,
2267 DWORD grfStateBits,/* [in] */
2268 DWORD grfMask) /* [in] */
2270 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2272 if (This->reverted)
2273 return STG_E_REVERTED;
2275 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2276 return S_OK;
2279 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2280 DirRef index, const DirEntry *data)
2282 StorageImpl *This = (StorageImpl*)base;
2283 return StorageImpl_WriteDirEntry(This, index, data);
2286 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2287 DirRef index, DirEntry *data)
2289 StorageImpl *This = (StorageImpl*)base;
2290 return StorageImpl_ReadDirEntry(This, index, data);
2293 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2295 int i;
2297 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2299 if (!This->blockChainCache[i])
2301 return &This->blockChainCache[i];
2305 i = This->blockChainToEvict;
2307 BlockChainStream_Destroy(This->blockChainCache[i]);
2308 This->blockChainCache[i] = NULL;
2310 This->blockChainToEvict++;
2311 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2312 This->blockChainToEvict = 0;
2314 return &This->blockChainCache[i];
2317 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2318 DirRef index)
2320 int i, free_index=-1;
2322 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2324 if (!This->blockChainCache[i])
2326 if (free_index == -1) free_index = i;
2328 else if (This->blockChainCache[i]->ownerDirEntry == index)
2330 return &This->blockChainCache[i];
2334 if (free_index == -1)
2336 free_index = This->blockChainToEvict;
2338 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2339 This->blockChainCache[free_index] = NULL;
2341 This->blockChainToEvict++;
2342 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2343 This->blockChainToEvict = 0;
2346 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2347 return &This->blockChainCache[free_index];
2350 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2351 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2353 StorageImpl *This = (StorageImpl*)base;
2354 DirEntry data;
2355 HRESULT hr;
2356 ULONG bytesToRead;
2358 hr = StorageImpl_ReadDirEntry(This, index, &data);
2359 if (FAILED(hr)) return hr;
2361 if (data.size.QuadPart == 0)
2363 *bytesRead = 0;
2364 return S_OK;
2367 if (offset.QuadPart + size > data.size.QuadPart)
2369 bytesToRead = data.size.QuadPart - offset.QuadPart;
2371 else
2373 bytesToRead = size;
2376 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2378 SmallBlockChainStream *stream;
2380 stream = SmallBlockChainStream_Construct(This, NULL, index);
2381 if (!stream) return E_OUTOFMEMORY;
2383 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2385 SmallBlockChainStream_Destroy(stream);
2387 return hr;
2389 else
2391 BlockChainStream *stream = NULL;
2393 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2394 if (!stream) return E_OUTOFMEMORY;
2396 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2398 return hr;
2402 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2403 ULARGE_INTEGER newsize)
2405 StorageImpl *This = (StorageImpl*)base;
2406 DirEntry data;
2407 HRESULT hr;
2408 SmallBlockChainStream *smallblock=NULL;
2409 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2411 hr = StorageImpl_ReadDirEntry(This, index, &data);
2412 if (FAILED(hr)) return hr;
2414 /* In simple mode keep the stream size above the small block limit */
2415 if (This->base.openFlags & STGM_SIMPLE)
2416 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2418 if (data.size.QuadPart == newsize.QuadPart)
2419 return S_OK;
2421 /* Create a block chain object of the appropriate type */
2422 if (data.size.QuadPart == 0)
2424 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2426 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2427 if (!smallblock) return E_OUTOFMEMORY;
2429 else
2431 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2432 bigblock = *pbigblock;
2433 if (!bigblock) return E_OUTOFMEMORY;
2436 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2438 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2439 if (!smallblock) return E_OUTOFMEMORY;
2441 else
2443 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2444 bigblock = *pbigblock;
2445 if (!bigblock) return E_OUTOFMEMORY;
2448 /* Change the block chain type if necessary. */
2449 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2451 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2452 if (!bigblock)
2454 SmallBlockChainStream_Destroy(smallblock);
2455 return E_FAIL;
2458 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2459 *pbigblock = bigblock;
2461 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2463 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2464 if (!smallblock)
2465 return E_FAIL;
2468 /* Set the size of the block chain. */
2469 if (smallblock)
2471 SmallBlockChainStream_SetSize(smallblock, newsize);
2472 SmallBlockChainStream_Destroy(smallblock);
2474 else
2476 BlockChainStream_SetSize(bigblock, newsize);
2479 /* Set the size in the directory entry. */
2480 hr = StorageImpl_ReadDirEntry(This, index, &data);
2481 if (SUCCEEDED(hr))
2483 data.size = newsize;
2485 hr = StorageImpl_WriteDirEntry(This, index, &data);
2487 return hr;
2490 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2491 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2493 StorageImpl *This = (StorageImpl*)base;
2494 DirEntry data;
2495 HRESULT hr;
2496 ULARGE_INTEGER newSize;
2498 hr = StorageImpl_ReadDirEntry(This, index, &data);
2499 if (FAILED(hr)) return hr;
2501 /* Grow the stream if necessary */
2502 newSize.QuadPart = 0;
2503 newSize.QuadPart = offset.QuadPart + size;
2505 if (newSize.QuadPart > data.size.QuadPart)
2507 hr = StorageImpl_StreamSetSize(base, index, newSize);
2508 if (FAILED(hr))
2509 return hr;
2511 hr = StorageImpl_ReadDirEntry(This, index, &data);
2512 if (FAILED(hr)) return hr;
2515 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2517 SmallBlockChainStream *stream;
2519 stream = SmallBlockChainStream_Construct(This, NULL, index);
2520 if (!stream) return E_OUTOFMEMORY;
2522 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2524 SmallBlockChainStream_Destroy(stream);
2526 return hr;
2528 else
2530 BlockChainStream *stream;
2532 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2533 if (!stream) return E_OUTOFMEMORY;
2535 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2537 return hr;
2542 * Virtual function table for the IStorage32Impl class.
2544 static const IStorageVtbl Storage32Impl_Vtbl =
2546 StorageBaseImpl_QueryInterface,
2547 StorageBaseImpl_AddRef,
2548 StorageBaseImpl_Release,
2549 StorageBaseImpl_CreateStream,
2550 StorageBaseImpl_OpenStream,
2551 StorageBaseImpl_CreateStorage,
2552 StorageBaseImpl_OpenStorage,
2553 StorageBaseImpl_CopyTo,
2554 StorageBaseImpl_MoveElementTo,
2555 StorageImpl_Commit,
2556 StorageImpl_Revert,
2557 StorageBaseImpl_EnumElements,
2558 StorageBaseImpl_DestroyElement,
2559 StorageBaseImpl_RenameElement,
2560 StorageBaseImpl_SetElementTimes,
2561 StorageBaseImpl_SetClass,
2562 StorageBaseImpl_SetStateBits,
2563 StorageBaseImpl_Stat
2566 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2568 StorageImpl_Destroy,
2569 StorageImpl_Invalidate,
2570 StorageImpl_CreateDirEntry,
2571 StorageImpl_BaseWriteDirEntry,
2572 StorageImpl_BaseReadDirEntry,
2573 StorageImpl_DestroyDirEntry,
2574 StorageImpl_StreamReadAt,
2575 StorageImpl_StreamWriteAt,
2576 StorageImpl_StreamSetSize
2579 static HRESULT StorageImpl_Construct(
2580 HANDLE hFile,
2581 LPCOLESTR pwcsName,
2582 ILockBytes* pLkbyt,
2583 DWORD openFlags,
2584 BOOL fileBased,
2585 BOOL create,
2586 ULONG sector_size,
2587 StorageImpl** result)
2589 StorageImpl* This;
2590 HRESULT hr = S_OK;
2591 DirEntry currentEntry;
2592 DirRef currentEntryRef;
2593 WCHAR fullpath[MAX_PATH];
2595 if ( FAILED( validateSTGM(openFlags) ))
2596 return STG_E_INVALIDFLAG;
2598 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2599 if (!This)
2600 return E_OUTOFMEMORY;
2602 memset(This, 0, sizeof(StorageImpl));
2604 list_init(&This->base.strmHead);
2606 list_init(&This->base.storageHead);
2608 This->base.lpVtbl = &Storage32Impl_Vtbl;
2609 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2610 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2611 This->base.openFlags = (openFlags & ~STGM_CREATE);
2612 This->base.ref = 1;
2613 This->base.create = create;
2615 This->base.reverted = 0;
2617 This->hFile = hFile;
2619 if(pwcsName) {
2620 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2622 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2624 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2625 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2626 if (!This->pwcsName)
2628 hr = STG_E_INSUFFICIENTMEMORY;
2629 goto end;
2631 strcpyW(This->pwcsName, fullpath);
2632 This->base.filename = This->pwcsName;
2636 * Initialize the big block cache.
2638 This->bigBlockSize = sector_size;
2639 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2640 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2641 pLkbyt,
2642 openFlags,
2643 fileBased);
2645 if (This->bigBlockFile == 0)
2647 hr = E_FAIL;
2648 goto end;
2651 if (create)
2653 ULARGE_INTEGER size;
2654 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2657 * Initialize all header variables:
2658 * - The big block depot consists of one block and it is at block 0
2659 * - The directory table starts at block 1
2660 * - There is no small block depot
2662 memset( This->bigBlockDepotStart,
2663 BLOCK_UNUSED,
2664 sizeof(This->bigBlockDepotStart));
2666 This->bigBlockDepotCount = 1;
2667 This->bigBlockDepotStart[0] = 0;
2668 This->rootStartBlock = 1;
2669 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2670 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2671 if (sector_size == 4096)
2672 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2673 else
2674 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2675 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2676 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2677 This->extBigBlockDepotCount = 0;
2679 StorageImpl_SaveFileHeader(This);
2682 * Add one block for the big block depot and one block for the directory table
2684 size.u.HighPart = 0;
2685 size.u.LowPart = This->bigBlockSize * 3;
2686 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2689 * Initialize the big block depot
2691 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2692 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2693 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2694 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2696 else
2699 * Load the header for the file.
2701 hr = StorageImpl_LoadFileHeader(This);
2703 if (FAILED(hr))
2705 goto end;
2710 * There is no block depot cached yet.
2712 This->indexBlockDepotCached = 0xFFFFFFFF;
2715 * Start searching for free blocks with block 0.
2717 This->prevFreeBlock = 0;
2719 This->firstFreeSmallBlock = 0;
2722 * Create the block chain abstractions.
2724 if(!(This->rootBlockChain =
2725 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2727 hr = STG_E_READFAULT;
2728 goto end;
2731 if(!(This->smallBlockDepotChain =
2732 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2733 DIRENTRY_NULL)))
2735 hr = STG_E_READFAULT;
2736 goto end;
2740 * Write the root storage entry (memory only)
2742 if (create)
2744 DirEntry rootEntry;
2746 * Initialize the directory table
2748 memset(&rootEntry, 0, sizeof(rootEntry));
2749 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2750 sizeof(rootEntry.name)/sizeof(WCHAR) );
2751 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2752 rootEntry.stgType = STGTY_ROOT;
2753 rootEntry.leftChild = DIRENTRY_NULL;
2754 rootEntry.rightChild = DIRENTRY_NULL;
2755 rootEntry.dirRootEntry = DIRENTRY_NULL;
2756 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2757 rootEntry.size.u.HighPart = 0;
2758 rootEntry.size.u.LowPart = 0;
2760 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2764 * Find the ID of the root storage.
2766 currentEntryRef = 0;
2770 hr = StorageImpl_ReadDirEntry(
2771 This,
2772 currentEntryRef,
2773 &currentEntry);
2775 if (SUCCEEDED(hr))
2777 if ( (currentEntry.sizeOfNameString != 0 ) &&
2778 (currentEntry.stgType == STGTY_ROOT) )
2780 This->base.storageDirEntry = currentEntryRef;
2784 currentEntryRef++;
2786 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2788 if (FAILED(hr))
2790 hr = STG_E_READFAULT;
2791 goto end;
2795 * Create the block chain abstraction for the small block root chain.
2797 if(!(This->smallBlockRootChain =
2798 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2800 hr = STG_E_READFAULT;
2803 end:
2804 if (FAILED(hr))
2806 IStorage_Release((IStorage*)This);
2807 *result = NULL;
2809 else
2810 *result = This;
2812 return hr;
2815 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2817 StorageImpl *This = (StorageImpl*) iface;
2819 StorageBaseImpl_DeleteAll(&This->base);
2821 This->base.reverted = 1;
2824 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2826 StorageImpl *This = (StorageImpl*) iface;
2827 int i;
2828 TRACE("(%p)\n", This);
2830 StorageImpl_Invalidate(iface);
2832 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2834 BlockChainStream_Destroy(This->smallBlockRootChain);
2835 BlockChainStream_Destroy(This->rootBlockChain);
2836 BlockChainStream_Destroy(This->smallBlockDepotChain);
2838 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2839 BlockChainStream_Destroy(This->blockChainCache[i]);
2841 if (This->bigBlockFile)
2842 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2843 HeapFree(GetProcessHeap(), 0, This);
2846 /******************************************************************************
2847 * Storage32Impl_GetNextFreeBigBlock
2849 * Returns the index of the next free big block.
2850 * If the big block depot is filled, this method will enlarge it.
2853 static ULONG StorageImpl_GetNextFreeBigBlock(
2854 StorageImpl* This)
2856 ULONG depotBlockIndexPos;
2857 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2858 BOOL success;
2859 ULONG depotBlockOffset;
2860 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2861 ULONG nextBlockIndex = BLOCK_SPECIAL;
2862 int depotIndex = 0;
2863 ULONG freeBlock = BLOCK_UNUSED;
2864 ULARGE_INTEGER neededSize;
2866 depotIndex = This->prevFreeBlock / blocksPerDepot;
2867 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2870 * Scan the entire big block depot until we find a block marked free
2872 while (nextBlockIndex != BLOCK_UNUSED)
2874 if (depotIndex < COUNT_BBDEPOTINHEADER)
2876 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2879 * Grow the primary depot.
2881 if (depotBlockIndexPos == BLOCK_UNUSED)
2883 depotBlockIndexPos = depotIndex*blocksPerDepot;
2886 * Add a block depot.
2888 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2889 This->bigBlockDepotCount++;
2890 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2893 * Flag it as a block depot.
2895 StorageImpl_SetNextBlockInChain(This,
2896 depotBlockIndexPos,
2897 BLOCK_SPECIAL);
2899 /* Save new header information.
2901 StorageImpl_SaveFileHeader(This);
2904 else
2906 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2908 if (depotBlockIndexPos == BLOCK_UNUSED)
2911 * Grow the extended depot.
2913 ULONG extIndex = BLOCK_UNUSED;
2914 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2915 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2917 if (extBlockOffset == 0)
2919 /* We need an extended block.
2921 extIndex = Storage32Impl_AddExtBlockDepot(This);
2922 This->extBigBlockDepotCount++;
2923 depotBlockIndexPos = extIndex + 1;
2925 else
2926 depotBlockIndexPos = depotIndex * blocksPerDepot;
2929 * Add a block depot and mark it in the extended block.
2931 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2932 This->bigBlockDepotCount++;
2933 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2935 /* Flag the block depot.
2937 StorageImpl_SetNextBlockInChain(This,
2938 depotBlockIndexPos,
2939 BLOCK_SPECIAL);
2941 /* If necessary, flag the extended depot block.
2943 if (extIndex != BLOCK_UNUSED)
2944 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2946 /* Save header information.
2948 StorageImpl_SaveFileHeader(This);
2952 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2954 if (success)
2956 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2957 ( nextBlockIndex != BLOCK_UNUSED))
2959 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2961 if (nextBlockIndex == BLOCK_UNUSED)
2963 freeBlock = (depotIndex * blocksPerDepot) +
2964 (depotBlockOffset/sizeof(ULONG));
2967 depotBlockOffset += sizeof(ULONG);
2971 depotIndex++;
2972 depotBlockOffset = 0;
2976 * make sure that the block physically exists before using it
2978 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
2979 BIGBLOCKFILE_Expand(This->bigBlockFile, neededSize);
2981 This->prevFreeBlock = freeBlock;
2983 return freeBlock;
2986 /******************************************************************************
2987 * Storage32Impl_AddBlockDepot
2989 * This will create a depot block, essentially it is a block initialized
2990 * to BLOCK_UNUSEDs.
2992 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2994 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
2997 * Initialize blocks as free
2999 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3000 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3003 /******************************************************************************
3004 * Storage32Impl_GetExtDepotBlock
3006 * Returns the index of the block that corresponds to the specified depot
3007 * index. This method is only for depot indexes equal or greater than
3008 * COUNT_BBDEPOTINHEADER.
3010 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3012 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3013 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3014 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3015 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3016 ULONG blockIndex = BLOCK_UNUSED;
3017 ULONG extBlockIndex = This->extBigBlockDepotStart;
3019 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3021 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3022 return BLOCK_UNUSED;
3024 while (extBlockCount > 0)
3026 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3027 extBlockCount--;
3030 if (extBlockIndex != BLOCK_UNUSED)
3031 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3032 extBlockOffset * sizeof(ULONG), &blockIndex);
3034 return blockIndex;
3037 /******************************************************************************
3038 * Storage32Impl_SetExtDepotBlock
3040 * Associates the specified block index to the specified depot index.
3041 * This method is only for depot indexes equal or greater than
3042 * COUNT_BBDEPOTINHEADER.
3044 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3046 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3047 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3048 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3049 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3050 ULONG extBlockIndex = This->extBigBlockDepotStart;
3052 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3054 while (extBlockCount > 0)
3056 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3057 extBlockCount--;
3060 if (extBlockIndex != BLOCK_UNUSED)
3062 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3063 extBlockOffset * sizeof(ULONG),
3064 blockIndex);
3068 /******************************************************************************
3069 * Storage32Impl_AddExtBlockDepot
3071 * Creates an extended depot block.
3073 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3075 ULONG numExtBlocks = This->extBigBlockDepotCount;
3076 ULONG nextExtBlock = This->extBigBlockDepotStart;
3077 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3078 ULONG index = BLOCK_UNUSED;
3079 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3080 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3081 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3083 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3084 blocksPerDepotBlock;
3086 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3089 * The first extended block.
3091 This->extBigBlockDepotStart = index;
3093 else
3095 unsigned int i;
3097 * Follow the chain to the last one.
3099 for (i = 0; i < (numExtBlocks - 1); i++)
3101 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3105 * Add the new extended block to the chain.
3107 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3108 index);
3112 * Initialize this block.
3114 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3115 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3117 return index;
3120 /******************************************************************************
3121 * Storage32Impl_FreeBigBlock
3123 * This method will flag the specified block as free in the big block depot.
3125 static void StorageImpl_FreeBigBlock(
3126 StorageImpl* This,
3127 ULONG blockIndex)
3129 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3131 if (blockIndex < This->prevFreeBlock)
3132 This->prevFreeBlock = blockIndex;
3135 /************************************************************************
3136 * Storage32Impl_GetNextBlockInChain
3138 * This method will retrieve the block index of the next big block in
3139 * in the chain.
3141 * Params: This - Pointer to the Storage object.
3142 * blockIndex - Index of the block to retrieve the chain
3143 * for.
3144 * nextBlockIndex - receives the return value.
3146 * Returns: This method returns the index of the next block in the chain.
3147 * It will return the constants:
3148 * BLOCK_SPECIAL - If the block given was not part of a
3149 * chain.
3150 * BLOCK_END_OF_CHAIN - If the block given was the last in
3151 * a chain.
3152 * BLOCK_UNUSED - If the block given was not past of a chain
3153 * and is available.
3154 * BLOCK_EXTBBDEPOT - This block is part of the extended
3155 * big block depot.
3157 * See Windows documentation for more details on IStorage methods.
3159 static HRESULT StorageImpl_GetNextBlockInChain(
3160 StorageImpl* This,
3161 ULONG blockIndex,
3162 ULONG* nextBlockIndex)
3164 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3165 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3166 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3167 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3168 BOOL success;
3169 ULONG depotBlockIndexPos;
3170 int index, num_blocks;
3172 *nextBlockIndex = BLOCK_SPECIAL;
3174 if(depotBlockCount >= This->bigBlockDepotCount)
3176 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3177 This->bigBlockDepotCount);
3178 return STG_E_READFAULT;
3182 * Cache the currently accessed depot block.
3184 if (depotBlockCount != This->indexBlockDepotCached)
3186 This->indexBlockDepotCached = depotBlockCount;
3188 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3190 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3192 else
3195 * We have to look in the extended depot.
3197 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3200 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3202 if (!success)
3203 return STG_E_READFAULT;
3205 num_blocks = This->bigBlockSize / 4;
3207 for (index = 0; index < num_blocks; index++)
3209 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3210 This->blockDepotCached[index] = *nextBlockIndex;
3214 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3216 return S_OK;
3219 /******************************************************************************
3220 * Storage32Impl_GetNextExtendedBlock
3222 * Given an extended block this method will return the next extended block.
3224 * NOTES:
3225 * The last ULONG of an extended block is the block index of the next
3226 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3227 * depot.
3229 * Return values:
3230 * - The index of the next extended block
3231 * - BLOCK_UNUSED: there is no next extended block.
3232 * - Any other return values denotes failure.
3234 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3236 ULONG nextBlockIndex = BLOCK_SPECIAL;
3237 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3239 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3240 &nextBlockIndex);
3242 return nextBlockIndex;
3245 /******************************************************************************
3246 * Storage32Impl_SetNextBlockInChain
3248 * This method will write the index of the specified block's next block
3249 * in the big block depot.
3251 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3252 * do the following
3254 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3255 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3256 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3259 static void StorageImpl_SetNextBlockInChain(
3260 StorageImpl* This,
3261 ULONG blockIndex,
3262 ULONG nextBlock)
3264 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3265 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3266 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3267 ULONG depotBlockIndexPos;
3269 assert(depotBlockCount < This->bigBlockDepotCount);
3270 assert(blockIndex != nextBlock);
3272 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3274 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3276 else
3279 * We have to look in the extended depot.
3281 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3284 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3285 nextBlock);
3287 * Update the cached block depot, if necessary.
3289 if (depotBlockCount == This->indexBlockDepotCached)
3291 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3295 /******************************************************************************
3296 * Storage32Impl_LoadFileHeader
3298 * This method will read in the file header
3300 static HRESULT StorageImpl_LoadFileHeader(
3301 StorageImpl* This)
3303 HRESULT hr;
3304 BYTE headerBigBlock[HEADER_SIZE];
3305 int index;
3306 ULARGE_INTEGER offset;
3307 DWORD bytes_read;
3309 TRACE("\n");
3311 * Get a pointer to the big block of data containing the header.
3313 offset.u.HighPart = 0;
3314 offset.u.LowPart = 0;
3315 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3316 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3317 hr = STG_E_FILENOTFOUND;
3320 * Extract the information from the header.
3322 if (SUCCEEDED(hr))
3325 * Check for the "magic number" signature and return an error if it is not
3326 * found.
3328 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3330 return STG_E_OLDFORMAT;
3333 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3335 return STG_E_INVALIDHEADER;
3338 StorageUtl_ReadWord(
3339 headerBigBlock,
3340 OFFSET_BIGBLOCKSIZEBITS,
3341 &This->bigBlockSizeBits);
3343 StorageUtl_ReadWord(
3344 headerBigBlock,
3345 OFFSET_SMALLBLOCKSIZEBITS,
3346 &This->smallBlockSizeBits);
3348 StorageUtl_ReadDWord(
3349 headerBigBlock,
3350 OFFSET_BBDEPOTCOUNT,
3351 &This->bigBlockDepotCount);
3353 StorageUtl_ReadDWord(
3354 headerBigBlock,
3355 OFFSET_ROOTSTARTBLOCK,
3356 &This->rootStartBlock);
3358 StorageUtl_ReadDWord(
3359 headerBigBlock,
3360 OFFSET_SMALLBLOCKLIMIT,
3361 &This->smallBlockLimit);
3363 StorageUtl_ReadDWord(
3364 headerBigBlock,
3365 OFFSET_SBDEPOTSTART,
3366 &This->smallBlockDepotStart);
3368 StorageUtl_ReadDWord(
3369 headerBigBlock,
3370 OFFSET_EXTBBDEPOTSTART,
3371 &This->extBigBlockDepotStart);
3373 StorageUtl_ReadDWord(
3374 headerBigBlock,
3375 OFFSET_EXTBBDEPOTCOUNT,
3376 &This->extBigBlockDepotCount);
3378 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3380 StorageUtl_ReadDWord(
3381 headerBigBlock,
3382 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3383 &(This->bigBlockDepotStart[index]));
3387 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3389 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3390 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3393 * Right now, the code is making some assumptions about the size of the
3394 * blocks, just make sure they are what we're expecting.
3396 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3397 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3398 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3400 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3401 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3402 hr = STG_E_INVALIDHEADER;
3404 else
3405 hr = S_OK;
3408 return hr;
3411 /******************************************************************************
3412 * Storage32Impl_SaveFileHeader
3414 * This method will save to the file the header
3416 static void StorageImpl_SaveFileHeader(
3417 StorageImpl* This)
3419 BYTE headerBigBlock[HEADER_SIZE];
3420 int index;
3421 HRESULT hr;
3422 ULARGE_INTEGER offset;
3423 DWORD bytes_read, bytes_written;
3426 * Get a pointer to the big block of data containing the header.
3428 offset.u.HighPart = 0;
3429 offset.u.LowPart = 0;
3430 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3431 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3432 hr = STG_E_FILENOTFOUND;
3435 * If the block read failed, the file is probably new.
3437 if (FAILED(hr))
3440 * Initialize for all unknown fields.
3442 memset(headerBigBlock, 0, HEADER_SIZE);
3445 * Initialize the magic number.
3447 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3450 * And a bunch of things we don't know what they mean
3452 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3453 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3454 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3458 * Write the information to the header.
3460 StorageUtl_WriteWord(
3461 headerBigBlock,
3462 OFFSET_BIGBLOCKSIZEBITS,
3463 This->bigBlockSizeBits);
3465 StorageUtl_WriteWord(
3466 headerBigBlock,
3467 OFFSET_SMALLBLOCKSIZEBITS,
3468 This->smallBlockSizeBits);
3470 StorageUtl_WriteDWord(
3471 headerBigBlock,
3472 OFFSET_BBDEPOTCOUNT,
3473 This->bigBlockDepotCount);
3475 StorageUtl_WriteDWord(
3476 headerBigBlock,
3477 OFFSET_ROOTSTARTBLOCK,
3478 This->rootStartBlock);
3480 StorageUtl_WriteDWord(
3481 headerBigBlock,
3482 OFFSET_SMALLBLOCKLIMIT,
3483 This->smallBlockLimit);
3485 StorageUtl_WriteDWord(
3486 headerBigBlock,
3487 OFFSET_SBDEPOTSTART,
3488 This->smallBlockDepotStart);
3490 StorageUtl_WriteDWord(
3491 headerBigBlock,
3492 OFFSET_SBDEPOTCOUNT,
3493 This->smallBlockDepotChain ?
3494 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3496 StorageUtl_WriteDWord(
3497 headerBigBlock,
3498 OFFSET_EXTBBDEPOTSTART,
3499 This->extBigBlockDepotStart);
3501 StorageUtl_WriteDWord(
3502 headerBigBlock,
3503 OFFSET_EXTBBDEPOTCOUNT,
3504 This->extBigBlockDepotCount);
3506 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3508 StorageUtl_WriteDWord(
3509 headerBigBlock,
3510 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3511 (This->bigBlockDepotStart[index]));
3515 * Write the big block back to the file.
3517 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3520 /******************************************************************************
3521 * StorageImpl_ReadRawDirEntry
3523 * This method will read the raw data from a directory entry in the file.
3525 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3527 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3529 ULARGE_INTEGER offset;
3530 HRESULT hr;
3531 ULONG bytesRead;
3533 offset.u.HighPart = 0;
3534 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3536 hr = BlockChainStream_ReadAt(
3537 This->rootBlockChain,
3538 offset,
3539 RAW_DIRENTRY_SIZE,
3540 buffer,
3541 &bytesRead);
3543 return hr;
3546 /******************************************************************************
3547 * StorageImpl_WriteRawDirEntry
3549 * This method will write the raw data from a directory entry in the file.
3551 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3553 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3555 ULARGE_INTEGER offset;
3556 HRESULT hr;
3557 ULONG bytesRead;
3559 offset.u.HighPart = 0;
3560 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3562 hr = BlockChainStream_WriteAt(
3563 This->rootBlockChain,
3564 offset,
3565 RAW_DIRENTRY_SIZE,
3566 buffer,
3567 &bytesRead);
3569 return hr;
3572 /******************************************************************************
3573 * UpdateRawDirEntry
3575 * Update raw directory entry data from the fields in newData.
3577 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3579 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3581 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3583 memcpy(
3584 buffer + OFFSET_PS_NAME,
3585 newData->name,
3586 DIRENTRY_NAME_BUFFER_LEN );
3588 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3590 StorageUtl_WriteWord(
3591 buffer,
3592 OFFSET_PS_NAMELENGTH,
3593 newData->sizeOfNameString);
3595 StorageUtl_WriteDWord(
3596 buffer,
3597 OFFSET_PS_LEFTCHILD,
3598 newData->leftChild);
3600 StorageUtl_WriteDWord(
3601 buffer,
3602 OFFSET_PS_RIGHTCHILD,
3603 newData->rightChild);
3605 StorageUtl_WriteDWord(
3606 buffer,
3607 OFFSET_PS_DIRROOT,
3608 newData->dirRootEntry);
3610 StorageUtl_WriteGUID(
3611 buffer,
3612 OFFSET_PS_GUID,
3613 &newData->clsid);
3615 StorageUtl_WriteDWord(
3616 buffer,
3617 OFFSET_PS_CTIMELOW,
3618 newData->ctime.dwLowDateTime);
3620 StorageUtl_WriteDWord(
3621 buffer,
3622 OFFSET_PS_CTIMEHIGH,
3623 newData->ctime.dwHighDateTime);
3625 StorageUtl_WriteDWord(
3626 buffer,
3627 OFFSET_PS_MTIMELOW,
3628 newData->mtime.dwLowDateTime);
3630 StorageUtl_WriteDWord(
3631 buffer,
3632 OFFSET_PS_MTIMEHIGH,
3633 newData->ctime.dwHighDateTime);
3635 StorageUtl_WriteDWord(
3636 buffer,
3637 OFFSET_PS_STARTBLOCK,
3638 newData->startingBlock);
3640 StorageUtl_WriteDWord(
3641 buffer,
3642 OFFSET_PS_SIZE,
3643 newData->size.u.LowPart);
3646 /******************************************************************************
3647 * Storage32Impl_ReadDirEntry
3649 * This method will read the specified directory entry.
3651 HRESULT StorageImpl_ReadDirEntry(
3652 StorageImpl* This,
3653 DirRef index,
3654 DirEntry* buffer)
3656 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3657 HRESULT readRes;
3659 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3661 if (SUCCEEDED(readRes))
3663 memset(buffer->name, 0, sizeof(buffer->name));
3664 memcpy(
3665 buffer->name,
3666 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3667 DIRENTRY_NAME_BUFFER_LEN );
3668 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3670 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3672 StorageUtl_ReadWord(
3673 currentEntry,
3674 OFFSET_PS_NAMELENGTH,
3675 &buffer->sizeOfNameString);
3677 StorageUtl_ReadDWord(
3678 currentEntry,
3679 OFFSET_PS_LEFTCHILD,
3680 &buffer->leftChild);
3682 StorageUtl_ReadDWord(
3683 currentEntry,
3684 OFFSET_PS_RIGHTCHILD,
3685 &buffer->rightChild);
3687 StorageUtl_ReadDWord(
3688 currentEntry,
3689 OFFSET_PS_DIRROOT,
3690 &buffer->dirRootEntry);
3692 StorageUtl_ReadGUID(
3693 currentEntry,
3694 OFFSET_PS_GUID,
3695 &buffer->clsid);
3697 StorageUtl_ReadDWord(
3698 currentEntry,
3699 OFFSET_PS_CTIMELOW,
3700 &buffer->ctime.dwLowDateTime);
3702 StorageUtl_ReadDWord(
3703 currentEntry,
3704 OFFSET_PS_CTIMEHIGH,
3705 &buffer->ctime.dwHighDateTime);
3707 StorageUtl_ReadDWord(
3708 currentEntry,
3709 OFFSET_PS_MTIMELOW,
3710 &buffer->mtime.dwLowDateTime);
3712 StorageUtl_ReadDWord(
3713 currentEntry,
3714 OFFSET_PS_MTIMEHIGH,
3715 &buffer->mtime.dwHighDateTime);
3717 StorageUtl_ReadDWord(
3718 currentEntry,
3719 OFFSET_PS_STARTBLOCK,
3720 &buffer->startingBlock);
3722 StorageUtl_ReadDWord(
3723 currentEntry,
3724 OFFSET_PS_SIZE,
3725 &buffer->size.u.LowPart);
3727 buffer->size.u.HighPart = 0;
3730 return readRes;
3733 /*********************************************************************
3734 * Write the specified directory entry to the file
3736 HRESULT StorageImpl_WriteDirEntry(
3737 StorageImpl* This,
3738 DirRef index,
3739 const DirEntry* buffer)
3741 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3742 HRESULT writeRes;
3744 UpdateRawDirEntry(currentEntry, buffer);
3746 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3747 return writeRes;
3750 static BOOL StorageImpl_ReadBigBlock(
3751 StorageImpl* This,
3752 ULONG blockIndex,
3753 void* buffer)
3755 ULARGE_INTEGER ulOffset;
3756 DWORD read;
3758 ulOffset.u.HighPart = 0;
3759 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3761 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3762 return (read == This->bigBlockSize);
3765 static BOOL StorageImpl_ReadDWordFromBigBlock(
3766 StorageImpl* This,
3767 ULONG blockIndex,
3768 ULONG offset,
3769 DWORD* value)
3771 ULARGE_INTEGER ulOffset;
3772 DWORD read;
3773 DWORD tmp;
3775 ulOffset.u.HighPart = 0;
3776 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3777 ulOffset.u.LowPart += offset;
3779 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3780 *value = lendian32toh(tmp);
3781 return (read == sizeof(DWORD));
3784 static BOOL StorageImpl_WriteBigBlock(
3785 StorageImpl* This,
3786 ULONG blockIndex,
3787 const void* buffer)
3789 ULARGE_INTEGER ulOffset;
3790 DWORD wrote;
3792 ulOffset.u.HighPart = 0;
3793 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3795 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3796 return (wrote == This->bigBlockSize);
3799 static BOOL StorageImpl_WriteDWordToBigBlock(
3800 StorageImpl* This,
3801 ULONG blockIndex,
3802 ULONG offset,
3803 DWORD value)
3805 ULARGE_INTEGER ulOffset;
3806 DWORD wrote;
3808 ulOffset.u.HighPart = 0;
3809 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3810 ulOffset.u.LowPart += offset;
3812 value = htole32(value);
3813 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3814 return (wrote == sizeof(DWORD));
3817 /******************************************************************************
3818 * Storage32Impl_SmallBlocksToBigBlocks
3820 * This method will convert a small block chain to a big block chain.
3821 * The small block chain will be destroyed.
3823 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3824 StorageImpl* This,
3825 SmallBlockChainStream** ppsbChain)
3827 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3828 ULARGE_INTEGER size, offset;
3829 ULONG cbRead, cbWritten;
3830 ULARGE_INTEGER cbTotalRead;
3831 DirRef streamEntryRef;
3832 HRESULT resWrite = S_OK;
3833 HRESULT resRead;
3834 DirEntry streamEntry;
3835 BYTE *buffer;
3836 BlockChainStream *bbTempChain = NULL;
3837 BlockChainStream *bigBlockChain = NULL;
3840 * Create a temporary big block chain that doesn't have
3841 * an associated directory entry. This temporary chain will be
3842 * used to copy data from small blocks to big blocks.
3844 bbTempChain = BlockChainStream_Construct(This,
3845 &bbHeadOfChain,
3846 DIRENTRY_NULL);
3847 if(!bbTempChain) return NULL;
3849 * Grow the big block chain.
3851 size = SmallBlockChainStream_GetSize(*ppsbChain);
3852 BlockChainStream_SetSize(bbTempChain, size);
3855 * Copy the contents of the small block chain to the big block chain
3856 * by small block size increments.
3858 offset.u.LowPart = 0;
3859 offset.u.HighPart = 0;
3860 cbTotalRead.QuadPart = 0;
3862 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3865 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3866 offset,
3867 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3868 buffer,
3869 &cbRead);
3870 if (FAILED(resRead))
3871 break;
3873 if (cbRead > 0)
3875 cbTotalRead.QuadPart += cbRead;
3877 resWrite = BlockChainStream_WriteAt(bbTempChain,
3878 offset,
3879 cbRead,
3880 buffer,
3881 &cbWritten);
3883 if (FAILED(resWrite))
3884 break;
3886 offset.u.LowPart += cbRead;
3888 } while (cbTotalRead.QuadPart < size.QuadPart);
3889 HeapFree(GetProcessHeap(),0,buffer);
3891 size.u.HighPart = 0;
3892 size.u.LowPart = 0;
3894 if (FAILED(resRead) || FAILED(resWrite))
3896 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3897 BlockChainStream_SetSize(bbTempChain, size);
3898 BlockChainStream_Destroy(bbTempChain);
3899 return NULL;
3903 * Destroy the small block chain.
3905 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3906 SmallBlockChainStream_SetSize(*ppsbChain, size);
3907 SmallBlockChainStream_Destroy(*ppsbChain);
3908 *ppsbChain = 0;
3911 * Change the directory entry. This chain is now a big block chain
3912 * and it doesn't reside in the small blocks chain anymore.
3914 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3916 streamEntry.startingBlock = bbHeadOfChain;
3918 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3921 * Destroy the temporary entryless big block chain.
3922 * Create a new big block chain associated with this entry.
3924 BlockChainStream_Destroy(bbTempChain);
3925 bigBlockChain = BlockChainStream_Construct(This,
3926 NULL,
3927 streamEntryRef);
3929 return bigBlockChain;
3932 /******************************************************************************
3933 * Storage32Impl_BigBlocksToSmallBlocks
3935 * This method will convert a big block chain to a small block chain.
3936 * The big block chain will be destroyed on success.
3938 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3939 StorageImpl* This,
3940 BlockChainStream** ppbbChain)
3942 ULARGE_INTEGER size, offset, cbTotalRead;
3943 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3944 DirRef streamEntryRef;
3945 HRESULT resWrite = S_OK, resRead;
3946 DirEntry streamEntry;
3947 BYTE* buffer;
3948 SmallBlockChainStream* sbTempChain;
3950 TRACE("%p %p\n", This, ppbbChain);
3952 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3953 DIRENTRY_NULL);
3955 if(!sbTempChain)
3956 return NULL;
3958 size = BlockChainStream_GetSize(*ppbbChain);
3959 SmallBlockChainStream_SetSize(sbTempChain, size);
3961 offset.u.HighPart = 0;
3962 offset.u.LowPart = 0;
3963 cbTotalRead.QuadPart = 0;
3964 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3967 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3968 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3969 buffer, &cbRead);
3971 if(FAILED(resRead))
3972 break;
3974 if(cbRead > 0)
3976 cbTotalRead.QuadPart += cbRead;
3978 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3979 cbRead, buffer, &cbWritten);
3981 if(FAILED(resWrite))
3982 break;
3984 offset.u.LowPart += cbRead;
3986 }while(cbTotalRead.QuadPart < size.QuadPart);
3987 HeapFree(GetProcessHeap(), 0, buffer);
3989 size.u.HighPart = 0;
3990 size.u.LowPart = 0;
3992 if(FAILED(resRead) || FAILED(resWrite))
3994 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3995 SmallBlockChainStream_SetSize(sbTempChain, size);
3996 SmallBlockChainStream_Destroy(sbTempChain);
3997 return NULL;
4000 /* destroy the original big block chain */
4001 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4002 BlockChainStream_SetSize(*ppbbChain, size);
4003 BlockChainStream_Destroy(*ppbbChain);
4004 *ppbbChain = NULL;
4006 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4007 streamEntry.startingBlock = sbHeadOfChain;
4008 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4010 SmallBlockChainStream_Destroy(sbTempChain);
4011 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4014 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
4016 HRESULT hr;
4017 DirEntry parentData, snapshotData;
4019 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
4020 0, (IStorage**)snapshot);
4022 if (SUCCEEDED(hr))
4024 hr = StorageBaseImpl_ReadDirEntry(original,
4025 original->storageDirEntry, &parentData);
4027 if (SUCCEEDED(hr))
4028 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
4029 (*snapshot)->storageDirEntry, &snapshotData);
4031 if (SUCCEEDED(hr))
4033 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
4034 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
4035 snapshotData.stgType = parentData.stgType;
4036 snapshotData.clsid = parentData.clsid;
4037 snapshotData.ctime = parentData.ctime;
4038 snapshotData.mtime = parentData.mtime;
4039 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
4040 (*snapshot)->storageDirEntry, &snapshotData);
4043 if (SUCCEEDED(hr))
4044 hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
4045 (IStorage*)(*snapshot));
4047 if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
4050 return hr;
4053 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4054 IStorage* iface,
4055 DWORD grfCommitFlags) /* [in] */
4057 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4058 HRESULT hr;
4059 DirEntry data, tempStorageData, snapshotRootData;
4060 DirRef tempStorageEntry, oldDirRoot;
4061 StorageInternalImpl *tempStorage;
4063 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4065 /* Cannot commit a read-only transacted storage */
4066 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4067 return STG_E_ACCESSDENIED;
4069 /* To prevent data loss, we create the new structure in the file before we
4070 * delete the old one, so that in case of errors the old data is intact. We
4071 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4072 * needed in the rare situation where we have just enough free disk space to
4073 * overwrite the existing data. */
4075 /* Create an orphaned storage in the parent for the new directory structure. */
4076 memset(&data, 0, sizeof(data));
4077 data.name[0] = 'D';
4078 data.sizeOfNameString = 1;
4079 data.stgType = STGTY_STORAGE;
4080 data.leftChild = DIRENTRY_NULL;
4081 data.rightChild = DIRENTRY_NULL;
4082 data.dirRootEntry = DIRENTRY_NULL;
4083 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
4085 if (FAILED(hr)) return hr;
4087 tempStorage = StorageInternalImpl_Construct(This->transactedParent,
4088 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
4089 if (tempStorage)
4091 hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
4092 (IStorage*)tempStorage);
4094 list_init(&tempStorage->ParentListEntry);
4096 IStorage_Release((IStorage*) tempStorage);
4098 else
4099 hr = E_OUTOFMEMORY;
4101 if (FAILED(hr))
4103 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4104 return hr;
4107 /* Update the storage to use the new data in one step. */
4108 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4109 This->transactedParent->storageDirEntry, &data);
4111 if (SUCCEEDED(hr))
4113 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4114 tempStorageEntry, &tempStorageData);
4117 if (SUCCEEDED(hr))
4119 hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
4120 This->snapshot->storageDirEntry, &snapshotRootData);
4123 if (SUCCEEDED(hr))
4125 oldDirRoot = data.dirRootEntry;
4126 data.dirRootEntry = tempStorageData.dirRootEntry;
4127 data.clsid = snapshotRootData.clsid;
4128 data.ctime = snapshotRootData.ctime;
4129 data.mtime = snapshotRootData.mtime;
4131 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4132 This->transactedParent->storageDirEntry, &data);
4135 if (SUCCEEDED(hr))
4137 /* Destroy the old now-orphaned data. */
4138 DestroyReachableEntries(This->transactedParent, oldDirRoot);
4139 StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
4141 else
4143 DestroyReachableEntries(This->transactedParent, tempStorageEntry);
4146 return hr;
4149 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4150 IStorage* iface)
4152 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4153 StorageBaseImpl *newSnapshot;
4154 HRESULT hr;
4156 TRACE("(%p)\n", iface);
4158 /* Create a new copy of the parent data. */
4159 hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
4160 if (FAILED(hr)) return hr;
4162 /* Destroy the open objects. */
4163 StorageBaseImpl_DeleteAll(&This->base);
4165 /* Replace our current snapshot. */
4166 IStorage_Release((IStorage*)This->snapshot);
4167 This->snapshot = newSnapshot;
4169 return S_OK;
4172 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4174 if (!This->reverted)
4176 TRACE("Storage invalidated (stg=%p)\n", This);
4178 This->reverted = 1;
4180 StorageBaseImpl_DeleteAll(This);
4184 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4186 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4188 TransactedSnapshotImpl_Invalidate(iface);
4190 IStorage_Release((IStorage*)This->transactedParent);
4192 IStorage_Release((IStorage*)This->snapshot);
4194 HeapFree(GetProcessHeap(), 0, This);
4197 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4198 const DirEntry *newData, DirRef *index)
4200 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4202 return StorageBaseImpl_CreateDirEntry(This->snapshot,
4203 newData, index);
4206 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4207 DirRef index, const DirEntry *data)
4209 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4211 return StorageBaseImpl_WriteDirEntry(This->snapshot,
4212 index, data);
4215 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4216 DirRef index, DirEntry *data)
4218 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4220 return StorageBaseImpl_ReadDirEntry(This->snapshot,
4221 index, data);
4224 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4225 DirRef index)
4227 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4229 return StorageBaseImpl_DestroyDirEntry(This->snapshot,
4230 index);
4233 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4234 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4236 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4238 return StorageBaseImpl_StreamReadAt(This->snapshot,
4239 index, offset, size, buffer, bytesRead);
4242 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4243 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4245 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4247 return StorageBaseImpl_StreamWriteAt(This->snapshot,
4248 index, offset, size, buffer, bytesWritten);
4251 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4252 DirRef index, ULARGE_INTEGER newsize)
4254 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4256 return StorageBaseImpl_StreamSetSize(This->snapshot,
4257 index, newsize);
4260 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
4262 StorageBaseImpl_QueryInterface,
4263 StorageBaseImpl_AddRef,
4264 StorageBaseImpl_Release,
4265 StorageBaseImpl_CreateStream,
4266 StorageBaseImpl_OpenStream,
4267 StorageBaseImpl_CreateStorage,
4268 StorageBaseImpl_OpenStorage,
4269 StorageBaseImpl_CopyTo,
4270 StorageBaseImpl_MoveElementTo,
4271 TransactedSnapshotImpl_Commit,
4272 TransactedSnapshotImpl_Revert,
4273 StorageBaseImpl_EnumElements,
4274 StorageBaseImpl_DestroyElement,
4275 StorageBaseImpl_RenameElement,
4276 StorageBaseImpl_SetElementTimes,
4277 StorageBaseImpl_SetClass,
4278 StorageBaseImpl_SetStateBits,
4279 StorageBaseImpl_Stat
4282 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
4284 TransactedSnapshotImpl_Destroy,
4285 TransactedSnapshotImpl_Invalidate,
4286 TransactedSnapshotImpl_CreateDirEntry,
4287 TransactedSnapshotImpl_WriteDirEntry,
4288 TransactedSnapshotImpl_ReadDirEntry,
4289 TransactedSnapshotImpl_DestroyDirEntry,
4290 TransactedSnapshotImpl_StreamReadAt,
4291 TransactedSnapshotImpl_StreamWriteAt,
4292 TransactedSnapshotImpl_StreamSetSize
4295 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
4296 TransactedSnapshotImpl** result)
4298 HRESULT hr;
4300 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
4301 if (*result)
4303 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
4305 /* This is OK because the property set storage functions use the IStorage functions. */
4306 (*result)->base.pssVtbl = parentStorage->pssVtbl;
4308 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
4310 list_init(&(*result)->base.strmHead);
4312 list_init(&(*result)->base.storageHead);
4314 (*result)->base.ref = 1;
4316 (*result)->base.openFlags = parentStorage->openFlags;
4318 (*result)->base.filename = parentStorage->filename;
4320 /* Create a new temporary storage to act as the snapshot */
4321 hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
4323 if (SUCCEEDED(hr))
4325 (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
4327 /* parentStorage already has 1 reference, which we take over here. */
4328 (*result)->transactedParent = parentStorage;
4330 parentStorage->transactedChild = (StorageBaseImpl*)*result;
4333 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
4335 return hr;
4337 else
4338 return E_OUTOFMEMORY;
4341 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
4342 StorageBaseImpl** result)
4344 static int fixme=0;
4346 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
4348 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
4351 return TransactedSnapshotImpl_Construct(parentStorage,
4352 (TransactedSnapshotImpl**)result);
4355 static HRESULT Storage_Construct(
4356 HANDLE hFile,
4357 LPCOLESTR pwcsName,
4358 ILockBytes* pLkbyt,
4359 DWORD openFlags,
4360 BOOL fileBased,
4361 BOOL create,
4362 ULONG sector_size,
4363 StorageBaseImpl** result)
4365 StorageImpl *newStorage;
4366 StorageBaseImpl *newTransactedStorage;
4367 HRESULT hr;
4369 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
4370 if (FAILED(hr)) goto end;
4372 if (openFlags & STGM_TRANSACTED)
4374 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
4375 if (FAILED(hr))
4376 IStorage_Release((IStorage*)newStorage);
4377 else
4378 *result = newTransactedStorage;
4380 else
4381 *result = &newStorage->base;
4383 end:
4384 return hr;
4387 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
4389 StorageInternalImpl* This = (StorageInternalImpl*) base;
4391 if (!This->base.reverted)
4393 TRACE("Storage invalidated (stg=%p)\n", This);
4395 This->base.reverted = 1;
4397 This->parentStorage = NULL;
4399 StorageBaseImpl_DeleteAll(&This->base);
4401 list_remove(&This->ParentListEntry);
4405 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
4407 StorageInternalImpl* This = (StorageInternalImpl*) iface;
4409 StorageInternalImpl_Invalidate(&This->base);
4411 HeapFree(GetProcessHeap(), 0, This);
4414 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
4415 const DirEntry *newData, DirRef *index)
4417 StorageInternalImpl* This = (StorageInternalImpl*) base;
4419 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
4420 newData, index);
4423 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
4424 DirRef index, const DirEntry *data)
4426 StorageInternalImpl* This = (StorageInternalImpl*) base;
4428 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
4429 index, data);
4432 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
4433 DirRef index, DirEntry *data)
4435 StorageInternalImpl* This = (StorageInternalImpl*) base;
4437 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
4438 index, data);
4441 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
4442 DirRef index)
4444 StorageInternalImpl* This = (StorageInternalImpl*) base;
4446 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
4447 index);
4450 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
4451 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4453 StorageInternalImpl* This = (StorageInternalImpl*) base;
4455 return StorageBaseImpl_StreamReadAt(This->parentStorage,
4456 index, offset, size, buffer, bytesRead);
4459 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
4460 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4462 StorageInternalImpl* This = (StorageInternalImpl*) base;
4464 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
4465 index, offset, size, buffer, bytesWritten);
4468 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
4469 DirRef index, ULARGE_INTEGER newsize)
4471 StorageInternalImpl* This = (StorageInternalImpl*) base;
4473 return StorageBaseImpl_StreamSetSize(This->parentStorage,
4474 index, newsize);
4477 /******************************************************************************
4479 ** Storage32InternalImpl_Commit
4482 static HRESULT WINAPI StorageInternalImpl_Commit(
4483 IStorage* iface,
4484 DWORD grfCommitFlags) /* [in] */
4486 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
4487 return S_OK;
4490 /******************************************************************************
4492 ** Storage32InternalImpl_Revert
4495 static HRESULT WINAPI StorageInternalImpl_Revert(
4496 IStorage* iface)
4498 FIXME("(%p): stub\n", iface);
4499 return S_OK;
4502 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
4504 IStorage_Release((IStorage*)This->parentStorage);
4505 HeapFree(GetProcessHeap(), 0, This);
4508 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
4509 IEnumSTATSTG* iface,
4510 REFIID riid,
4511 void** ppvObject)
4513 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4515 if (ppvObject==0)
4516 return E_INVALIDARG;
4518 *ppvObject = 0;
4520 if (IsEqualGUID(&IID_IUnknown, riid) ||
4521 IsEqualGUID(&IID_IEnumSTATSTG, riid))
4523 *ppvObject = This;
4524 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
4525 return S_OK;
4528 return E_NOINTERFACE;
4531 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
4532 IEnumSTATSTG* iface)
4534 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4535 return InterlockedIncrement(&This->ref);
4538 static ULONG WINAPI IEnumSTATSTGImpl_Release(
4539 IEnumSTATSTG* iface)
4541 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4543 ULONG newRef;
4545 newRef = InterlockedDecrement(&This->ref);
4547 if (newRef==0)
4549 IEnumSTATSTGImpl_Destroy(This);
4552 return newRef;
4555 static HRESULT IEnumSTATSTGImpl_GetNextRef(
4556 IEnumSTATSTGImpl* This,
4557 DirRef *ref)
4559 DirRef result = DIRENTRY_NULL;
4560 DirRef searchNode;
4561 DirEntry entry;
4562 HRESULT hr;
4563 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
4565 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
4566 This->parentStorage->storageDirEntry, &entry);
4567 searchNode = entry.dirRootEntry;
4569 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
4571 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
4573 if (SUCCEEDED(hr))
4575 LONG diff = entryNameCmp( entry.name, This->name);
4577 if (diff <= 0)
4579 searchNode = entry.rightChild;
4581 else
4583 result = searchNode;
4584 memcpy(result_name, entry.name, sizeof(result_name));
4585 searchNode = entry.leftChild;
4590 if (SUCCEEDED(hr))
4592 *ref = result;
4593 if (result != DIRENTRY_NULL)
4594 memcpy(This->name, result_name, sizeof(result_name));
4597 return hr;
4600 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
4601 IEnumSTATSTG* iface,
4602 ULONG celt,
4603 STATSTG* rgelt,
4604 ULONG* pceltFetched)
4606 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4608 DirEntry currentEntry;
4609 STATSTG* currentReturnStruct = rgelt;
4610 ULONG objectFetched = 0;
4611 DirRef currentSearchNode;
4612 HRESULT hr=S_OK;
4614 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
4615 return E_INVALIDARG;
4617 if (This->parentStorage->reverted)
4618 return STG_E_REVERTED;
4621 * To avoid the special case, get another pointer to a ULONG value if
4622 * the caller didn't supply one.
4624 if (pceltFetched==0)
4625 pceltFetched = &objectFetched;
4628 * Start the iteration, we will iterate until we hit the end of the
4629 * linked list or until we hit the number of items to iterate through
4631 *pceltFetched = 0;
4633 while ( *pceltFetched < celt )
4635 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
4637 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4638 break;
4641 * Read the entry from the storage.
4643 StorageBaseImpl_ReadDirEntry(This->parentStorage,
4644 currentSearchNode,
4645 &currentEntry);
4648 * Copy the information to the return buffer.
4650 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
4651 currentReturnStruct,
4652 &currentEntry,
4653 STATFLAG_DEFAULT);
4656 * Step to the next item in the iteration
4658 (*pceltFetched)++;
4659 currentReturnStruct++;
4662 if (SUCCEEDED(hr) && *pceltFetched != celt)
4663 hr = S_FALSE;
4665 return hr;
4669 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
4670 IEnumSTATSTG* iface,
4671 ULONG celt)
4673 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4675 ULONG objectFetched = 0;
4676 DirRef currentSearchNode;
4677 HRESULT hr=S_OK;
4679 if (This->parentStorage->reverted)
4680 return STG_E_REVERTED;
4682 while ( (objectFetched < celt) )
4684 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
4686 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
4687 break;
4689 objectFetched++;
4692 if (SUCCEEDED(hr) && objectFetched != celt)
4693 return S_FALSE;
4695 return hr;
4698 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
4699 IEnumSTATSTG* iface)
4701 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4703 if (This->parentStorage->reverted)
4704 return STG_E_REVERTED;
4706 This->name[0] = 0;
4708 return S_OK;
4711 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
4712 IEnumSTATSTG* iface,
4713 IEnumSTATSTG** ppenum)
4715 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
4717 IEnumSTATSTGImpl* newClone;
4719 if (This->parentStorage->reverted)
4720 return STG_E_REVERTED;
4723 * Perform a sanity check on the parameters.
4725 if (ppenum==0)
4726 return E_INVALIDARG;
4728 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
4729 This->storageDirEntry);
4733 * The new clone enumeration must point to the same current node as
4734 * the ole one.
4736 memcpy(newClone->name, This->name, sizeof(newClone->name));
4738 *ppenum = (IEnumSTATSTG*)newClone;
4741 * Don't forget to nail down a reference to the clone before
4742 * returning it.
4744 IEnumSTATSTGImpl_AddRef(*ppenum);
4746 return S_OK;
4750 * Virtual function table for the IEnumSTATSTGImpl class.
4752 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4754 IEnumSTATSTGImpl_QueryInterface,
4755 IEnumSTATSTGImpl_AddRef,
4756 IEnumSTATSTGImpl_Release,
4757 IEnumSTATSTGImpl_Next,
4758 IEnumSTATSTGImpl_Skip,
4759 IEnumSTATSTGImpl_Reset,
4760 IEnumSTATSTGImpl_Clone
4763 /******************************************************************************
4764 ** IEnumSTATSTGImpl implementation
4767 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4768 StorageBaseImpl* parentStorage,
4769 DirRef storageDirEntry)
4771 IEnumSTATSTGImpl* newEnumeration;
4773 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4775 if (newEnumeration!=0)
4778 * Set-up the virtual function table and reference count.
4780 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4781 newEnumeration->ref = 0;
4784 * We want to nail-down the reference to the storage in case the
4785 * enumeration out-lives the storage in the client application.
4787 newEnumeration->parentStorage = parentStorage;
4788 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4790 newEnumeration->storageDirEntry = storageDirEntry;
4793 * Make sure the current node of the iterator is the first one.
4795 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4798 return newEnumeration;
4802 * Virtual function table for the Storage32InternalImpl class.
4804 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4806 StorageBaseImpl_QueryInterface,
4807 StorageBaseImpl_AddRef,
4808 StorageBaseImpl_Release,
4809 StorageBaseImpl_CreateStream,
4810 StorageBaseImpl_OpenStream,
4811 StorageBaseImpl_CreateStorage,
4812 StorageBaseImpl_OpenStorage,
4813 StorageBaseImpl_CopyTo,
4814 StorageBaseImpl_MoveElementTo,
4815 StorageInternalImpl_Commit,
4816 StorageInternalImpl_Revert,
4817 StorageBaseImpl_EnumElements,
4818 StorageBaseImpl_DestroyElement,
4819 StorageBaseImpl_RenameElement,
4820 StorageBaseImpl_SetElementTimes,
4821 StorageBaseImpl_SetClass,
4822 StorageBaseImpl_SetStateBits,
4823 StorageBaseImpl_Stat
4826 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4828 StorageInternalImpl_Destroy,
4829 StorageInternalImpl_Invalidate,
4830 StorageInternalImpl_CreateDirEntry,
4831 StorageInternalImpl_WriteDirEntry,
4832 StorageInternalImpl_ReadDirEntry,
4833 StorageInternalImpl_DestroyDirEntry,
4834 StorageInternalImpl_StreamReadAt,
4835 StorageInternalImpl_StreamWriteAt,
4836 StorageInternalImpl_StreamSetSize
4839 /******************************************************************************
4840 ** Storage32InternalImpl implementation
4843 static StorageInternalImpl* StorageInternalImpl_Construct(
4844 StorageBaseImpl* parentStorage,
4845 DWORD openFlags,
4846 DirRef storageDirEntry)
4848 StorageInternalImpl* newStorage;
4850 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4852 if (newStorage!=0)
4854 list_init(&newStorage->base.strmHead);
4856 list_init(&newStorage->base.storageHead);
4859 * Initialize the virtual function table.
4861 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4862 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
4863 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4864 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4866 newStorage->base.reverted = 0;
4868 newStorage->base.ref = 1;
4870 newStorage->parentStorage = parentStorage;
4873 * Keep a reference to the directory entry of this storage
4875 newStorage->base.storageDirEntry = storageDirEntry;
4877 newStorage->base.create = 0;
4879 return newStorage;
4882 return 0;
4885 /******************************************************************************
4886 ** StorageUtl implementation
4889 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4891 WORD tmp;
4893 memcpy(&tmp, buffer+offset, sizeof(WORD));
4894 *value = lendian16toh(tmp);
4897 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4899 value = htole16(value);
4900 memcpy(buffer+offset, &value, sizeof(WORD));
4903 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4905 DWORD tmp;
4907 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4908 *value = lendian32toh(tmp);
4911 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4913 value = htole32(value);
4914 memcpy(buffer+offset, &value, sizeof(DWORD));
4917 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4918 ULARGE_INTEGER* value)
4920 #ifdef WORDS_BIGENDIAN
4921 ULARGE_INTEGER tmp;
4923 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4924 value->u.LowPart = htole32(tmp.u.HighPart);
4925 value->u.HighPart = htole32(tmp.u.LowPart);
4926 #else
4927 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4928 #endif
4931 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4932 const ULARGE_INTEGER *value)
4934 #ifdef WORDS_BIGENDIAN
4935 ULARGE_INTEGER tmp;
4937 tmp.u.LowPart = htole32(value->u.HighPart);
4938 tmp.u.HighPart = htole32(value->u.LowPart);
4939 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4940 #else
4941 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4942 #endif
4945 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4947 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4948 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4949 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4951 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4954 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4956 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4957 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4958 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4960 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4963 void StorageUtl_CopyDirEntryToSTATSTG(
4964 StorageBaseImpl* storage,
4965 STATSTG* destination,
4966 const DirEntry* source,
4967 int statFlags)
4969 LPCWSTR entryName;
4971 if (source->stgType == STGTY_ROOT)
4973 /* replace the name of root entry (often "Root Entry") by the file name */
4974 entryName = storage->filename;
4976 else
4978 entryName = source->name;
4982 * The copy of the string occurs only when the flag is not set
4984 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4985 (entryName == NULL) ||
4986 (entryName[0] == 0) )
4988 destination->pwcsName = 0;
4990 else
4992 destination->pwcsName =
4993 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4995 strcpyW(destination->pwcsName, entryName);
4998 switch (source->stgType)
5000 case STGTY_STORAGE:
5001 case STGTY_ROOT:
5002 destination->type = STGTY_STORAGE;
5003 break;
5004 case STGTY_STREAM:
5005 destination->type = STGTY_STREAM;
5006 break;
5007 default:
5008 destination->type = STGTY_STREAM;
5009 break;
5012 destination->cbSize = source->size;
5014 currentReturnStruct->mtime = {0}; TODO
5015 currentReturnStruct->ctime = {0};
5016 currentReturnStruct->atime = {0};
5018 destination->grfMode = 0;
5019 destination->grfLocksSupported = 0;
5020 destination->clsid = source->clsid;
5021 destination->grfStateBits = 0;
5022 destination->reserved = 0;
5025 /******************************************************************************
5026 ** BlockChainStream implementation
5029 BlockChainStream* BlockChainStream_Construct(
5030 StorageImpl* parentStorage,
5031 ULONG* headOfStreamPlaceHolder,
5032 DirRef dirEntry)
5034 BlockChainStream* newStream;
5035 ULONG blockIndex;
5037 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5039 newStream->parentStorage = parentStorage;
5040 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5041 newStream->ownerDirEntry = dirEntry;
5042 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
5043 newStream->tailIndex = BLOCK_END_OF_CHAIN;
5044 newStream->numBlocks = 0;
5046 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
5048 while (blockIndex != BLOCK_END_OF_CHAIN)
5050 newStream->numBlocks++;
5051 newStream->tailIndex = blockIndex;
5053 if(FAILED(StorageImpl_GetNextBlockInChain(
5054 parentStorage,
5055 blockIndex,
5056 &blockIndex)))
5058 HeapFree(GetProcessHeap(), 0, newStream);
5059 return NULL;
5063 return newStream;
5066 void BlockChainStream_Destroy(BlockChainStream* This)
5068 HeapFree(GetProcessHeap(), 0, This);
5071 /******************************************************************************
5072 * BlockChainStream_GetHeadOfChain
5074 * Returns the head of this stream chain.
5075 * Some special chains don't have directory entries, their heads are kept in
5076 * This->headOfStreamPlaceHolder.
5079 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5081 DirEntry chainEntry;
5082 HRESULT hr;
5084 if (This->headOfStreamPlaceHolder != 0)
5085 return *(This->headOfStreamPlaceHolder);
5087 if (This->ownerDirEntry != DIRENTRY_NULL)
5089 hr = StorageImpl_ReadDirEntry(
5090 This->parentStorage,
5091 This->ownerDirEntry,
5092 &chainEntry);
5094 if (SUCCEEDED(hr))
5096 return chainEntry.startingBlock;
5100 return BLOCK_END_OF_CHAIN;
5103 /******************************************************************************
5104 * BlockChainStream_GetCount
5106 * Returns the number of blocks that comprises this chain.
5107 * This is not the size of the stream as the last block may not be full!
5110 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5112 ULONG blockIndex;
5113 ULONG count = 0;
5115 blockIndex = BlockChainStream_GetHeadOfChain(This);
5117 while (blockIndex != BLOCK_END_OF_CHAIN)
5119 count++;
5121 if(FAILED(StorageImpl_GetNextBlockInChain(
5122 This->parentStorage,
5123 blockIndex,
5124 &blockIndex)))
5125 return 0;
5128 return count;
5131 /******************************************************************************
5132 * BlockChainStream_ReadAt
5134 * Reads a specified number of bytes from this chain at the specified offset.
5135 * bytesRead may be NULL.
5136 * Failure will be returned if the specified number of bytes has not been read.
5138 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5139 ULARGE_INTEGER offset,
5140 ULONG size,
5141 void* buffer,
5142 ULONG* bytesRead)
5144 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5145 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5146 ULONG bytesToReadInBuffer;
5147 ULONG blockIndex;
5148 BYTE* bufferWalker;
5150 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5153 * Find the first block in the stream that contains part of the buffer.
5155 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5156 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5157 (blockNoInSequence < This->lastBlockNoInSequence) )
5159 blockIndex = BlockChainStream_GetHeadOfChain(This);
5160 This->lastBlockNoInSequence = blockNoInSequence;
5162 else
5164 ULONG temp = blockNoInSequence;
5166 blockIndex = This->lastBlockNoInSequenceIndex;
5167 blockNoInSequence -= This->lastBlockNoInSequence;
5168 This->lastBlockNoInSequence = temp;
5171 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5173 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5174 return STG_E_DOCFILECORRUPT;
5175 blockNoInSequence--;
5178 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
5179 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5181 This->lastBlockNoInSequenceIndex = blockIndex;
5184 * Start reading the buffer.
5186 *bytesRead = 0;
5187 bufferWalker = buffer;
5189 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5191 ULARGE_INTEGER ulOffset;
5192 DWORD bytesReadAt;
5194 * Calculate how many bytes we can copy from this big block.
5196 bytesToReadInBuffer =
5197 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5199 TRACE("block %i\n",blockIndex);
5200 ulOffset.u.HighPart = 0;
5201 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5202 offsetInBlock;
5204 StorageImpl_ReadAt(This->parentStorage,
5205 ulOffset,
5206 bufferWalker,
5207 bytesToReadInBuffer,
5208 &bytesReadAt);
5210 * Step to the next big block.
5212 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5213 return STG_E_DOCFILECORRUPT;
5215 bufferWalker += bytesReadAt;
5216 size -= bytesReadAt;
5217 *bytesRead += bytesReadAt;
5218 offsetInBlock = 0; /* There is no offset on the next block */
5220 if (bytesToReadInBuffer != bytesReadAt)
5221 break;
5224 return (size == 0) ? S_OK : STG_E_READFAULT;
5227 /******************************************************************************
5228 * BlockChainStream_WriteAt
5230 * Writes the specified number of bytes to this chain at the specified offset.
5231 * Will fail if not all specified number of bytes have been written.
5233 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5234 ULARGE_INTEGER offset,
5235 ULONG size,
5236 const void* buffer,
5237 ULONG* bytesWritten)
5239 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5240 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5241 ULONG bytesToWrite;
5242 ULONG blockIndex;
5243 const BYTE* bufferWalker;
5246 * Find the first block in the stream that contains part of the buffer.
5248 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
5249 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
5250 (blockNoInSequence < This->lastBlockNoInSequence) )
5252 blockIndex = BlockChainStream_GetHeadOfChain(This);
5253 This->lastBlockNoInSequence = blockNoInSequence;
5255 else
5257 ULONG temp = blockNoInSequence;
5259 blockIndex = This->lastBlockNoInSequenceIndex;
5260 blockNoInSequence -= This->lastBlockNoInSequence;
5261 This->lastBlockNoInSequence = temp;
5264 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5266 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5267 &blockIndex)))
5268 return STG_E_DOCFILECORRUPT;
5269 blockNoInSequence--;
5272 This->lastBlockNoInSequenceIndex = blockIndex;
5274 /* BlockChainStream_SetSize should have already been called to ensure we have
5275 * enough blocks in the chain to write into */
5276 if (blockIndex == BLOCK_END_OF_CHAIN)
5278 ERR("not enough blocks in chain to write data\n");
5279 return STG_E_DOCFILECORRUPT;
5282 *bytesWritten = 0;
5283 bufferWalker = buffer;
5285 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5287 ULARGE_INTEGER ulOffset;
5288 DWORD bytesWrittenAt;
5290 * Calculate how many bytes we can copy from this big block.
5292 bytesToWrite =
5293 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5295 TRACE("block %i\n",blockIndex);
5296 ulOffset.u.HighPart = 0;
5297 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5298 offsetInBlock;
5300 StorageImpl_WriteAt(This->parentStorage,
5301 ulOffset,
5302 bufferWalker,
5303 bytesToWrite,
5304 &bytesWrittenAt);
5307 * Step to the next big block.
5309 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5310 &blockIndex)))
5311 return STG_E_DOCFILECORRUPT;
5313 bufferWalker += bytesWrittenAt;
5314 size -= bytesWrittenAt;
5315 *bytesWritten += bytesWrittenAt;
5316 offsetInBlock = 0; /* There is no offset on the next block */
5318 if (bytesWrittenAt != bytesToWrite)
5319 break;
5322 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5325 /******************************************************************************
5326 * BlockChainStream_Shrink
5328 * Shrinks this chain in the big block depot.
5330 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5331 ULARGE_INTEGER newSize)
5333 ULONG blockIndex, extraBlock;
5334 ULONG numBlocks;
5335 ULONG count = 1;
5338 * Reset the last accessed block cache.
5340 This->lastBlockNoInSequence = 0xFFFFFFFF;
5341 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
5344 * Figure out how many blocks are needed to contain the new size
5346 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5348 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5349 numBlocks++;
5351 blockIndex = BlockChainStream_GetHeadOfChain(This);
5354 * Go to the new end of chain
5356 while (count < numBlocks)
5358 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5359 &blockIndex)))
5360 return FALSE;
5361 count++;
5364 /* Get the next block before marking the new end */
5365 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5366 &extraBlock)))
5367 return FALSE;
5369 /* Mark the new end of chain */
5370 StorageImpl_SetNextBlockInChain(
5371 This->parentStorage,
5372 blockIndex,
5373 BLOCK_END_OF_CHAIN);
5375 This->tailIndex = blockIndex;
5376 This->numBlocks = numBlocks;
5379 * Mark the extra blocks as free
5381 while (extraBlock != BLOCK_END_OF_CHAIN)
5383 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
5384 &blockIndex)))
5385 return FALSE;
5386 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
5387 extraBlock = blockIndex;
5390 return TRUE;
5393 /******************************************************************************
5394 * BlockChainStream_Enlarge
5396 * Grows this chain in the big block depot.
5398 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5399 ULARGE_INTEGER newSize)
5401 ULONG blockIndex, currentBlock;
5402 ULONG newNumBlocks;
5403 ULONG oldNumBlocks = 0;
5405 blockIndex = BlockChainStream_GetHeadOfChain(This);
5408 * Empty chain. Create the head.
5410 if (blockIndex == BLOCK_END_OF_CHAIN)
5412 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5413 StorageImpl_SetNextBlockInChain(This->parentStorage,
5414 blockIndex,
5415 BLOCK_END_OF_CHAIN);
5417 if (This->headOfStreamPlaceHolder != 0)
5419 *(This->headOfStreamPlaceHolder) = blockIndex;
5421 else
5423 DirEntry chainEntry;
5424 assert(This->ownerDirEntry != DIRENTRY_NULL);
5426 StorageImpl_ReadDirEntry(
5427 This->parentStorage,
5428 This->ownerDirEntry,
5429 &chainEntry);
5431 chainEntry.startingBlock = blockIndex;
5433 StorageImpl_WriteDirEntry(
5434 This->parentStorage,
5435 This->ownerDirEntry,
5436 &chainEntry);
5439 This->tailIndex = blockIndex;
5440 This->numBlocks = 1;
5444 * Figure out how many blocks are needed to contain this stream
5446 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5448 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5449 newNumBlocks++;
5452 * Go to the current end of chain
5454 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5456 currentBlock = blockIndex;
5458 while (blockIndex != BLOCK_END_OF_CHAIN)
5460 This->numBlocks++;
5461 currentBlock = blockIndex;
5463 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5464 &blockIndex)))
5465 return FALSE;
5468 This->tailIndex = currentBlock;
5471 currentBlock = This->tailIndex;
5472 oldNumBlocks = This->numBlocks;
5475 * Add new blocks to the chain
5477 if (oldNumBlocks < newNumBlocks)
5479 while (oldNumBlocks < newNumBlocks)
5481 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5483 StorageImpl_SetNextBlockInChain(
5484 This->parentStorage,
5485 currentBlock,
5486 blockIndex);
5488 StorageImpl_SetNextBlockInChain(
5489 This->parentStorage,
5490 blockIndex,
5491 BLOCK_END_OF_CHAIN);
5493 currentBlock = blockIndex;
5494 oldNumBlocks++;
5497 This->tailIndex = blockIndex;
5498 This->numBlocks = newNumBlocks;
5501 return TRUE;
5504 /******************************************************************************
5505 * BlockChainStream_SetSize
5507 * Sets the size of this stream. The big block depot will be updated.
5508 * The file will grow if we grow the chain.
5510 * TODO: Free the actual blocks in the file when we shrink the chain.
5511 * Currently, the blocks are still in the file. So the file size
5512 * doesn't shrink even if we shrink streams.
5514 BOOL BlockChainStream_SetSize(
5515 BlockChainStream* This,
5516 ULARGE_INTEGER newSize)
5518 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5520 if (newSize.u.LowPart == size.u.LowPart)
5521 return TRUE;
5523 if (newSize.u.LowPart < size.u.LowPart)
5525 BlockChainStream_Shrink(This, newSize);
5527 else
5529 BlockChainStream_Enlarge(This, newSize);
5532 return TRUE;
5535 /******************************************************************************
5536 * BlockChainStream_GetSize
5538 * Returns the size of this chain.
5539 * Will return the block count if this chain doesn't have a directory entry.
5541 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5543 DirEntry chainEntry;
5545 if(This->headOfStreamPlaceHolder == NULL)
5548 * This chain has a directory entry so use the size value from there.
5550 StorageImpl_ReadDirEntry(
5551 This->parentStorage,
5552 This->ownerDirEntry,
5553 &chainEntry);
5555 return chainEntry.size;
5557 else
5560 * this chain is a chain that does not have a directory entry, figure out the
5561 * size by making the product number of used blocks times the
5562 * size of them
5564 ULARGE_INTEGER result;
5565 result.u.HighPart = 0;
5567 result.u.LowPart =
5568 BlockChainStream_GetCount(This) *
5569 This->parentStorage->bigBlockSize;
5571 return result;
5575 /******************************************************************************
5576 ** SmallBlockChainStream implementation
5579 SmallBlockChainStream* SmallBlockChainStream_Construct(
5580 StorageImpl* parentStorage,
5581 ULONG* headOfStreamPlaceHolder,
5582 DirRef dirEntry)
5584 SmallBlockChainStream* newStream;
5586 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5588 newStream->parentStorage = parentStorage;
5589 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5590 newStream->ownerDirEntry = dirEntry;
5592 return newStream;
5595 void SmallBlockChainStream_Destroy(
5596 SmallBlockChainStream* This)
5598 HeapFree(GetProcessHeap(), 0, This);
5601 /******************************************************************************
5602 * SmallBlockChainStream_GetHeadOfChain
5604 * Returns the head of this chain of small blocks.
5606 static ULONG SmallBlockChainStream_GetHeadOfChain(
5607 SmallBlockChainStream* This)
5609 DirEntry chainEntry;
5610 HRESULT hr;
5612 if (This->headOfStreamPlaceHolder != NULL)
5613 return *(This->headOfStreamPlaceHolder);
5615 if (This->ownerDirEntry)
5617 hr = StorageImpl_ReadDirEntry(
5618 This->parentStorage,
5619 This->ownerDirEntry,
5620 &chainEntry);
5622 if (SUCCEEDED(hr))
5624 return chainEntry.startingBlock;
5629 return BLOCK_END_OF_CHAIN;
5632 /******************************************************************************
5633 * SmallBlockChainStream_GetNextBlockInChain
5635 * Returns the index of the next small block in this chain.
5637 * Return Values:
5638 * - BLOCK_END_OF_CHAIN: end of this chain
5639 * - BLOCK_UNUSED: small block 'blockIndex' is free
5641 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5642 SmallBlockChainStream* This,
5643 ULONG blockIndex,
5644 ULONG* nextBlockInChain)
5646 ULARGE_INTEGER offsetOfBlockInDepot;
5647 DWORD buffer;
5648 ULONG bytesRead;
5649 HRESULT res;
5651 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5653 offsetOfBlockInDepot.u.HighPart = 0;
5654 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5657 * Read those bytes in the buffer from the small block file.
5659 res = BlockChainStream_ReadAt(
5660 This->parentStorage->smallBlockDepotChain,
5661 offsetOfBlockInDepot,
5662 sizeof(DWORD),
5663 &buffer,
5664 &bytesRead);
5666 if (SUCCEEDED(res))
5668 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5669 return S_OK;
5672 return res;
5675 /******************************************************************************
5676 * SmallBlockChainStream_SetNextBlockInChain
5678 * Writes the index of the next block of the specified block in the small
5679 * block depot.
5680 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5681 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5683 static void SmallBlockChainStream_SetNextBlockInChain(
5684 SmallBlockChainStream* This,
5685 ULONG blockIndex,
5686 ULONG nextBlock)
5688 ULARGE_INTEGER offsetOfBlockInDepot;
5689 DWORD buffer;
5690 ULONG bytesWritten;
5692 offsetOfBlockInDepot.u.HighPart = 0;
5693 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5695 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5698 * Read those bytes in the buffer from the small block file.
5700 BlockChainStream_WriteAt(
5701 This->parentStorage->smallBlockDepotChain,
5702 offsetOfBlockInDepot,
5703 sizeof(DWORD),
5704 &buffer,
5705 &bytesWritten);
5708 /******************************************************************************
5709 * SmallBlockChainStream_FreeBlock
5711 * Flag small block 'blockIndex' as free in the small block depot.
5713 static void SmallBlockChainStream_FreeBlock(
5714 SmallBlockChainStream* This,
5715 ULONG blockIndex)
5717 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5720 /******************************************************************************
5721 * SmallBlockChainStream_GetNextFreeBlock
5723 * Returns the index of a free small block. The small block depot will be
5724 * enlarged if necessary. The small block chain will also be enlarged if
5725 * necessary.
5727 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5728 SmallBlockChainStream* This)
5730 ULARGE_INTEGER offsetOfBlockInDepot;
5731 DWORD buffer;
5732 ULONG bytesRead;
5733 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
5734 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5735 HRESULT res = S_OK;
5736 ULONG smallBlocksPerBigBlock;
5738 offsetOfBlockInDepot.u.HighPart = 0;
5741 * Scan the small block depot for a free block
5743 while (nextBlockIndex != BLOCK_UNUSED)
5745 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5747 res = BlockChainStream_ReadAt(
5748 This->parentStorage->smallBlockDepotChain,
5749 offsetOfBlockInDepot,
5750 sizeof(DWORD),
5751 &buffer,
5752 &bytesRead);
5755 * If we run out of space for the small block depot, enlarge it
5757 if (SUCCEEDED(res))
5759 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5761 if (nextBlockIndex != BLOCK_UNUSED)
5762 blockIndex++;
5764 else
5766 ULONG count =
5767 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5769 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5770 ULONG nextBlock, newsbdIndex;
5771 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
5773 nextBlock = sbdIndex;
5774 while (nextBlock != BLOCK_END_OF_CHAIN)
5776 sbdIndex = nextBlock;
5777 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5780 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5781 if (sbdIndex != BLOCK_END_OF_CHAIN)
5782 StorageImpl_SetNextBlockInChain(
5783 This->parentStorage,
5784 sbdIndex,
5785 newsbdIndex);
5787 StorageImpl_SetNextBlockInChain(
5788 This->parentStorage,
5789 newsbdIndex,
5790 BLOCK_END_OF_CHAIN);
5793 * Initialize all the small blocks to free
5795 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5796 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5798 if (count == 0)
5801 * We have just created the small block depot.
5803 DirEntry rootEntry;
5804 ULONG sbStartIndex;
5807 * Save it in the header
5809 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5810 StorageImpl_SaveFileHeader(This->parentStorage);
5813 * And allocate the first big block that will contain small blocks
5815 sbStartIndex =
5816 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5818 StorageImpl_SetNextBlockInChain(
5819 This->parentStorage,
5820 sbStartIndex,
5821 BLOCK_END_OF_CHAIN);
5823 StorageImpl_ReadDirEntry(
5824 This->parentStorage,
5825 This->parentStorage->base.storageDirEntry,
5826 &rootEntry);
5828 rootEntry.startingBlock = sbStartIndex;
5829 rootEntry.size.u.HighPart = 0;
5830 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5832 StorageImpl_WriteDirEntry(
5833 This->parentStorage,
5834 This->parentStorage->base.storageDirEntry,
5835 &rootEntry);
5837 else
5838 StorageImpl_SaveFileHeader(This->parentStorage);
5842 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
5844 smallBlocksPerBigBlock =
5845 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5848 * Verify if we have to allocate big blocks to contain small blocks
5850 if (blockIndex % smallBlocksPerBigBlock == 0)
5852 DirEntry rootEntry;
5853 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5855 StorageImpl_ReadDirEntry(
5856 This->parentStorage,
5857 This->parentStorage->base.storageDirEntry,
5858 &rootEntry);
5860 if (rootEntry.size.u.LowPart <
5861 (blocksRequired * This->parentStorage->bigBlockSize))
5863 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5865 BlockChainStream_SetSize(
5866 This->parentStorage->smallBlockRootChain,
5867 rootEntry.size);
5869 StorageImpl_WriteDirEntry(
5870 This->parentStorage,
5871 This->parentStorage->base.storageDirEntry,
5872 &rootEntry);
5876 return blockIndex;
5879 /******************************************************************************
5880 * SmallBlockChainStream_ReadAt
5882 * Reads a specified number of bytes from this chain at the specified offset.
5883 * bytesRead may be NULL.
5884 * Failure will be returned if the specified number of bytes has not been read.
5886 HRESULT SmallBlockChainStream_ReadAt(
5887 SmallBlockChainStream* This,
5888 ULARGE_INTEGER offset,
5889 ULONG size,
5890 void* buffer,
5891 ULONG* bytesRead)
5893 HRESULT rc = S_OK;
5894 ULARGE_INTEGER offsetInBigBlockFile;
5895 ULONG blockNoInSequence =
5896 offset.u.LowPart / This->parentStorage->smallBlockSize;
5898 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5899 ULONG bytesToReadInBuffer;
5900 ULONG blockIndex;
5901 ULONG bytesReadFromBigBlockFile;
5902 BYTE* bufferWalker;
5905 * This should never happen on a small block file.
5907 assert(offset.u.HighPart==0);
5910 * Find the first block in the stream that contains part of the buffer.
5912 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5914 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5916 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5917 if(FAILED(rc))
5918 return rc;
5919 blockNoInSequence--;
5923 * Start reading the buffer.
5925 *bytesRead = 0;
5926 bufferWalker = buffer;
5928 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5931 * Calculate how many bytes we can copy from this small block.
5933 bytesToReadInBuffer =
5934 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5937 * Calculate the offset of the small block in the small block file.
5939 offsetInBigBlockFile.u.HighPart = 0;
5940 offsetInBigBlockFile.u.LowPart =
5941 blockIndex * This->parentStorage->smallBlockSize;
5943 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5946 * Read those bytes in the buffer from the small block file.
5947 * The small block has already been identified so it shouldn't fail
5948 * unless the file is corrupt.
5950 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5951 offsetInBigBlockFile,
5952 bytesToReadInBuffer,
5953 bufferWalker,
5954 &bytesReadFromBigBlockFile);
5956 if (FAILED(rc))
5957 return rc;
5960 * Step to the next big block.
5962 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5963 if(FAILED(rc))
5964 return STG_E_DOCFILECORRUPT;
5966 bufferWalker += bytesReadFromBigBlockFile;
5967 size -= bytesReadFromBigBlockFile;
5968 *bytesRead += bytesReadFromBigBlockFile;
5969 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5972 return (size == 0) ? S_OK : STG_E_READFAULT;
5975 /******************************************************************************
5976 * SmallBlockChainStream_WriteAt
5978 * Writes the specified number of bytes to this chain at the specified offset.
5979 * Will fail if not all specified number of bytes have been written.
5981 HRESULT SmallBlockChainStream_WriteAt(
5982 SmallBlockChainStream* This,
5983 ULARGE_INTEGER offset,
5984 ULONG size,
5985 const void* buffer,
5986 ULONG* bytesWritten)
5988 ULARGE_INTEGER offsetInBigBlockFile;
5989 ULONG blockNoInSequence =
5990 offset.u.LowPart / This->parentStorage->smallBlockSize;
5992 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5993 ULONG bytesToWriteInBuffer;
5994 ULONG blockIndex;
5995 ULONG bytesWrittenToBigBlockFile;
5996 const BYTE* bufferWalker;
5997 HRESULT res;
6000 * This should never happen on a small block file.
6002 assert(offset.u.HighPart==0);
6005 * Find the first block in the stream that contains part of the buffer.
6007 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6009 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6011 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6012 return STG_E_DOCFILECORRUPT;
6013 blockNoInSequence--;
6017 * Start writing the buffer.
6019 *bytesWritten = 0;
6020 bufferWalker = buffer;
6021 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6024 * Calculate how many bytes we can copy to this small block.
6026 bytesToWriteInBuffer =
6027 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6030 * Calculate the offset of the small block in the small block file.
6032 offsetInBigBlockFile.u.HighPart = 0;
6033 offsetInBigBlockFile.u.LowPart =
6034 blockIndex * This->parentStorage->smallBlockSize;
6036 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6039 * Write those bytes in the buffer to the small block file.
6041 res = BlockChainStream_WriteAt(
6042 This->parentStorage->smallBlockRootChain,
6043 offsetInBigBlockFile,
6044 bytesToWriteInBuffer,
6045 bufferWalker,
6046 &bytesWrittenToBigBlockFile);
6047 if (FAILED(res))
6048 return res;
6051 * Step to the next big block.
6053 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6054 &blockIndex)))
6055 return FALSE;
6056 bufferWalker += bytesWrittenToBigBlockFile;
6057 size -= bytesWrittenToBigBlockFile;
6058 *bytesWritten += bytesWrittenToBigBlockFile;
6059 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6062 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6065 /******************************************************************************
6066 * SmallBlockChainStream_Shrink
6068 * Shrinks this chain in the small block depot.
6070 static BOOL SmallBlockChainStream_Shrink(
6071 SmallBlockChainStream* This,
6072 ULARGE_INTEGER newSize)
6074 ULONG blockIndex, extraBlock;
6075 ULONG numBlocks;
6076 ULONG count = 0;
6078 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6080 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6081 numBlocks++;
6083 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6086 * Go to the new end of chain
6088 while (count < numBlocks)
6090 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6091 &blockIndex)))
6092 return FALSE;
6093 count++;
6097 * If the count is 0, we have a special case, the head of the chain was
6098 * just freed.
6100 if (count == 0)
6102 DirEntry chainEntry;
6104 StorageImpl_ReadDirEntry(This->parentStorage,
6105 This->ownerDirEntry,
6106 &chainEntry);
6108 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6110 StorageImpl_WriteDirEntry(This->parentStorage,
6111 This->ownerDirEntry,
6112 &chainEntry);
6115 * We start freeing the chain at the head block.
6117 extraBlock = blockIndex;
6119 else
6121 /* Get the next block before marking the new end */
6122 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6123 &extraBlock)))
6124 return FALSE;
6126 /* Mark the new end of chain */
6127 SmallBlockChainStream_SetNextBlockInChain(
6128 This,
6129 blockIndex,
6130 BLOCK_END_OF_CHAIN);
6134 * Mark the extra blocks as free
6136 while (extraBlock != BLOCK_END_OF_CHAIN)
6138 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6139 &blockIndex)))
6140 return FALSE;
6141 SmallBlockChainStream_FreeBlock(This, extraBlock);
6142 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
6143 extraBlock = blockIndex;
6146 return TRUE;
6149 /******************************************************************************
6150 * SmallBlockChainStream_Enlarge
6152 * Grows this chain in the small block depot.
6154 static BOOL SmallBlockChainStream_Enlarge(
6155 SmallBlockChainStream* This,
6156 ULARGE_INTEGER newSize)
6158 ULONG blockIndex, currentBlock;
6159 ULONG newNumBlocks;
6160 ULONG oldNumBlocks = 0;
6162 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6165 * Empty chain. Create the head.
6167 if (blockIndex == BLOCK_END_OF_CHAIN)
6169 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6170 SmallBlockChainStream_SetNextBlockInChain(
6171 This,
6172 blockIndex,
6173 BLOCK_END_OF_CHAIN);
6175 if (This->headOfStreamPlaceHolder != NULL)
6177 *(This->headOfStreamPlaceHolder) = blockIndex;
6179 else
6181 DirEntry chainEntry;
6183 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6184 &chainEntry);
6186 chainEntry.startingBlock = blockIndex;
6188 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6189 &chainEntry);
6193 currentBlock = blockIndex;
6196 * Figure out how many blocks are needed to contain this stream
6198 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6200 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6201 newNumBlocks++;
6204 * Go to the current end of chain
6206 while (blockIndex != BLOCK_END_OF_CHAIN)
6208 oldNumBlocks++;
6209 currentBlock = blockIndex;
6210 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6211 return FALSE;
6215 * Add new blocks to the chain
6217 while (oldNumBlocks < newNumBlocks)
6219 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6220 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6222 SmallBlockChainStream_SetNextBlockInChain(
6223 This,
6224 blockIndex,
6225 BLOCK_END_OF_CHAIN);
6227 currentBlock = blockIndex;
6228 oldNumBlocks++;
6231 return TRUE;
6234 /******************************************************************************
6235 * SmallBlockChainStream_SetSize
6237 * Sets the size of this stream.
6238 * The file will grow if we grow the chain.
6240 * TODO: Free the actual blocks in the file when we shrink the chain.
6241 * Currently, the blocks are still in the file. So the file size
6242 * doesn't shrink even if we shrink streams.
6244 BOOL SmallBlockChainStream_SetSize(
6245 SmallBlockChainStream* This,
6246 ULARGE_INTEGER newSize)
6248 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6250 if (newSize.u.LowPart == size.u.LowPart)
6251 return TRUE;
6253 if (newSize.u.LowPart < size.u.LowPart)
6255 SmallBlockChainStream_Shrink(This, newSize);
6257 else
6259 SmallBlockChainStream_Enlarge(This, newSize);
6262 return TRUE;
6265 /******************************************************************************
6266 * SmallBlockChainStream_GetCount
6268 * Returns the number of small blocks that comprises this chain.
6269 * This is not the size of the stream as the last block may not be full!
6272 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6274 ULONG blockIndex;
6275 ULONG count = 0;
6277 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6279 while(blockIndex != BLOCK_END_OF_CHAIN)
6281 count++;
6283 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6284 blockIndex, &blockIndex)))
6285 return 0;
6288 return count;
6291 /******************************************************************************
6292 * SmallBlockChainStream_GetSize
6294 * Returns the size of this chain.
6296 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6298 DirEntry chainEntry;
6300 if(This->headOfStreamPlaceHolder != NULL)
6302 ULARGE_INTEGER result;
6303 result.u.HighPart = 0;
6305 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6306 This->parentStorage->smallBlockSize;
6308 return result;
6311 StorageImpl_ReadDirEntry(
6312 This->parentStorage,
6313 This->ownerDirEntry,
6314 &chainEntry);
6316 return chainEntry.size;
6319 static HRESULT create_storagefile(
6320 LPCOLESTR pwcsName,
6321 DWORD grfMode,
6322 DWORD grfAttrs,
6323 STGOPTIONS* pStgOptions,
6324 REFIID riid,
6325 void** ppstgOpen)
6327 StorageBaseImpl* newStorage = 0;
6328 HANDLE hFile = INVALID_HANDLE_VALUE;
6329 HRESULT hr = STG_E_INVALIDFLAG;
6330 DWORD shareMode;
6331 DWORD accessMode;
6332 DWORD creationMode;
6333 DWORD fileAttributes;
6334 WCHAR tempFileName[MAX_PATH];
6336 if (ppstgOpen == 0)
6337 return STG_E_INVALIDPOINTER;
6339 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
6340 return STG_E_INVALIDPARAMETER;
6342 /* if no share mode given then DENY_NONE is the default */
6343 if (STGM_SHARE_MODE(grfMode) == 0)
6344 grfMode |= STGM_SHARE_DENY_NONE;
6346 if ( FAILED( validateSTGM(grfMode) ))
6347 goto end;
6349 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6350 switch(STGM_ACCESS_MODE(grfMode))
6352 case STGM_WRITE:
6353 case STGM_READWRITE:
6354 break;
6355 default:
6356 goto end;
6359 /* in direct mode, can only use SHARE_EXCLUSIVE */
6360 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6361 goto end;
6363 /* but in transacted mode, any share mode is valid */
6366 * Generate a unique name.
6368 if (pwcsName == 0)
6370 WCHAR tempPath[MAX_PATH];
6371 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6373 memset(tempPath, 0, sizeof(tempPath));
6374 memset(tempFileName, 0, sizeof(tempFileName));
6376 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6377 tempPath[0] = '.';
6379 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6380 pwcsName = tempFileName;
6381 else
6383 hr = STG_E_INSUFFICIENTMEMORY;
6384 goto end;
6387 creationMode = TRUNCATE_EXISTING;
6389 else
6391 creationMode = GetCreationModeFromSTGM(grfMode);
6395 * Interpret the STGM value grfMode
6397 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6398 accessMode = GetAccessModeFromSTGM(grfMode);
6400 if (grfMode & STGM_DELETEONRELEASE)
6401 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6402 else
6403 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6405 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6407 static int fixme;
6408 if (!fixme++)
6409 FIXME("Storage share mode not implemented.\n");
6412 *ppstgOpen = 0;
6414 hFile = CreateFileW(pwcsName,
6415 accessMode,
6416 shareMode,
6417 NULL,
6418 creationMode,
6419 fileAttributes,
6422 if (hFile == INVALID_HANDLE_VALUE)
6424 if(GetLastError() == ERROR_FILE_EXISTS)
6425 hr = STG_E_FILEALREADYEXISTS;
6426 else
6427 hr = E_FAIL;
6428 goto end;
6432 * Allocate and initialize the new IStorage32object.
6434 hr = Storage_Construct(
6435 hFile,
6436 pwcsName,
6437 NULL,
6438 grfMode,
6439 TRUE,
6440 TRUE,
6441 pStgOptions->ulSectorSize,
6442 &newStorage);
6444 if (FAILED(hr))
6446 goto end;
6449 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
6451 IStorage_Release((IStorage*)newStorage);
6453 end:
6454 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6456 return hr;
6459 /******************************************************************************
6460 * StgCreateDocfile [OLE32.@]
6461 * Creates a new compound file storage object
6463 * PARAMS
6464 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6465 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6466 * reserved [ ?] unused?, usually 0
6467 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6469 * RETURNS
6470 * S_OK if the file was successfully created
6471 * some STG_E_ value if error
6472 * NOTES
6473 * if pwcsName is NULL, create file with new unique name
6474 * the function can returns
6475 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6476 * (unrealized now)
6478 HRESULT WINAPI StgCreateDocfile(
6479 LPCOLESTR pwcsName,
6480 DWORD grfMode,
6481 DWORD reserved,
6482 IStorage **ppstgOpen)
6484 STGOPTIONS stgoptions = {1, 0, 512};
6486 TRACE("(%s, %x, %d, %p)\n",
6487 debugstr_w(pwcsName), grfMode,
6488 reserved, ppstgOpen);
6490 if (ppstgOpen == 0)
6491 return STG_E_INVALIDPOINTER;
6492 if (reserved != 0)
6493 return STG_E_INVALIDPARAMETER;
6495 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
6498 /******************************************************************************
6499 * StgCreateStorageEx [OLE32.@]
6501 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6503 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6504 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6506 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6508 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6509 return STG_E_INVALIDPARAMETER;
6512 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6514 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6515 return STG_E_INVALIDPARAMETER;
6518 if (stgfmt == STGFMT_FILE)
6520 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6521 return STG_E_INVALIDPARAMETER;
6524 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6526 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
6530 ERR("Invalid stgfmt argument\n");
6531 return STG_E_INVALIDPARAMETER;
6534 /******************************************************************************
6535 * StgCreatePropSetStg [OLE32.@]
6537 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6538 IPropertySetStorage **ppPropSetStg)
6540 HRESULT hr;
6542 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6543 if (reserved)
6544 hr = STG_E_INVALIDPARAMETER;
6545 else
6546 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6547 (void**)ppPropSetStg);
6548 return hr;
6551 /******************************************************************************
6552 * StgOpenStorageEx [OLE32.@]
6554 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6556 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6557 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6559 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6561 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6562 return STG_E_INVALIDPARAMETER;
6565 switch (stgfmt)
6567 case STGFMT_FILE:
6568 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6569 return STG_E_INVALIDPARAMETER;
6571 case STGFMT_STORAGE:
6572 break;
6574 case STGFMT_DOCFILE:
6575 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6577 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6578 return STG_E_INVALIDPARAMETER;
6580 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6581 break;
6583 case STGFMT_ANY:
6584 WARN("STGFMT_ANY assuming storage\n");
6585 break;
6587 default:
6588 return STG_E_INVALIDPARAMETER;
6591 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6595 /******************************************************************************
6596 * StgOpenStorage [OLE32.@]
6598 HRESULT WINAPI StgOpenStorage(
6599 const OLECHAR *pwcsName,
6600 IStorage *pstgPriority,
6601 DWORD grfMode,
6602 SNB snbExclude,
6603 DWORD reserved,
6604 IStorage **ppstgOpen)
6606 StorageBaseImpl* newStorage = 0;
6607 HRESULT hr = S_OK;
6608 HANDLE hFile = 0;
6609 DWORD shareMode;
6610 DWORD accessMode;
6612 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6613 debugstr_w(pwcsName), pstgPriority, grfMode,
6614 snbExclude, reserved, ppstgOpen);
6616 if (pwcsName == 0)
6618 hr = STG_E_INVALIDNAME;
6619 goto end;
6622 if (ppstgOpen == 0)
6624 hr = STG_E_INVALIDPOINTER;
6625 goto end;
6628 if (reserved)
6630 hr = STG_E_INVALIDPARAMETER;
6631 goto end;
6634 if (grfMode & STGM_PRIORITY)
6636 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6637 return STG_E_INVALIDFLAG;
6638 if (grfMode & STGM_DELETEONRELEASE)
6639 return STG_E_INVALIDFUNCTION;
6640 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6641 return STG_E_INVALIDFLAG;
6642 grfMode &= ~0xf0; /* remove the existing sharing mode */
6643 grfMode |= STGM_SHARE_DENY_NONE;
6645 /* STGM_PRIORITY stops other IStorage objects on the same file from
6646 * committing until the STGM_PRIORITY IStorage is closed. it also
6647 * stops non-transacted mode StgOpenStorage calls with write access from
6648 * succeeding. obviously, both of these cannot be achieved through just
6649 * file share flags */
6650 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6654 * Validate the sharing mode
6656 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6657 switch(STGM_SHARE_MODE(grfMode))
6659 case STGM_SHARE_EXCLUSIVE:
6660 case STGM_SHARE_DENY_WRITE:
6661 break;
6662 default:
6663 hr = STG_E_INVALIDFLAG;
6664 goto end;
6667 if ( FAILED( validateSTGM(grfMode) ) ||
6668 (grfMode&STGM_CREATE))
6670 hr = STG_E_INVALIDFLAG;
6671 goto end;
6674 /* shared reading requires transacted mode */
6675 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6676 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6677 !(grfMode&STGM_TRANSACTED) )
6679 hr = STG_E_INVALIDFLAG;
6680 goto end;
6684 * Interpret the STGM value grfMode
6686 shareMode = GetShareModeFromSTGM(grfMode);
6687 accessMode = GetAccessModeFromSTGM(grfMode);
6689 *ppstgOpen = 0;
6691 hFile = CreateFileW( pwcsName,
6692 accessMode,
6693 shareMode,
6694 NULL,
6695 OPEN_EXISTING,
6696 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6699 if (hFile==INVALID_HANDLE_VALUE)
6701 DWORD last_error = GetLastError();
6703 hr = E_FAIL;
6705 switch (last_error)
6707 case ERROR_FILE_NOT_FOUND:
6708 hr = STG_E_FILENOTFOUND;
6709 break;
6711 case ERROR_PATH_NOT_FOUND:
6712 hr = STG_E_PATHNOTFOUND;
6713 break;
6715 case ERROR_ACCESS_DENIED:
6716 case ERROR_WRITE_PROTECT:
6717 hr = STG_E_ACCESSDENIED;
6718 break;
6720 case ERROR_SHARING_VIOLATION:
6721 hr = STG_E_SHAREVIOLATION;
6722 break;
6724 default:
6725 hr = E_FAIL;
6728 goto end;
6732 * Refuse to open the file if it's too small to be a structured storage file
6733 * FIXME: verify the file when reading instead of here
6735 if (GetFileSize(hFile, NULL) < 0x100)
6737 CloseHandle(hFile);
6738 hr = STG_E_FILEALREADYEXISTS;
6739 goto end;
6743 * Allocate and initialize the new IStorage32object.
6745 hr = Storage_Construct(
6746 hFile,
6747 pwcsName,
6748 NULL,
6749 grfMode,
6750 TRUE,
6751 FALSE,
6752 512,
6753 &newStorage);
6755 if (FAILED(hr))
6758 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6760 if(hr == STG_E_INVALIDHEADER)
6761 hr = STG_E_FILEALREADYEXISTS;
6762 goto end;
6766 * Get an "out" pointer for the caller.
6768 *ppstgOpen = (IStorage*)newStorage;
6770 end:
6771 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6772 return hr;
6775 /******************************************************************************
6776 * StgCreateDocfileOnILockBytes [OLE32.@]
6778 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6779 ILockBytes *plkbyt,
6780 DWORD grfMode,
6781 DWORD reserved,
6782 IStorage** ppstgOpen)
6784 StorageBaseImpl* newStorage = 0;
6785 HRESULT hr = S_OK;
6787 if ((ppstgOpen == 0) || (plkbyt == 0))
6788 return STG_E_INVALIDPOINTER;
6791 * Allocate and initialize the new IStorage object.
6793 hr = Storage_Construct(
6796 plkbyt,
6797 grfMode,
6798 FALSE,
6799 TRUE,
6800 512,
6801 &newStorage);
6803 if (FAILED(hr))
6805 return hr;
6809 * Get an "out" pointer for the caller.
6811 *ppstgOpen = (IStorage*)newStorage;
6813 return hr;
6816 /******************************************************************************
6817 * StgOpenStorageOnILockBytes [OLE32.@]
6819 HRESULT WINAPI StgOpenStorageOnILockBytes(
6820 ILockBytes *plkbyt,
6821 IStorage *pstgPriority,
6822 DWORD grfMode,
6823 SNB snbExclude,
6824 DWORD reserved,
6825 IStorage **ppstgOpen)
6827 StorageBaseImpl* newStorage = 0;
6828 HRESULT hr = S_OK;
6830 if ((plkbyt == 0) || (ppstgOpen == 0))
6831 return STG_E_INVALIDPOINTER;
6833 if ( FAILED( validateSTGM(grfMode) ))
6834 return STG_E_INVALIDFLAG;
6836 *ppstgOpen = 0;
6839 * Allocate and initialize the new IStorage object.
6841 hr = Storage_Construct(
6844 plkbyt,
6845 grfMode,
6846 FALSE,
6847 FALSE,
6848 512,
6849 &newStorage);
6851 if (FAILED(hr))
6853 return hr;
6857 * Get an "out" pointer for the caller.
6859 *ppstgOpen = (IStorage*)newStorage;
6861 return hr;
6864 /******************************************************************************
6865 * StgSetTimes [ole32.@]
6866 * StgSetTimes [OLE32.@]
6870 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6871 FILETIME const *patime, FILETIME const *pmtime)
6873 IStorage *stg = NULL;
6874 HRESULT r;
6876 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6878 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6879 0, 0, &stg);
6880 if( SUCCEEDED(r) )
6882 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6883 IStorage_Release(stg);
6886 return r;
6889 /******************************************************************************
6890 * StgIsStorageILockBytes [OLE32.@]
6892 * Determines if the ILockBytes contains a storage object.
6894 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6896 BYTE sig[8];
6897 ULARGE_INTEGER offset;
6899 offset.u.HighPart = 0;
6900 offset.u.LowPart = 0;
6902 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6904 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6905 return S_OK;
6907 return S_FALSE;
6910 /******************************************************************************
6911 * WriteClassStg [OLE32.@]
6913 * This method will store the specified CLSID in the specified storage object
6915 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6917 HRESULT hRes;
6919 if(!pStg)
6920 return E_INVALIDARG;
6922 if(!rclsid)
6923 return STG_E_INVALIDPOINTER;
6925 hRes = IStorage_SetClass(pStg, rclsid);
6927 return hRes;
6930 /***********************************************************************
6931 * ReadClassStg (OLE32.@)
6933 * This method reads the CLSID previously written to a storage object with
6934 * the WriteClassStg.
6936 * PARAMS
6937 * pstg [I] IStorage pointer
6938 * pclsid [O] Pointer to where the CLSID is written
6940 * RETURNS
6941 * Success: S_OK.
6942 * Failure: HRESULT code.
6944 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6946 STATSTG pstatstg;
6947 HRESULT hRes;
6949 TRACE("(%p, %p)\n", pstg, pclsid);
6951 if(!pstg || !pclsid)
6952 return E_INVALIDARG;
6955 * read a STATSTG structure (contains the clsid) from the storage
6957 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6959 if(SUCCEEDED(hRes))
6960 *pclsid=pstatstg.clsid;
6962 return hRes;
6965 /***********************************************************************
6966 * OleLoadFromStream (OLE32.@)
6968 * This function loads an object from stream
6970 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6972 CLSID clsid;
6973 HRESULT res;
6974 LPPERSISTSTREAM xstm;
6976 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6978 res=ReadClassStm(pStm,&clsid);
6979 if (FAILED(res))
6980 return res;
6981 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6982 if (FAILED(res))
6983 return res;
6984 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6985 if (FAILED(res)) {
6986 IUnknown_Release((IUnknown*)*ppvObj);
6987 return res;
6989 res=IPersistStream_Load(xstm,pStm);
6990 IPersistStream_Release(xstm);
6991 /* FIXME: all refcounts ok at this point? I think they should be:
6992 * pStm : unchanged
6993 * ppvObj : 1
6994 * xstm : 0 (released)
6996 return res;
6999 /***********************************************************************
7000 * OleSaveToStream (OLE32.@)
7002 * This function saves an object with the IPersistStream interface on it
7003 * to the specified stream.
7005 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7008 CLSID clsid;
7009 HRESULT res;
7011 TRACE("(%p,%p)\n",pPStm,pStm);
7013 res=IPersistStream_GetClassID(pPStm,&clsid);
7015 if (SUCCEEDED(res)){
7017 res=WriteClassStm(pStm,&clsid);
7019 if (SUCCEEDED(res))
7021 res=IPersistStream_Save(pPStm,pStm,TRUE);
7024 TRACE("Finished Save\n");
7025 return res;
7028 /****************************************************************************
7029 * This method validate a STGM parameter that can contain the values below
7031 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7032 * The stgm values contained in 0xffff0000 are bitmasks.
7034 * STGM_DIRECT 0x00000000
7035 * STGM_TRANSACTED 0x00010000
7036 * STGM_SIMPLE 0x08000000
7038 * STGM_READ 0x00000000
7039 * STGM_WRITE 0x00000001
7040 * STGM_READWRITE 0x00000002
7042 * STGM_SHARE_DENY_NONE 0x00000040
7043 * STGM_SHARE_DENY_READ 0x00000030
7044 * STGM_SHARE_DENY_WRITE 0x00000020
7045 * STGM_SHARE_EXCLUSIVE 0x00000010
7047 * STGM_PRIORITY 0x00040000
7048 * STGM_DELETEONRELEASE 0x04000000
7050 * STGM_CREATE 0x00001000
7051 * STGM_CONVERT 0x00020000
7052 * STGM_FAILIFTHERE 0x00000000
7054 * STGM_NOSCRATCH 0x00100000
7055 * STGM_NOSNAPSHOT 0x00200000
7057 static HRESULT validateSTGM(DWORD stgm)
7059 DWORD access = STGM_ACCESS_MODE(stgm);
7060 DWORD share = STGM_SHARE_MODE(stgm);
7061 DWORD create = STGM_CREATE_MODE(stgm);
7063 if (stgm&~STGM_KNOWN_FLAGS)
7065 ERR("unknown flags %08x\n", stgm);
7066 return E_FAIL;
7069 switch (access)
7071 case STGM_READ:
7072 case STGM_WRITE:
7073 case STGM_READWRITE:
7074 break;
7075 default:
7076 return E_FAIL;
7079 switch (share)
7081 case STGM_SHARE_DENY_NONE:
7082 case STGM_SHARE_DENY_READ:
7083 case STGM_SHARE_DENY_WRITE:
7084 case STGM_SHARE_EXCLUSIVE:
7085 break;
7086 default:
7087 return E_FAIL;
7090 switch (create)
7092 case STGM_CREATE:
7093 case STGM_FAILIFTHERE:
7094 break;
7095 default:
7096 return E_FAIL;
7100 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7102 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7103 return E_FAIL;
7106 * STGM_CREATE | STGM_CONVERT
7107 * if both are false, STGM_FAILIFTHERE is set to TRUE
7109 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7110 return E_FAIL;
7113 * STGM_NOSCRATCH requires STGM_TRANSACTED
7115 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7116 return E_FAIL;
7119 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7120 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7122 if ( (stgm & STGM_NOSNAPSHOT) &&
7123 (!(stgm & STGM_TRANSACTED) ||
7124 share == STGM_SHARE_EXCLUSIVE ||
7125 share == STGM_SHARE_DENY_WRITE) )
7126 return E_FAIL;
7128 return S_OK;
7131 /****************************************************************************
7132 * GetShareModeFromSTGM
7134 * This method will return a share mode flag from a STGM value.
7135 * The STGM value is assumed valid.
7137 static DWORD GetShareModeFromSTGM(DWORD stgm)
7139 switch (STGM_SHARE_MODE(stgm))
7141 case STGM_SHARE_DENY_NONE:
7142 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7143 case STGM_SHARE_DENY_READ:
7144 return FILE_SHARE_WRITE;
7145 case STGM_SHARE_DENY_WRITE:
7146 return FILE_SHARE_READ;
7147 case STGM_SHARE_EXCLUSIVE:
7148 return 0;
7150 ERR("Invalid share mode!\n");
7151 assert(0);
7152 return 0;
7155 /****************************************************************************
7156 * GetAccessModeFromSTGM
7158 * This method will return an access mode flag from a STGM value.
7159 * The STGM value is assumed valid.
7161 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7163 switch (STGM_ACCESS_MODE(stgm))
7165 case STGM_READ:
7166 return GENERIC_READ;
7167 case STGM_WRITE:
7168 case STGM_READWRITE:
7169 return GENERIC_READ | GENERIC_WRITE;
7171 ERR("Invalid access mode!\n");
7172 assert(0);
7173 return 0;
7176 /****************************************************************************
7177 * GetCreationModeFromSTGM
7179 * This method will return a creation mode flag from a STGM value.
7180 * The STGM value is assumed valid.
7182 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7184 switch(STGM_CREATE_MODE(stgm))
7186 case STGM_CREATE:
7187 return CREATE_ALWAYS;
7188 case STGM_CONVERT:
7189 FIXME("STGM_CONVERT not implemented!\n");
7190 return CREATE_NEW;
7191 case STGM_FAILIFTHERE:
7192 return CREATE_NEW;
7194 ERR("Invalid create mode!\n");
7195 assert(0);
7196 return 0;
7200 /*************************************************************************
7201 * OLECONVERT_LoadOLE10 [Internal]
7203 * Loads the OLE10 STREAM to memory
7205 * PARAMS
7206 * pOleStream [I] The OLESTREAM
7207 * pData [I] Data Structure for the OLESTREAM Data
7209 * RETURNS
7210 * Success: S_OK
7211 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7212 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7214 * NOTES
7215 * This function is used by OleConvertOLESTREAMToIStorage only.
7217 * Memory allocated for pData must be freed by the caller
7219 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7221 DWORD dwSize;
7222 HRESULT hRes = S_OK;
7223 int nTryCnt=0;
7224 int max_try = 6;
7226 pData->pData = NULL;
7227 pData->pstrOleObjFileName = NULL;
7229 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7231 /* Get the OleID */
7232 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7233 if(dwSize != sizeof(pData->dwOleID))
7235 hRes = CONVERT10_E_OLESTREAM_GET;
7237 else if(pData->dwOleID != OLESTREAM_ID)
7239 hRes = CONVERT10_E_OLESTREAM_FMT;
7241 else
7243 hRes = S_OK;
7244 break;
7248 if(hRes == S_OK)
7250 /* Get the TypeID... more info needed for this field */
7251 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7252 if(dwSize != sizeof(pData->dwTypeID))
7254 hRes = CONVERT10_E_OLESTREAM_GET;
7257 if(hRes == S_OK)
7259 if(pData->dwTypeID != 0)
7261 /* Get the length of the OleTypeName */
7262 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7263 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7265 hRes = CONVERT10_E_OLESTREAM_GET;
7268 if(hRes == S_OK)
7270 if(pData->dwOleTypeNameLength > 0)
7272 /* Get the OleTypeName */
7273 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7274 if(dwSize != pData->dwOleTypeNameLength)
7276 hRes = CONVERT10_E_OLESTREAM_GET;
7280 if(bStrem1)
7282 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7283 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7285 hRes = CONVERT10_E_OLESTREAM_GET;
7287 if(hRes == S_OK)
7289 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7290 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7291 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7292 if(pData->pstrOleObjFileName)
7294 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7295 if(dwSize != pData->dwOleObjFileNameLength)
7297 hRes = CONVERT10_E_OLESTREAM_GET;
7300 else
7301 hRes = CONVERT10_E_OLESTREAM_GET;
7304 else
7306 /* Get the Width of the Metafile */
7307 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7308 if(dwSize != sizeof(pData->dwMetaFileWidth))
7310 hRes = CONVERT10_E_OLESTREAM_GET;
7312 if(hRes == S_OK)
7314 /* Get the Height of the Metafile */
7315 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7316 if(dwSize != sizeof(pData->dwMetaFileHeight))
7318 hRes = CONVERT10_E_OLESTREAM_GET;
7322 if(hRes == S_OK)
7324 /* Get the Length of the Data */
7325 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7326 if(dwSize != sizeof(pData->dwDataLength))
7328 hRes = CONVERT10_E_OLESTREAM_GET;
7332 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7334 if(!bStrem1) /* if it is a second OLE stream data */
7336 pData->dwDataLength -= 8;
7337 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7338 if(dwSize != sizeof(pData->strUnknown))
7340 hRes = CONVERT10_E_OLESTREAM_GET;
7344 if(hRes == S_OK)
7346 if(pData->dwDataLength > 0)
7348 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7350 /* Get Data (ex. IStorage, Metafile, or BMP) */
7351 if(pData->pData)
7353 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7354 if(dwSize != pData->dwDataLength)
7356 hRes = CONVERT10_E_OLESTREAM_GET;
7359 else
7361 hRes = CONVERT10_E_OLESTREAM_GET;
7367 return hRes;
7370 /*************************************************************************
7371 * OLECONVERT_SaveOLE10 [Internal]
7373 * Saves the OLE10 STREAM From memory
7375 * PARAMS
7376 * pData [I] Data Structure for the OLESTREAM Data
7377 * pOleStream [I] The OLESTREAM to save
7379 * RETURNS
7380 * Success: S_OK
7381 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7383 * NOTES
7384 * This function is used by OleConvertIStorageToOLESTREAM only.
7387 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7389 DWORD dwSize;
7390 HRESULT hRes = S_OK;
7393 /* Set the OleID */
7394 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7395 if(dwSize != sizeof(pData->dwOleID))
7397 hRes = CONVERT10_E_OLESTREAM_PUT;
7400 if(hRes == S_OK)
7402 /* Set the TypeID */
7403 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7404 if(dwSize != sizeof(pData->dwTypeID))
7406 hRes = CONVERT10_E_OLESTREAM_PUT;
7410 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7412 /* Set the Length of the OleTypeName */
7413 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7414 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7416 hRes = CONVERT10_E_OLESTREAM_PUT;
7419 if(hRes == S_OK)
7421 if(pData->dwOleTypeNameLength > 0)
7423 /* Set the OleTypeName */
7424 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7425 if(dwSize != pData->dwOleTypeNameLength)
7427 hRes = CONVERT10_E_OLESTREAM_PUT;
7432 if(hRes == S_OK)
7434 /* Set the width of the Metafile */
7435 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7436 if(dwSize != sizeof(pData->dwMetaFileWidth))
7438 hRes = CONVERT10_E_OLESTREAM_PUT;
7442 if(hRes == S_OK)
7444 /* Set the height of the Metafile */
7445 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7446 if(dwSize != sizeof(pData->dwMetaFileHeight))
7448 hRes = CONVERT10_E_OLESTREAM_PUT;
7452 if(hRes == S_OK)
7454 /* Set the length of the Data */
7455 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7456 if(dwSize != sizeof(pData->dwDataLength))
7458 hRes = CONVERT10_E_OLESTREAM_PUT;
7462 if(hRes == S_OK)
7464 if(pData->dwDataLength > 0)
7466 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7467 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7468 if(dwSize != pData->dwDataLength)
7470 hRes = CONVERT10_E_OLESTREAM_PUT;
7475 return hRes;
7478 /*************************************************************************
7479 * OLECONVERT_GetOLE20FromOLE10[Internal]
7481 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7482 * opens it, and copies the content to the dest IStorage for
7483 * OleConvertOLESTREAMToIStorage
7486 * PARAMS
7487 * pDestStorage [I] The IStorage to copy the data to
7488 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7489 * nBufferLength [I] The size of the buffer
7491 * RETURNS
7492 * Nothing
7494 * NOTES
7498 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7500 HRESULT hRes;
7501 HANDLE hFile;
7502 IStorage *pTempStorage;
7503 DWORD dwNumOfBytesWritten;
7504 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7505 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7507 /* Create a temp File */
7508 GetTempPathW(MAX_PATH, wstrTempDir);
7509 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7510 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7512 if(hFile != INVALID_HANDLE_VALUE)
7514 /* Write IStorage Data to File */
7515 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7516 CloseHandle(hFile);
7518 /* Open and copy temp storage to the Dest Storage */
7519 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7520 if(hRes == S_OK)
7522 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7523 IStorage_Release(pTempStorage);
7525 DeleteFileW(wstrTempFile);
7530 /*************************************************************************
7531 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7533 * Saves the OLE10 STREAM From memory
7535 * PARAMS
7536 * pStorage [I] The Src IStorage to copy
7537 * pData [I] The Dest Memory to write to.
7539 * RETURNS
7540 * The size in bytes allocated for pData
7542 * NOTES
7543 * Memory allocated for pData must be freed by the caller
7545 * Used by OleConvertIStorageToOLESTREAM only.
7548 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7550 HANDLE hFile;
7551 HRESULT hRes;
7552 DWORD nDataLength = 0;
7553 IStorage *pTempStorage;
7554 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7555 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7557 *pData = NULL;
7559 /* Create temp Storage */
7560 GetTempPathW(MAX_PATH, wstrTempDir);
7561 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7562 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7564 if(hRes == S_OK)
7566 /* Copy Src Storage to the Temp Storage */
7567 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7568 IStorage_Release(pTempStorage);
7570 /* Open Temp Storage as a file and copy to memory */
7571 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7572 if(hFile != INVALID_HANDLE_VALUE)
7574 nDataLength = GetFileSize(hFile, NULL);
7575 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7576 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7577 CloseHandle(hFile);
7579 DeleteFileW(wstrTempFile);
7581 return nDataLength;
7584 /*************************************************************************
7585 * OLECONVERT_CreateOleStream [Internal]
7587 * Creates the "\001OLE" stream in the IStorage if necessary.
7589 * PARAMS
7590 * pStorage [I] Dest storage to create the stream in
7592 * RETURNS
7593 * Nothing
7595 * NOTES
7596 * This function is used by OleConvertOLESTREAMToIStorage only.
7598 * This stream is still unknown, MS Word seems to have extra data
7599 * but since the data is stored in the OLESTREAM there should be
7600 * no need to recreate the stream. If the stream is manually
7601 * deleted it will create it with this default data.
7604 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7606 HRESULT hRes;
7607 IStream *pStream;
7608 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7609 BYTE pOleStreamHeader [] =
7611 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7612 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7613 0x00, 0x00, 0x00, 0x00
7616 /* Create stream if not present */
7617 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7618 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7620 if(hRes == S_OK)
7622 /* Write default Data */
7623 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7624 IStream_Release(pStream);
7628 /* write a string to a stream, preceded by its length */
7629 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7631 HRESULT r;
7632 LPSTR str;
7633 DWORD len = 0;
7635 if( string )
7636 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7637 r = IStream_Write( stm, &len, sizeof(len), NULL);
7638 if( FAILED( r ) )
7639 return r;
7640 if(len == 0)
7641 return r;
7642 str = CoTaskMemAlloc( len );
7643 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7644 r = IStream_Write( stm, str, len, NULL);
7645 CoTaskMemFree( str );
7646 return r;
7649 /* read a string preceded by its length from a stream */
7650 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7652 HRESULT r;
7653 DWORD len, count = 0;
7654 LPSTR str;
7655 LPWSTR wstr;
7657 r = IStream_Read( stm, &len, sizeof(len), &count );
7658 if( FAILED( r ) )
7659 return r;
7660 if( count != sizeof(len) )
7661 return E_OUTOFMEMORY;
7663 TRACE("%d bytes\n",len);
7665 str = CoTaskMemAlloc( len );
7666 if( !str )
7667 return E_OUTOFMEMORY;
7668 count = 0;
7669 r = IStream_Read( stm, str, len, &count );
7670 if( FAILED( r ) )
7671 return r;
7672 if( count != len )
7674 CoTaskMemFree( str );
7675 return E_OUTOFMEMORY;
7678 TRACE("Read string %s\n",debugstr_an(str,len));
7680 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7681 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7682 if( wstr )
7683 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7684 CoTaskMemFree( str );
7686 *string = wstr;
7688 return r;
7692 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7693 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7695 IStream *pstm;
7696 HRESULT r = S_OK;
7697 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7699 static const BYTE unknown1[12] =
7700 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7701 0xFF, 0xFF, 0xFF, 0xFF};
7702 static const BYTE unknown2[16] =
7703 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7704 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7706 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7707 debugstr_w(lpszUserType), debugstr_w(szClipName),
7708 debugstr_w(szProgIDName));
7710 /* Create a CompObj stream */
7711 r = IStorage_CreateStream(pstg, szwStreamName,
7712 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7713 if( FAILED (r) )
7714 return r;
7716 /* Write CompObj Structure to stream */
7717 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7719 if( SUCCEEDED( r ) )
7720 r = WriteClassStm( pstm, clsid );
7722 if( SUCCEEDED( r ) )
7723 r = STREAM_WriteString( pstm, lpszUserType );
7724 if( SUCCEEDED( r ) )
7725 r = STREAM_WriteString( pstm, szClipName );
7726 if( SUCCEEDED( r ) )
7727 r = STREAM_WriteString( pstm, szProgIDName );
7728 if( SUCCEEDED( r ) )
7729 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7731 IStream_Release( pstm );
7733 return r;
7736 /***********************************************************************
7737 * WriteFmtUserTypeStg (OLE32.@)
7739 HRESULT WINAPI WriteFmtUserTypeStg(
7740 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7742 HRESULT r;
7743 WCHAR szwClipName[0x40];
7744 CLSID clsid = CLSID_NULL;
7745 LPWSTR wstrProgID = NULL;
7746 DWORD n;
7748 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7750 /* get the clipboard format name */
7751 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7752 szwClipName[n]=0;
7754 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7756 /* FIXME: There's room to save a CLSID and its ProgID, but
7757 the CLSID is not looked up in the registry and in all the
7758 tests I wrote it was CLSID_NULL. Where does it come from?
7761 /* get the real program ID. This may fail, but that's fine */
7762 ProgIDFromCLSID(&clsid, &wstrProgID);
7764 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7766 r = STORAGE_WriteCompObj( pstg, &clsid,
7767 lpszUserType, szwClipName, wstrProgID );
7769 CoTaskMemFree(wstrProgID);
7771 return r;
7775 /******************************************************************************
7776 * ReadFmtUserTypeStg [OLE32.@]
7778 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7780 HRESULT r;
7781 IStream *stm = 0;
7782 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7783 unsigned char unknown1[12];
7784 unsigned char unknown2[16];
7785 DWORD count;
7786 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7787 CLSID clsid;
7789 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7791 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7792 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7793 if( FAILED ( r ) )
7795 WARN("Failed to open stream r = %08x\n", r);
7796 return r;
7799 /* read the various parts of the structure */
7800 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7801 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7802 goto end;
7803 r = ReadClassStm( stm, &clsid );
7804 if( FAILED( r ) )
7805 goto end;
7807 r = STREAM_ReadString( stm, &szCLSIDName );
7808 if( FAILED( r ) )
7809 goto end;
7811 r = STREAM_ReadString( stm, &szOleTypeName );
7812 if( FAILED( r ) )
7813 goto end;
7815 r = STREAM_ReadString( stm, &szProgIDName );
7816 if( FAILED( r ) )
7817 goto end;
7819 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7820 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7821 goto end;
7823 /* ok, success... now we just need to store what we found */
7824 if( pcf )
7825 *pcf = RegisterClipboardFormatW( szOleTypeName );
7826 CoTaskMemFree( szOleTypeName );
7828 if( lplpszUserType )
7829 *lplpszUserType = szCLSIDName;
7830 CoTaskMemFree( szProgIDName );
7832 end:
7833 IStream_Release( stm );
7835 return r;
7839 /*************************************************************************
7840 * OLECONVERT_CreateCompObjStream [Internal]
7842 * Creates a "\001CompObj" is the destination IStorage if necessary.
7844 * PARAMS
7845 * pStorage [I] The dest IStorage to create the CompObj Stream
7846 * if necessary.
7847 * strOleTypeName [I] The ProgID
7849 * RETURNS
7850 * Success: S_OK
7851 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7853 * NOTES
7854 * This function is used by OleConvertOLESTREAMToIStorage only.
7856 * The stream data is stored in the OLESTREAM and there should be
7857 * no need to recreate the stream. If the stream is manually
7858 * deleted it will attempt to create it by querying the registry.
7862 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7864 IStream *pStream;
7865 HRESULT hStorageRes, hRes = S_OK;
7866 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7867 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7868 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7870 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7871 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7873 /* Initialize the CompObj structure */
7874 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7875 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7876 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7879 /* Create a CompObj stream if it doesn't exist */
7880 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7881 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7882 if(hStorageRes == S_OK)
7884 /* copy the OleTypeName to the compobj struct */
7885 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7886 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7888 /* copy the OleTypeName to the compobj struct */
7889 /* Note: in the test made, these were Identical */
7890 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7891 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7893 /* Get the CLSID */
7894 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7895 bufferW, OLESTREAM_MAX_STR_LEN );
7896 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7898 if(hRes == S_OK)
7900 HKEY hKey;
7901 LONG hErr;
7902 /* Get the CLSID Default Name from the Registry */
7903 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7904 if(hErr == ERROR_SUCCESS)
7906 char strTemp[OLESTREAM_MAX_STR_LEN];
7907 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7908 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7909 if(hErr == ERROR_SUCCESS)
7911 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7913 RegCloseKey(hKey);
7917 /* Write CompObj Structure to stream */
7918 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7920 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7922 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7923 if(IStorageCompObj.dwCLSIDNameLength > 0)
7925 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7927 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7928 if(IStorageCompObj.dwOleTypeNameLength > 0)
7930 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7932 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7933 if(IStorageCompObj.dwProgIDNameLength > 0)
7935 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7937 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7938 IStream_Release(pStream);
7940 return hRes;
7944 /*************************************************************************
7945 * OLECONVERT_CreateOlePresStream[Internal]
7947 * Creates the "\002OlePres000" Stream with the Metafile data
7949 * PARAMS
7950 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7951 * dwExtentX [I] Width of the Metafile
7952 * dwExtentY [I] Height of the Metafile
7953 * pData [I] Metafile data
7954 * dwDataLength [I] Size of the Metafile data
7956 * RETURNS
7957 * Success: S_OK
7958 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7960 * NOTES
7961 * This function is used by OleConvertOLESTREAMToIStorage only.
7964 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7966 HRESULT hRes;
7967 IStream *pStream;
7968 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7969 BYTE pOlePresStreamHeader [] =
7971 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7972 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7973 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7974 0x00, 0x00, 0x00, 0x00
7977 BYTE pOlePresStreamHeaderEmpty [] =
7979 0x00, 0x00, 0x00, 0x00,
7980 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7981 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7982 0x00, 0x00, 0x00, 0x00
7985 /* Create the OlePres000 Stream */
7986 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7987 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7989 if(hRes == S_OK)
7991 DWORD nHeaderSize;
7992 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7994 memset(&OlePres, 0, sizeof(OlePres));
7995 /* Do we have any metafile data to save */
7996 if(dwDataLength > 0)
7998 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7999 nHeaderSize = sizeof(pOlePresStreamHeader);
8001 else
8003 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8004 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8006 /* Set width and height of the metafile */
8007 OlePres.dwExtentX = dwExtentX;
8008 OlePres.dwExtentY = -dwExtentY;
8010 /* Set Data and Length */
8011 if(dwDataLength > sizeof(METAFILEPICT16))
8013 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8014 OlePres.pData = &(pData[8]);
8016 /* Save OlePres000 Data to Stream */
8017 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8018 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8019 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8020 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8021 if(OlePres.dwSize > 0)
8023 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8025 IStream_Release(pStream);
8029 /*************************************************************************
8030 * OLECONVERT_CreateOle10NativeStream [Internal]
8032 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8034 * PARAMS
8035 * pStorage [I] Dest storage to create the stream in
8036 * pData [I] Ole10 Native Data (ex. bmp)
8037 * dwDataLength [I] Size of the Ole10 Native Data
8039 * RETURNS
8040 * Nothing
8042 * NOTES
8043 * This function is used by OleConvertOLESTREAMToIStorage only.
8045 * Might need to verify the data and return appropriate error message
8048 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8050 HRESULT hRes;
8051 IStream *pStream;
8052 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8054 /* Create the Ole10Native Stream */
8055 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8056 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8058 if(hRes == S_OK)
8060 /* Write info to stream */
8061 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8062 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8063 IStream_Release(pStream);
8068 /*************************************************************************
8069 * OLECONVERT_GetOLE10ProgID [Internal]
8071 * Finds the ProgID (or OleTypeID) from the IStorage
8073 * PARAMS
8074 * pStorage [I] The Src IStorage to get the ProgID
8075 * strProgID [I] the ProgID string to get
8076 * dwSize [I] the size of the string
8078 * RETURNS
8079 * Success: S_OK
8080 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8082 * NOTES
8083 * This function is used by OleConvertIStorageToOLESTREAM only.
8087 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8089 HRESULT hRes;
8090 IStream *pStream;
8091 LARGE_INTEGER iSeekPos;
8092 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8093 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8095 /* Open the CompObj Stream */
8096 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8097 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8098 if(hRes == S_OK)
8101 /*Get the OleType from the CompObj Stream */
8102 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8103 iSeekPos.u.HighPart = 0;
8105 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8106 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8107 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8108 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8109 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8110 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8111 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8113 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8114 if(*dwSize > 0)
8116 IStream_Read(pStream, strProgID, *dwSize, NULL);
8118 IStream_Release(pStream);
8120 else
8122 STATSTG stat;
8123 LPOLESTR wstrProgID;
8125 /* Get the OleType from the registry */
8126 REFCLSID clsid = &(stat.clsid);
8127 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8128 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8129 if(hRes == S_OK)
8131 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8135 return hRes;
8138 /*************************************************************************
8139 * OLECONVERT_GetOle10PresData [Internal]
8141 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8143 * PARAMS
8144 * pStorage [I] Src IStroage
8145 * pOleStream [I] Dest OleStream Mem Struct
8147 * RETURNS
8148 * Nothing
8150 * NOTES
8151 * This function is used by OleConvertIStorageToOLESTREAM only.
8153 * Memory allocated for pData must be freed by the caller
8157 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8160 HRESULT hRes;
8161 IStream *pStream;
8162 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8164 /* Initialize Default data for OLESTREAM */
8165 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8166 pOleStreamData[0].dwTypeID = 2;
8167 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8168 pOleStreamData[1].dwTypeID = 0;
8169 pOleStreamData[0].dwMetaFileWidth = 0;
8170 pOleStreamData[0].dwMetaFileHeight = 0;
8171 pOleStreamData[0].pData = NULL;
8172 pOleStreamData[1].pData = NULL;
8174 /* Open Ole10Native Stream */
8175 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8176 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8177 if(hRes == S_OK)
8180 /* Read Size and Data */
8181 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8182 if(pOleStreamData->dwDataLength > 0)
8184 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8185 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8187 IStream_Release(pStream);
8193 /*************************************************************************
8194 * OLECONVERT_GetOle20PresData[Internal]
8196 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8198 * PARAMS
8199 * pStorage [I] Src IStroage
8200 * pOleStreamData [I] Dest OleStream Mem Struct
8202 * RETURNS
8203 * Nothing
8205 * NOTES
8206 * This function is used by OleConvertIStorageToOLESTREAM only.
8208 * Memory allocated for pData must be freed by the caller
8210 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8212 HRESULT hRes;
8213 IStream *pStream;
8214 OLECONVERT_ISTORAGE_OLEPRES olePress;
8215 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8217 /* Initialize Default data for OLESTREAM */
8218 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8219 pOleStreamData[0].dwTypeID = 2;
8220 pOleStreamData[0].dwMetaFileWidth = 0;
8221 pOleStreamData[0].dwMetaFileHeight = 0;
8222 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8223 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8224 pOleStreamData[1].dwTypeID = 0;
8225 pOleStreamData[1].dwOleTypeNameLength = 0;
8226 pOleStreamData[1].strOleTypeName[0] = 0;
8227 pOleStreamData[1].dwMetaFileWidth = 0;
8228 pOleStreamData[1].dwMetaFileHeight = 0;
8229 pOleStreamData[1].pData = NULL;
8230 pOleStreamData[1].dwDataLength = 0;
8233 /* Open OlePress000 stream */
8234 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8235 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8236 if(hRes == S_OK)
8238 LARGE_INTEGER iSeekPos;
8239 METAFILEPICT16 MetaFilePict;
8240 static const char strMetafilePictName[] = "METAFILEPICT";
8242 /* Set the TypeID for a Metafile */
8243 pOleStreamData[1].dwTypeID = 5;
8245 /* Set the OleTypeName to Metafile */
8246 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8247 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8249 iSeekPos.u.HighPart = 0;
8250 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8252 /* Get Presentation Data */
8253 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8254 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8255 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8256 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8258 /*Set width and Height */
8259 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8260 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8261 if(olePress.dwSize > 0)
8263 /* Set Length */
8264 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8266 /* Set MetaFilePict struct */
8267 MetaFilePict.mm = 8;
8268 MetaFilePict.xExt = olePress.dwExtentX;
8269 MetaFilePict.yExt = olePress.dwExtentY;
8270 MetaFilePict.hMF = 0;
8272 /* Get Metafile Data */
8273 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8274 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8275 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8277 IStream_Release(pStream);
8281 /*************************************************************************
8282 * OleConvertOLESTREAMToIStorage [OLE32.@]
8284 * Read info on MSDN
8286 * TODO
8287 * DVTARGETDEVICE parameter is not handled
8288 * Still unsure of some mem fields for OLE 10 Stream
8289 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8290 * and "\001OLE" streams
8293 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8294 LPOLESTREAM pOleStream,
8295 LPSTORAGE pstg,
8296 const DVTARGETDEVICE* ptd)
8298 int i;
8299 HRESULT hRes=S_OK;
8300 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8302 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8304 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8306 if(ptd != NULL)
8308 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8311 if(pstg == NULL || pOleStream == NULL)
8313 hRes = E_INVALIDARG;
8316 if(hRes == S_OK)
8318 /* Load the OLESTREAM to Memory */
8319 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8322 if(hRes == S_OK)
8324 /* Load the OLESTREAM to Memory (part 2)*/
8325 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8328 if(hRes == S_OK)
8331 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8333 /* Do we have the IStorage Data in the OLESTREAM */
8334 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8336 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8337 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8339 else
8341 /* It must be an original OLE 1.0 source */
8342 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8345 else
8347 /* It must be an original OLE 1.0 source */
8348 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8351 /* Create CompObj Stream if necessary */
8352 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8353 if(hRes == S_OK)
8355 /*Create the Ole Stream if necessary */
8356 OLECONVERT_CreateOleStream(pstg);
8361 /* Free allocated memory */
8362 for(i=0; i < 2; i++)
8364 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8365 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8366 pOleStreamData[i].pstrOleObjFileName = NULL;
8368 return hRes;
8371 /*************************************************************************
8372 * OleConvertIStorageToOLESTREAM [OLE32.@]
8374 * Read info on MSDN
8376 * Read info on MSDN
8378 * TODO
8379 * Still unsure of some mem fields for OLE 10 Stream
8380 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8381 * and "\001OLE" streams.
8384 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8385 LPSTORAGE pstg,
8386 LPOLESTREAM pOleStream)
8388 int i;
8389 HRESULT hRes = S_OK;
8390 IStream *pStream;
8391 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8392 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8394 TRACE("%p %p\n", pstg, pOleStream);
8396 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8398 if(pstg == NULL || pOleStream == NULL)
8400 hRes = E_INVALIDARG;
8402 if(hRes == S_OK)
8404 /* Get the ProgID */
8405 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8406 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8408 if(hRes == S_OK)
8410 /* Was it originally Ole10 */
8411 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8412 if(hRes == S_OK)
8414 IStream_Release(pStream);
8415 /* Get Presentation Data for Ole10Native */
8416 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8418 else
8420 /* Get Presentation Data (OLE20) */
8421 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8424 /* Save OLESTREAM */
8425 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8426 if(hRes == S_OK)
8428 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8433 /* Free allocated memory */
8434 for(i=0; i < 2; i++)
8436 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8439 return hRes;
8442 /***********************************************************************
8443 * GetConvertStg (OLE32.@)
8445 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8446 FIXME("unimplemented stub!\n");
8447 return E_FAIL;
8450 /******************************************************************************
8451 * StgIsStorageFile [OLE32.@]
8452 * Verify if the file contains a storage object
8454 * PARAMS
8455 * fn [ I] Filename
8457 * RETURNS
8458 * S_OK if file has magic bytes as a storage object
8459 * S_FALSE if file is not storage
8461 HRESULT WINAPI
8462 StgIsStorageFile(LPCOLESTR fn)
8464 HANDLE hf;
8465 BYTE magic[8];
8466 DWORD bytes_read;
8468 TRACE("%s\n", debugstr_w(fn));
8469 hf = CreateFileW(fn, GENERIC_READ,
8470 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8471 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8473 if (hf == INVALID_HANDLE_VALUE)
8474 return STG_E_FILENOTFOUND;
8476 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8478 WARN(" unable to read file\n");
8479 CloseHandle(hf);
8480 return S_FALSE;
8483 CloseHandle(hf);
8485 if (bytes_read != 8) {
8486 TRACE(" too short\n");
8487 return S_FALSE;
8490 if (!memcmp(magic,STORAGE_magic,8)) {
8491 TRACE(" -> YES\n");
8492 return S_OK;
8495 TRACE(" -> Invalid header.\n");
8496 return S_FALSE;
8499 /***********************************************************************
8500 * WriteClassStm (OLE32.@)
8502 * Writes a CLSID to a stream.
8504 * PARAMS
8505 * pStm [I] Stream to write to.
8506 * rclsid [I] CLSID to write.
8508 * RETURNS
8509 * Success: S_OK.
8510 * Failure: HRESULT code.
8512 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8514 TRACE("(%p,%p)\n",pStm,rclsid);
8516 if (!pStm || !rclsid)
8517 return E_INVALIDARG;
8519 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8522 /***********************************************************************
8523 * ReadClassStm (OLE32.@)
8525 * Reads a CLSID from a stream.
8527 * PARAMS
8528 * pStm [I] Stream to read from.
8529 * rclsid [O] CLSID to read.
8531 * RETURNS
8532 * Success: S_OK.
8533 * Failure: HRESULT code.
8535 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8537 ULONG nbByte;
8538 HRESULT res;
8540 TRACE("(%p,%p)\n",pStm,pclsid);
8542 if (!pStm || !pclsid)
8543 return E_INVALIDARG;
8545 /* clear the output args */
8546 *pclsid = CLSID_NULL;
8548 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8550 if (FAILED(res))
8551 return res;
8553 if (nbByte != sizeof(CLSID))
8554 return STG_E_READFAULT;
8555 else
8556 return S_OK;