comdlg32: Fix some alignment issues in the Dutch translation.
[wine/hramrach.git] / dlls / ole32 / storage32.c
blob22d33b1566e2733733ec449c1871d6d3dc56a1a9
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 /* Read and save the index of all blocks in this stream. */
5030 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5032 ULONG next_sector, next_offset;
5033 HRESULT hr;
5034 struct BlockChainRun *last_run;
5036 if (This->indexCacheLen == 0)
5038 last_run = NULL;
5039 next_offset = 0;
5040 next_sector = BlockChainStream_GetHeadOfChain(This);
5042 else
5044 last_run = &This->indexCache[This->indexCacheLen-1];
5045 next_offset = last_run->lastOffset+1;
5046 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5047 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5048 &next_sector);
5049 if (FAILED(hr)) return hr;
5052 while (next_sector != BLOCK_END_OF_CHAIN)
5054 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5056 /* Add the current block to the cache. */
5057 if (This->indexCacheSize == 0)
5059 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5060 if (!This->indexCache) return E_OUTOFMEMORY;
5061 This->indexCacheSize = 16;
5063 else if (This->indexCacheSize == This->indexCacheLen)
5065 struct BlockChainRun *new_cache;
5066 ULONG new_size;
5068 new_size = This->indexCacheSize * 2;
5069 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5070 if (!new_cache) return E_OUTOFMEMORY;
5071 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5073 HeapFree(GetProcessHeap(), 0, This->indexCache);
5074 This->indexCache = new_cache;
5075 This->indexCacheSize = new_size;
5078 This->indexCacheLen++;
5079 last_run = &This->indexCache[This->indexCacheLen-1];
5080 last_run->firstSector = next_sector;
5081 last_run->firstOffset = next_offset;
5084 last_run->lastOffset = next_offset;
5086 /* Find the next block. */
5087 next_offset++;
5088 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5089 if (FAILED(hr)) return hr;
5092 if (This->indexCacheLen)
5094 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5095 This->numBlocks = last_run->lastOffset+1;
5097 else
5099 This->tailIndex = BLOCK_END_OF_CHAIN;
5100 This->numBlocks = 0;
5103 return S_OK;
5106 /* Locate the nth block in this stream. */
5107 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5109 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5110 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5112 if (offset >= This->numBlocks)
5113 return BLOCK_END_OF_CHAIN;
5115 while (min_run < max_run)
5117 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5118 if (offset < This->indexCache[run_to_check].firstOffset)
5120 max_offset = This->indexCache[run_to_check].firstOffset-1;
5121 max_run = run_to_check-1;
5123 else if (offset > This->indexCache[run_to_check].lastOffset)
5125 min_offset = This->indexCache[run_to_check].lastOffset+1;
5126 min_run = run_to_check+1;
5128 else
5129 /* Block is in this run. */
5130 min_run = max_run = run_to_check;
5133 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5136 BlockChainStream* BlockChainStream_Construct(
5137 StorageImpl* parentStorage,
5138 ULONG* headOfStreamPlaceHolder,
5139 DirRef dirEntry)
5141 BlockChainStream* newStream;
5143 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5145 newStream->parentStorage = parentStorage;
5146 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5147 newStream->ownerDirEntry = dirEntry;
5148 newStream->indexCache = NULL;
5149 newStream->indexCacheLen = 0;
5150 newStream->indexCacheSize = 0;
5152 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5154 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5155 HeapFree(GetProcessHeap(), 0, newStream);
5156 return NULL;
5159 return newStream;
5162 void BlockChainStream_Destroy(BlockChainStream* This)
5164 if (This)
5165 HeapFree(GetProcessHeap(), 0, This->indexCache);
5166 HeapFree(GetProcessHeap(), 0, This);
5169 /******************************************************************************
5170 * BlockChainStream_GetHeadOfChain
5172 * Returns the head of this stream chain.
5173 * Some special chains don't have directory entries, their heads are kept in
5174 * This->headOfStreamPlaceHolder.
5177 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
5179 DirEntry chainEntry;
5180 HRESULT hr;
5182 if (This->headOfStreamPlaceHolder != 0)
5183 return *(This->headOfStreamPlaceHolder);
5185 if (This->ownerDirEntry != DIRENTRY_NULL)
5187 hr = StorageImpl_ReadDirEntry(
5188 This->parentStorage,
5189 This->ownerDirEntry,
5190 &chainEntry);
5192 if (SUCCEEDED(hr))
5194 return chainEntry.startingBlock;
5198 return BLOCK_END_OF_CHAIN;
5201 /******************************************************************************
5202 * BlockChainStream_GetCount
5204 * Returns the number of blocks that comprises this chain.
5205 * This is not the size of the stream as the last block may not be full!
5207 * FIXME: Use the cache to get this information.
5209 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
5211 ULONG blockIndex;
5212 ULONG count = 0;
5214 blockIndex = BlockChainStream_GetHeadOfChain(This);
5216 while (blockIndex != BLOCK_END_OF_CHAIN)
5218 count++;
5220 if(FAILED(StorageImpl_GetNextBlockInChain(
5221 This->parentStorage,
5222 blockIndex,
5223 &blockIndex)))
5224 return 0;
5227 return count;
5230 /******************************************************************************
5231 * BlockChainStream_ReadAt
5233 * Reads a specified number of bytes from this chain at the specified offset.
5234 * bytesRead may be NULL.
5235 * Failure will be returned if the specified number of bytes has not been read.
5237 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
5238 ULARGE_INTEGER offset,
5239 ULONG size,
5240 void* buffer,
5241 ULONG* bytesRead)
5243 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5244 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5245 ULONG bytesToReadInBuffer;
5246 ULONG blockIndex;
5247 BYTE* bufferWalker;
5249 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
5252 * Find the first block in the stream that contains part of the buffer.
5254 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5256 if (blockIndex == BLOCK_END_OF_CHAIN)
5257 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
5260 * Start reading the buffer.
5262 *bytesRead = 0;
5263 bufferWalker = buffer;
5265 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5267 ULARGE_INTEGER ulOffset;
5268 DWORD bytesReadAt;
5270 * Calculate how many bytes we can copy from this big block.
5272 bytesToReadInBuffer =
5273 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5275 TRACE("block %i\n",blockIndex);
5276 ulOffset.u.HighPart = 0;
5277 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5278 offsetInBlock;
5280 StorageImpl_ReadAt(This->parentStorage,
5281 ulOffset,
5282 bufferWalker,
5283 bytesToReadInBuffer,
5284 &bytesReadAt);
5286 * Step to the next big block.
5288 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
5289 return STG_E_DOCFILECORRUPT;
5291 bufferWalker += bytesReadAt;
5292 size -= bytesReadAt;
5293 *bytesRead += bytesReadAt;
5294 offsetInBlock = 0; /* There is no offset on the next block */
5296 if (bytesToReadInBuffer != bytesReadAt)
5297 break;
5300 return (size == 0) ? S_OK : STG_E_READFAULT;
5303 /******************************************************************************
5304 * BlockChainStream_WriteAt
5306 * Writes the specified number of bytes to this chain at the specified offset.
5307 * Will fail if not all specified number of bytes have been written.
5309 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
5310 ULARGE_INTEGER offset,
5311 ULONG size,
5312 const void* buffer,
5313 ULONG* bytesWritten)
5315 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
5316 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
5317 ULONG bytesToWrite;
5318 ULONG blockIndex;
5319 const BYTE* bufferWalker;
5322 * Find the first block in the stream that contains part of the buffer.
5324 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
5326 /* BlockChainStream_SetSize should have already been called to ensure we have
5327 * enough blocks in the chain to write into */
5328 if (blockIndex == BLOCK_END_OF_CHAIN)
5330 ERR("not enough blocks in chain to write data\n");
5331 return STG_E_DOCFILECORRUPT;
5334 *bytesWritten = 0;
5335 bufferWalker = buffer;
5337 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5339 ULARGE_INTEGER ulOffset;
5340 DWORD bytesWrittenAt;
5342 * Calculate how many bytes we can copy from this big block.
5344 bytesToWrite =
5345 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
5347 TRACE("block %i\n",blockIndex);
5348 ulOffset.u.HighPart = 0;
5349 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
5350 offsetInBlock;
5352 StorageImpl_WriteAt(This->parentStorage,
5353 ulOffset,
5354 bufferWalker,
5355 bytesToWrite,
5356 &bytesWrittenAt);
5359 * Step to the next big block.
5361 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
5362 &blockIndex)))
5363 return STG_E_DOCFILECORRUPT;
5365 bufferWalker += bytesWrittenAt;
5366 size -= bytesWrittenAt;
5367 *bytesWritten += bytesWrittenAt;
5368 offsetInBlock = 0; /* There is no offset on the next block */
5370 if (bytesWrittenAt != bytesToWrite)
5371 break;
5374 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5377 /******************************************************************************
5378 * BlockChainStream_Shrink
5380 * Shrinks this chain in the big block depot.
5382 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
5383 ULARGE_INTEGER newSize)
5385 ULONG blockIndex;
5386 ULONG numBlocks;
5389 * Figure out how many blocks are needed to contain the new size
5391 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5393 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5394 numBlocks++;
5396 if (numBlocks)
5399 * Go to the new end of chain
5401 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
5403 /* Mark the new end of chain */
5404 StorageImpl_SetNextBlockInChain(
5405 This->parentStorage,
5406 blockIndex,
5407 BLOCK_END_OF_CHAIN);
5409 This->tailIndex = blockIndex;
5411 else
5413 if (This->headOfStreamPlaceHolder != 0)
5415 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
5417 else
5419 DirEntry chainEntry;
5420 assert(This->ownerDirEntry != DIRENTRY_NULL);
5422 StorageImpl_ReadDirEntry(
5423 This->parentStorage,
5424 This->ownerDirEntry,
5425 &chainEntry);
5427 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
5429 StorageImpl_WriteDirEntry(
5430 This->parentStorage,
5431 This->ownerDirEntry,
5432 &chainEntry);
5435 This->tailIndex = BLOCK_END_OF_CHAIN;
5438 This->numBlocks = numBlocks;
5441 * Mark the extra blocks as free
5443 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
5445 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
5446 StorageImpl_FreeBigBlock(This->parentStorage,
5447 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
5448 if (last_run->lastOffset == last_run->firstOffset)
5449 This->indexCacheLen--;
5450 else
5451 last_run->lastOffset--;
5454 return TRUE;
5457 /******************************************************************************
5458 * BlockChainStream_Enlarge
5460 * Grows this chain in the big block depot.
5462 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
5463 ULARGE_INTEGER newSize)
5465 ULONG blockIndex, currentBlock;
5466 ULONG newNumBlocks;
5467 ULONG oldNumBlocks = 0;
5469 blockIndex = BlockChainStream_GetHeadOfChain(This);
5472 * Empty chain. Create the head.
5474 if (blockIndex == BLOCK_END_OF_CHAIN)
5476 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5477 StorageImpl_SetNextBlockInChain(This->parentStorage,
5478 blockIndex,
5479 BLOCK_END_OF_CHAIN);
5481 if (This->headOfStreamPlaceHolder != 0)
5483 *(This->headOfStreamPlaceHolder) = blockIndex;
5485 else
5487 DirEntry chainEntry;
5488 assert(This->ownerDirEntry != DIRENTRY_NULL);
5490 StorageImpl_ReadDirEntry(
5491 This->parentStorage,
5492 This->ownerDirEntry,
5493 &chainEntry);
5495 chainEntry.startingBlock = blockIndex;
5497 StorageImpl_WriteDirEntry(
5498 This->parentStorage,
5499 This->ownerDirEntry,
5500 &chainEntry);
5503 This->tailIndex = blockIndex;
5504 This->numBlocks = 1;
5508 * Figure out how many blocks are needed to contain this stream
5510 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
5512 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
5513 newNumBlocks++;
5516 * Go to the current end of chain
5518 if (This->tailIndex == BLOCK_END_OF_CHAIN)
5520 currentBlock = blockIndex;
5522 while (blockIndex != BLOCK_END_OF_CHAIN)
5524 This->numBlocks++;
5525 currentBlock = blockIndex;
5527 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
5528 &blockIndex)))
5529 return FALSE;
5532 This->tailIndex = currentBlock;
5535 currentBlock = This->tailIndex;
5536 oldNumBlocks = This->numBlocks;
5539 * Add new blocks to the chain
5541 if (oldNumBlocks < newNumBlocks)
5543 while (oldNumBlocks < newNumBlocks)
5545 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5547 StorageImpl_SetNextBlockInChain(
5548 This->parentStorage,
5549 currentBlock,
5550 blockIndex);
5552 StorageImpl_SetNextBlockInChain(
5553 This->parentStorage,
5554 blockIndex,
5555 BLOCK_END_OF_CHAIN);
5557 currentBlock = blockIndex;
5558 oldNumBlocks++;
5561 This->tailIndex = blockIndex;
5562 This->numBlocks = newNumBlocks;
5565 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
5566 return FALSE;
5568 return TRUE;
5571 /******************************************************************************
5572 * BlockChainStream_SetSize
5574 * Sets the size of this stream. The big block depot will be updated.
5575 * The file will grow if we grow the chain.
5577 * TODO: Free the actual blocks in the file when we shrink the chain.
5578 * Currently, the blocks are still in the file. So the file size
5579 * doesn't shrink even if we shrink streams.
5581 BOOL BlockChainStream_SetSize(
5582 BlockChainStream* This,
5583 ULARGE_INTEGER newSize)
5585 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
5587 if (newSize.u.LowPart == size.u.LowPart)
5588 return TRUE;
5590 if (newSize.u.LowPart < size.u.LowPart)
5592 BlockChainStream_Shrink(This, newSize);
5594 else
5596 BlockChainStream_Enlarge(This, newSize);
5599 return TRUE;
5602 /******************************************************************************
5603 * BlockChainStream_GetSize
5605 * Returns the size of this chain.
5606 * Will return the block count if this chain doesn't have a directory entry.
5608 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
5610 DirEntry chainEntry;
5612 if(This->headOfStreamPlaceHolder == NULL)
5615 * This chain has a directory entry so use the size value from there.
5617 StorageImpl_ReadDirEntry(
5618 This->parentStorage,
5619 This->ownerDirEntry,
5620 &chainEntry);
5622 return chainEntry.size;
5624 else
5627 * this chain is a chain that does not have a directory entry, figure out the
5628 * size by making the product number of used blocks times the
5629 * size of them
5631 ULARGE_INTEGER result;
5632 result.u.HighPart = 0;
5634 result.u.LowPart =
5635 BlockChainStream_GetCount(This) *
5636 This->parentStorage->bigBlockSize;
5638 return result;
5642 /******************************************************************************
5643 ** SmallBlockChainStream implementation
5646 SmallBlockChainStream* SmallBlockChainStream_Construct(
5647 StorageImpl* parentStorage,
5648 ULONG* headOfStreamPlaceHolder,
5649 DirRef dirEntry)
5651 SmallBlockChainStream* newStream;
5653 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
5655 newStream->parentStorage = parentStorage;
5656 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5657 newStream->ownerDirEntry = dirEntry;
5659 return newStream;
5662 void SmallBlockChainStream_Destroy(
5663 SmallBlockChainStream* This)
5665 HeapFree(GetProcessHeap(), 0, This);
5668 /******************************************************************************
5669 * SmallBlockChainStream_GetHeadOfChain
5671 * Returns the head of this chain of small blocks.
5673 static ULONG SmallBlockChainStream_GetHeadOfChain(
5674 SmallBlockChainStream* This)
5676 DirEntry chainEntry;
5677 HRESULT hr;
5679 if (This->headOfStreamPlaceHolder != NULL)
5680 return *(This->headOfStreamPlaceHolder);
5682 if (This->ownerDirEntry)
5684 hr = StorageImpl_ReadDirEntry(
5685 This->parentStorage,
5686 This->ownerDirEntry,
5687 &chainEntry);
5689 if (SUCCEEDED(hr))
5691 return chainEntry.startingBlock;
5696 return BLOCK_END_OF_CHAIN;
5699 /******************************************************************************
5700 * SmallBlockChainStream_GetNextBlockInChain
5702 * Returns the index of the next small block in this chain.
5704 * Return Values:
5705 * - BLOCK_END_OF_CHAIN: end of this chain
5706 * - BLOCK_UNUSED: small block 'blockIndex' is free
5708 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5709 SmallBlockChainStream* This,
5710 ULONG blockIndex,
5711 ULONG* nextBlockInChain)
5713 ULARGE_INTEGER offsetOfBlockInDepot;
5714 DWORD buffer;
5715 ULONG bytesRead;
5716 HRESULT res;
5718 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5720 offsetOfBlockInDepot.u.HighPart = 0;
5721 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5724 * Read those bytes in the buffer from the small block file.
5726 res = BlockChainStream_ReadAt(
5727 This->parentStorage->smallBlockDepotChain,
5728 offsetOfBlockInDepot,
5729 sizeof(DWORD),
5730 &buffer,
5731 &bytesRead);
5733 if (SUCCEEDED(res))
5735 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5736 return S_OK;
5739 return res;
5742 /******************************************************************************
5743 * SmallBlockChainStream_SetNextBlockInChain
5745 * Writes the index of the next block of the specified block in the small
5746 * block depot.
5747 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5748 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5750 static void SmallBlockChainStream_SetNextBlockInChain(
5751 SmallBlockChainStream* This,
5752 ULONG blockIndex,
5753 ULONG nextBlock)
5755 ULARGE_INTEGER offsetOfBlockInDepot;
5756 DWORD buffer;
5757 ULONG bytesWritten;
5759 offsetOfBlockInDepot.u.HighPart = 0;
5760 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5762 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5765 * Read those bytes in the buffer from the small block file.
5767 BlockChainStream_WriteAt(
5768 This->parentStorage->smallBlockDepotChain,
5769 offsetOfBlockInDepot,
5770 sizeof(DWORD),
5771 &buffer,
5772 &bytesWritten);
5775 /******************************************************************************
5776 * SmallBlockChainStream_FreeBlock
5778 * Flag small block 'blockIndex' as free in the small block depot.
5780 static void SmallBlockChainStream_FreeBlock(
5781 SmallBlockChainStream* This,
5782 ULONG blockIndex)
5784 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5787 /******************************************************************************
5788 * SmallBlockChainStream_GetNextFreeBlock
5790 * Returns the index of a free small block. The small block depot will be
5791 * enlarged if necessary. The small block chain will also be enlarged if
5792 * necessary.
5794 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5795 SmallBlockChainStream* This)
5797 ULARGE_INTEGER offsetOfBlockInDepot;
5798 DWORD buffer;
5799 ULONG bytesRead;
5800 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
5801 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5802 HRESULT res = S_OK;
5803 ULONG smallBlocksPerBigBlock;
5805 offsetOfBlockInDepot.u.HighPart = 0;
5808 * Scan the small block depot for a free block
5810 while (nextBlockIndex != BLOCK_UNUSED)
5812 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5814 res = BlockChainStream_ReadAt(
5815 This->parentStorage->smallBlockDepotChain,
5816 offsetOfBlockInDepot,
5817 sizeof(DWORD),
5818 &buffer,
5819 &bytesRead);
5822 * If we run out of space for the small block depot, enlarge it
5824 if (SUCCEEDED(res))
5826 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5828 if (nextBlockIndex != BLOCK_UNUSED)
5829 blockIndex++;
5831 else
5833 ULONG count =
5834 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5836 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
5837 ULARGE_INTEGER newSize, offset;
5838 ULONG bytesWritten;
5840 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
5841 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
5844 * Initialize all the small blocks to free
5846 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5847 offset.QuadPart = count * This->parentStorage->bigBlockSize;
5848 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
5849 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
5851 StorageImpl_SaveFileHeader(This->parentStorage);
5855 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
5857 smallBlocksPerBigBlock =
5858 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5861 * Verify if we have to allocate big blocks to contain small blocks
5863 if (blockIndex % smallBlocksPerBigBlock == 0)
5865 DirEntry rootEntry;
5866 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5867 ULARGE_INTEGER old_size, size_required;
5869 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
5871 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
5873 if (size_required.QuadPart > old_size.QuadPart)
5875 BlockChainStream_SetSize(
5876 This->parentStorage->smallBlockRootChain,
5877 size_required);
5879 StorageImpl_ReadDirEntry(
5880 This->parentStorage,
5881 This->parentStorage->base.storageDirEntry,
5882 &rootEntry);
5884 rootEntry.size = size_required;
5886 StorageImpl_WriteDirEntry(
5887 This->parentStorage,
5888 This->parentStorage->base.storageDirEntry,
5889 &rootEntry);
5893 return blockIndex;
5896 /******************************************************************************
5897 * SmallBlockChainStream_ReadAt
5899 * Reads a specified number of bytes from this chain at the specified offset.
5900 * bytesRead may be NULL.
5901 * Failure will be returned if the specified number of bytes has not been read.
5903 HRESULT SmallBlockChainStream_ReadAt(
5904 SmallBlockChainStream* This,
5905 ULARGE_INTEGER offset,
5906 ULONG size,
5907 void* buffer,
5908 ULONG* bytesRead)
5910 HRESULT rc = S_OK;
5911 ULARGE_INTEGER offsetInBigBlockFile;
5912 ULONG blockNoInSequence =
5913 offset.u.LowPart / This->parentStorage->smallBlockSize;
5915 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5916 ULONG bytesToReadInBuffer;
5917 ULONG blockIndex;
5918 ULONG bytesReadFromBigBlockFile;
5919 BYTE* bufferWalker;
5922 * This should never happen on a small block file.
5924 assert(offset.u.HighPart==0);
5927 * Find the first block in the stream that contains part of the buffer.
5929 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5931 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5933 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5934 if(FAILED(rc))
5935 return rc;
5936 blockNoInSequence--;
5940 * Start reading the buffer.
5942 *bytesRead = 0;
5943 bufferWalker = buffer;
5945 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5948 * Calculate how many bytes we can copy from this small block.
5950 bytesToReadInBuffer =
5951 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5954 * Calculate the offset of the small block in the small block file.
5956 offsetInBigBlockFile.u.HighPart = 0;
5957 offsetInBigBlockFile.u.LowPart =
5958 blockIndex * This->parentStorage->smallBlockSize;
5960 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5963 * Read those bytes in the buffer from the small block file.
5964 * The small block has already been identified so it shouldn't fail
5965 * unless the file is corrupt.
5967 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5968 offsetInBigBlockFile,
5969 bytesToReadInBuffer,
5970 bufferWalker,
5971 &bytesReadFromBigBlockFile);
5973 if (FAILED(rc))
5974 return rc;
5977 * Step to the next big block.
5979 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5980 if(FAILED(rc))
5981 return STG_E_DOCFILECORRUPT;
5983 bufferWalker += bytesReadFromBigBlockFile;
5984 size -= bytesReadFromBigBlockFile;
5985 *bytesRead += bytesReadFromBigBlockFile;
5986 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5989 return (size == 0) ? S_OK : STG_E_READFAULT;
5992 /******************************************************************************
5993 * SmallBlockChainStream_WriteAt
5995 * Writes the specified number of bytes to this chain at the specified offset.
5996 * Will fail if not all specified number of bytes have been written.
5998 HRESULT SmallBlockChainStream_WriteAt(
5999 SmallBlockChainStream* This,
6000 ULARGE_INTEGER offset,
6001 ULONG size,
6002 const void* buffer,
6003 ULONG* bytesWritten)
6005 ULARGE_INTEGER offsetInBigBlockFile;
6006 ULONG blockNoInSequence =
6007 offset.u.LowPart / This->parentStorage->smallBlockSize;
6009 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6010 ULONG bytesToWriteInBuffer;
6011 ULONG blockIndex;
6012 ULONG bytesWrittenToBigBlockFile;
6013 const BYTE* bufferWalker;
6014 HRESULT res;
6017 * This should never happen on a small block file.
6019 assert(offset.u.HighPart==0);
6022 * Find the first block in the stream that contains part of the buffer.
6024 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6026 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6028 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6029 return STG_E_DOCFILECORRUPT;
6030 blockNoInSequence--;
6034 * Start writing the buffer.
6036 *bytesWritten = 0;
6037 bufferWalker = buffer;
6038 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6041 * Calculate how many bytes we can copy to this small block.
6043 bytesToWriteInBuffer =
6044 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6047 * Calculate the offset of the small block in the small block file.
6049 offsetInBigBlockFile.u.HighPart = 0;
6050 offsetInBigBlockFile.u.LowPart =
6051 blockIndex * This->parentStorage->smallBlockSize;
6053 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6056 * Write those bytes in the buffer to the small block file.
6058 res = BlockChainStream_WriteAt(
6059 This->parentStorage->smallBlockRootChain,
6060 offsetInBigBlockFile,
6061 bytesToWriteInBuffer,
6062 bufferWalker,
6063 &bytesWrittenToBigBlockFile);
6064 if (FAILED(res))
6065 return res;
6068 * Step to the next big block.
6070 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6071 &blockIndex)))
6072 return FALSE;
6073 bufferWalker += bytesWrittenToBigBlockFile;
6074 size -= bytesWrittenToBigBlockFile;
6075 *bytesWritten += bytesWrittenToBigBlockFile;
6076 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6079 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6082 /******************************************************************************
6083 * SmallBlockChainStream_Shrink
6085 * Shrinks this chain in the small block depot.
6087 static BOOL SmallBlockChainStream_Shrink(
6088 SmallBlockChainStream* This,
6089 ULARGE_INTEGER newSize)
6091 ULONG blockIndex, extraBlock;
6092 ULONG numBlocks;
6093 ULONG count = 0;
6095 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6097 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6098 numBlocks++;
6100 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6103 * Go to the new end of chain
6105 while (count < numBlocks)
6107 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6108 &blockIndex)))
6109 return FALSE;
6110 count++;
6114 * If the count is 0, we have a special case, the head of the chain was
6115 * just freed.
6117 if (count == 0)
6119 DirEntry chainEntry;
6121 StorageImpl_ReadDirEntry(This->parentStorage,
6122 This->ownerDirEntry,
6123 &chainEntry);
6125 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6127 StorageImpl_WriteDirEntry(This->parentStorage,
6128 This->ownerDirEntry,
6129 &chainEntry);
6132 * We start freeing the chain at the head block.
6134 extraBlock = blockIndex;
6136 else
6138 /* Get the next block before marking the new end */
6139 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6140 &extraBlock)))
6141 return FALSE;
6143 /* Mark the new end of chain */
6144 SmallBlockChainStream_SetNextBlockInChain(
6145 This,
6146 blockIndex,
6147 BLOCK_END_OF_CHAIN);
6151 * Mark the extra blocks as free
6153 while (extraBlock != BLOCK_END_OF_CHAIN)
6155 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
6156 &blockIndex)))
6157 return FALSE;
6158 SmallBlockChainStream_FreeBlock(This, extraBlock);
6159 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
6160 extraBlock = blockIndex;
6163 return TRUE;
6166 /******************************************************************************
6167 * SmallBlockChainStream_Enlarge
6169 * Grows this chain in the small block depot.
6171 static BOOL SmallBlockChainStream_Enlarge(
6172 SmallBlockChainStream* This,
6173 ULARGE_INTEGER newSize)
6175 ULONG blockIndex, currentBlock;
6176 ULONG newNumBlocks;
6177 ULONG oldNumBlocks = 0;
6179 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6182 * Empty chain. Create the head.
6184 if (blockIndex == BLOCK_END_OF_CHAIN)
6186 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6187 SmallBlockChainStream_SetNextBlockInChain(
6188 This,
6189 blockIndex,
6190 BLOCK_END_OF_CHAIN);
6192 if (This->headOfStreamPlaceHolder != NULL)
6194 *(This->headOfStreamPlaceHolder) = blockIndex;
6196 else
6198 DirEntry chainEntry;
6200 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
6201 &chainEntry);
6203 chainEntry.startingBlock = blockIndex;
6205 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
6206 &chainEntry);
6210 currentBlock = blockIndex;
6213 * Figure out how many blocks are needed to contain this stream
6215 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6217 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6218 newNumBlocks++;
6221 * Go to the current end of chain
6223 while (blockIndex != BLOCK_END_OF_CHAIN)
6225 oldNumBlocks++;
6226 currentBlock = blockIndex;
6227 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
6228 return FALSE;
6232 * Add new blocks to the chain
6234 while (oldNumBlocks < newNumBlocks)
6236 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
6237 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
6239 SmallBlockChainStream_SetNextBlockInChain(
6240 This,
6241 blockIndex,
6242 BLOCK_END_OF_CHAIN);
6244 currentBlock = blockIndex;
6245 oldNumBlocks++;
6248 return TRUE;
6251 /******************************************************************************
6252 * SmallBlockChainStream_SetSize
6254 * Sets the size of this stream.
6255 * The file will grow if we grow the chain.
6257 * TODO: Free the actual blocks in the file when we shrink the chain.
6258 * Currently, the blocks are still in the file. So the file size
6259 * doesn't shrink even if we shrink streams.
6261 BOOL SmallBlockChainStream_SetSize(
6262 SmallBlockChainStream* This,
6263 ULARGE_INTEGER newSize)
6265 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
6267 if (newSize.u.LowPart == size.u.LowPart)
6268 return TRUE;
6270 if (newSize.u.LowPart < size.u.LowPart)
6272 SmallBlockChainStream_Shrink(This, newSize);
6274 else
6276 SmallBlockChainStream_Enlarge(This, newSize);
6279 return TRUE;
6282 /******************************************************************************
6283 * SmallBlockChainStream_GetCount
6285 * Returns the number of small blocks that comprises this chain.
6286 * This is not the size of the stream as the last block may not be full!
6289 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
6291 ULONG blockIndex;
6292 ULONG count = 0;
6294 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6296 while(blockIndex != BLOCK_END_OF_CHAIN)
6298 count++;
6300 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
6301 blockIndex, &blockIndex)))
6302 return 0;
6305 return count;
6308 /******************************************************************************
6309 * SmallBlockChainStream_GetSize
6311 * Returns the size of this chain.
6313 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
6315 DirEntry chainEntry;
6317 if(This->headOfStreamPlaceHolder != NULL)
6319 ULARGE_INTEGER result;
6320 result.u.HighPart = 0;
6322 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
6323 This->parentStorage->smallBlockSize;
6325 return result;
6328 StorageImpl_ReadDirEntry(
6329 This->parentStorage,
6330 This->ownerDirEntry,
6331 &chainEntry);
6333 return chainEntry.size;
6336 static HRESULT create_storagefile(
6337 LPCOLESTR pwcsName,
6338 DWORD grfMode,
6339 DWORD grfAttrs,
6340 STGOPTIONS* pStgOptions,
6341 REFIID riid,
6342 void** ppstgOpen)
6344 StorageBaseImpl* newStorage = 0;
6345 HANDLE hFile = INVALID_HANDLE_VALUE;
6346 HRESULT hr = STG_E_INVALIDFLAG;
6347 DWORD shareMode;
6348 DWORD accessMode;
6349 DWORD creationMode;
6350 DWORD fileAttributes;
6351 WCHAR tempFileName[MAX_PATH];
6353 if (ppstgOpen == 0)
6354 return STG_E_INVALIDPOINTER;
6356 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
6357 return STG_E_INVALIDPARAMETER;
6359 /* if no share mode given then DENY_NONE is the default */
6360 if (STGM_SHARE_MODE(grfMode) == 0)
6361 grfMode |= STGM_SHARE_DENY_NONE;
6363 if ( FAILED( validateSTGM(grfMode) ))
6364 goto end;
6366 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
6367 switch(STGM_ACCESS_MODE(grfMode))
6369 case STGM_WRITE:
6370 case STGM_READWRITE:
6371 break;
6372 default:
6373 goto end;
6376 /* in direct mode, can only use SHARE_EXCLUSIVE */
6377 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
6378 goto end;
6380 /* but in transacted mode, any share mode is valid */
6383 * Generate a unique name.
6385 if (pwcsName == 0)
6387 WCHAR tempPath[MAX_PATH];
6388 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
6390 memset(tempPath, 0, sizeof(tempPath));
6391 memset(tempFileName, 0, sizeof(tempFileName));
6393 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
6394 tempPath[0] = '.';
6396 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
6397 pwcsName = tempFileName;
6398 else
6400 hr = STG_E_INSUFFICIENTMEMORY;
6401 goto end;
6404 creationMode = TRUNCATE_EXISTING;
6406 else
6408 creationMode = GetCreationModeFromSTGM(grfMode);
6412 * Interpret the STGM value grfMode
6414 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6415 accessMode = GetAccessModeFromSTGM(grfMode);
6417 if (grfMode & STGM_DELETEONRELEASE)
6418 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
6419 else
6420 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6422 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
6424 static int fixme;
6425 if (!fixme++)
6426 FIXME("Storage share mode not implemented.\n");
6429 *ppstgOpen = 0;
6431 hFile = CreateFileW(pwcsName,
6432 accessMode,
6433 shareMode,
6434 NULL,
6435 creationMode,
6436 fileAttributes,
6439 if (hFile == INVALID_HANDLE_VALUE)
6441 if(GetLastError() == ERROR_FILE_EXISTS)
6442 hr = STG_E_FILEALREADYEXISTS;
6443 else
6444 hr = E_FAIL;
6445 goto end;
6449 * Allocate and initialize the new IStorage32object.
6451 hr = Storage_Construct(
6452 hFile,
6453 pwcsName,
6454 NULL,
6455 grfMode,
6456 TRUE,
6457 TRUE,
6458 pStgOptions->ulSectorSize,
6459 &newStorage);
6461 if (FAILED(hr))
6463 goto end;
6466 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
6468 IStorage_Release((IStorage*)newStorage);
6470 end:
6471 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
6473 return hr;
6476 /******************************************************************************
6477 * StgCreateDocfile [OLE32.@]
6478 * Creates a new compound file storage object
6480 * PARAMS
6481 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
6482 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
6483 * reserved [ ?] unused?, usually 0
6484 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
6486 * RETURNS
6487 * S_OK if the file was successfully created
6488 * some STG_E_ value if error
6489 * NOTES
6490 * if pwcsName is NULL, create file with new unique name
6491 * the function can returns
6492 * STG_S_CONVERTED if the specified file was successfully converted to storage format
6493 * (unrealized now)
6495 HRESULT WINAPI StgCreateDocfile(
6496 LPCOLESTR pwcsName,
6497 DWORD grfMode,
6498 DWORD reserved,
6499 IStorage **ppstgOpen)
6501 STGOPTIONS stgoptions = {1, 0, 512};
6503 TRACE("(%s, %x, %d, %p)\n",
6504 debugstr_w(pwcsName), grfMode,
6505 reserved, ppstgOpen);
6507 if (ppstgOpen == 0)
6508 return STG_E_INVALIDPOINTER;
6509 if (reserved != 0)
6510 return STG_E_INVALIDPARAMETER;
6512 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
6515 /******************************************************************************
6516 * StgCreateStorageEx [OLE32.@]
6518 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6520 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6521 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6523 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
6525 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
6526 return STG_E_INVALIDPARAMETER;
6529 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
6531 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
6532 return STG_E_INVALIDPARAMETER;
6535 if (stgfmt == STGFMT_FILE)
6537 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6538 return STG_E_INVALIDPARAMETER;
6541 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
6543 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
6547 ERR("Invalid stgfmt argument\n");
6548 return STG_E_INVALIDPARAMETER;
6551 /******************************************************************************
6552 * StgCreatePropSetStg [OLE32.@]
6554 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
6555 IPropertySetStorage **ppPropSetStg)
6557 HRESULT hr;
6559 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
6560 if (reserved)
6561 hr = STG_E_INVALIDPARAMETER;
6562 else
6563 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
6564 (void**)ppPropSetStg);
6565 return hr;
6568 /******************************************************************************
6569 * StgOpenStorageEx [OLE32.@]
6571 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
6573 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
6574 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
6576 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
6578 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
6579 return STG_E_INVALIDPARAMETER;
6582 switch (stgfmt)
6584 case STGFMT_FILE:
6585 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
6586 return STG_E_INVALIDPARAMETER;
6588 case STGFMT_STORAGE:
6589 break;
6591 case STGFMT_DOCFILE:
6592 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
6594 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
6595 return STG_E_INVALIDPARAMETER;
6597 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
6598 break;
6600 case STGFMT_ANY:
6601 WARN("STGFMT_ANY assuming storage\n");
6602 break;
6604 default:
6605 return STG_E_INVALIDPARAMETER;
6608 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
6612 /******************************************************************************
6613 * StgOpenStorage [OLE32.@]
6615 HRESULT WINAPI StgOpenStorage(
6616 const OLECHAR *pwcsName,
6617 IStorage *pstgPriority,
6618 DWORD grfMode,
6619 SNB snbExclude,
6620 DWORD reserved,
6621 IStorage **ppstgOpen)
6623 StorageBaseImpl* newStorage = 0;
6624 HRESULT hr = S_OK;
6625 HANDLE hFile = 0;
6626 DWORD shareMode;
6627 DWORD accessMode;
6629 TRACE("(%s, %p, %x, %p, %d, %p)\n",
6630 debugstr_w(pwcsName), pstgPriority, grfMode,
6631 snbExclude, reserved, ppstgOpen);
6633 if (pwcsName == 0)
6635 hr = STG_E_INVALIDNAME;
6636 goto end;
6639 if (ppstgOpen == 0)
6641 hr = STG_E_INVALIDPOINTER;
6642 goto end;
6645 if (reserved)
6647 hr = STG_E_INVALIDPARAMETER;
6648 goto end;
6651 if (grfMode & STGM_PRIORITY)
6653 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
6654 return STG_E_INVALIDFLAG;
6655 if (grfMode & STGM_DELETEONRELEASE)
6656 return STG_E_INVALIDFUNCTION;
6657 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
6658 return STG_E_INVALIDFLAG;
6659 grfMode &= ~0xf0; /* remove the existing sharing mode */
6660 grfMode |= STGM_SHARE_DENY_NONE;
6662 /* STGM_PRIORITY stops other IStorage objects on the same file from
6663 * committing until the STGM_PRIORITY IStorage is closed. it also
6664 * stops non-transacted mode StgOpenStorage calls with write access from
6665 * succeeding. obviously, both of these cannot be achieved through just
6666 * file share flags */
6667 FIXME("STGM_PRIORITY mode not implemented correctly\n");
6671 * Validate the sharing mode
6673 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
6674 switch(STGM_SHARE_MODE(grfMode))
6676 case STGM_SHARE_EXCLUSIVE:
6677 case STGM_SHARE_DENY_WRITE:
6678 break;
6679 default:
6680 hr = STG_E_INVALIDFLAG;
6681 goto end;
6684 if ( FAILED( validateSTGM(grfMode) ) ||
6685 (grfMode&STGM_CREATE))
6687 hr = STG_E_INVALIDFLAG;
6688 goto end;
6691 /* shared reading requires transacted mode */
6692 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6693 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6694 !(grfMode&STGM_TRANSACTED) )
6696 hr = STG_E_INVALIDFLAG;
6697 goto end;
6701 * Interpret the STGM value grfMode
6703 shareMode = GetShareModeFromSTGM(grfMode);
6704 accessMode = GetAccessModeFromSTGM(grfMode);
6706 *ppstgOpen = 0;
6708 hFile = CreateFileW( pwcsName,
6709 accessMode,
6710 shareMode,
6711 NULL,
6712 OPEN_EXISTING,
6713 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6716 if (hFile==INVALID_HANDLE_VALUE)
6718 DWORD last_error = GetLastError();
6720 hr = E_FAIL;
6722 switch (last_error)
6724 case ERROR_FILE_NOT_FOUND:
6725 hr = STG_E_FILENOTFOUND;
6726 break;
6728 case ERROR_PATH_NOT_FOUND:
6729 hr = STG_E_PATHNOTFOUND;
6730 break;
6732 case ERROR_ACCESS_DENIED:
6733 case ERROR_WRITE_PROTECT:
6734 hr = STG_E_ACCESSDENIED;
6735 break;
6737 case ERROR_SHARING_VIOLATION:
6738 hr = STG_E_SHAREVIOLATION;
6739 break;
6741 default:
6742 hr = E_FAIL;
6745 goto end;
6749 * Refuse to open the file if it's too small to be a structured storage file
6750 * FIXME: verify the file when reading instead of here
6752 if (GetFileSize(hFile, NULL) < 0x100)
6754 CloseHandle(hFile);
6755 hr = STG_E_FILEALREADYEXISTS;
6756 goto end;
6760 * Allocate and initialize the new IStorage32object.
6762 hr = Storage_Construct(
6763 hFile,
6764 pwcsName,
6765 NULL,
6766 grfMode,
6767 TRUE,
6768 FALSE,
6769 512,
6770 &newStorage);
6772 if (FAILED(hr))
6775 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6777 if(hr == STG_E_INVALIDHEADER)
6778 hr = STG_E_FILEALREADYEXISTS;
6779 goto end;
6783 * Get an "out" pointer for the caller.
6785 *ppstgOpen = (IStorage*)newStorage;
6787 end:
6788 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6789 return hr;
6792 /******************************************************************************
6793 * StgCreateDocfileOnILockBytes [OLE32.@]
6795 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6796 ILockBytes *plkbyt,
6797 DWORD grfMode,
6798 DWORD reserved,
6799 IStorage** ppstgOpen)
6801 StorageBaseImpl* newStorage = 0;
6802 HRESULT hr = S_OK;
6804 if ((ppstgOpen == 0) || (plkbyt == 0))
6805 return STG_E_INVALIDPOINTER;
6808 * Allocate and initialize the new IStorage object.
6810 hr = Storage_Construct(
6813 plkbyt,
6814 grfMode,
6815 FALSE,
6816 TRUE,
6817 512,
6818 &newStorage);
6820 if (FAILED(hr))
6822 return hr;
6826 * Get an "out" pointer for the caller.
6828 *ppstgOpen = (IStorage*)newStorage;
6830 return hr;
6833 /******************************************************************************
6834 * StgOpenStorageOnILockBytes [OLE32.@]
6836 HRESULT WINAPI StgOpenStorageOnILockBytes(
6837 ILockBytes *plkbyt,
6838 IStorage *pstgPriority,
6839 DWORD grfMode,
6840 SNB snbExclude,
6841 DWORD reserved,
6842 IStorage **ppstgOpen)
6844 StorageBaseImpl* newStorage = 0;
6845 HRESULT hr = S_OK;
6847 if ((plkbyt == 0) || (ppstgOpen == 0))
6848 return STG_E_INVALIDPOINTER;
6850 if ( FAILED( validateSTGM(grfMode) ))
6851 return STG_E_INVALIDFLAG;
6853 *ppstgOpen = 0;
6856 * Allocate and initialize the new IStorage object.
6858 hr = Storage_Construct(
6861 plkbyt,
6862 grfMode,
6863 FALSE,
6864 FALSE,
6865 512,
6866 &newStorage);
6868 if (FAILED(hr))
6870 return hr;
6874 * Get an "out" pointer for the caller.
6876 *ppstgOpen = (IStorage*)newStorage;
6878 return hr;
6881 /******************************************************************************
6882 * StgSetTimes [ole32.@]
6883 * StgSetTimes [OLE32.@]
6887 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6888 FILETIME const *patime, FILETIME const *pmtime)
6890 IStorage *stg = NULL;
6891 HRESULT r;
6893 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6895 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6896 0, 0, &stg);
6897 if( SUCCEEDED(r) )
6899 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6900 IStorage_Release(stg);
6903 return r;
6906 /******************************************************************************
6907 * StgIsStorageILockBytes [OLE32.@]
6909 * Determines if the ILockBytes contains a storage object.
6911 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6913 BYTE sig[8];
6914 ULARGE_INTEGER offset;
6916 offset.u.HighPart = 0;
6917 offset.u.LowPart = 0;
6919 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6921 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6922 return S_OK;
6924 return S_FALSE;
6927 /******************************************************************************
6928 * WriteClassStg [OLE32.@]
6930 * This method will store the specified CLSID in the specified storage object
6932 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6934 HRESULT hRes;
6936 if(!pStg)
6937 return E_INVALIDARG;
6939 if(!rclsid)
6940 return STG_E_INVALIDPOINTER;
6942 hRes = IStorage_SetClass(pStg, rclsid);
6944 return hRes;
6947 /***********************************************************************
6948 * ReadClassStg (OLE32.@)
6950 * This method reads the CLSID previously written to a storage object with
6951 * the WriteClassStg.
6953 * PARAMS
6954 * pstg [I] IStorage pointer
6955 * pclsid [O] Pointer to where the CLSID is written
6957 * RETURNS
6958 * Success: S_OK.
6959 * Failure: HRESULT code.
6961 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6963 STATSTG pstatstg;
6964 HRESULT hRes;
6966 TRACE("(%p, %p)\n", pstg, pclsid);
6968 if(!pstg || !pclsid)
6969 return E_INVALIDARG;
6972 * read a STATSTG structure (contains the clsid) from the storage
6974 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6976 if(SUCCEEDED(hRes))
6977 *pclsid=pstatstg.clsid;
6979 return hRes;
6982 /***********************************************************************
6983 * OleLoadFromStream (OLE32.@)
6985 * This function loads an object from stream
6987 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6989 CLSID clsid;
6990 HRESULT res;
6991 LPPERSISTSTREAM xstm;
6993 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6995 res=ReadClassStm(pStm,&clsid);
6996 if (FAILED(res))
6997 return res;
6998 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6999 if (FAILED(res))
7000 return res;
7001 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7002 if (FAILED(res)) {
7003 IUnknown_Release((IUnknown*)*ppvObj);
7004 return res;
7006 res=IPersistStream_Load(xstm,pStm);
7007 IPersistStream_Release(xstm);
7008 /* FIXME: all refcounts ok at this point? I think they should be:
7009 * pStm : unchanged
7010 * ppvObj : 1
7011 * xstm : 0 (released)
7013 return res;
7016 /***********************************************************************
7017 * OleSaveToStream (OLE32.@)
7019 * This function saves an object with the IPersistStream interface on it
7020 * to the specified stream.
7022 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7025 CLSID clsid;
7026 HRESULT res;
7028 TRACE("(%p,%p)\n",pPStm,pStm);
7030 res=IPersistStream_GetClassID(pPStm,&clsid);
7032 if (SUCCEEDED(res)){
7034 res=WriteClassStm(pStm,&clsid);
7036 if (SUCCEEDED(res))
7038 res=IPersistStream_Save(pPStm,pStm,TRUE);
7041 TRACE("Finished Save\n");
7042 return res;
7045 /****************************************************************************
7046 * This method validate a STGM parameter that can contain the values below
7048 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7049 * The stgm values contained in 0xffff0000 are bitmasks.
7051 * STGM_DIRECT 0x00000000
7052 * STGM_TRANSACTED 0x00010000
7053 * STGM_SIMPLE 0x08000000
7055 * STGM_READ 0x00000000
7056 * STGM_WRITE 0x00000001
7057 * STGM_READWRITE 0x00000002
7059 * STGM_SHARE_DENY_NONE 0x00000040
7060 * STGM_SHARE_DENY_READ 0x00000030
7061 * STGM_SHARE_DENY_WRITE 0x00000020
7062 * STGM_SHARE_EXCLUSIVE 0x00000010
7064 * STGM_PRIORITY 0x00040000
7065 * STGM_DELETEONRELEASE 0x04000000
7067 * STGM_CREATE 0x00001000
7068 * STGM_CONVERT 0x00020000
7069 * STGM_FAILIFTHERE 0x00000000
7071 * STGM_NOSCRATCH 0x00100000
7072 * STGM_NOSNAPSHOT 0x00200000
7074 static HRESULT validateSTGM(DWORD stgm)
7076 DWORD access = STGM_ACCESS_MODE(stgm);
7077 DWORD share = STGM_SHARE_MODE(stgm);
7078 DWORD create = STGM_CREATE_MODE(stgm);
7080 if (stgm&~STGM_KNOWN_FLAGS)
7082 ERR("unknown flags %08x\n", stgm);
7083 return E_FAIL;
7086 switch (access)
7088 case STGM_READ:
7089 case STGM_WRITE:
7090 case STGM_READWRITE:
7091 break;
7092 default:
7093 return E_FAIL;
7096 switch (share)
7098 case STGM_SHARE_DENY_NONE:
7099 case STGM_SHARE_DENY_READ:
7100 case STGM_SHARE_DENY_WRITE:
7101 case STGM_SHARE_EXCLUSIVE:
7102 break;
7103 default:
7104 return E_FAIL;
7107 switch (create)
7109 case STGM_CREATE:
7110 case STGM_FAILIFTHERE:
7111 break;
7112 default:
7113 return E_FAIL;
7117 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
7119 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
7120 return E_FAIL;
7123 * STGM_CREATE | STGM_CONVERT
7124 * if both are false, STGM_FAILIFTHERE is set to TRUE
7126 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
7127 return E_FAIL;
7130 * STGM_NOSCRATCH requires STGM_TRANSACTED
7132 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
7133 return E_FAIL;
7136 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
7137 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
7139 if ( (stgm & STGM_NOSNAPSHOT) &&
7140 (!(stgm & STGM_TRANSACTED) ||
7141 share == STGM_SHARE_EXCLUSIVE ||
7142 share == STGM_SHARE_DENY_WRITE) )
7143 return E_FAIL;
7145 return S_OK;
7148 /****************************************************************************
7149 * GetShareModeFromSTGM
7151 * This method will return a share mode flag from a STGM value.
7152 * The STGM value is assumed valid.
7154 static DWORD GetShareModeFromSTGM(DWORD stgm)
7156 switch (STGM_SHARE_MODE(stgm))
7158 case STGM_SHARE_DENY_NONE:
7159 return FILE_SHARE_READ | FILE_SHARE_WRITE;
7160 case STGM_SHARE_DENY_READ:
7161 return FILE_SHARE_WRITE;
7162 case STGM_SHARE_DENY_WRITE:
7163 return FILE_SHARE_READ;
7164 case STGM_SHARE_EXCLUSIVE:
7165 return 0;
7167 ERR("Invalid share mode!\n");
7168 assert(0);
7169 return 0;
7172 /****************************************************************************
7173 * GetAccessModeFromSTGM
7175 * This method will return an access mode flag from a STGM value.
7176 * The STGM value is assumed valid.
7178 static DWORD GetAccessModeFromSTGM(DWORD stgm)
7180 switch (STGM_ACCESS_MODE(stgm))
7182 case STGM_READ:
7183 return GENERIC_READ;
7184 case STGM_WRITE:
7185 case STGM_READWRITE:
7186 return GENERIC_READ | GENERIC_WRITE;
7188 ERR("Invalid access mode!\n");
7189 assert(0);
7190 return 0;
7193 /****************************************************************************
7194 * GetCreationModeFromSTGM
7196 * This method will return a creation mode flag from a STGM value.
7197 * The STGM value is assumed valid.
7199 static DWORD GetCreationModeFromSTGM(DWORD stgm)
7201 switch(STGM_CREATE_MODE(stgm))
7203 case STGM_CREATE:
7204 return CREATE_ALWAYS;
7205 case STGM_CONVERT:
7206 FIXME("STGM_CONVERT not implemented!\n");
7207 return CREATE_NEW;
7208 case STGM_FAILIFTHERE:
7209 return CREATE_NEW;
7211 ERR("Invalid create mode!\n");
7212 assert(0);
7213 return 0;
7217 /*************************************************************************
7218 * OLECONVERT_LoadOLE10 [Internal]
7220 * Loads the OLE10 STREAM to memory
7222 * PARAMS
7223 * pOleStream [I] The OLESTREAM
7224 * pData [I] Data Structure for the OLESTREAM Data
7226 * RETURNS
7227 * Success: S_OK
7228 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
7229 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
7231 * NOTES
7232 * This function is used by OleConvertOLESTREAMToIStorage only.
7234 * Memory allocated for pData must be freed by the caller
7236 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
7238 DWORD dwSize;
7239 HRESULT hRes = S_OK;
7240 int nTryCnt=0;
7241 int max_try = 6;
7243 pData->pData = NULL;
7244 pData->pstrOleObjFileName = NULL;
7246 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
7248 /* Get the OleID */
7249 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7250 if(dwSize != sizeof(pData->dwOleID))
7252 hRes = CONVERT10_E_OLESTREAM_GET;
7254 else if(pData->dwOleID != OLESTREAM_ID)
7256 hRes = CONVERT10_E_OLESTREAM_FMT;
7258 else
7260 hRes = S_OK;
7261 break;
7265 if(hRes == S_OK)
7267 /* Get the TypeID... more info needed for this field */
7268 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7269 if(dwSize != sizeof(pData->dwTypeID))
7271 hRes = CONVERT10_E_OLESTREAM_GET;
7274 if(hRes == S_OK)
7276 if(pData->dwTypeID != 0)
7278 /* Get the length of the OleTypeName */
7279 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7280 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7282 hRes = CONVERT10_E_OLESTREAM_GET;
7285 if(hRes == S_OK)
7287 if(pData->dwOleTypeNameLength > 0)
7289 /* Get the OleTypeName */
7290 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7291 if(dwSize != pData->dwOleTypeNameLength)
7293 hRes = CONVERT10_E_OLESTREAM_GET;
7297 if(bStrem1)
7299 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
7300 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
7302 hRes = CONVERT10_E_OLESTREAM_GET;
7304 if(hRes == S_OK)
7306 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
7307 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
7308 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
7309 if(pData->pstrOleObjFileName)
7311 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
7312 if(dwSize != pData->dwOleObjFileNameLength)
7314 hRes = CONVERT10_E_OLESTREAM_GET;
7317 else
7318 hRes = CONVERT10_E_OLESTREAM_GET;
7321 else
7323 /* Get the Width of the Metafile */
7324 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7325 if(dwSize != sizeof(pData->dwMetaFileWidth))
7327 hRes = CONVERT10_E_OLESTREAM_GET;
7329 if(hRes == S_OK)
7331 /* Get the Height of the Metafile */
7332 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7333 if(dwSize != sizeof(pData->dwMetaFileHeight))
7335 hRes = CONVERT10_E_OLESTREAM_GET;
7339 if(hRes == S_OK)
7341 /* Get the Length of the Data */
7342 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7343 if(dwSize != sizeof(pData->dwDataLength))
7345 hRes = CONVERT10_E_OLESTREAM_GET;
7349 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
7351 if(!bStrem1) /* if it is a second OLE stream data */
7353 pData->dwDataLength -= 8;
7354 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
7355 if(dwSize != sizeof(pData->strUnknown))
7357 hRes = CONVERT10_E_OLESTREAM_GET;
7361 if(hRes == S_OK)
7363 if(pData->dwDataLength > 0)
7365 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
7367 /* Get Data (ex. IStorage, Metafile, or BMP) */
7368 if(pData->pData)
7370 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
7371 if(dwSize != pData->dwDataLength)
7373 hRes = CONVERT10_E_OLESTREAM_GET;
7376 else
7378 hRes = CONVERT10_E_OLESTREAM_GET;
7384 return hRes;
7387 /*************************************************************************
7388 * OLECONVERT_SaveOLE10 [Internal]
7390 * Saves the OLE10 STREAM From memory
7392 * PARAMS
7393 * pData [I] Data Structure for the OLESTREAM Data
7394 * pOleStream [I] The OLESTREAM to save
7396 * RETURNS
7397 * Success: S_OK
7398 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7400 * NOTES
7401 * This function is used by OleConvertIStorageToOLESTREAM only.
7404 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
7406 DWORD dwSize;
7407 HRESULT hRes = S_OK;
7410 /* Set the OleID */
7411 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
7412 if(dwSize != sizeof(pData->dwOleID))
7414 hRes = CONVERT10_E_OLESTREAM_PUT;
7417 if(hRes == S_OK)
7419 /* Set the TypeID */
7420 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
7421 if(dwSize != sizeof(pData->dwTypeID))
7423 hRes = CONVERT10_E_OLESTREAM_PUT;
7427 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
7429 /* Set the Length of the OleTypeName */
7430 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
7431 if(dwSize != sizeof(pData->dwOleTypeNameLength))
7433 hRes = CONVERT10_E_OLESTREAM_PUT;
7436 if(hRes == S_OK)
7438 if(pData->dwOleTypeNameLength > 0)
7440 /* Set the OleTypeName */
7441 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
7442 if(dwSize != pData->dwOleTypeNameLength)
7444 hRes = CONVERT10_E_OLESTREAM_PUT;
7449 if(hRes == S_OK)
7451 /* Set the width of the Metafile */
7452 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
7453 if(dwSize != sizeof(pData->dwMetaFileWidth))
7455 hRes = CONVERT10_E_OLESTREAM_PUT;
7459 if(hRes == S_OK)
7461 /* Set the height of the Metafile */
7462 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
7463 if(dwSize != sizeof(pData->dwMetaFileHeight))
7465 hRes = CONVERT10_E_OLESTREAM_PUT;
7469 if(hRes == S_OK)
7471 /* Set the length of the Data */
7472 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
7473 if(dwSize != sizeof(pData->dwDataLength))
7475 hRes = CONVERT10_E_OLESTREAM_PUT;
7479 if(hRes == S_OK)
7481 if(pData->dwDataLength > 0)
7483 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
7484 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
7485 if(dwSize != pData->dwDataLength)
7487 hRes = CONVERT10_E_OLESTREAM_PUT;
7492 return hRes;
7495 /*************************************************************************
7496 * OLECONVERT_GetOLE20FromOLE10[Internal]
7498 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
7499 * opens it, and copies the content to the dest IStorage for
7500 * OleConvertOLESTREAMToIStorage
7503 * PARAMS
7504 * pDestStorage [I] The IStorage to copy the data to
7505 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
7506 * nBufferLength [I] The size of the buffer
7508 * RETURNS
7509 * Nothing
7511 * NOTES
7515 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
7517 HRESULT hRes;
7518 HANDLE hFile;
7519 IStorage *pTempStorage;
7520 DWORD dwNumOfBytesWritten;
7521 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7522 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7524 /* Create a temp File */
7525 GetTempPathW(MAX_PATH, wstrTempDir);
7526 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7527 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
7529 if(hFile != INVALID_HANDLE_VALUE)
7531 /* Write IStorage Data to File */
7532 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
7533 CloseHandle(hFile);
7535 /* Open and copy temp storage to the Dest Storage */
7536 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
7537 if(hRes == S_OK)
7539 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
7540 IStorage_Release(pTempStorage);
7542 DeleteFileW(wstrTempFile);
7547 /*************************************************************************
7548 * OLECONVERT_WriteOLE20ToBuffer [Internal]
7550 * Saves the OLE10 STREAM From memory
7552 * PARAMS
7553 * pStorage [I] The Src IStorage to copy
7554 * pData [I] The Dest Memory to write to.
7556 * RETURNS
7557 * The size in bytes allocated for pData
7559 * NOTES
7560 * Memory allocated for pData must be freed by the caller
7562 * Used by OleConvertIStorageToOLESTREAM only.
7565 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
7567 HANDLE hFile;
7568 HRESULT hRes;
7569 DWORD nDataLength = 0;
7570 IStorage *pTempStorage;
7571 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
7572 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
7574 *pData = NULL;
7576 /* Create temp Storage */
7577 GetTempPathW(MAX_PATH, wstrTempDir);
7578 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
7579 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
7581 if(hRes == S_OK)
7583 /* Copy Src Storage to the Temp Storage */
7584 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
7585 IStorage_Release(pTempStorage);
7587 /* Open Temp Storage as a file and copy to memory */
7588 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7589 if(hFile != INVALID_HANDLE_VALUE)
7591 nDataLength = GetFileSize(hFile, NULL);
7592 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
7593 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
7594 CloseHandle(hFile);
7596 DeleteFileW(wstrTempFile);
7598 return nDataLength;
7601 /*************************************************************************
7602 * OLECONVERT_CreateOleStream [Internal]
7604 * Creates the "\001OLE" stream in the IStorage if necessary.
7606 * PARAMS
7607 * pStorage [I] Dest storage to create the stream in
7609 * RETURNS
7610 * Nothing
7612 * NOTES
7613 * This function is used by OleConvertOLESTREAMToIStorage only.
7615 * This stream is still unknown, MS Word seems to have extra data
7616 * but since the data is stored in the OLESTREAM there should be
7617 * no need to recreate the stream. If the stream is manually
7618 * deleted it will create it with this default data.
7621 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7623 HRESULT hRes;
7624 IStream *pStream;
7625 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7626 BYTE pOleStreamHeader [] =
7628 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7629 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7630 0x00, 0x00, 0x00, 0x00
7633 /* Create stream if not present */
7634 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7635 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7637 if(hRes == S_OK)
7639 /* Write default Data */
7640 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7641 IStream_Release(pStream);
7645 /* write a string to a stream, preceded by its length */
7646 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7648 HRESULT r;
7649 LPSTR str;
7650 DWORD len = 0;
7652 if( string )
7653 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7654 r = IStream_Write( stm, &len, sizeof(len), NULL);
7655 if( FAILED( r ) )
7656 return r;
7657 if(len == 0)
7658 return r;
7659 str = CoTaskMemAlloc( len );
7660 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7661 r = IStream_Write( stm, str, len, NULL);
7662 CoTaskMemFree( str );
7663 return r;
7666 /* read a string preceded by its length from a stream */
7667 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7669 HRESULT r;
7670 DWORD len, count = 0;
7671 LPSTR str;
7672 LPWSTR wstr;
7674 r = IStream_Read( stm, &len, sizeof(len), &count );
7675 if( FAILED( r ) )
7676 return r;
7677 if( count != sizeof(len) )
7678 return E_OUTOFMEMORY;
7680 TRACE("%d bytes\n",len);
7682 str = CoTaskMemAlloc( len );
7683 if( !str )
7684 return E_OUTOFMEMORY;
7685 count = 0;
7686 r = IStream_Read( stm, str, len, &count );
7687 if( FAILED( r ) )
7688 return r;
7689 if( count != len )
7691 CoTaskMemFree( str );
7692 return E_OUTOFMEMORY;
7695 TRACE("Read string %s\n",debugstr_an(str,len));
7697 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7698 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7699 if( wstr )
7700 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7701 CoTaskMemFree( str );
7703 *string = wstr;
7705 return r;
7709 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7710 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7712 IStream *pstm;
7713 HRESULT r = S_OK;
7714 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7716 static const BYTE unknown1[12] =
7717 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7718 0xFF, 0xFF, 0xFF, 0xFF};
7719 static const BYTE unknown2[16] =
7720 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7721 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7723 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7724 debugstr_w(lpszUserType), debugstr_w(szClipName),
7725 debugstr_w(szProgIDName));
7727 /* Create a CompObj stream */
7728 r = IStorage_CreateStream(pstg, szwStreamName,
7729 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7730 if( FAILED (r) )
7731 return r;
7733 /* Write CompObj Structure to stream */
7734 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7736 if( SUCCEEDED( r ) )
7737 r = WriteClassStm( pstm, clsid );
7739 if( SUCCEEDED( r ) )
7740 r = STREAM_WriteString( pstm, lpszUserType );
7741 if( SUCCEEDED( r ) )
7742 r = STREAM_WriteString( pstm, szClipName );
7743 if( SUCCEEDED( r ) )
7744 r = STREAM_WriteString( pstm, szProgIDName );
7745 if( SUCCEEDED( r ) )
7746 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7748 IStream_Release( pstm );
7750 return r;
7753 /***********************************************************************
7754 * WriteFmtUserTypeStg (OLE32.@)
7756 HRESULT WINAPI WriteFmtUserTypeStg(
7757 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7759 HRESULT r;
7760 WCHAR szwClipName[0x40];
7761 CLSID clsid = CLSID_NULL;
7762 LPWSTR wstrProgID = NULL;
7763 DWORD n;
7765 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7767 /* get the clipboard format name */
7768 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7769 szwClipName[n]=0;
7771 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7773 /* FIXME: There's room to save a CLSID and its ProgID, but
7774 the CLSID is not looked up in the registry and in all the
7775 tests I wrote it was CLSID_NULL. Where does it come from?
7778 /* get the real program ID. This may fail, but that's fine */
7779 ProgIDFromCLSID(&clsid, &wstrProgID);
7781 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7783 r = STORAGE_WriteCompObj( pstg, &clsid,
7784 lpszUserType, szwClipName, wstrProgID );
7786 CoTaskMemFree(wstrProgID);
7788 return r;
7792 /******************************************************************************
7793 * ReadFmtUserTypeStg [OLE32.@]
7795 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7797 HRESULT r;
7798 IStream *stm = 0;
7799 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7800 unsigned char unknown1[12];
7801 unsigned char unknown2[16];
7802 DWORD count;
7803 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7804 CLSID clsid;
7806 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7808 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7809 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7810 if( FAILED ( r ) )
7812 WARN("Failed to open stream r = %08x\n", r);
7813 return r;
7816 /* read the various parts of the structure */
7817 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7818 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7819 goto end;
7820 r = ReadClassStm( stm, &clsid );
7821 if( FAILED( r ) )
7822 goto end;
7824 r = STREAM_ReadString( stm, &szCLSIDName );
7825 if( FAILED( r ) )
7826 goto end;
7828 r = STREAM_ReadString( stm, &szOleTypeName );
7829 if( FAILED( r ) )
7830 goto end;
7832 r = STREAM_ReadString( stm, &szProgIDName );
7833 if( FAILED( r ) )
7834 goto end;
7836 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7837 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7838 goto end;
7840 /* ok, success... now we just need to store what we found */
7841 if( pcf )
7842 *pcf = RegisterClipboardFormatW( szOleTypeName );
7843 CoTaskMemFree( szOleTypeName );
7845 if( lplpszUserType )
7846 *lplpszUserType = szCLSIDName;
7847 CoTaskMemFree( szProgIDName );
7849 end:
7850 IStream_Release( stm );
7852 return r;
7856 /*************************************************************************
7857 * OLECONVERT_CreateCompObjStream [Internal]
7859 * Creates a "\001CompObj" is the destination IStorage if necessary.
7861 * PARAMS
7862 * pStorage [I] The dest IStorage to create the CompObj Stream
7863 * if necessary.
7864 * strOleTypeName [I] The ProgID
7866 * RETURNS
7867 * Success: S_OK
7868 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7870 * NOTES
7871 * This function is used by OleConvertOLESTREAMToIStorage only.
7873 * The stream data is stored in the OLESTREAM and there should be
7874 * no need to recreate the stream. If the stream is manually
7875 * deleted it will attempt to create it by querying the registry.
7879 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7881 IStream *pStream;
7882 HRESULT hStorageRes, hRes = S_OK;
7883 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7884 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7885 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7887 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7888 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7890 /* Initialize the CompObj structure */
7891 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7892 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7893 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7896 /* Create a CompObj stream if it doesn't exist */
7897 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7898 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7899 if(hStorageRes == S_OK)
7901 /* copy the OleTypeName to the compobj struct */
7902 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7903 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7905 /* copy the OleTypeName to the compobj struct */
7906 /* Note: in the test made, these were Identical */
7907 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7908 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7910 /* Get the CLSID */
7911 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7912 bufferW, OLESTREAM_MAX_STR_LEN );
7913 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7915 if(hRes == S_OK)
7917 HKEY hKey;
7918 LONG hErr;
7919 /* Get the CLSID Default Name from the Registry */
7920 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7921 if(hErr == ERROR_SUCCESS)
7923 char strTemp[OLESTREAM_MAX_STR_LEN];
7924 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7925 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7926 if(hErr == ERROR_SUCCESS)
7928 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7930 RegCloseKey(hKey);
7934 /* Write CompObj Structure to stream */
7935 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7937 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7939 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7940 if(IStorageCompObj.dwCLSIDNameLength > 0)
7942 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7944 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7945 if(IStorageCompObj.dwOleTypeNameLength > 0)
7947 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7949 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7950 if(IStorageCompObj.dwProgIDNameLength > 0)
7952 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7954 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7955 IStream_Release(pStream);
7957 return hRes;
7961 /*************************************************************************
7962 * OLECONVERT_CreateOlePresStream[Internal]
7964 * Creates the "\002OlePres000" Stream with the Metafile data
7966 * PARAMS
7967 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7968 * dwExtentX [I] Width of the Metafile
7969 * dwExtentY [I] Height of the Metafile
7970 * pData [I] Metafile data
7971 * dwDataLength [I] Size of the Metafile data
7973 * RETURNS
7974 * Success: S_OK
7975 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7977 * NOTES
7978 * This function is used by OleConvertOLESTREAMToIStorage only.
7981 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7983 HRESULT hRes;
7984 IStream *pStream;
7985 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7986 BYTE pOlePresStreamHeader [] =
7988 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7989 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7990 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7991 0x00, 0x00, 0x00, 0x00
7994 BYTE pOlePresStreamHeaderEmpty [] =
7996 0x00, 0x00, 0x00, 0x00,
7997 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7998 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7999 0x00, 0x00, 0x00, 0x00
8002 /* Create the OlePres000 Stream */
8003 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8004 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8006 if(hRes == S_OK)
8008 DWORD nHeaderSize;
8009 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8011 memset(&OlePres, 0, sizeof(OlePres));
8012 /* Do we have any metafile data to save */
8013 if(dwDataLength > 0)
8015 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8016 nHeaderSize = sizeof(pOlePresStreamHeader);
8018 else
8020 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8021 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8023 /* Set width and height of the metafile */
8024 OlePres.dwExtentX = dwExtentX;
8025 OlePres.dwExtentY = -dwExtentY;
8027 /* Set Data and Length */
8028 if(dwDataLength > sizeof(METAFILEPICT16))
8030 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8031 OlePres.pData = &(pData[8]);
8033 /* Save OlePres000 Data to Stream */
8034 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8035 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8036 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8037 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8038 if(OlePres.dwSize > 0)
8040 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8042 IStream_Release(pStream);
8046 /*************************************************************************
8047 * OLECONVERT_CreateOle10NativeStream [Internal]
8049 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8051 * PARAMS
8052 * pStorage [I] Dest storage to create the stream in
8053 * pData [I] Ole10 Native Data (ex. bmp)
8054 * dwDataLength [I] Size of the Ole10 Native Data
8056 * RETURNS
8057 * Nothing
8059 * NOTES
8060 * This function is used by OleConvertOLESTREAMToIStorage only.
8062 * Might need to verify the data and return appropriate error message
8065 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8067 HRESULT hRes;
8068 IStream *pStream;
8069 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8071 /* Create the Ole10Native Stream */
8072 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8073 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8075 if(hRes == S_OK)
8077 /* Write info to stream */
8078 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8079 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8080 IStream_Release(pStream);
8085 /*************************************************************************
8086 * OLECONVERT_GetOLE10ProgID [Internal]
8088 * Finds the ProgID (or OleTypeID) from the IStorage
8090 * PARAMS
8091 * pStorage [I] The Src IStorage to get the ProgID
8092 * strProgID [I] the ProgID string to get
8093 * dwSize [I] the size of the string
8095 * RETURNS
8096 * Success: S_OK
8097 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8099 * NOTES
8100 * This function is used by OleConvertIStorageToOLESTREAM only.
8104 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
8106 HRESULT hRes;
8107 IStream *pStream;
8108 LARGE_INTEGER iSeekPos;
8109 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
8110 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8112 /* Open the CompObj Stream */
8113 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8114 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8115 if(hRes == S_OK)
8118 /*Get the OleType from the CompObj Stream */
8119 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
8120 iSeekPos.u.HighPart = 0;
8122 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8123 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
8124 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
8125 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8126 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
8127 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
8128 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
8130 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
8131 if(*dwSize > 0)
8133 IStream_Read(pStream, strProgID, *dwSize, NULL);
8135 IStream_Release(pStream);
8137 else
8139 STATSTG stat;
8140 LPOLESTR wstrProgID;
8142 /* Get the OleType from the registry */
8143 REFCLSID clsid = &(stat.clsid);
8144 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
8145 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
8146 if(hRes == S_OK)
8148 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
8152 return hRes;
8155 /*************************************************************************
8156 * OLECONVERT_GetOle10PresData [Internal]
8158 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
8160 * PARAMS
8161 * pStorage [I] Src IStroage
8162 * pOleStream [I] Dest OleStream Mem Struct
8164 * RETURNS
8165 * Nothing
8167 * NOTES
8168 * This function is used by OleConvertIStorageToOLESTREAM only.
8170 * Memory allocated for pData must be freed by the caller
8174 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8177 HRESULT hRes;
8178 IStream *pStream;
8179 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8181 /* Initialize Default data for OLESTREAM */
8182 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8183 pOleStreamData[0].dwTypeID = 2;
8184 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8185 pOleStreamData[1].dwTypeID = 0;
8186 pOleStreamData[0].dwMetaFileWidth = 0;
8187 pOleStreamData[0].dwMetaFileHeight = 0;
8188 pOleStreamData[0].pData = NULL;
8189 pOleStreamData[1].pData = NULL;
8191 /* Open Ole10Native Stream */
8192 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8193 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8194 if(hRes == S_OK)
8197 /* Read Size and Data */
8198 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
8199 if(pOleStreamData->dwDataLength > 0)
8201 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
8202 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
8204 IStream_Release(pStream);
8210 /*************************************************************************
8211 * OLECONVERT_GetOle20PresData[Internal]
8213 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
8215 * PARAMS
8216 * pStorage [I] Src IStroage
8217 * pOleStreamData [I] Dest OleStream Mem Struct
8219 * RETURNS
8220 * Nothing
8222 * NOTES
8223 * This function is used by OleConvertIStorageToOLESTREAM only.
8225 * Memory allocated for pData must be freed by the caller
8227 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
8229 HRESULT hRes;
8230 IStream *pStream;
8231 OLECONVERT_ISTORAGE_OLEPRES olePress;
8232 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8234 /* Initialize Default data for OLESTREAM */
8235 pOleStreamData[0].dwOleID = OLESTREAM_ID;
8236 pOleStreamData[0].dwTypeID = 2;
8237 pOleStreamData[0].dwMetaFileWidth = 0;
8238 pOleStreamData[0].dwMetaFileHeight = 0;
8239 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
8240 pOleStreamData[1].dwOleID = OLESTREAM_ID;
8241 pOleStreamData[1].dwTypeID = 0;
8242 pOleStreamData[1].dwOleTypeNameLength = 0;
8243 pOleStreamData[1].strOleTypeName[0] = 0;
8244 pOleStreamData[1].dwMetaFileWidth = 0;
8245 pOleStreamData[1].dwMetaFileHeight = 0;
8246 pOleStreamData[1].pData = NULL;
8247 pOleStreamData[1].dwDataLength = 0;
8250 /* Open OlePress000 stream */
8251 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
8252 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
8253 if(hRes == S_OK)
8255 LARGE_INTEGER iSeekPos;
8256 METAFILEPICT16 MetaFilePict;
8257 static const char strMetafilePictName[] = "METAFILEPICT";
8259 /* Set the TypeID for a Metafile */
8260 pOleStreamData[1].dwTypeID = 5;
8262 /* Set the OleTypeName to Metafile */
8263 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
8264 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
8266 iSeekPos.u.HighPart = 0;
8267 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
8269 /* Get Presentation Data */
8270 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
8271 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
8272 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
8273 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
8275 /*Set width and Height */
8276 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
8277 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
8278 if(olePress.dwSize > 0)
8280 /* Set Length */
8281 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
8283 /* Set MetaFilePict struct */
8284 MetaFilePict.mm = 8;
8285 MetaFilePict.xExt = olePress.dwExtentX;
8286 MetaFilePict.yExt = olePress.dwExtentY;
8287 MetaFilePict.hMF = 0;
8289 /* Get Metafile Data */
8290 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
8291 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
8292 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
8294 IStream_Release(pStream);
8298 /*************************************************************************
8299 * OleConvertOLESTREAMToIStorage [OLE32.@]
8301 * Read info on MSDN
8303 * TODO
8304 * DVTARGETDEVICE parameter is not handled
8305 * Still unsure of some mem fields for OLE 10 Stream
8306 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8307 * and "\001OLE" streams
8310 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
8311 LPOLESTREAM pOleStream,
8312 LPSTORAGE pstg,
8313 const DVTARGETDEVICE* ptd)
8315 int i;
8316 HRESULT hRes=S_OK;
8317 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8319 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
8321 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8323 if(ptd != NULL)
8325 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
8328 if(pstg == NULL || pOleStream == NULL)
8330 hRes = E_INVALIDARG;
8333 if(hRes == S_OK)
8335 /* Load the OLESTREAM to Memory */
8336 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
8339 if(hRes == S_OK)
8341 /* Load the OLESTREAM to Memory (part 2)*/
8342 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
8345 if(hRes == S_OK)
8348 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
8350 /* Do we have the IStorage Data in the OLESTREAM */
8351 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
8353 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8354 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
8356 else
8358 /* It must be an original OLE 1.0 source */
8359 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8362 else
8364 /* It must be an original OLE 1.0 source */
8365 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
8368 /* Create CompObj Stream if necessary */
8369 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
8370 if(hRes == S_OK)
8372 /*Create the Ole Stream if necessary */
8373 OLECONVERT_CreateOleStream(pstg);
8378 /* Free allocated memory */
8379 for(i=0; i < 2; i++)
8381 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8382 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
8383 pOleStreamData[i].pstrOleObjFileName = NULL;
8385 return hRes;
8388 /*************************************************************************
8389 * OleConvertIStorageToOLESTREAM [OLE32.@]
8391 * Read info on MSDN
8393 * Read info on MSDN
8395 * TODO
8396 * Still unsure of some mem fields for OLE 10 Stream
8397 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
8398 * and "\001OLE" streams.
8401 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
8402 LPSTORAGE pstg,
8403 LPOLESTREAM pOleStream)
8405 int i;
8406 HRESULT hRes = S_OK;
8407 IStream *pStream;
8408 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
8409 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8411 TRACE("%p %p\n", pstg, pOleStream);
8413 memset(pOleStreamData, 0, sizeof(pOleStreamData));
8415 if(pstg == NULL || pOleStream == NULL)
8417 hRes = E_INVALIDARG;
8419 if(hRes == S_OK)
8421 /* Get the ProgID */
8422 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
8423 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
8425 if(hRes == S_OK)
8427 /* Was it originally Ole10 */
8428 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
8429 if(hRes == S_OK)
8431 IStream_Release(pStream);
8432 /* Get Presentation Data for Ole10Native */
8433 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
8435 else
8437 /* Get Presentation Data (OLE20) */
8438 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
8441 /* Save OLESTREAM */
8442 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
8443 if(hRes == S_OK)
8445 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
8450 /* Free allocated memory */
8451 for(i=0; i < 2; i++)
8453 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
8456 return hRes;
8459 /***********************************************************************
8460 * GetConvertStg (OLE32.@)
8462 HRESULT WINAPI GetConvertStg(IStorage *stg) {
8463 FIXME("unimplemented stub!\n");
8464 return E_FAIL;
8467 /******************************************************************************
8468 * StgIsStorageFile [OLE32.@]
8469 * Verify if the file contains a storage object
8471 * PARAMS
8472 * fn [ I] Filename
8474 * RETURNS
8475 * S_OK if file has magic bytes as a storage object
8476 * S_FALSE if file is not storage
8478 HRESULT WINAPI
8479 StgIsStorageFile(LPCOLESTR fn)
8481 HANDLE hf;
8482 BYTE magic[8];
8483 DWORD bytes_read;
8485 TRACE("%s\n", debugstr_w(fn));
8486 hf = CreateFileW(fn, GENERIC_READ,
8487 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
8488 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8490 if (hf == INVALID_HANDLE_VALUE)
8491 return STG_E_FILENOTFOUND;
8493 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
8495 WARN(" unable to read file\n");
8496 CloseHandle(hf);
8497 return S_FALSE;
8500 CloseHandle(hf);
8502 if (bytes_read != 8) {
8503 TRACE(" too short\n");
8504 return S_FALSE;
8507 if (!memcmp(magic,STORAGE_magic,8)) {
8508 TRACE(" -> YES\n");
8509 return S_OK;
8512 TRACE(" -> Invalid header.\n");
8513 return S_FALSE;
8516 /***********************************************************************
8517 * WriteClassStm (OLE32.@)
8519 * Writes a CLSID to a stream.
8521 * PARAMS
8522 * pStm [I] Stream to write to.
8523 * rclsid [I] CLSID to write.
8525 * RETURNS
8526 * Success: S_OK.
8527 * Failure: HRESULT code.
8529 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
8531 TRACE("(%p,%p)\n",pStm,rclsid);
8533 if (!pStm || !rclsid)
8534 return E_INVALIDARG;
8536 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
8539 /***********************************************************************
8540 * ReadClassStm (OLE32.@)
8542 * Reads a CLSID from a stream.
8544 * PARAMS
8545 * pStm [I] Stream to read from.
8546 * rclsid [O] CLSID to read.
8548 * RETURNS
8549 * Success: S_OK.
8550 * Failure: HRESULT code.
8552 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
8554 ULONG nbByte;
8555 HRESULT res;
8557 TRACE("(%p,%p)\n",pStm,pclsid);
8559 if (!pStm || !pclsid)
8560 return E_INVALIDARG;
8562 /* clear the output args */
8563 *pclsid = CLSID_NULL;
8565 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
8567 if (FAILED(res))
8568 return res;
8570 if (nbByte != sizeof(CLSID))
8571 return STG_E_READFAULT;
8572 else
8573 return S_OK;