d3dx9: Implement UpdateSemantics mesh method.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blobe983bf3715c87abd68af14676932f4dcee5153bd
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 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
91 static const IStorageVtbl Storage32InternalImpl_Vtbl;
93 /* Method definitions for the Storage32InternalImpl class. */
94 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
95 DWORD openFlags, DirRef storageDirEntry);
96 static void StorageImpl_Destroy(StorageBaseImpl* iface);
97 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
98 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
99 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
100 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
101 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
102 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
103 static void StorageImpl_SaveFileHeader(StorageImpl* This);
105 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
106 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
107 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
108 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
109 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
111 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
112 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
113 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
115 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
116 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
117 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
118 ULONG blockIndex, ULONG offset, DWORD value);
119 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
120 ULONG blockIndex, ULONG offset, DWORD* value);
122 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
123 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
125 typedef struct TransactedDirEntry
127 /* If applicable, a reference to the original DirEntry in the transacted
128 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
129 DirRef transactedParentEntry;
131 /* True if this entry is being used. */
132 int inuse;
134 /* True if data is up to date. */
135 int read;
137 /* True if this entry has been modified. */
138 int dirty;
140 /* True if this entry's stream has been modified. */
141 int stream_dirty;
143 /* True if this entry has been deleted in the transacted storage, but the
144 * delete has not yet been committed. */
145 int deleted;
147 /* If this entry's stream has been modified, a reference to where the stream
148 * is stored in the snapshot file. */
149 DirRef stream_entry;
151 /* This directory entry's data, including any changes that have been made. */
152 DirEntry data;
154 /* A reference to the parent of this node. This is only valid while we are
155 * committing changes. */
156 DirRef parent;
158 /* A reference to a newly-created entry in the transacted parent. This is
159 * always equal to transactedParentEntry except when committing changes. */
160 DirRef newTransactedParentEntry;
161 } TransactedDirEntry;
163 /****************************************************************************
164 * Transacted storage object.
166 typedef struct TransactedSnapshotImpl
168 struct StorageBaseImpl base;
171 * Modified streams are temporarily saved to the scratch file.
173 StorageBaseImpl *scratch;
175 /* The directory structure is kept here, so that we can track how these
176 * entries relate to those in the parent storage. */
177 TransactedDirEntry *entries;
178 ULONG entries_size;
179 ULONG firstFreeEntry;
182 * Changes are committed to the transacted parent.
184 StorageBaseImpl *transactedParent;
185 } TransactedSnapshotImpl;
187 /* Generic function to create a transacted wrapper for a direct storage object. */
188 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
190 /* OLESTREAM memory structure to use for Get and Put Routines */
191 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
192 typedef struct
194 DWORD dwOleID;
195 DWORD dwTypeID;
196 DWORD dwOleTypeNameLength;
197 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
198 CHAR *pstrOleObjFileName;
199 DWORD dwOleObjFileNameLength;
200 DWORD dwMetaFileWidth;
201 DWORD dwMetaFileHeight;
202 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
203 DWORD dwDataLength;
204 BYTE *pData;
205 }OLECONVERT_OLESTREAM_DATA;
207 /* CompObj Stream structure */
208 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
209 typedef struct
211 BYTE byUnknown1[12];
212 CLSID clsid;
213 DWORD dwCLSIDNameLength;
214 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
215 DWORD dwOleTypeNameLength;
216 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
217 DWORD dwProgIDNameLength;
218 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
219 BYTE byUnknown2[16];
220 }OLECONVERT_ISTORAGE_COMPOBJ;
223 /* Ole Presentation Stream structure */
224 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
225 typedef struct
227 BYTE byUnknown1[28];
228 DWORD dwExtentX;
229 DWORD dwExtentY;
230 DWORD dwSize;
231 BYTE *pData;
232 }OLECONVERT_ISTORAGE_OLEPRES;
236 /***********************************************************************
237 * Forward declaration of internal functions used by the method DestroyElement
239 static HRESULT deleteStorageContents(
240 StorageBaseImpl *parentStorage,
241 DirRef indexToDelete,
242 DirEntry entryDataToDelete);
244 static HRESULT deleteStreamContents(
245 StorageBaseImpl *parentStorage,
246 DirRef indexToDelete,
247 DirEntry entryDataToDelete);
249 static HRESULT removeFromTree(
250 StorageBaseImpl *This,
251 DirRef parentStorageIndex,
252 DirRef deletedIndex);
254 /***********************************************************************
255 * Declaration of the functions used to manipulate DirEntry
258 static HRESULT insertIntoTree(
259 StorageBaseImpl *This,
260 DirRef parentStorageIndex,
261 DirRef newEntryIndex);
263 static LONG entryNameCmp(
264 const OLECHAR *name1,
265 const OLECHAR *name2);
267 static DirRef findElement(
268 StorageBaseImpl *storage,
269 DirRef storageEntry,
270 const OLECHAR *name,
271 DirEntry *data);
273 static HRESULT findTreeParent(
274 StorageBaseImpl *storage,
275 DirRef storageEntry,
276 const OLECHAR *childName,
277 DirEntry *parentData,
278 DirRef *parentEntry,
279 ULONG *relation);
281 /***********************************************************************
282 * Declaration of miscellaneous functions...
284 static HRESULT validateSTGM(DWORD stgmValue);
286 static DWORD GetShareModeFromSTGM(DWORD stgm);
287 static DWORD GetAccessModeFromSTGM(DWORD stgm);
288 static DWORD GetCreationModeFromSTGM(DWORD stgm);
290 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
293 /****************************************************************************
294 * IEnumSTATSTGImpl definitions.
296 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
297 * This class allows iterating through the content of a storage and to find
298 * specific items inside it.
300 struct IEnumSTATSTGImpl
302 IEnumSTATSTG IEnumSTATSTG_iface;
304 LONG ref; /* Reference count */
305 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
306 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
308 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
311 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
313 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
317 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
318 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
320 /************************************************************************
321 ** Block Functions
324 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
326 return (index+1) * This->bigBlockSize;
329 /************************************************************************
330 ** Storage32BaseImpl implementation
332 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
333 ULARGE_INTEGER offset,
334 void* buffer,
335 ULONG size,
336 ULONG* bytesRead)
338 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
341 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
342 ULARGE_INTEGER offset,
343 const void* buffer,
344 const ULONG size,
345 ULONG* bytesWritten)
347 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
350 /************************************************************************
351 * Storage32BaseImpl_QueryInterface (IUnknown)
353 * This method implements the common QueryInterface for all IStorage32
354 * implementations contained in this file.
356 * See Windows documentation for more details on IUnknown methods.
358 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
359 IStorage* iface,
360 REFIID riid,
361 void** ppvObject)
363 StorageBaseImpl *This = (StorageBaseImpl *)iface;
365 if ( (This==0) || (ppvObject==0) )
366 return E_INVALIDARG;
368 *ppvObject = 0;
370 if (IsEqualGUID(&IID_IUnknown, riid) ||
371 IsEqualGUID(&IID_IStorage, riid))
373 *ppvObject = This;
375 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
377 *ppvObject = &This->pssVtbl;
380 if ((*ppvObject)==0)
381 return E_NOINTERFACE;
383 IStorage_AddRef(iface);
385 return S_OK;
388 /************************************************************************
389 * Storage32BaseImpl_AddRef (IUnknown)
391 * This method implements the common AddRef for all IStorage32
392 * implementations contained in this file.
394 * See Windows documentation for more details on IUnknown methods.
396 static ULONG WINAPI StorageBaseImpl_AddRef(
397 IStorage* iface)
399 StorageBaseImpl *This = (StorageBaseImpl *)iface;
400 ULONG ref = InterlockedIncrement(&This->ref);
402 TRACE("(%p) AddRef to %d\n", This, ref);
404 return ref;
407 /************************************************************************
408 * Storage32BaseImpl_Release (IUnknown)
410 * This method implements the common Release for all IStorage32
411 * implementations contained in this file.
413 * See Windows documentation for more details on IUnknown methods.
415 static ULONG WINAPI StorageBaseImpl_Release(
416 IStorage* iface)
418 StorageBaseImpl *This = (StorageBaseImpl *)iface;
420 ULONG ref = InterlockedDecrement(&This->ref);
422 TRACE("(%p) ReleaseRef to %d\n", This, ref);
424 if (ref == 0)
427 * Since we are using a system of base-classes, we want to call the
428 * destructor of the appropriate derived class. To do this, we are
429 * using virtual functions to implement the destructor.
431 StorageBaseImpl_Destroy(This);
434 return ref;
437 /************************************************************************
438 * Storage32BaseImpl_OpenStream (IStorage)
440 * This method will open the specified stream object from the current storage.
442 * See Windows documentation for more details on IStorage methods.
444 static HRESULT WINAPI StorageBaseImpl_OpenStream(
445 IStorage* iface,
446 const OLECHAR* pwcsName, /* [string][in] */
447 void* reserved1, /* [unique][in] */
448 DWORD grfMode, /* [in] */
449 DWORD reserved2, /* [in] */
450 IStream** ppstm) /* [out] */
452 StorageBaseImpl *This = (StorageBaseImpl *)iface;
453 StgStreamImpl* newStream;
454 DirEntry currentEntry;
455 DirRef streamEntryRef;
456 HRESULT res = STG_E_UNKNOWN;
458 TRACE("(%p, %s, %p, %x, %d, %p)\n",
459 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
461 if ( (pwcsName==NULL) || (ppstm==0) )
463 res = E_INVALIDARG;
464 goto end;
467 *ppstm = NULL;
469 if ( FAILED( validateSTGM(grfMode) ) ||
470 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
472 res = STG_E_INVALIDFLAG;
473 goto end;
477 * As documented.
479 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
481 res = STG_E_INVALIDFUNCTION;
482 goto end;
485 if (This->reverted)
487 res = STG_E_REVERTED;
488 goto end;
492 * Check that we're compatible with the parent's storage mode, but
493 * only if we are not in transacted mode
495 if(!(This->openFlags & STGM_TRANSACTED)) {
496 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
498 res = STG_E_INVALIDFLAG;
499 goto end;
504 * Search for the element with the given name
506 streamEntryRef = findElement(
507 This,
508 This->storageDirEntry,
509 pwcsName,
510 &currentEntry);
513 * If it was found, construct the stream object and return a pointer to it.
515 if ( (streamEntryRef!=DIRENTRY_NULL) &&
516 (currentEntry.stgType==STGTY_STREAM) )
518 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
520 /* A single stream cannot be opened a second time. */
521 res = STG_E_ACCESSDENIED;
522 goto end;
525 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
527 if (newStream!=0)
529 newStream->grfMode = grfMode;
530 *ppstm = (IStream*)newStream;
532 IStream_AddRef(*ppstm);
534 res = S_OK;
535 goto end;
538 res = E_OUTOFMEMORY;
539 goto end;
542 res = STG_E_FILENOTFOUND;
544 end:
545 if (res == S_OK)
546 TRACE("<-- IStream %p\n", *ppstm);
547 TRACE("<-- %08x\n", res);
548 return res;
551 /************************************************************************
552 * Storage32BaseImpl_OpenStorage (IStorage)
554 * This method will open a new storage object from the current storage.
556 * See Windows documentation for more details on IStorage methods.
558 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
559 IStorage* iface,
560 const OLECHAR* pwcsName, /* [string][unique][in] */
561 IStorage* pstgPriority, /* [unique][in] */
562 DWORD grfMode, /* [in] */
563 SNB snbExclude, /* [unique][in] */
564 DWORD reserved, /* [in] */
565 IStorage** ppstg) /* [out] */
567 StorageBaseImpl *This = (StorageBaseImpl *)iface;
568 StorageInternalImpl* newStorage;
569 StorageBaseImpl* newTransactedStorage;
570 DirEntry currentEntry;
571 DirRef storageEntryRef;
572 HRESULT res = STG_E_UNKNOWN;
574 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
575 iface, debugstr_w(pwcsName), pstgPriority,
576 grfMode, snbExclude, reserved, ppstg);
578 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
580 res = E_INVALIDARG;
581 goto end;
584 if (This->openFlags & STGM_SIMPLE)
586 res = STG_E_INVALIDFUNCTION;
587 goto end;
590 /* as documented */
591 if (snbExclude != NULL)
593 res = STG_E_INVALIDPARAMETER;
594 goto end;
597 if ( FAILED( validateSTGM(grfMode) ))
599 res = STG_E_INVALIDFLAG;
600 goto end;
604 * As documented.
606 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
607 (grfMode & STGM_DELETEONRELEASE) ||
608 (grfMode & STGM_PRIORITY) )
610 res = STG_E_INVALIDFUNCTION;
611 goto end;
614 if (This->reverted)
615 return STG_E_REVERTED;
618 * Check that we're compatible with the parent's storage mode,
619 * but only if we are not transacted
621 if(!(This->openFlags & STGM_TRANSACTED)) {
622 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
624 res = STG_E_ACCESSDENIED;
625 goto end;
629 *ppstg = NULL;
631 storageEntryRef = findElement(
632 This,
633 This->storageDirEntry,
634 pwcsName,
635 &currentEntry);
637 if ( (storageEntryRef!=DIRENTRY_NULL) &&
638 (currentEntry.stgType==STGTY_STORAGE) )
640 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
642 /* A single storage cannot be opened a second time. */
643 res = STG_E_ACCESSDENIED;
644 goto end;
647 newStorage = StorageInternalImpl_Construct(
648 This,
649 grfMode,
650 storageEntryRef);
652 if (newStorage != 0)
654 if (grfMode & STGM_TRANSACTED)
656 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
658 if (FAILED(res))
660 HeapFree(GetProcessHeap(), 0, newStorage);
661 goto end;
664 *ppstg = (IStorage*)newTransactedStorage;
666 else
668 *ppstg = (IStorage*)newStorage;
671 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
673 res = S_OK;
674 goto end;
677 res = STG_E_INSUFFICIENTMEMORY;
678 goto end;
681 res = STG_E_FILENOTFOUND;
683 end:
684 TRACE("<-- %08x\n", res);
685 return res;
688 /************************************************************************
689 * Storage32BaseImpl_EnumElements (IStorage)
691 * This method will create an enumerator object that can be used to
692 * retrieve information about all the elements in the storage object.
694 * See Windows documentation for more details on IStorage methods.
696 static HRESULT WINAPI StorageBaseImpl_EnumElements(
697 IStorage* iface,
698 DWORD reserved1, /* [in] */
699 void* reserved2, /* [size_is][unique][in] */
700 DWORD reserved3, /* [in] */
701 IEnumSTATSTG** ppenum) /* [out] */
703 StorageBaseImpl *This = (StorageBaseImpl *)iface;
704 IEnumSTATSTGImpl* newEnum;
706 TRACE("(%p, %d, %p, %d, %p)\n",
707 iface, reserved1, reserved2, reserved3, ppenum);
709 if ( (This==0) || (ppenum==0))
710 return E_INVALIDARG;
712 if (This->reverted)
713 return STG_E_REVERTED;
715 newEnum = IEnumSTATSTGImpl_Construct(
716 This,
717 This->storageDirEntry);
719 if (newEnum!=0)
721 *ppenum = &newEnum->IEnumSTATSTG_iface;
723 IEnumSTATSTG_AddRef(*ppenum);
725 return S_OK;
728 return E_OUTOFMEMORY;
731 /************************************************************************
732 * Storage32BaseImpl_Stat (IStorage)
734 * This method will retrieve information about this storage object.
736 * See Windows documentation for more details on IStorage methods.
738 static HRESULT WINAPI StorageBaseImpl_Stat(
739 IStorage* iface,
740 STATSTG* pstatstg, /* [out] */
741 DWORD grfStatFlag) /* [in] */
743 StorageBaseImpl *This = (StorageBaseImpl *)iface;
744 DirEntry currentEntry;
745 HRESULT res = STG_E_UNKNOWN;
747 TRACE("(%p, %p, %x)\n",
748 iface, pstatstg, grfStatFlag);
750 if ( (This==0) || (pstatstg==0))
752 res = E_INVALIDARG;
753 goto end;
756 if (This->reverted)
758 res = STG_E_REVERTED;
759 goto end;
762 res = StorageBaseImpl_ReadDirEntry(
763 This,
764 This->storageDirEntry,
765 &currentEntry);
767 if (SUCCEEDED(res))
769 StorageUtl_CopyDirEntryToSTATSTG(
770 This,
771 pstatstg,
772 &currentEntry,
773 grfStatFlag);
775 pstatstg->grfMode = This->openFlags;
776 pstatstg->grfStateBits = This->stateBits;
779 end:
780 if (res == S_OK)
782 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);
784 TRACE("<-- %08x\n", res);
785 return res;
788 /************************************************************************
789 * Storage32BaseImpl_RenameElement (IStorage)
791 * This method will rename the specified element.
793 * See Windows documentation for more details on IStorage methods.
795 static HRESULT WINAPI StorageBaseImpl_RenameElement(
796 IStorage* iface,
797 const OLECHAR* pwcsOldName, /* [in] */
798 const OLECHAR* pwcsNewName) /* [in] */
800 StorageBaseImpl *This = (StorageBaseImpl *)iface;
801 DirEntry currentEntry;
802 DirRef currentEntryRef;
804 TRACE("(%p, %s, %s)\n",
805 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
807 if (This->reverted)
808 return STG_E_REVERTED;
810 currentEntryRef = findElement(This,
811 This->storageDirEntry,
812 pwcsNewName,
813 &currentEntry);
815 if (currentEntryRef != DIRENTRY_NULL)
818 * There is already an element with the new name
820 return STG_E_FILEALREADYEXISTS;
824 * Search for the old element name
826 currentEntryRef = findElement(This,
827 This->storageDirEntry,
828 pwcsOldName,
829 &currentEntry);
831 if (currentEntryRef != DIRENTRY_NULL)
833 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
834 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
836 WARN("Element is already open; cannot rename.\n");
837 return STG_E_ACCESSDENIED;
840 /* Remove the element from its current position in the tree */
841 removeFromTree(This, This->storageDirEntry,
842 currentEntryRef);
844 /* Change the name of the element */
845 strcpyW(currentEntry.name, pwcsNewName);
847 /* Delete any sibling links */
848 currentEntry.leftChild = DIRENTRY_NULL;
849 currentEntry.rightChild = DIRENTRY_NULL;
851 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
852 &currentEntry);
854 /* Insert the element in a new position in the tree */
855 insertIntoTree(This, This->storageDirEntry,
856 currentEntryRef);
858 else
861 * There is no element with the old name
863 return STG_E_FILENOTFOUND;
866 return StorageBaseImpl_Flush(This);
869 /************************************************************************
870 * Storage32BaseImpl_CreateStream (IStorage)
872 * This method will create a stream object within this storage
874 * See Windows documentation for more details on IStorage methods.
876 static HRESULT WINAPI StorageBaseImpl_CreateStream(
877 IStorage* iface,
878 const OLECHAR* pwcsName, /* [string][in] */
879 DWORD grfMode, /* [in] */
880 DWORD reserved1, /* [in] */
881 DWORD reserved2, /* [in] */
882 IStream** ppstm) /* [out] */
884 StorageBaseImpl *This = (StorageBaseImpl *)iface;
885 StgStreamImpl* newStream;
886 DirEntry currentEntry, newStreamEntry;
887 DirRef currentEntryRef, newStreamEntryRef;
888 HRESULT hr;
890 TRACE("(%p, %s, %x, %d, %d, %p)\n",
891 iface, debugstr_w(pwcsName), grfMode,
892 reserved1, reserved2, ppstm);
894 if (ppstm == 0)
895 return STG_E_INVALIDPOINTER;
897 if (pwcsName == 0)
898 return STG_E_INVALIDNAME;
900 if (reserved1 || reserved2)
901 return STG_E_INVALIDPARAMETER;
903 if ( FAILED( validateSTGM(grfMode) ))
904 return STG_E_INVALIDFLAG;
906 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
907 return STG_E_INVALIDFLAG;
909 if (This->reverted)
910 return STG_E_REVERTED;
913 * As documented.
915 if ((grfMode & STGM_DELETEONRELEASE) ||
916 (grfMode & STGM_TRANSACTED))
917 return STG_E_INVALIDFUNCTION;
920 * Don't worry about permissions in transacted mode, as we can always write
921 * changes; we just can't always commit them.
923 if(!(This->openFlags & STGM_TRANSACTED)) {
924 /* Can't create a stream on read-only storage */
925 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
926 return STG_E_ACCESSDENIED;
928 /* Can't create a stream with greater access than the parent. */
929 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
930 return STG_E_ACCESSDENIED;
933 if(This->openFlags & STGM_SIMPLE)
934 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
936 *ppstm = 0;
938 currentEntryRef = findElement(This,
939 This->storageDirEntry,
940 pwcsName,
941 &currentEntry);
943 if (currentEntryRef != DIRENTRY_NULL)
946 * An element with this name already exists
948 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
950 IStorage_DestroyElement(iface, pwcsName);
952 else
953 return STG_E_FILEALREADYEXISTS;
957 * memset the empty entry
959 memset(&newStreamEntry, 0, sizeof(DirEntry));
961 newStreamEntry.sizeOfNameString =
962 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
964 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
965 return STG_E_INVALIDNAME;
967 strcpyW(newStreamEntry.name, pwcsName);
969 newStreamEntry.stgType = STGTY_STREAM;
970 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
971 newStreamEntry.size.u.LowPart = 0;
972 newStreamEntry.size.u.HighPart = 0;
974 newStreamEntry.leftChild = DIRENTRY_NULL;
975 newStreamEntry.rightChild = DIRENTRY_NULL;
976 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
978 /* call CoFileTime to get the current time
979 newStreamEntry.ctime
980 newStreamEntry.mtime
983 /* newStreamEntry.clsid */
986 * Create an entry with the new data
988 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
989 if (FAILED(hr))
990 return hr;
993 * Insert the new entry in the parent storage's tree.
995 hr = insertIntoTree(
996 This,
997 This->storageDirEntry,
998 newStreamEntryRef);
999 if (FAILED(hr))
1001 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1002 return hr;
1006 * Open the stream to return it.
1008 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1010 if (newStream != 0)
1012 *ppstm = (IStream*)newStream;
1014 IStream_AddRef(*ppstm);
1016 else
1018 return STG_E_INSUFFICIENTMEMORY;
1021 return StorageBaseImpl_Flush(This);
1024 /************************************************************************
1025 * Storage32BaseImpl_SetClass (IStorage)
1027 * This method will write the specified CLSID in the directory entry of this
1028 * storage.
1030 * See Windows documentation for more details on IStorage methods.
1032 static HRESULT WINAPI StorageBaseImpl_SetClass(
1033 IStorage* iface,
1034 REFCLSID clsid) /* [in] */
1036 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1037 HRESULT hRes;
1038 DirEntry currentEntry;
1040 TRACE("(%p, %p)\n", iface, clsid);
1042 if (This->reverted)
1043 return STG_E_REVERTED;
1045 hRes = StorageBaseImpl_ReadDirEntry(This,
1046 This->storageDirEntry,
1047 &currentEntry);
1048 if (SUCCEEDED(hRes))
1050 currentEntry.clsid = *clsid;
1052 hRes = StorageBaseImpl_WriteDirEntry(This,
1053 This->storageDirEntry,
1054 &currentEntry);
1057 if (SUCCEEDED(hRes))
1058 hRes = StorageBaseImpl_Flush(This);
1060 return hRes;
1063 /************************************************************************
1064 ** Storage32Impl implementation
1067 /************************************************************************
1068 * Storage32BaseImpl_CreateStorage (IStorage)
1070 * This method will create the storage object within the provided storage.
1072 * See Windows documentation for more details on IStorage methods.
1074 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1075 IStorage* iface,
1076 const OLECHAR *pwcsName, /* [string][in] */
1077 DWORD grfMode, /* [in] */
1078 DWORD reserved1, /* [in] */
1079 DWORD reserved2, /* [in] */
1080 IStorage **ppstg) /* [out] */
1082 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1084 DirEntry currentEntry;
1085 DirEntry newEntry;
1086 DirRef currentEntryRef;
1087 DirRef newEntryRef;
1088 HRESULT hr;
1090 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1091 iface, debugstr_w(pwcsName), grfMode,
1092 reserved1, reserved2, ppstg);
1094 if (ppstg == 0)
1095 return STG_E_INVALIDPOINTER;
1097 if (This->openFlags & STGM_SIMPLE)
1099 return STG_E_INVALIDFUNCTION;
1102 if (pwcsName == 0)
1103 return STG_E_INVALIDNAME;
1105 *ppstg = NULL;
1107 if ( FAILED( validateSTGM(grfMode) ) ||
1108 (grfMode & STGM_DELETEONRELEASE) )
1110 WARN("bad grfMode: 0x%x\n", grfMode);
1111 return STG_E_INVALIDFLAG;
1114 if (This->reverted)
1115 return STG_E_REVERTED;
1118 * Check that we're compatible with the parent's storage mode
1120 if ( !(This->openFlags & STGM_TRANSACTED) &&
1121 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1123 WARN("access denied\n");
1124 return STG_E_ACCESSDENIED;
1127 currentEntryRef = findElement(This,
1128 This->storageDirEntry,
1129 pwcsName,
1130 &currentEntry);
1132 if (currentEntryRef != DIRENTRY_NULL)
1135 * An element with this name already exists
1137 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1138 ((This->openFlags & STGM_TRANSACTED) ||
1139 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1141 hr = IStorage_DestroyElement(iface, pwcsName);
1142 if (FAILED(hr))
1143 return hr;
1145 else
1147 WARN("file already exists\n");
1148 return STG_E_FILEALREADYEXISTS;
1151 else if (!(This->openFlags & STGM_TRANSACTED) &&
1152 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1154 WARN("read-only storage\n");
1155 return STG_E_ACCESSDENIED;
1158 memset(&newEntry, 0, sizeof(DirEntry));
1160 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1162 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1164 FIXME("name too long\n");
1165 return STG_E_INVALIDNAME;
1168 strcpyW(newEntry.name, pwcsName);
1170 newEntry.stgType = STGTY_STORAGE;
1171 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1172 newEntry.size.u.LowPart = 0;
1173 newEntry.size.u.HighPart = 0;
1175 newEntry.leftChild = DIRENTRY_NULL;
1176 newEntry.rightChild = DIRENTRY_NULL;
1177 newEntry.dirRootEntry = DIRENTRY_NULL;
1179 /* call CoFileTime to get the current time
1180 newEntry.ctime
1181 newEntry.mtime
1184 /* newEntry.clsid */
1187 * Create a new directory entry for the storage
1189 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1190 if (FAILED(hr))
1191 return hr;
1194 * Insert the new directory entry into the parent storage's tree
1196 hr = insertIntoTree(
1197 This,
1198 This->storageDirEntry,
1199 newEntryRef);
1200 if (FAILED(hr))
1202 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1203 return hr;
1207 * Open it to get a pointer to return.
1209 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1211 if( (hr != S_OK) || (*ppstg == NULL))
1213 return hr;
1216 if (SUCCEEDED(hr))
1217 hr = StorageBaseImpl_Flush(This);
1219 return S_OK;
1223 /***************************************************************************
1225 * Internal Method
1227 * Reserve a directory entry in the file and initialize it.
1229 static HRESULT StorageImpl_CreateDirEntry(
1230 StorageBaseImpl *base,
1231 const DirEntry *newData,
1232 DirRef *index)
1234 StorageImpl *storage = (StorageImpl*)base;
1235 ULONG currentEntryIndex = 0;
1236 ULONG newEntryIndex = DIRENTRY_NULL;
1237 HRESULT hr = S_OK;
1238 BYTE currentData[RAW_DIRENTRY_SIZE];
1239 WORD sizeOfNameString;
1243 hr = StorageImpl_ReadRawDirEntry(storage,
1244 currentEntryIndex,
1245 currentData);
1247 if (SUCCEEDED(hr))
1249 StorageUtl_ReadWord(
1250 currentData,
1251 OFFSET_PS_NAMELENGTH,
1252 &sizeOfNameString);
1254 if (sizeOfNameString == 0)
1257 * The entry exists and is available, we found it.
1259 newEntryIndex = currentEntryIndex;
1262 else
1265 * We exhausted the directory entries, we will create more space below
1267 newEntryIndex = currentEntryIndex;
1269 currentEntryIndex++;
1271 } while (newEntryIndex == DIRENTRY_NULL);
1274 * grow the directory stream
1276 if (FAILED(hr))
1278 BYTE emptyData[RAW_DIRENTRY_SIZE];
1279 ULARGE_INTEGER newSize;
1280 ULONG entryIndex;
1281 ULONG lastEntry = 0;
1282 ULONG blockCount = 0;
1285 * obtain the new count of blocks in the directory stream
1287 blockCount = BlockChainStream_GetCount(
1288 storage->rootBlockChain)+1;
1291 * initialize the size used by the directory stream
1293 newSize.u.HighPart = 0;
1294 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1297 * add a block to the directory stream
1299 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1302 * memset the empty entry in order to initialize the unused newly
1303 * created entries
1305 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1308 * initialize them
1310 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1312 for(
1313 entryIndex = newEntryIndex + 1;
1314 entryIndex < lastEntry;
1315 entryIndex++)
1317 StorageImpl_WriteRawDirEntry(
1318 storage,
1319 entryIndex,
1320 emptyData);
1323 StorageImpl_SaveFileHeader(storage);
1326 UpdateRawDirEntry(currentData, newData);
1328 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1330 if (SUCCEEDED(hr))
1331 *index = newEntryIndex;
1333 return hr;
1336 /***************************************************************************
1338 * Internal Method
1340 * Mark a directory entry in the file as free.
1342 static HRESULT StorageImpl_DestroyDirEntry(
1343 StorageBaseImpl *base,
1344 DirRef index)
1346 HRESULT hr;
1347 BYTE emptyData[RAW_DIRENTRY_SIZE];
1348 StorageImpl *storage = (StorageImpl*)base;
1350 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1352 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1354 return hr;
1358 /****************************************************************************
1360 * Internal Method
1362 * Case insensitive comparison of DirEntry.name by first considering
1363 * their size.
1365 * Returns <0 when name1 < name2
1366 * >0 when name1 > name2
1367 * 0 when name1 == name2
1369 static LONG entryNameCmp(
1370 const OLECHAR *name1,
1371 const OLECHAR *name2)
1373 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1375 while (diff == 0 && *name1 != 0)
1378 * We compare the string themselves only when they are of the same length
1380 diff = toupperW(*name1++) - toupperW(*name2++);
1383 return diff;
1386 /****************************************************************************
1388 * Internal Method
1390 * Add a directory entry to a storage
1392 static HRESULT insertIntoTree(
1393 StorageBaseImpl *This,
1394 DirRef parentStorageIndex,
1395 DirRef newEntryIndex)
1397 DirEntry currentEntry;
1398 DirEntry newEntry;
1401 * Read the inserted entry
1403 StorageBaseImpl_ReadDirEntry(This,
1404 newEntryIndex,
1405 &newEntry);
1408 * Read the storage entry
1410 StorageBaseImpl_ReadDirEntry(This,
1411 parentStorageIndex,
1412 &currentEntry);
1414 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1417 * The root storage contains some element, therefore, start the research
1418 * for the appropriate location.
1420 BOOL found = 0;
1421 DirRef current, next, previous, currentEntryId;
1424 * Keep a reference to the root of the storage's element tree
1426 currentEntryId = currentEntry.dirRootEntry;
1429 * Read
1431 StorageBaseImpl_ReadDirEntry(This,
1432 currentEntry.dirRootEntry,
1433 &currentEntry);
1435 previous = currentEntry.leftChild;
1436 next = currentEntry.rightChild;
1437 current = currentEntryId;
1439 while (found == 0)
1441 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1443 if (diff < 0)
1445 if (previous != DIRENTRY_NULL)
1447 StorageBaseImpl_ReadDirEntry(This,
1448 previous,
1449 &currentEntry);
1450 current = previous;
1452 else
1454 currentEntry.leftChild = newEntryIndex;
1455 StorageBaseImpl_WriteDirEntry(This,
1456 current,
1457 &currentEntry);
1458 found = 1;
1461 else if (diff > 0)
1463 if (next != DIRENTRY_NULL)
1465 StorageBaseImpl_ReadDirEntry(This,
1466 next,
1467 &currentEntry);
1468 current = next;
1470 else
1472 currentEntry.rightChild = newEntryIndex;
1473 StorageBaseImpl_WriteDirEntry(This,
1474 current,
1475 &currentEntry);
1476 found = 1;
1479 else
1482 * Trying to insert an item with the same name in the
1483 * subtree structure.
1485 return STG_E_FILEALREADYEXISTS;
1488 previous = currentEntry.leftChild;
1489 next = currentEntry.rightChild;
1492 else
1495 * The storage is empty, make the new entry the root of its element tree
1497 currentEntry.dirRootEntry = newEntryIndex;
1498 StorageBaseImpl_WriteDirEntry(This,
1499 parentStorageIndex,
1500 &currentEntry);
1503 return S_OK;
1506 /****************************************************************************
1508 * Internal Method
1510 * Find and read the element of a storage with the given name.
1512 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1513 const OLECHAR *name, DirEntry *data)
1515 DirRef currentEntry;
1517 /* Read the storage entry to find the root of the tree. */
1518 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1520 currentEntry = data->dirRootEntry;
1522 while (currentEntry != DIRENTRY_NULL)
1524 LONG cmp;
1526 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1528 cmp = entryNameCmp(name, data->name);
1530 if (cmp == 0)
1531 /* found it */
1532 break;
1534 else if (cmp < 0)
1535 currentEntry = data->leftChild;
1537 else if (cmp > 0)
1538 currentEntry = data->rightChild;
1541 return currentEntry;
1544 /****************************************************************************
1546 * Internal Method
1548 * Find and read the binary tree parent of the element with the given name.
1550 * If there is no such element, find a place where it could be inserted and
1551 * return STG_E_FILENOTFOUND.
1553 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1554 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1555 ULONG *relation)
1557 DirRef childEntry;
1558 DirEntry childData;
1560 /* Read the storage entry to find the root of the tree. */
1561 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1563 *parentEntry = storageEntry;
1564 *relation = DIRENTRY_RELATION_DIR;
1566 childEntry = parentData->dirRootEntry;
1568 while (childEntry != DIRENTRY_NULL)
1570 LONG cmp;
1572 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1574 cmp = entryNameCmp(childName, childData.name);
1576 if (cmp == 0)
1577 /* found it */
1578 break;
1580 else if (cmp < 0)
1582 *parentData = childData;
1583 *parentEntry = childEntry;
1584 *relation = DIRENTRY_RELATION_PREVIOUS;
1586 childEntry = parentData->leftChild;
1589 else if (cmp > 0)
1591 *parentData = childData;
1592 *parentEntry = childEntry;
1593 *relation = DIRENTRY_RELATION_NEXT;
1595 childEntry = parentData->rightChild;
1599 if (childEntry == DIRENTRY_NULL)
1600 return STG_E_FILENOTFOUND;
1601 else
1602 return S_OK;
1606 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1607 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1608 SNB snbExclude, IStorage *pstgDest);
1610 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1611 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1612 SNB snbExclude, IStorage *pstgDest)
1614 DirEntry data;
1615 HRESULT hr;
1616 BOOL skip = FALSE;
1617 IStorage *pstgTmp;
1618 IStream *pstrChild, *pstrTmp;
1619 STATSTG strStat;
1621 if (srcEntry == DIRENTRY_NULL)
1622 return S_OK;
1624 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1626 if (FAILED(hr))
1627 return hr;
1629 if ( snbExclude )
1631 WCHAR **snb = snbExclude;
1633 while ( *snb != NULL && !skip )
1635 if ( lstrcmpW(data.name, *snb) == 0 )
1636 skip = TRUE;
1637 ++snb;
1641 if (!skip)
1643 if (data.stgType == STGTY_STORAGE && !skip_storage)
1646 * create a new storage in destination storage
1648 hr = IStorage_CreateStorage( pstgDest, data.name,
1649 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1650 0, 0,
1651 &pstgTmp );
1654 * if it already exist, don't create a new one use this one
1656 if (hr == STG_E_FILEALREADYEXISTS)
1658 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1659 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1660 NULL, 0, &pstgTmp );
1663 if (SUCCEEDED(hr))
1665 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1666 skip_stream, NULL, pstgTmp );
1668 IStorage_Release(pstgTmp);
1671 else if (data.stgType == STGTY_STREAM && !skip_stream)
1674 * create a new stream in destination storage. If the stream already
1675 * exist, it will be deleted and a new one will be created.
1677 hr = IStorage_CreateStream( pstgDest, data.name,
1678 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1679 0, 0, &pstrTmp );
1682 * open child stream storage. This operation must succeed even if the
1683 * stream is already open, so we use internal functions to do it.
1685 if (hr == S_OK)
1687 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1688 if (pstrChild)
1689 IStream_AddRef(pstrChild);
1690 else
1691 hr = E_OUTOFMEMORY;
1694 if (hr == S_OK)
1697 * Get the size of the source stream
1699 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1702 * Set the size of the destination stream.
1704 IStream_SetSize(pstrTmp, strStat.cbSize);
1707 * do the copy
1709 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1710 NULL, NULL );
1712 IStream_Release( pstrChild );
1715 IStream_Release( pstrTmp );
1719 /* copy siblings */
1720 if (SUCCEEDED(hr))
1721 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1722 skip_stream, snbExclude, pstgDest );
1724 if (SUCCEEDED(hr))
1725 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1726 skip_stream, snbExclude, pstgDest );
1728 return hr;
1731 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1732 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1733 SNB snbExclude, IStorage *pstgDest)
1735 DirEntry data;
1736 HRESULT hr;
1738 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1740 if (SUCCEEDED(hr))
1741 hr = IStorage_SetClass( pstgDest, &data.clsid );
1743 if (SUCCEEDED(hr))
1744 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1745 skip_stream, snbExclude, pstgDest );
1747 return hr;
1750 /*************************************************************************
1751 * CopyTo (IStorage)
1753 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1754 IStorage* iface,
1755 DWORD ciidExclude, /* [in] */
1756 const IID* rgiidExclude, /* [size_is][unique][in] */
1757 SNB snbExclude, /* [unique][in] */
1758 IStorage* pstgDest) /* [unique][in] */
1760 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1762 BOOL skip_storage = FALSE, skip_stream = FALSE;
1763 int i;
1765 TRACE("(%p, %d, %p, %p, %p)\n",
1766 iface, ciidExclude, rgiidExclude,
1767 snbExclude, pstgDest);
1769 if ( pstgDest == 0 )
1770 return STG_E_INVALIDPOINTER;
1772 for(i = 0; i < ciidExclude; ++i)
1774 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1775 skip_storage = TRUE;
1776 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1777 skip_stream = TRUE;
1778 else
1779 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1782 if (!skip_storage)
1784 /* Give up early if it looks like this would be infinitely recursive.
1785 * Oddly enough, this includes some cases that aren't really recursive, like
1786 * copying to a transacted child. */
1787 IStorage *pstgDestAncestor = pstgDest;
1788 IStorage *pstgDestAncestorChild = NULL;
1790 /* Go up the chain from the destination until we find the source storage. */
1791 while (pstgDestAncestor != iface) {
1792 pstgDestAncestorChild = pstgDest;
1794 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1796 TransactedSnapshotImpl *impl = (TransactedSnapshotImpl*) pstgDestAncestor;
1798 pstgDestAncestor = (IStorage*)impl->transactedParent;
1800 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1802 StorageInternalImpl *impl = (StorageInternalImpl*) pstgDestAncestor;
1804 pstgDestAncestor = (IStorage*)impl->parentStorage;
1806 else
1807 break;
1810 if (pstgDestAncestor == iface)
1812 BOOL fail = TRUE;
1814 if (pstgDestAncestorChild && snbExclude)
1816 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1817 DirEntry data;
1818 WCHAR **snb = snbExclude;
1820 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1822 while ( *snb != NULL && fail )
1824 if ( lstrcmpW(data.name, *snb) == 0 )
1825 fail = FALSE;
1826 ++snb;
1830 if (fail)
1831 return STG_E_ACCESSDENIED;
1835 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1836 skip_storage, skip_stream, snbExclude, pstgDest );
1839 /*************************************************************************
1840 * MoveElementTo (IStorage)
1842 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1843 IStorage* iface,
1844 const OLECHAR *pwcsName, /* [string][in] */
1845 IStorage *pstgDest, /* [unique][in] */
1846 const OLECHAR *pwcsNewName,/* [string][in] */
1847 DWORD grfFlags) /* [in] */
1849 FIXME("(%p %s %p %s %u): stub\n", iface,
1850 debugstr_w(pwcsName), pstgDest,
1851 debugstr_w(pwcsNewName), grfFlags);
1852 return E_NOTIMPL;
1855 /*************************************************************************
1856 * Commit (IStorage)
1858 * Ensures that any changes made to a storage object open in transacted mode
1859 * are reflected in the parent storage
1861 * In a non-transacted mode, this ensures all cached writes are completed.
1863 static HRESULT WINAPI StorageImpl_Commit(
1864 IStorage* iface,
1865 DWORD grfCommitFlags)/* [in] */
1867 StorageBaseImpl* const base=(StorageBaseImpl*)iface;
1868 TRACE("(%p %d)\n", iface, grfCommitFlags);
1869 return StorageBaseImpl_Flush(base);
1872 /*************************************************************************
1873 * Revert (IStorage)
1875 * Discard all changes that have been made since the last commit operation
1877 static HRESULT WINAPI StorageImpl_Revert(
1878 IStorage* iface)
1880 TRACE("(%p)\n", iface);
1881 return S_OK;
1884 /*************************************************************************
1885 * DestroyElement (IStorage)
1887 * Strategy: This implementation is built this way for simplicity not for speed.
1888 * I always delete the topmost element of the enumeration and adjust
1889 * the deleted element pointer all the time. This takes longer to
1890 * do but allow to reinvoke DestroyElement whenever we encounter a
1891 * storage object. The optimisation resides in the usage of another
1892 * enumeration strategy that would give all the leaves of a storage
1893 * first. (postfix order)
1895 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1896 IStorage* iface,
1897 const OLECHAR *pwcsName)/* [string][in] */
1899 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1901 HRESULT hr = S_OK;
1902 DirEntry entryToDelete;
1903 DirRef entryToDeleteRef;
1905 TRACE("(%p, %s)\n",
1906 iface, debugstr_w(pwcsName));
1908 if (pwcsName==NULL)
1909 return STG_E_INVALIDPOINTER;
1911 if (This->reverted)
1912 return STG_E_REVERTED;
1914 if ( !(This->openFlags & STGM_TRANSACTED) &&
1915 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1916 return STG_E_ACCESSDENIED;
1918 entryToDeleteRef = findElement(
1919 This,
1920 This->storageDirEntry,
1921 pwcsName,
1922 &entryToDelete);
1924 if ( entryToDeleteRef == DIRENTRY_NULL )
1926 return STG_E_FILENOTFOUND;
1929 if ( entryToDelete.stgType == STGTY_STORAGE )
1931 hr = deleteStorageContents(
1932 This,
1933 entryToDeleteRef,
1934 entryToDelete);
1936 else if ( entryToDelete.stgType == STGTY_STREAM )
1938 hr = deleteStreamContents(
1939 This,
1940 entryToDeleteRef,
1941 entryToDelete);
1944 if (hr!=S_OK)
1945 return hr;
1948 * Remove the entry from its parent storage
1950 hr = removeFromTree(
1951 This,
1952 This->storageDirEntry,
1953 entryToDeleteRef);
1956 * Invalidate the entry
1958 if (SUCCEEDED(hr))
1959 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1961 if (SUCCEEDED(hr))
1962 hr = StorageBaseImpl_Flush(This);
1964 return hr;
1968 /******************************************************************************
1969 * Internal stream list handlers
1972 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1974 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1975 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1978 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1980 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1981 list_remove(&(strm->StrmListEntry));
1984 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1986 StgStreamImpl *strm;
1988 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1990 if (strm->dirEntry == streamEntry)
1992 return TRUE;
1996 return FALSE;
1999 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2001 StorageInternalImpl *childstg;
2003 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2005 if (childstg->base.storageDirEntry == storageEntry)
2007 return TRUE;
2011 return FALSE;
2014 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2016 struct list *cur, *cur2;
2017 StgStreamImpl *strm=NULL;
2018 StorageInternalImpl *childstg=NULL;
2020 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2021 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2022 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2023 strm->parentStorage = NULL;
2024 list_remove(cur);
2027 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2028 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2029 StorageBaseImpl_Invalidate( &childstg->base );
2032 if (stg->transactedChild)
2034 StorageBaseImpl_Invalidate(stg->transactedChild);
2036 stg->transactedChild = NULL;
2041 /*********************************************************************
2043 * Internal Method
2045 * Delete the contents of a storage entry.
2048 static HRESULT deleteStorageContents(
2049 StorageBaseImpl *parentStorage,
2050 DirRef indexToDelete,
2051 DirEntry entryDataToDelete)
2053 IEnumSTATSTG *elements = 0;
2054 IStorage *childStorage = 0;
2055 STATSTG currentElement;
2056 HRESULT hr;
2057 HRESULT destroyHr = S_OK;
2058 StorageInternalImpl *stg, *stg2;
2060 /* Invalidate any open storage objects. */
2061 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2063 if (stg->base.storageDirEntry == indexToDelete)
2065 StorageBaseImpl_Invalidate(&stg->base);
2070 * Open the storage and enumerate it
2072 hr = StorageBaseImpl_OpenStorage(
2073 (IStorage*)parentStorage,
2074 entryDataToDelete.name,
2076 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2079 &childStorage);
2081 if (hr != S_OK)
2083 return hr;
2087 * Enumerate the elements
2089 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2094 * Obtain the next element
2096 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2097 if (hr==S_OK)
2099 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2101 CoTaskMemFree(currentElement.pwcsName);
2105 * We need to Reset the enumeration every time because we delete elements
2106 * and the enumeration could be invalid
2108 IEnumSTATSTG_Reset(elements);
2110 } while ((hr == S_OK) && (destroyHr == S_OK));
2112 IStorage_Release(childStorage);
2113 IEnumSTATSTG_Release(elements);
2115 return destroyHr;
2118 /*********************************************************************
2120 * Internal Method
2122 * Perform the deletion of a stream's data
2125 static HRESULT deleteStreamContents(
2126 StorageBaseImpl *parentStorage,
2127 DirRef indexToDelete,
2128 DirEntry entryDataToDelete)
2130 IStream *pis;
2131 HRESULT hr;
2132 ULARGE_INTEGER size;
2133 StgStreamImpl *strm, *strm2;
2135 /* Invalidate any open stream objects. */
2136 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2138 if (strm->dirEntry == indexToDelete)
2140 TRACE("Stream deleted %p\n", strm);
2141 strm->parentStorage = NULL;
2142 list_remove(&strm->StrmListEntry);
2146 size.u.HighPart = 0;
2147 size.u.LowPart = 0;
2149 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2150 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2152 if (hr!=S_OK)
2154 return(hr);
2158 * Zap the stream
2160 hr = IStream_SetSize(pis, size);
2162 if(hr != S_OK)
2164 return hr;
2168 * Release the stream object.
2170 IStream_Release(pis);
2172 return S_OK;
2175 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2177 switch (relation)
2179 case DIRENTRY_RELATION_PREVIOUS:
2180 entry->leftChild = new_target;
2181 break;
2182 case DIRENTRY_RELATION_NEXT:
2183 entry->rightChild = new_target;
2184 break;
2185 case DIRENTRY_RELATION_DIR:
2186 entry->dirRootEntry = new_target;
2187 break;
2188 default:
2189 assert(0);
2193 /*************************************************************************
2195 * Internal Method
2197 * This method removes a directory entry from its parent storage tree without
2198 * freeing any resources attached to it.
2200 static HRESULT removeFromTree(
2201 StorageBaseImpl *This,
2202 DirRef parentStorageIndex,
2203 DirRef deletedIndex)
2205 HRESULT hr = S_OK;
2206 DirEntry entryToDelete;
2207 DirEntry parentEntry;
2208 DirRef parentEntryRef;
2209 ULONG typeOfRelation;
2211 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2213 if (hr != S_OK)
2214 return hr;
2217 * Find the element that links to the one we want to delete.
2219 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2220 &parentEntry, &parentEntryRef, &typeOfRelation);
2222 if (hr != S_OK)
2223 return hr;
2225 if (entryToDelete.leftChild != DIRENTRY_NULL)
2228 * Replace the deleted entry with its left child
2230 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2232 hr = StorageBaseImpl_WriteDirEntry(
2233 This,
2234 parentEntryRef,
2235 &parentEntry);
2236 if(FAILED(hr))
2238 return hr;
2241 if (entryToDelete.rightChild != DIRENTRY_NULL)
2244 * We need to reinsert the right child somewhere. We already know it and
2245 * its children are greater than everything in the left tree, so we
2246 * insert it at the rightmost point in the left tree.
2248 DirRef newRightChildParent = entryToDelete.leftChild;
2249 DirEntry newRightChildParentEntry;
2253 hr = StorageBaseImpl_ReadDirEntry(
2254 This,
2255 newRightChildParent,
2256 &newRightChildParentEntry);
2257 if (FAILED(hr))
2259 return hr;
2262 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2263 newRightChildParent = newRightChildParentEntry.rightChild;
2264 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2266 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2268 hr = StorageBaseImpl_WriteDirEntry(
2269 This,
2270 newRightChildParent,
2271 &newRightChildParentEntry);
2272 if (FAILED(hr))
2274 return hr;
2278 else
2281 * Replace the deleted entry with its right child
2283 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2285 hr = StorageBaseImpl_WriteDirEntry(
2286 This,
2287 parentEntryRef,
2288 &parentEntry);
2289 if(FAILED(hr))
2291 return hr;
2295 return hr;
2299 /******************************************************************************
2300 * SetElementTimes (IStorage)
2302 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2303 IStorage* iface,
2304 const OLECHAR *pwcsName,/* [string][in] */
2305 const FILETIME *pctime, /* [in] */
2306 const FILETIME *patime, /* [in] */
2307 const FILETIME *pmtime) /* [in] */
2309 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2310 return S_OK;
2313 /******************************************************************************
2314 * SetStateBits (IStorage)
2316 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2317 IStorage* iface,
2318 DWORD grfStateBits,/* [in] */
2319 DWORD grfMask) /* [in] */
2321 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2323 if (This->reverted)
2324 return STG_E_REVERTED;
2326 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2327 return S_OK;
2330 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2331 DirRef index, const DirEntry *data)
2333 StorageImpl *This = (StorageImpl*)base;
2334 return StorageImpl_WriteDirEntry(This, index, data);
2337 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2338 DirRef index, DirEntry *data)
2340 StorageImpl *This = (StorageImpl*)base;
2341 return StorageImpl_ReadDirEntry(This, index, data);
2344 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2346 int i;
2348 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2350 if (!This->blockChainCache[i])
2352 return &This->blockChainCache[i];
2356 i = This->blockChainToEvict;
2358 BlockChainStream_Destroy(This->blockChainCache[i]);
2359 This->blockChainCache[i] = NULL;
2361 This->blockChainToEvict++;
2362 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2363 This->blockChainToEvict = 0;
2365 return &This->blockChainCache[i];
2368 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2369 DirRef index)
2371 int i, free_index=-1;
2373 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2375 if (!This->blockChainCache[i])
2377 if (free_index == -1) free_index = i;
2379 else if (This->blockChainCache[i]->ownerDirEntry == index)
2381 return &This->blockChainCache[i];
2385 if (free_index == -1)
2387 free_index = This->blockChainToEvict;
2389 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2390 This->blockChainCache[free_index] = NULL;
2392 This->blockChainToEvict++;
2393 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2394 This->blockChainToEvict = 0;
2397 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2398 return &This->blockChainCache[free_index];
2401 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2403 int i;
2405 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2407 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2409 BlockChainStream_Destroy(This->blockChainCache[i]);
2410 This->blockChainCache[i] = NULL;
2411 return;
2416 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2417 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2419 StorageImpl *This = (StorageImpl*)base;
2420 DirEntry data;
2421 HRESULT hr;
2422 ULONG bytesToRead;
2424 hr = StorageImpl_ReadDirEntry(This, index, &data);
2425 if (FAILED(hr)) return hr;
2427 if (data.size.QuadPart == 0)
2429 *bytesRead = 0;
2430 return S_OK;
2433 if (offset.QuadPart + size > data.size.QuadPart)
2435 bytesToRead = data.size.QuadPart - offset.QuadPart;
2437 else
2439 bytesToRead = size;
2442 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2444 SmallBlockChainStream *stream;
2446 stream = SmallBlockChainStream_Construct(This, NULL, index);
2447 if (!stream) return E_OUTOFMEMORY;
2449 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2451 SmallBlockChainStream_Destroy(stream);
2453 return hr;
2455 else
2457 BlockChainStream *stream = NULL;
2459 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2460 if (!stream) return E_OUTOFMEMORY;
2462 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2464 return hr;
2468 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2469 ULARGE_INTEGER newsize)
2471 StorageImpl *This = (StorageImpl*)base;
2472 DirEntry data;
2473 HRESULT hr;
2474 SmallBlockChainStream *smallblock=NULL;
2475 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2477 hr = StorageImpl_ReadDirEntry(This, index, &data);
2478 if (FAILED(hr)) return hr;
2480 /* In simple mode keep the stream size above the small block limit */
2481 if (This->base.openFlags & STGM_SIMPLE)
2482 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2484 if (data.size.QuadPart == newsize.QuadPart)
2485 return S_OK;
2487 /* Create a block chain object of the appropriate type */
2488 if (data.size.QuadPart == 0)
2490 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2492 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2493 if (!smallblock) return E_OUTOFMEMORY;
2495 else
2497 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2498 bigblock = *pbigblock;
2499 if (!bigblock) return E_OUTOFMEMORY;
2502 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2504 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2505 if (!smallblock) return E_OUTOFMEMORY;
2507 else
2509 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2510 bigblock = *pbigblock;
2511 if (!bigblock) return E_OUTOFMEMORY;
2514 /* Change the block chain type if necessary. */
2515 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2517 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2518 if (!bigblock)
2520 SmallBlockChainStream_Destroy(smallblock);
2521 return E_FAIL;
2524 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2525 *pbigblock = bigblock;
2527 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2529 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2530 if (!smallblock)
2531 return E_FAIL;
2534 /* Set the size of the block chain. */
2535 if (smallblock)
2537 SmallBlockChainStream_SetSize(smallblock, newsize);
2538 SmallBlockChainStream_Destroy(smallblock);
2540 else
2542 BlockChainStream_SetSize(bigblock, newsize);
2545 /* Set the size in the directory entry. */
2546 hr = StorageImpl_ReadDirEntry(This, index, &data);
2547 if (SUCCEEDED(hr))
2549 data.size = newsize;
2551 hr = StorageImpl_WriteDirEntry(This, index, &data);
2553 return hr;
2556 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2557 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2559 StorageImpl *This = (StorageImpl*)base;
2560 DirEntry data;
2561 HRESULT hr;
2562 ULARGE_INTEGER newSize;
2564 hr = StorageImpl_ReadDirEntry(This, index, &data);
2565 if (FAILED(hr)) return hr;
2567 /* Grow the stream if necessary */
2568 newSize.QuadPart = 0;
2569 newSize.QuadPart = offset.QuadPart + size;
2571 if (newSize.QuadPart > data.size.QuadPart)
2573 hr = StorageImpl_StreamSetSize(base, index, newSize);
2574 if (FAILED(hr))
2575 return hr;
2577 hr = StorageImpl_ReadDirEntry(This, index, &data);
2578 if (FAILED(hr)) return hr;
2581 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2583 SmallBlockChainStream *stream;
2585 stream = SmallBlockChainStream_Construct(This, NULL, index);
2586 if (!stream) return E_OUTOFMEMORY;
2588 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2590 SmallBlockChainStream_Destroy(stream);
2592 return hr;
2594 else
2596 BlockChainStream *stream;
2598 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2599 if (!stream) return E_OUTOFMEMORY;
2601 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2603 return hr;
2607 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2608 DirRef src)
2610 StorageImpl *This = (StorageImpl*)base;
2611 DirEntry dst_data, src_data;
2612 HRESULT hr;
2614 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2616 if (SUCCEEDED(hr))
2617 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2619 if (SUCCEEDED(hr))
2621 StorageImpl_DeleteCachedBlockChainStream(This, src);
2622 dst_data.startingBlock = src_data.startingBlock;
2623 dst_data.size = src_data.size;
2625 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2628 return hr;
2631 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2633 StorageImpl *This = (StorageImpl*) iface;
2634 STATSTG statstg;
2635 HRESULT hr;
2637 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2639 *result = statstg.pwcsName;
2641 return hr;
2645 * Virtual function table for the IStorage32Impl class.
2647 static const IStorageVtbl Storage32Impl_Vtbl =
2649 StorageBaseImpl_QueryInterface,
2650 StorageBaseImpl_AddRef,
2651 StorageBaseImpl_Release,
2652 StorageBaseImpl_CreateStream,
2653 StorageBaseImpl_OpenStream,
2654 StorageBaseImpl_CreateStorage,
2655 StorageBaseImpl_OpenStorage,
2656 StorageBaseImpl_CopyTo,
2657 StorageBaseImpl_MoveElementTo,
2658 StorageImpl_Commit,
2659 StorageImpl_Revert,
2660 StorageBaseImpl_EnumElements,
2661 StorageBaseImpl_DestroyElement,
2662 StorageBaseImpl_RenameElement,
2663 StorageBaseImpl_SetElementTimes,
2664 StorageBaseImpl_SetClass,
2665 StorageBaseImpl_SetStateBits,
2666 StorageBaseImpl_Stat
2669 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2671 StorageImpl_Destroy,
2672 StorageImpl_Invalidate,
2673 StorageImpl_Flush,
2674 StorageImpl_GetFilename,
2675 StorageImpl_CreateDirEntry,
2676 StorageImpl_BaseWriteDirEntry,
2677 StorageImpl_BaseReadDirEntry,
2678 StorageImpl_DestroyDirEntry,
2679 StorageImpl_StreamReadAt,
2680 StorageImpl_StreamWriteAt,
2681 StorageImpl_StreamSetSize,
2682 StorageImpl_StreamLink
2685 static HRESULT StorageImpl_Construct(
2686 HANDLE hFile,
2687 LPCOLESTR pwcsName,
2688 ILockBytes* pLkbyt,
2689 DWORD openFlags,
2690 BOOL fileBased,
2691 BOOL create,
2692 ULONG sector_size,
2693 StorageImpl** result)
2695 StorageImpl* This;
2696 HRESULT hr = S_OK;
2697 DirEntry currentEntry;
2698 DirRef currentEntryRef;
2700 if ( FAILED( validateSTGM(openFlags) ))
2701 return STG_E_INVALIDFLAG;
2703 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2704 if (!This)
2705 return E_OUTOFMEMORY;
2707 memset(This, 0, sizeof(StorageImpl));
2709 list_init(&This->base.strmHead);
2711 list_init(&This->base.storageHead);
2713 This->base.lpVtbl = &Storage32Impl_Vtbl;
2714 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2715 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2716 This->base.openFlags = (openFlags & ~STGM_CREATE);
2717 This->base.ref = 1;
2718 This->base.create = create;
2720 This->base.reverted = 0;
2723 * Initialize the big block cache.
2725 This->bigBlockSize = sector_size;
2726 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2727 if (hFile)
2728 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2729 else
2731 This->lockBytes = pLkbyt;
2732 ILockBytes_AddRef(pLkbyt);
2735 if (FAILED(hr))
2736 goto end;
2738 if (create)
2740 ULARGE_INTEGER size;
2741 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2744 * Initialize all header variables:
2745 * - The big block depot consists of one block and it is at block 0
2746 * - The directory table starts at block 1
2747 * - There is no small block depot
2749 memset( This->bigBlockDepotStart,
2750 BLOCK_UNUSED,
2751 sizeof(This->bigBlockDepotStart));
2753 This->bigBlockDepotCount = 1;
2754 This->bigBlockDepotStart[0] = 0;
2755 This->rootStartBlock = 1;
2756 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2757 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2758 if (sector_size == 4096)
2759 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2760 else
2761 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2762 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2763 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2764 This->extBigBlockDepotCount = 0;
2766 StorageImpl_SaveFileHeader(This);
2769 * Add one block for the big block depot and one block for the directory table
2771 size.u.HighPart = 0;
2772 size.u.LowPart = This->bigBlockSize * 3;
2773 ILockBytes_SetSize(This->lockBytes, size);
2776 * Initialize the big block depot
2778 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2779 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2780 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2781 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2783 else
2786 * Load the header for the file.
2788 hr = StorageImpl_LoadFileHeader(This);
2790 if (FAILED(hr))
2792 goto end;
2797 * There is no block depot cached yet.
2799 This->indexBlockDepotCached = 0xFFFFFFFF;
2800 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2803 * Start searching for free blocks with block 0.
2805 This->prevFreeBlock = 0;
2807 This->firstFreeSmallBlock = 0;
2809 /* Read the extended big block depot locations. */
2810 if (This->extBigBlockDepotCount != 0)
2812 ULONG current_block = This->extBigBlockDepotStart;
2813 ULONG cache_size = This->extBigBlockDepotCount * 2;
2814 int i;
2816 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2817 if (!This->extBigBlockDepotLocations)
2819 hr = E_OUTOFMEMORY;
2820 goto end;
2823 This->extBigBlockDepotLocationsSize = cache_size;
2825 for (i=0; i<This->extBigBlockDepotCount; i++)
2827 if (current_block == BLOCK_END_OF_CHAIN)
2829 WARN("File has too few extended big block depot blocks.\n");
2830 hr = STG_E_DOCFILECORRUPT;
2831 goto end;
2833 This->extBigBlockDepotLocations[i] = current_block;
2834 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2837 else
2839 This->extBigBlockDepotLocations = NULL;
2840 This->extBigBlockDepotLocationsSize = 0;
2844 * Create the block chain abstractions.
2846 if(!(This->rootBlockChain =
2847 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2849 hr = STG_E_READFAULT;
2850 goto end;
2853 if(!(This->smallBlockDepotChain =
2854 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2855 DIRENTRY_NULL)))
2857 hr = STG_E_READFAULT;
2858 goto end;
2862 * Write the root storage entry (memory only)
2864 if (create)
2866 DirEntry rootEntry;
2868 * Initialize the directory table
2870 memset(&rootEntry, 0, sizeof(rootEntry));
2871 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2872 sizeof(rootEntry.name)/sizeof(WCHAR) );
2873 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2874 rootEntry.stgType = STGTY_ROOT;
2875 rootEntry.leftChild = DIRENTRY_NULL;
2876 rootEntry.rightChild = DIRENTRY_NULL;
2877 rootEntry.dirRootEntry = DIRENTRY_NULL;
2878 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2879 rootEntry.size.u.HighPart = 0;
2880 rootEntry.size.u.LowPart = 0;
2882 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2886 * Find the ID of the root storage.
2888 currentEntryRef = 0;
2892 hr = StorageImpl_ReadDirEntry(
2893 This,
2894 currentEntryRef,
2895 &currentEntry);
2897 if (SUCCEEDED(hr))
2899 if ( (currentEntry.sizeOfNameString != 0 ) &&
2900 (currentEntry.stgType == STGTY_ROOT) )
2902 This->base.storageDirEntry = currentEntryRef;
2906 currentEntryRef++;
2908 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2910 if (FAILED(hr))
2912 hr = STG_E_READFAULT;
2913 goto end;
2917 * Create the block chain abstraction for the small block root chain.
2919 if(!(This->smallBlockRootChain =
2920 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2922 hr = STG_E_READFAULT;
2925 end:
2926 if (FAILED(hr))
2928 IStorage_Release((IStorage*)This);
2929 *result = NULL;
2931 else
2933 StorageImpl_Flush((StorageBaseImpl*)This);
2934 *result = This;
2937 return hr;
2940 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2942 StorageImpl *This = (StorageImpl*) iface;
2944 StorageBaseImpl_DeleteAll(&This->base);
2946 This->base.reverted = 1;
2949 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2951 StorageImpl *This = (StorageImpl*) iface;
2952 int i;
2953 TRACE("(%p)\n", This);
2955 StorageImpl_Flush(iface);
2957 StorageImpl_Invalidate(iface);
2959 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
2961 BlockChainStream_Destroy(This->smallBlockRootChain);
2962 BlockChainStream_Destroy(This->rootBlockChain);
2963 BlockChainStream_Destroy(This->smallBlockDepotChain);
2965 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2966 BlockChainStream_Destroy(This->blockChainCache[i]);
2968 if (This->lockBytes)
2969 ILockBytes_Release(This->lockBytes);
2970 HeapFree(GetProcessHeap(), 0, This);
2973 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2975 StorageImpl *This = (StorageImpl*) iface;
2976 int i;
2977 HRESULT hr;
2978 TRACE("(%p)\n", This);
2980 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2982 if (SUCCEEDED(hr))
2983 hr = BlockChainStream_Flush(This->rootBlockChain);
2985 if (SUCCEEDED(hr))
2986 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2988 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
2989 if (This->blockChainCache[i])
2990 hr = BlockChainStream_Flush(This->blockChainCache[i]);
2992 if (SUCCEEDED(hr))
2993 hr = ILockBytes_Flush(This->lockBytes);
2995 return hr;
2998 /******************************************************************************
2999 * Storage32Impl_GetNextFreeBigBlock
3001 * Returns the index of the next free big block.
3002 * If the big block depot is filled, this method will enlarge it.
3005 static ULONG StorageImpl_GetNextFreeBigBlock(
3006 StorageImpl* This)
3008 ULONG depotBlockIndexPos;
3009 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3010 BOOL success;
3011 ULONG depotBlockOffset;
3012 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3013 ULONG nextBlockIndex = BLOCK_SPECIAL;
3014 int depotIndex = 0;
3015 ULONG freeBlock = BLOCK_UNUSED;
3016 ULARGE_INTEGER neededSize;
3017 STATSTG statstg;
3019 depotIndex = This->prevFreeBlock / blocksPerDepot;
3020 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3023 * Scan the entire big block depot until we find a block marked free
3025 while (nextBlockIndex != BLOCK_UNUSED)
3027 if (depotIndex < COUNT_BBDEPOTINHEADER)
3029 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3032 * Grow the primary depot.
3034 if (depotBlockIndexPos == BLOCK_UNUSED)
3036 depotBlockIndexPos = depotIndex*blocksPerDepot;
3039 * Add a block depot.
3041 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3042 This->bigBlockDepotCount++;
3043 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3046 * Flag it as a block depot.
3048 StorageImpl_SetNextBlockInChain(This,
3049 depotBlockIndexPos,
3050 BLOCK_SPECIAL);
3052 /* Save new header information.
3054 StorageImpl_SaveFileHeader(This);
3057 else
3059 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3061 if (depotBlockIndexPos == BLOCK_UNUSED)
3064 * Grow the extended depot.
3066 ULONG extIndex = BLOCK_UNUSED;
3067 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3068 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3070 if (extBlockOffset == 0)
3072 /* We need an extended block.
3074 extIndex = Storage32Impl_AddExtBlockDepot(This);
3075 This->extBigBlockDepotCount++;
3076 depotBlockIndexPos = extIndex + 1;
3078 else
3079 depotBlockIndexPos = depotIndex * blocksPerDepot;
3082 * Add a block depot and mark it in the extended block.
3084 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3085 This->bigBlockDepotCount++;
3086 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3088 /* Flag the block depot.
3090 StorageImpl_SetNextBlockInChain(This,
3091 depotBlockIndexPos,
3092 BLOCK_SPECIAL);
3094 /* If necessary, flag the extended depot block.
3096 if (extIndex != BLOCK_UNUSED)
3097 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3099 /* Save header information.
3101 StorageImpl_SaveFileHeader(This);
3105 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3107 if (success)
3109 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3110 ( nextBlockIndex != BLOCK_UNUSED))
3112 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3114 if (nextBlockIndex == BLOCK_UNUSED)
3116 freeBlock = (depotIndex * blocksPerDepot) +
3117 (depotBlockOffset/sizeof(ULONG));
3120 depotBlockOffset += sizeof(ULONG);
3124 depotIndex++;
3125 depotBlockOffset = 0;
3129 * make sure that the block physically exists before using it
3131 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3133 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3135 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3136 ILockBytes_SetSize(This->lockBytes, neededSize);
3138 This->prevFreeBlock = freeBlock;
3140 return freeBlock;
3143 /******************************************************************************
3144 * Storage32Impl_AddBlockDepot
3146 * This will create a depot block, essentially it is a block initialized
3147 * to BLOCK_UNUSEDs.
3149 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3151 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3154 * Initialize blocks as free
3156 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3157 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3160 /******************************************************************************
3161 * Storage32Impl_GetExtDepotBlock
3163 * Returns the index of the block that corresponds to the specified depot
3164 * index. This method is only for depot indexes equal or greater than
3165 * COUNT_BBDEPOTINHEADER.
3167 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3169 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3170 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3171 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3172 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3173 ULONG blockIndex = BLOCK_UNUSED;
3174 ULONG extBlockIndex;
3175 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3176 int index, num_blocks;
3178 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3180 if (extBlockCount >= This->extBigBlockDepotCount)
3181 return BLOCK_UNUSED;
3183 if (This->indexExtBlockDepotCached != extBlockCount)
3185 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3187 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);
3189 num_blocks = This->bigBlockSize / 4;
3191 for (index = 0; index < num_blocks; index++)
3193 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3194 This->extBlockDepotCached[index] = blockIndex;
3197 This->indexExtBlockDepotCached = extBlockCount;
3200 blockIndex = This->extBlockDepotCached[extBlockOffset];
3202 return blockIndex;
3205 /******************************************************************************
3206 * Storage32Impl_SetExtDepotBlock
3208 * Associates the specified block index to the specified depot index.
3209 * This method is only for depot indexes equal or greater than
3210 * COUNT_BBDEPOTINHEADER.
3212 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3214 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3215 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3216 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3217 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3218 ULONG extBlockIndex;
3220 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3222 assert(extBlockCount < This->extBigBlockDepotCount);
3224 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3226 if (extBlockIndex != BLOCK_UNUSED)
3228 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3229 extBlockOffset * sizeof(ULONG),
3230 blockIndex);
3233 if (This->indexExtBlockDepotCached == extBlockCount)
3235 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3239 /******************************************************************************
3240 * Storage32Impl_AddExtBlockDepot
3242 * Creates an extended depot block.
3244 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3246 ULONG numExtBlocks = This->extBigBlockDepotCount;
3247 ULONG nextExtBlock = This->extBigBlockDepotStart;
3248 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3249 ULONG index = BLOCK_UNUSED;
3250 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3251 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3252 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3254 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3255 blocksPerDepotBlock;
3257 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3260 * The first extended block.
3262 This->extBigBlockDepotStart = index;
3264 else
3267 * Find the last existing extended block.
3269 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3272 * Add the new extended block to the chain.
3274 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3275 index);
3279 * Initialize this block.
3281 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3282 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3284 /* Add the block to our cache. */
3285 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3287 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3288 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3290 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3291 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3293 This->extBigBlockDepotLocations = new_cache;
3294 This->extBigBlockDepotLocationsSize = new_cache_size;
3296 This->extBigBlockDepotLocations[numExtBlocks] = index;
3298 return index;
3301 /******************************************************************************
3302 * Storage32Impl_FreeBigBlock
3304 * This method will flag the specified block as free in the big block depot.
3306 static void StorageImpl_FreeBigBlock(
3307 StorageImpl* This,
3308 ULONG blockIndex)
3310 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3312 if (blockIndex < This->prevFreeBlock)
3313 This->prevFreeBlock = blockIndex;
3316 /************************************************************************
3317 * Storage32Impl_GetNextBlockInChain
3319 * This method will retrieve the block index of the next big block in
3320 * in the chain.
3322 * Params: This - Pointer to the Storage object.
3323 * blockIndex - Index of the block to retrieve the chain
3324 * for.
3325 * nextBlockIndex - receives the return value.
3327 * Returns: This method returns the index of the next block in the chain.
3328 * It will return the constants:
3329 * BLOCK_SPECIAL - If the block given was not part of a
3330 * chain.
3331 * BLOCK_END_OF_CHAIN - If the block given was the last in
3332 * a chain.
3333 * BLOCK_UNUSED - If the block given was not past of a chain
3334 * and is available.
3335 * BLOCK_EXTBBDEPOT - This block is part of the extended
3336 * big block depot.
3338 * See Windows documentation for more details on IStorage methods.
3340 static HRESULT StorageImpl_GetNextBlockInChain(
3341 StorageImpl* This,
3342 ULONG blockIndex,
3343 ULONG* nextBlockIndex)
3345 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3346 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3347 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3348 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3349 BOOL success;
3350 ULONG depotBlockIndexPos;
3351 int index, num_blocks;
3353 *nextBlockIndex = BLOCK_SPECIAL;
3355 if(depotBlockCount >= This->bigBlockDepotCount)
3357 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3358 This->bigBlockDepotCount);
3359 return STG_E_READFAULT;
3363 * Cache the currently accessed depot block.
3365 if (depotBlockCount != This->indexBlockDepotCached)
3367 This->indexBlockDepotCached = depotBlockCount;
3369 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3371 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3373 else
3376 * We have to look in the extended depot.
3378 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3381 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3383 if (!success)
3384 return STG_E_READFAULT;
3386 num_blocks = This->bigBlockSize / 4;
3388 for (index = 0; index < num_blocks; index++)
3390 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3391 This->blockDepotCached[index] = *nextBlockIndex;
3395 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3397 return S_OK;
3400 /******************************************************************************
3401 * Storage32Impl_GetNextExtendedBlock
3403 * Given an extended block this method will return the next extended block.
3405 * NOTES:
3406 * The last ULONG of an extended block is the block index of the next
3407 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3408 * depot.
3410 * Return values:
3411 * - The index of the next extended block
3412 * - BLOCK_UNUSED: there is no next extended block.
3413 * - Any other return values denotes failure.
3415 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3417 ULONG nextBlockIndex = BLOCK_SPECIAL;
3418 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3420 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3421 &nextBlockIndex);
3423 return nextBlockIndex;
3426 /******************************************************************************
3427 * Storage32Impl_SetNextBlockInChain
3429 * This method will write the index of the specified block's next block
3430 * in the big block depot.
3432 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3433 * do the following
3435 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3436 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3437 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3440 static void StorageImpl_SetNextBlockInChain(
3441 StorageImpl* This,
3442 ULONG blockIndex,
3443 ULONG nextBlock)
3445 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3446 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3447 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3448 ULONG depotBlockIndexPos;
3450 assert(depotBlockCount < This->bigBlockDepotCount);
3451 assert(blockIndex != nextBlock);
3453 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3455 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3457 else
3460 * We have to look in the extended depot.
3462 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3465 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3466 nextBlock);
3468 * Update the cached block depot, if necessary.
3470 if (depotBlockCount == This->indexBlockDepotCached)
3472 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3476 /******************************************************************************
3477 * Storage32Impl_LoadFileHeader
3479 * This method will read in the file header
3481 static HRESULT StorageImpl_LoadFileHeader(
3482 StorageImpl* This)
3484 HRESULT hr;
3485 BYTE headerBigBlock[HEADER_SIZE];
3486 int index;
3487 ULARGE_INTEGER offset;
3488 DWORD bytes_read;
3490 TRACE("\n");
3492 * Get a pointer to the big block of data containing the header.
3494 offset.u.HighPart = 0;
3495 offset.u.LowPart = 0;
3496 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3497 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3498 hr = STG_E_FILENOTFOUND;
3501 * Extract the information from the header.
3503 if (SUCCEEDED(hr))
3506 * Check for the "magic number" signature and return an error if it is not
3507 * found.
3509 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3511 return STG_E_OLDFORMAT;
3514 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3516 return STG_E_INVALIDHEADER;
3519 StorageUtl_ReadWord(
3520 headerBigBlock,
3521 OFFSET_BIGBLOCKSIZEBITS,
3522 &This->bigBlockSizeBits);
3524 StorageUtl_ReadWord(
3525 headerBigBlock,
3526 OFFSET_SMALLBLOCKSIZEBITS,
3527 &This->smallBlockSizeBits);
3529 StorageUtl_ReadDWord(
3530 headerBigBlock,
3531 OFFSET_BBDEPOTCOUNT,
3532 &This->bigBlockDepotCount);
3534 StorageUtl_ReadDWord(
3535 headerBigBlock,
3536 OFFSET_ROOTSTARTBLOCK,
3537 &This->rootStartBlock);
3539 StorageUtl_ReadDWord(
3540 headerBigBlock,
3541 OFFSET_SMALLBLOCKLIMIT,
3542 &This->smallBlockLimit);
3544 StorageUtl_ReadDWord(
3545 headerBigBlock,
3546 OFFSET_SBDEPOTSTART,
3547 &This->smallBlockDepotStart);
3549 StorageUtl_ReadDWord(
3550 headerBigBlock,
3551 OFFSET_EXTBBDEPOTSTART,
3552 &This->extBigBlockDepotStart);
3554 StorageUtl_ReadDWord(
3555 headerBigBlock,
3556 OFFSET_EXTBBDEPOTCOUNT,
3557 &This->extBigBlockDepotCount);
3559 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3561 StorageUtl_ReadDWord(
3562 headerBigBlock,
3563 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3564 &(This->bigBlockDepotStart[index]));
3568 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3570 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3571 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3574 * Right now, the code is making some assumptions about the size of the
3575 * blocks, just make sure they are what we're expecting.
3577 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3578 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3579 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3581 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3582 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3583 hr = STG_E_INVALIDHEADER;
3585 else
3586 hr = S_OK;
3589 return hr;
3592 /******************************************************************************
3593 * Storage32Impl_SaveFileHeader
3595 * This method will save to the file the header
3597 static void StorageImpl_SaveFileHeader(
3598 StorageImpl* This)
3600 BYTE headerBigBlock[HEADER_SIZE];
3601 int index;
3602 HRESULT hr;
3603 ULARGE_INTEGER offset;
3604 DWORD bytes_read, bytes_written;
3605 DWORD major_version, dirsectorcount;
3608 * Get a pointer to the big block of data containing the header.
3610 offset.u.HighPart = 0;
3611 offset.u.LowPart = 0;
3612 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3613 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3614 hr = STG_E_FILENOTFOUND;
3616 if (This->bigBlockSizeBits == 0x9)
3617 major_version = 3;
3618 else if (This->bigBlockSizeBits == 0xc)
3619 major_version = 4;
3620 else
3622 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3623 major_version = 4;
3627 * If the block read failed, the file is probably new.
3629 if (FAILED(hr))
3632 * Initialize for all unknown fields.
3634 memset(headerBigBlock, 0, HEADER_SIZE);
3637 * Initialize the magic number.
3639 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3643 * Write the information to the header.
3645 StorageUtl_WriteWord(
3646 headerBigBlock,
3647 OFFSET_MINORVERSION,
3648 0x3e);
3650 StorageUtl_WriteWord(
3651 headerBigBlock,
3652 OFFSET_MAJORVERSION,
3653 major_version);
3655 StorageUtl_WriteWord(
3656 headerBigBlock,
3657 OFFSET_BYTEORDERMARKER,
3658 (WORD)-2);
3660 StorageUtl_WriteWord(
3661 headerBigBlock,
3662 OFFSET_BIGBLOCKSIZEBITS,
3663 This->bigBlockSizeBits);
3665 StorageUtl_WriteWord(
3666 headerBigBlock,
3667 OFFSET_SMALLBLOCKSIZEBITS,
3668 This->smallBlockSizeBits);
3670 if (major_version >= 4)
3672 if (This->rootBlockChain)
3673 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3674 else
3675 /* This file is being created, and it will start out with one block. */
3676 dirsectorcount = 1;
3678 else
3679 /* This field must be 0 in versions older than 4 */
3680 dirsectorcount = 0;
3682 StorageUtl_WriteDWord(
3683 headerBigBlock,
3684 OFFSET_DIRSECTORCOUNT,
3685 dirsectorcount);
3687 StorageUtl_WriteDWord(
3688 headerBigBlock,
3689 OFFSET_BBDEPOTCOUNT,
3690 This->bigBlockDepotCount);
3692 StorageUtl_WriteDWord(
3693 headerBigBlock,
3694 OFFSET_ROOTSTARTBLOCK,
3695 This->rootStartBlock);
3697 StorageUtl_WriteDWord(
3698 headerBigBlock,
3699 OFFSET_SMALLBLOCKLIMIT,
3700 This->smallBlockLimit);
3702 StorageUtl_WriteDWord(
3703 headerBigBlock,
3704 OFFSET_SBDEPOTSTART,
3705 This->smallBlockDepotStart);
3707 StorageUtl_WriteDWord(
3708 headerBigBlock,
3709 OFFSET_SBDEPOTCOUNT,
3710 This->smallBlockDepotChain ?
3711 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3713 StorageUtl_WriteDWord(
3714 headerBigBlock,
3715 OFFSET_EXTBBDEPOTSTART,
3716 This->extBigBlockDepotStart);
3718 StorageUtl_WriteDWord(
3719 headerBigBlock,
3720 OFFSET_EXTBBDEPOTCOUNT,
3721 This->extBigBlockDepotCount);
3723 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3725 StorageUtl_WriteDWord(
3726 headerBigBlock,
3727 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3728 (This->bigBlockDepotStart[index]));
3732 * Write the big block back to the file.
3734 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3737 /******************************************************************************
3738 * StorageImpl_ReadRawDirEntry
3740 * This method will read the raw data from a directory entry in the file.
3742 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3744 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3746 ULARGE_INTEGER offset;
3747 HRESULT hr;
3748 ULONG bytesRead;
3750 offset.u.HighPart = 0;
3751 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3753 hr = BlockChainStream_ReadAt(
3754 This->rootBlockChain,
3755 offset,
3756 RAW_DIRENTRY_SIZE,
3757 buffer,
3758 &bytesRead);
3760 if (bytesRead != RAW_DIRENTRY_SIZE)
3761 return STG_E_READFAULT;
3763 return hr;
3766 /******************************************************************************
3767 * StorageImpl_WriteRawDirEntry
3769 * This method will write the raw data from a directory entry in the file.
3771 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3773 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3775 ULARGE_INTEGER offset;
3776 HRESULT hr;
3777 ULONG bytesRead;
3779 offset.u.HighPart = 0;
3780 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3782 hr = BlockChainStream_WriteAt(
3783 This->rootBlockChain,
3784 offset,
3785 RAW_DIRENTRY_SIZE,
3786 buffer,
3787 &bytesRead);
3789 return hr;
3792 /******************************************************************************
3793 * UpdateRawDirEntry
3795 * Update raw directory entry data from the fields in newData.
3797 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3799 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3801 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3803 memcpy(
3804 buffer + OFFSET_PS_NAME,
3805 newData->name,
3806 DIRENTRY_NAME_BUFFER_LEN );
3808 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3810 StorageUtl_WriteWord(
3811 buffer,
3812 OFFSET_PS_NAMELENGTH,
3813 newData->sizeOfNameString);
3815 StorageUtl_WriteDWord(
3816 buffer,
3817 OFFSET_PS_LEFTCHILD,
3818 newData->leftChild);
3820 StorageUtl_WriteDWord(
3821 buffer,
3822 OFFSET_PS_RIGHTCHILD,
3823 newData->rightChild);
3825 StorageUtl_WriteDWord(
3826 buffer,
3827 OFFSET_PS_DIRROOT,
3828 newData->dirRootEntry);
3830 StorageUtl_WriteGUID(
3831 buffer,
3832 OFFSET_PS_GUID,
3833 &newData->clsid);
3835 StorageUtl_WriteDWord(
3836 buffer,
3837 OFFSET_PS_CTIMELOW,
3838 newData->ctime.dwLowDateTime);
3840 StorageUtl_WriteDWord(
3841 buffer,
3842 OFFSET_PS_CTIMEHIGH,
3843 newData->ctime.dwHighDateTime);
3845 StorageUtl_WriteDWord(
3846 buffer,
3847 OFFSET_PS_MTIMELOW,
3848 newData->mtime.dwLowDateTime);
3850 StorageUtl_WriteDWord(
3851 buffer,
3852 OFFSET_PS_MTIMEHIGH,
3853 newData->ctime.dwHighDateTime);
3855 StorageUtl_WriteDWord(
3856 buffer,
3857 OFFSET_PS_STARTBLOCK,
3858 newData->startingBlock);
3860 StorageUtl_WriteDWord(
3861 buffer,
3862 OFFSET_PS_SIZE,
3863 newData->size.u.LowPart);
3866 /******************************************************************************
3867 * Storage32Impl_ReadDirEntry
3869 * This method will read the specified directory entry.
3871 HRESULT StorageImpl_ReadDirEntry(
3872 StorageImpl* This,
3873 DirRef index,
3874 DirEntry* buffer)
3876 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3877 HRESULT readRes;
3879 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3881 if (SUCCEEDED(readRes))
3883 memset(buffer->name, 0, sizeof(buffer->name));
3884 memcpy(
3885 buffer->name,
3886 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3887 DIRENTRY_NAME_BUFFER_LEN );
3888 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3890 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3892 StorageUtl_ReadWord(
3893 currentEntry,
3894 OFFSET_PS_NAMELENGTH,
3895 &buffer->sizeOfNameString);
3897 StorageUtl_ReadDWord(
3898 currentEntry,
3899 OFFSET_PS_LEFTCHILD,
3900 &buffer->leftChild);
3902 StorageUtl_ReadDWord(
3903 currentEntry,
3904 OFFSET_PS_RIGHTCHILD,
3905 &buffer->rightChild);
3907 StorageUtl_ReadDWord(
3908 currentEntry,
3909 OFFSET_PS_DIRROOT,
3910 &buffer->dirRootEntry);
3912 StorageUtl_ReadGUID(
3913 currentEntry,
3914 OFFSET_PS_GUID,
3915 &buffer->clsid);
3917 StorageUtl_ReadDWord(
3918 currentEntry,
3919 OFFSET_PS_CTIMELOW,
3920 &buffer->ctime.dwLowDateTime);
3922 StorageUtl_ReadDWord(
3923 currentEntry,
3924 OFFSET_PS_CTIMEHIGH,
3925 &buffer->ctime.dwHighDateTime);
3927 StorageUtl_ReadDWord(
3928 currentEntry,
3929 OFFSET_PS_MTIMELOW,
3930 &buffer->mtime.dwLowDateTime);
3932 StorageUtl_ReadDWord(
3933 currentEntry,
3934 OFFSET_PS_MTIMEHIGH,
3935 &buffer->mtime.dwHighDateTime);
3937 StorageUtl_ReadDWord(
3938 currentEntry,
3939 OFFSET_PS_STARTBLOCK,
3940 &buffer->startingBlock);
3942 StorageUtl_ReadDWord(
3943 currentEntry,
3944 OFFSET_PS_SIZE,
3945 &buffer->size.u.LowPart);
3947 buffer->size.u.HighPart = 0;
3950 return readRes;
3953 /*********************************************************************
3954 * Write the specified directory entry to the file
3956 HRESULT StorageImpl_WriteDirEntry(
3957 StorageImpl* This,
3958 DirRef index,
3959 const DirEntry* buffer)
3961 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3962 HRESULT writeRes;
3964 UpdateRawDirEntry(currentEntry, buffer);
3966 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3967 return writeRes;
3970 static BOOL StorageImpl_ReadBigBlock(
3971 StorageImpl* This,
3972 ULONG blockIndex,
3973 void* buffer)
3975 ULARGE_INTEGER ulOffset;
3976 DWORD read=0;
3978 ulOffset.u.HighPart = 0;
3979 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3981 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3983 if (read && read < This->bigBlockSize)
3985 /* File ends during this block; fill the rest with 0's. */
3986 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3989 return (read != 0);
3992 static BOOL StorageImpl_ReadDWordFromBigBlock(
3993 StorageImpl* This,
3994 ULONG blockIndex,
3995 ULONG offset,
3996 DWORD* value)
3998 ULARGE_INTEGER ulOffset;
3999 DWORD read;
4000 DWORD tmp;
4002 ulOffset.u.HighPart = 0;
4003 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4004 ulOffset.u.LowPart += offset;
4006 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4007 *value = lendian32toh(tmp);
4008 return (read == sizeof(DWORD));
4011 static BOOL StorageImpl_WriteBigBlock(
4012 StorageImpl* This,
4013 ULONG blockIndex,
4014 const void* buffer)
4016 ULARGE_INTEGER ulOffset;
4017 DWORD wrote;
4019 ulOffset.u.HighPart = 0;
4020 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4022 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4023 return (wrote == This->bigBlockSize);
4026 static BOOL StorageImpl_WriteDWordToBigBlock(
4027 StorageImpl* This,
4028 ULONG blockIndex,
4029 ULONG offset,
4030 DWORD value)
4032 ULARGE_INTEGER ulOffset;
4033 DWORD wrote;
4035 ulOffset.u.HighPart = 0;
4036 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4037 ulOffset.u.LowPart += offset;
4039 value = htole32(value);
4040 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4041 return (wrote == sizeof(DWORD));
4044 /******************************************************************************
4045 * Storage32Impl_SmallBlocksToBigBlocks
4047 * This method will convert a small block chain to a big block chain.
4048 * The small block chain will be destroyed.
4050 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4051 StorageImpl* This,
4052 SmallBlockChainStream** ppsbChain)
4054 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4055 ULARGE_INTEGER size, offset;
4056 ULONG cbRead, cbWritten;
4057 ULARGE_INTEGER cbTotalRead;
4058 DirRef streamEntryRef;
4059 HRESULT resWrite = S_OK;
4060 HRESULT resRead;
4061 DirEntry streamEntry;
4062 BYTE *buffer;
4063 BlockChainStream *bbTempChain = NULL;
4064 BlockChainStream *bigBlockChain = NULL;
4067 * Create a temporary big block chain that doesn't have
4068 * an associated directory entry. This temporary chain will be
4069 * used to copy data from small blocks to big blocks.
4071 bbTempChain = BlockChainStream_Construct(This,
4072 &bbHeadOfChain,
4073 DIRENTRY_NULL);
4074 if(!bbTempChain) return NULL;
4076 * Grow the big block chain.
4078 size = SmallBlockChainStream_GetSize(*ppsbChain);
4079 BlockChainStream_SetSize(bbTempChain, size);
4082 * Copy the contents of the small block chain to the big block chain
4083 * by small block size increments.
4085 offset.u.LowPart = 0;
4086 offset.u.HighPart = 0;
4087 cbTotalRead.QuadPart = 0;
4089 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4092 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4093 offset,
4094 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4095 buffer,
4096 &cbRead);
4097 if (FAILED(resRead))
4098 break;
4100 if (cbRead > 0)
4102 cbTotalRead.QuadPart += cbRead;
4104 resWrite = BlockChainStream_WriteAt(bbTempChain,
4105 offset,
4106 cbRead,
4107 buffer,
4108 &cbWritten);
4110 if (FAILED(resWrite))
4111 break;
4113 offset.u.LowPart += cbRead;
4115 else
4117 resRead = STG_E_READFAULT;
4118 break;
4120 } while (cbTotalRead.QuadPart < size.QuadPart);
4121 HeapFree(GetProcessHeap(),0,buffer);
4123 size.u.HighPart = 0;
4124 size.u.LowPart = 0;
4126 if (FAILED(resRead) || FAILED(resWrite))
4128 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4129 BlockChainStream_SetSize(bbTempChain, size);
4130 BlockChainStream_Destroy(bbTempChain);
4131 return NULL;
4135 * Destroy the small block chain.
4137 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4138 SmallBlockChainStream_SetSize(*ppsbChain, size);
4139 SmallBlockChainStream_Destroy(*ppsbChain);
4140 *ppsbChain = 0;
4143 * Change the directory entry. This chain is now a big block chain
4144 * and it doesn't reside in the small blocks chain anymore.
4146 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4148 streamEntry.startingBlock = bbHeadOfChain;
4150 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4153 * Destroy the temporary entryless big block chain.
4154 * Create a new big block chain associated with this entry.
4156 BlockChainStream_Destroy(bbTempChain);
4157 bigBlockChain = BlockChainStream_Construct(This,
4158 NULL,
4159 streamEntryRef);
4161 return bigBlockChain;
4164 /******************************************************************************
4165 * Storage32Impl_BigBlocksToSmallBlocks
4167 * This method will convert a big block chain to a small block chain.
4168 * The big block chain will be destroyed on success.
4170 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4171 StorageImpl* This,
4172 BlockChainStream** ppbbChain,
4173 ULARGE_INTEGER newSize)
4175 ULARGE_INTEGER size, offset, cbTotalRead;
4176 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4177 DirRef streamEntryRef;
4178 HRESULT resWrite = S_OK, resRead = S_OK;
4179 DirEntry streamEntry;
4180 BYTE* buffer;
4181 SmallBlockChainStream* sbTempChain;
4183 TRACE("%p %p\n", This, ppbbChain);
4185 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4186 DIRENTRY_NULL);
4188 if(!sbTempChain)
4189 return NULL;
4191 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4192 size = BlockChainStream_GetSize(*ppbbChain);
4193 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4195 offset.u.HighPart = 0;
4196 offset.u.LowPart = 0;
4197 cbTotalRead.QuadPart = 0;
4198 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4199 while(cbTotalRead.QuadPart < size.QuadPart)
4201 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4202 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4203 buffer, &cbRead);
4205 if(FAILED(resRead))
4206 break;
4208 if(cbRead > 0)
4210 cbTotalRead.QuadPart += cbRead;
4212 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4213 cbRead, buffer, &cbWritten);
4215 if(FAILED(resWrite))
4216 break;
4218 offset.u.LowPart += cbRead;
4220 else
4222 resRead = STG_E_READFAULT;
4223 break;
4226 HeapFree(GetProcessHeap(), 0, buffer);
4228 size.u.HighPart = 0;
4229 size.u.LowPart = 0;
4231 if(FAILED(resRead) || FAILED(resWrite))
4233 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4234 SmallBlockChainStream_SetSize(sbTempChain, size);
4235 SmallBlockChainStream_Destroy(sbTempChain);
4236 return NULL;
4239 /* destroy the original big block chain */
4240 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4241 BlockChainStream_SetSize(*ppbbChain, size);
4242 BlockChainStream_Destroy(*ppbbChain);
4243 *ppbbChain = NULL;
4245 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4246 streamEntry.startingBlock = sbHeadOfChain;
4247 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4249 SmallBlockChainStream_Destroy(sbTempChain);
4250 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4253 static HRESULT StorageBaseImpl_CopyStream(
4254 StorageBaseImpl *dst, DirRef dst_entry,
4255 StorageBaseImpl *src, DirRef src_entry)
4257 HRESULT hr;
4258 BYTE data[4096];
4259 DirEntry srcdata;
4260 ULARGE_INTEGER bytes_copied;
4261 ULONG bytestocopy, bytesread, byteswritten;
4263 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4265 if (SUCCEEDED(hr))
4267 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4269 bytes_copied.QuadPart = 0;
4270 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4272 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4274 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4275 data, &bytesread);
4276 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4278 if (SUCCEEDED(hr))
4279 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4280 data, &byteswritten);
4281 if (SUCCEEDED(hr))
4283 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4284 bytes_copied.QuadPart += byteswritten;
4289 return hr;
4292 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4294 DirRef result=This->firstFreeEntry;
4296 while (result < This->entries_size && This->entries[result].inuse)
4297 result++;
4299 if (result == This->entries_size)
4301 ULONG new_size = This->entries_size * 2;
4302 TransactedDirEntry *new_entries;
4304 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4305 if (!new_entries) return DIRENTRY_NULL;
4307 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4308 HeapFree(GetProcessHeap(), 0, This->entries);
4310 This->entries = new_entries;
4311 This->entries_size = new_size;
4314 This->entries[result].inuse = 1;
4316 This->firstFreeEntry = result+1;
4318 return result;
4321 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4322 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4324 DirRef stubEntryRef;
4325 TransactedDirEntry *entry;
4327 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4329 if (stubEntryRef != DIRENTRY_NULL)
4331 entry = &This->entries[stubEntryRef];
4333 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4335 entry->read = 0;
4338 return stubEntryRef;
4341 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4342 TransactedSnapshotImpl *This, DirRef entry)
4344 HRESULT hr=S_OK;
4345 DirEntry data;
4347 if (!This->entries[entry].read)
4349 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4350 This->entries[entry].transactedParentEntry,
4351 &data);
4353 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4355 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4357 if (data.leftChild == DIRENTRY_NULL)
4358 hr = E_OUTOFMEMORY;
4361 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4363 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4365 if (data.rightChild == DIRENTRY_NULL)
4366 hr = E_OUTOFMEMORY;
4369 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4371 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4373 if (data.dirRootEntry == DIRENTRY_NULL)
4374 hr = E_OUTOFMEMORY;
4377 if (SUCCEEDED(hr))
4379 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4380 This->entries[entry].read = 1;
4384 return hr;
4387 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4388 TransactedSnapshotImpl *This, DirRef entry)
4390 HRESULT hr = S_OK;
4392 if (!This->entries[entry].stream_dirty)
4394 DirEntry new_entrydata;
4396 memset(&new_entrydata, 0, sizeof(DirEntry));
4397 new_entrydata.name[0] = 'S';
4398 new_entrydata.sizeOfNameString = 1;
4399 new_entrydata.stgType = STGTY_STREAM;
4400 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4401 new_entrydata.leftChild = DIRENTRY_NULL;
4402 new_entrydata.rightChild = DIRENTRY_NULL;
4403 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4405 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4406 &This->entries[entry].stream_entry);
4408 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4410 hr = StorageBaseImpl_CopyStream(
4411 This->scratch, This->entries[entry].stream_entry,
4412 This->transactedParent, This->entries[entry].transactedParentEntry);
4414 if (FAILED(hr))
4415 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4418 if (SUCCEEDED(hr))
4419 This->entries[entry].stream_dirty = 1;
4421 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4423 /* Since this entry is modified, and we aren't using its stream data, we
4424 * no longer care about the original entry. */
4425 DirRef delete_ref;
4426 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4428 if (delete_ref != DIRENTRY_NULL)
4429 This->entries[delete_ref].deleted = 1;
4431 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4435 return hr;
4438 /* Find the first entry in a depth-first traversal. */
4439 static DirRef TransactedSnapshotImpl_FindFirstChild(
4440 TransactedSnapshotImpl* This, DirRef parent)
4442 DirRef cursor, prev;
4443 TransactedDirEntry *entry;
4445 cursor = parent;
4446 entry = &This->entries[cursor];
4447 while (entry->read)
4449 if (entry->data.leftChild != DIRENTRY_NULL)
4451 prev = cursor;
4452 cursor = entry->data.leftChild;
4453 entry = &This->entries[cursor];
4454 entry->parent = prev;
4456 else if (entry->data.rightChild != DIRENTRY_NULL)
4458 prev = cursor;
4459 cursor = entry->data.rightChild;
4460 entry = &This->entries[cursor];
4461 entry->parent = prev;
4463 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4465 prev = cursor;
4466 cursor = entry->data.dirRootEntry;
4467 entry = &This->entries[cursor];
4468 entry->parent = prev;
4470 else
4471 break;
4474 return cursor;
4477 /* Find the next entry in a depth-first traversal. */
4478 static DirRef TransactedSnapshotImpl_FindNextChild(
4479 TransactedSnapshotImpl* This, DirRef current)
4481 DirRef parent;
4482 TransactedDirEntry *parent_entry;
4484 parent = This->entries[current].parent;
4485 parent_entry = &This->entries[parent];
4487 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4489 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4491 This->entries[parent_entry->data.rightChild].parent = parent;
4492 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4495 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4497 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4498 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4502 return parent;
4505 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4506 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4507 TransactedSnapshotImpl* This, DirRef entry)
4509 return entry != DIRENTRY_NULL &&
4510 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4513 /* Destroy the entries created by CopyTree. */
4514 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4515 TransactedSnapshotImpl* This, DirRef stop)
4517 DirRef cursor;
4518 TransactedDirEntry *entry;
4519 ULARGE_INTEGER zero;
4521 zero.QuadPart = 0;
4523 if (!This->entries[This->base.storageDirEntry].read)
4524 return;
4526 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4528 if (cursor == DIRENTRY_NULL)
4529 return;
4531 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4533 while (cursor != DIRENTRY_NULL && cursor != stop)
4535 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4537 entry = &This->entries[cursor];
4539 if (entry->stream_dirty)
4540 StorageBaseImpl_StreamSetSize(This->transactedParent,
4541 entry->newTransactedParentEntry, zero);
4543 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4544 entry->newTransactedParentEntry);
4546 entry->newTransactedParentEntry = entry->transactedParentEntry;
4549 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4553 /* Make a copy of our edited tree that we can use in the parent. */
4554 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4556 DirRef cursor;
4557 TransactedDirEntry *entry;
4558 HRESULT hr = S_OK;
4560 cursor = This->base.storageDirEntry;
4561 entry = &This->entries[cursor];
4562 entry->parent = DIRENTRY_NULL;
4563 entry->newTransactedParentEntry = entry->transactedParentEntry;
4565 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4566 return S_OK;
4568 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4570 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4571 entry = &This->entries[cursor];
4573 while (cursor != DIRENTRY_NULL)
4575 /* Make a copy of this entry in the transacted parent. */
4576 if (!entry->read ||
4577 (!entry->dirty && !entry->stream_dirty &&
4578 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4579 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4580 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4581 entry->newTransactedParentEntry = entry->transactedParentEntry;
4582 else
4584 DirEntry newData;
4586 memcpy(&newData, &entry->data, sizeof(DirEntry));
4588 newData.size.QuadPart = 0;
4589 newData.startingBlock = BLOCK_END_OF_CHAIN;
4591 if (newData.leftChild != DIRENTRY_NULL)
4592 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4594 if (newData.rightChild != DIRENTRY_NULL)
4595 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4597 if (newData.dirRootEntry != DIRENTRY_NULL)
4598 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4600 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4601 &entry->newTransactedParentEntry);
4602 if (FAILED(hr))
4604 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4605 return hr;
4608 if (entry->stream_dirty)
4610 hr = StorageBaseImpl_CopyStream(
4611 This->transactedParent, entry->newTransactedParentEntry,
4612 This->scratch, entry->stream_entry);
4614 else if (entry->data.size.QuadPart)
4616 hr = StorageBaseImpl_StreamLink(
4617 This->transactedParent, entry->newTransactedParentEntry,
4618 entry->transactedParentEntry);
4621 if (FAILED(hr))
4623 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4624 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4625 return hr;
4629 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4630 entry = &This->entries[cursor];
4633 return hr;
4636 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4637 IStorage* iface,
4638 DWORD grfCommitFlags) /* [in] */
4640 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4641 TransactedDirEntry *root_entry;
4642 DirRef i, dir_root_ref;
4643 DirEntry data;
4644 ULARGE_INTEGER zero;
4645 HRESULT hr;
4647 zero.QuadPart = 0;
4649 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4651 /* Cannot commit a read-only transacted storage */
4652 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4653 return STG_E_ACCESSDENIED;
4655 /* To prevent data loss, we create the new structure in the file before we
4656 * delete the old one, so that in case of errors the old data is intact. We
4657 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4658 * needed in the rare situation where we have just enough free disk space to
4659 * overwrite the existing data. */
4661 root_entry = &This->entries[This->base.storageDirEntry];
4663 if (!root_entry->read)
4664 return S_OK;
4666 hr = TransactedSnapshotImpl_CopyTree(This);
4667 if (FAILED(hr)) return hr;
4669 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4670 dir_root_ref = DIRENTRY_NULL;
4671 else
4672 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4674 hr = StorageBaseImpl_Flush(This->transactedParent);
4676 /* Update the storage to use the new data in one step. */
4677 if (SUCCEEDED(hr))
4678 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4679 root_entry->transactedParentEntry, &data);
4681 if (SUCCEEDED(hr))
4683 data.dirRootEntry = dir_root_ref;
4684 data.clsid = root_entry->data.clsid;
4685 data.ctime = root_entry->data.ctime;
4686 data.mtime = root_entry->data.mtime;
4688 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4689 root_entry->transactedParentEntry, &data);
4692 /* Try to flush after updating the root storage, but if the flush fails, keep
4693 * going, on the theory that it'll either succeed later or the subsequent
4694 * writes will fail. */
4695 StorageBaseImpl_Flush(This->transactedParent);
4697 if (SUCCEEDED(hr))
4699 /* Destroy the old now-orphaned data. */
4700 for (i=0; i<This->entries_size; i++)
4702 TransactedDirEntry *entry = &This->entries[i];
4703 if (entry->inuse)
4705 if (entry->deleted)
4707 StorageBaseImpl_StreamSetSize(This->transactedParent,
4708 entry->transactedParentEntry, zero);
4709 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4710 entry->transactedParentEntry);
4711 memset(entry, 0, sizeof(TransactedDirEntry));
4712 This->firstFreeEntry = min(i, This->firstFreeEntry);
4714 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4716 if (entry->transactedParentEntry != DIRENTRY_NULL)
4717 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4718 entry->transactedParentEntry);
4719 if (entry->stream_dirty)
4721 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4722 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4723 entry->stream_dirty = 0;
4725 entry->dirty = 0;
4726 entry->transactedParentEntry = entry->newTransactedParentEntry;
4731 else
4733 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4736 if (SUCCEEDED(hr))
4737 hr = StorageBaseImpl_Flush(This->transactedParent);
4739 return hr;
4742 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4743 IStorage* iface)
4745 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4746 ULARGE_INTEGER zero;
4747 ULONG i;
4749 TRACE("(%p)\n", iface);
4751 /* Destroy the open objects. */
4752 StorageBaseImpl_DeleteAll(&This->base);
4754 /* Clear out the scratch file. */
4755 zero.QuadPart = 0;
4756 for (i=0; i<This->entries_size; i++)
4758 if (This->entries[i].stream_dirty)
4760 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4761 zero);
4763 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4767 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4769 This->firstFreeEntry = 0;
4770 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4772 return S_OK;
4775 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4777 if (!This->reverted)
4779 TRACE("Storage invalidated (stg=%p)\n", This);
4781 This->reverted = 1;
4783 StorageBaseImpl_DeleteAll(This);
4787 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4789 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4791 TransactedSnapshotImpl_Revert((IStorage*)iface);
4793 IStorage_Release((IStorage*)This->transactedParent);
4795 IStorage_Release((IStorage*)This->scratch);
4797 HeapFree(GetProcessHeap(), 0, This->entries);
4799 HeapFree(GetProcessHeap(), 0, This);
4802 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4804 /* We only need to flush when committing. */
4805 return S_OK;
4808 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4810 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4812 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4815 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4816 const DirEntry *newData, DirRef *index)
4818 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4819 DirRef new_ref;
4820 TransactedDirEntry *new_entry;
4822 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4823 if (new_ref == DIRENTRY_NULL)
4824 return E_OUTOFMEMORY;
4826 new_entry = &This->entries[new_ref];
4828 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4829 new_entry->read = 1;
4830 new_entry->dirty = 1;
4831 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4833 *index = new_ref;
4835 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4837 return S_OK;
4840 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4841 DirRef index, const DirEntry *data)
4843 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4844 HRESULT hr;
4846 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4848 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4849 if (FAILED(hr)) return hr;
4851 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4853 if (index != This->base.storageDirEntry)
4855 This->entries[index].dirty = 1;
4857 if (data->size.QuadPart == 0 &&
4858 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4860 /* Since this entry is modified, and we aren't using its stream data, we
4861 * no longer care about the original entry. */
4862 DirRef delete_ref;
4863 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4865 if (delete_ref != DIRENTRY_NULL)
4866 This->entries[delete_ref].deleted = 1;
4868 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4872 return S_OK;
4875 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4876 DirRef index, DirEntry *data)
4878 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4879 HRESULT hr;
4881 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4882 if (FAILED(hr)) return hr;
4884 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4886 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4888 return S_OK;
4891 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4892 DirRef index)
4894 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4896 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4897 This->entries[index].data.size.QuadPart != 0)
4899 /* If we deleted this entry while it has stream data. We must have left the
4900 * data because some other entry is using it, and we need to leave the
4901 * original entry alone. */
4902 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4903 This->firstFreeEntry = min(index, This->firstFreeEntry);
4905 else
4907 This->entries[index].deleted = 1;
4910 return S_OK;
4913 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4914 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4916 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4918 if (This->entries[index].stream_dirty)
4920 return StorageBaseImpl_StreamReadAt(This->scratch,
4921 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4923 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4925 /* This stream doesn't live in the parent, and we haven't allocated storage
4926 * for it yet */
4927 *bytesRead = 0;
4928 return S_OK;
4930 else
4932 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4933 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4937 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4938 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4940 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4941 HRESULT hr;
4943 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4944 if (FAILED(hr)) return hr;
4946 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4947 if (FAILED(hr)) return hr;
4949 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4950 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4952 if (SUCCEEDED(hr) && size != 0)
4953 This->entries[index].data.size.QuadPart = max(
4954 This->entries[index].data.size.QuadPart,
4955 offset.QuadPart + size);
4957 return hr;
4960 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4961 DirRef index, ULARGE_INTEGER newsize)
4963 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4964 HRESULT hr;
4966 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4967 if (FAILED(hr)) return hr;
4969 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4970 return S_OK;
4972 if (newsize.QuadPart == 0)
4974 /* Destroy any parent references or entries in the scratch file. */
4975 if (This->entries[index].stream_dirty)
4977 ULARGE_INTEGER zero;
4978 zero.QuadPart = 0;
4979 StorageBaseImpl_StreamSetSize(This->scratch,
4980 This->entries[index].stream_entry, zero);
4981 StorageBaseImpl_DestroyDirEntry(This->scratch,
4982 This->entries[index].stream_entry);
4983 This->entries[index].stream_dirty = 0;
4985 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4987 DirRef delete_ref;
4988 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4990 if (delete_ref != DIRENTRY_NULL)
4991 This->entries[delete_ref].deleted = 1;
4993 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4996 else
4998 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4999 if (FAILED(hr)) return hr;
5001 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5002 This->entries[index].stream_entry, newsize);
5005 if (SUCCEEDED(hr))
5006 This->entries[index].data.size = newsize;
5008 return hr;
5011 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5012 DirRef dst, DirRef src)
5014 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5015 HRESULT hr;
5016 TransactedDirEntry *dst_entry, *src_entry;
5018 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5019 if (FAILED(hr)) return hr;
5021 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5022 if (FAILED(hr)) return hr;
5024 dst_entry = &This->entries[dst];
5025 src_entry = &This->entries[src];
5027 dst_entry->stream_dirty = src_entry->stream_dirty;
5028 dst_entry->stream_entry = src_entry->stream_entry;
5029 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5030 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5031 dst_entry->data.size = src_entry->data.size;
5033 return S_OK;
5036 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5038 StorageBaseImpl_QueryInterface,
5039 StorageBaseImpl_AddRef,
5040 StorageBaseImpl_Release,
5041 StorageBaseImpl_CreateStream,
5042 StorageBaseImpl_OpenStream,
5043 StorageBaseImpl_CreateStorage,
5044 StorageBaseImpl_OpenStorage,
5045 StorageBaseImpl_CopyTo,
5046 StorageBaseImpl_MoveElementTo,
5047 TransactedSnapshotImpl_Commit,
5048 TransactedSnapshotImpl_Revert,
5049 StorageBaseImpl_EnumElements,
5050 StorageBaseImpl_DestroyElement,
5051 StorageBaseImpl_RenameElement,
5052 StorageBaseImpl_SetElementTimes,
5053 StorageBaseImpl_SetClass,
5054 StorageBaseImpl_SetStateBits,
5055 StorageBaseImpl_Stat
5058 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5060 TransactedSnapshotImpl_Destroy,
5061 TransactedSnapshotImpl_Invalidate,
5062 TransactedSnapshotImpl_Flush,
5063 TransactedSnapshotImpl_GetFilename,
5064 TransactedSnapshotImpl_CreateDirEntry,
5065 TransactedSnapshotImpl_WriteDirEntry,
5066 TransactedSnapshotImpl_ReadDirEntry,
5067 TransactedSnapshotImpl_DestroyDirEntry,
5068 TransactedSnapshotImpl_StreamReadAt,
5069 TransactedSnapshotImpl_StreamWriteAt,
5070 TransactedSnapshotImpl_StreamSetSize,
5071 TransactedSnapshotImpl_StreamLink
5074 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5075 TransactedSnapshotImpl** result)
5077 HRESULT hr;
5079 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5080 if (*result)
5082 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5084 /* This is OK because the property set storage functions use the IStorage functions. */
5085 (*result)->base.pssVtbl = parentStorage->pssVtbl;
5087 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5089 list_init(&(*result)->base.strmHead);
5091 list_init(&(*result)->base.storageHead);
5093 (*result)->base.ref = 1;
5095 (*result)->base.openFlags = parentStorage->openFlags;
5097 /* Create a new temporary storage to act as the scratch file. */
5098 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5099 0, (IStorage**)&(*result)->scratch);
5101 if (SUCCEEDED(hr))
5103 ULONG num_entries = 20;
5105 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5107 (*result)->entries_size = num_entries;
5109 (*result)->firstFreeEntry = 0;
5111 if ((*result)->entries)
5113 /* parentStorage already has 1 reference, which we take over here. */
5114 (*result)->transactedParent = parentStorage;
5116 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5118 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5120 else
5122 IStorage_Release((IStorage*)(*result)->scratch);
5124 hr = E_OUTOFMEMORY;
5128 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5130 return hr;
5132 else
5133 return E_OUTOFMEMORY;
5136 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5137 StorageBaseImpl** result)
5139 static int fixme=0;
5141 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5143 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5146 return TransactedSnapshotImpl_Construct(parentStorage,
5147 (TransactedSnapshotImpl**)result);
5150 static HRESULT Storage_Construct(
5151 HANDLE hFile,
5152 LPCOLESTR pwcsName,
5153 ILockBytes* pLkbyt,
5154 DWORD openFlags,
5155 BOOL fileBased,
5156 BOOL create,
5157 ULONG sector_size,
5158 StorageBaseImpl** result)
5160 StorageImpl *newStorage;
5161 StorageBaseImpl *newTransactedStorage;
5162 HRESULT hr;
5164 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5165 if (FAILED(hr)) goto end;
5167 if (openFlags & STGM_TRANSACTED)
5169 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5170 if (FAILED(hr))
5171 IStorage_Release((IStorage*)newStorage);
5172 else
5173 *result = newTransactedStorage;
5175 else
5176 *result = &newStorage->base;
5178 end:
5179 return hr;
5182 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5184 StorageInternalImpl* This = (StorageInternalImpl*) base;
5186 if (!This->base.reverted)
5188 TRACE("Storage invalidated (stg=%p)\n", This);
5190 This->base.reverted = 1;
5192 This->parentStorage = NULL;
5194 StorageBaseImpl_DeleteAll(&This->base);
5196 list_remove(&This->ParentListEntry);
5200 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5202 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5204 StorageInternalImpl_Invalidate(&This->base);
5206 HeapFree(GetProcessHeap(), 0, This);
5209 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5211 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5213 return StorageBaseImpl_Flush(This->parentStorage);
5216 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5218 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5220 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5223 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5224 const DirEntry *newData, DirRef *index)
5226 StorageInternalImpl* This = (StorageInternalImpl*) base;
5228 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5229 newData, index);
5232 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5233 DirRef index, const DirEntry *data)
5235 StorageInternalImpl* This = (StorageInternalImpl*) base;
5237 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5238 index, data);
5241 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5242 DirRef index, DirEntry *data)
5244 StorageInternalImpl* This = (StorageInternalImpl*) base;
5246 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5247 index, data);
5250 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5251 DirRef index)
5253 StorageInternalImpl* This = (StorageInternalImpl*) base;
5255 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5256 index);
5259 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5260 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5262 StorageInternalImpl* This = (StorageInternalImpl*) base;
5264 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5265 index, offset, size, buffer, bytesRead);
5268 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5269 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5271 StorageInternalImpl* This = (StorageInternalImpl*) base;
5273 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5274 index, offset, size, buffer, bytesWritten);
5277 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5278 DirRef index, ULARGE_INTEGER newsize)
5280 StorageInternalImpl* This = (StorageInternalImpl*) base;
5282 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5283 index, newsize);
5286 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5287 DirRef dst, DirRef src)
5289 StorageInternalImpl* This = (StorageInternalImpl*) base;
5291 return StorageBaseImpl_StreamLink(This->parentStorage,
5292 dst, src);
5295 /******************************************************************************
5297 ** Storage32InternalImpl_Commit
5300 static HRESULT WINAPI StorageInternalImpl_Commit(
5301 IStorage* iface,
5302 DWORD grfCommitFlags) /* [in] */
5304 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5305 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5306 return StorageBaseImpl_Flush(base);
5309 /******************************************************************************
5311 ** Storage32InternalImpl_Revert
5314 static HRESULT WINAPI StorageInternalImpl_Revert(
5315 IStorage* iface)
5317 FIXME("(%p): stub\n", iface);
5318 return S_OK;
5321 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5323 IStorage_Release((IStorage*)This->parentStorage);
5324 HeapFree(GetProcessHeap(), 0, This);
5327 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5328 IEnumSTATSTG* iface,
5329 REFIID riid,
5330 void** ppvObject)
5332 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5334 if (ppvObject==0)
5335 return E_INVALIDARG;
5337 *ppvObject = 0;
5339 if (IsEqualGUID(&IID_IUnknown, riid) ||
5340 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5342 *ppvObject = This;
5343 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5344 return S_OK;
5347 return E_NOINTERFACE;
5350 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5351 IEnumSTATSTG* iface)
5353 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5354 return InterlockedIncrement(&This->ref);
5357 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5358 IEnumSTATSTG* iface)
5360 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5362 ULONG newRef;
5364 newRef = InterlockedDecrement(&This->ref);
5366 if (newRef==0)
5368 IEnumSTATSTGImpl_Destroy(This);
5371 return newRef;
5374 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5375 IEnumSTATSTGImpl* This,
5376 DirRef *ref)
5378 DirRef result = DIRENTRY_NULL;
5379 DirRef searchNode;
5380 DirEntry entry;
5381 HRESULT hr;
5382 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5384 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5385 This->parentStorage->storageDirEntry, &entry);
5386 searchNode = entry.dirRootEntry;
5388 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5390 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5392 if (SUCCEEDED(hr))
5394 LONG diff = entryNameCmp( entry.name, This->name);
5396 if (diff <= 0)
5398 searchNode = entry.rightChild;
5400 else
5402 result = searchNode;
5403 memcpy(result_name, entry.name, sizeof(result_name));
5404 searchNode = entry.leftChild;
5409 if (SUCCEEDED(hr))
5411 *ref = result;
5412 if (result != DIRENTRY_NULL)
5413 memcpy(This->name, result_name, sizeof(result_name));
5416 return hr;
5419 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5420 IEnumSTATSTG* iface,
5421 ULONG celt,
5422 STATSTG* rgelt,
5423 ULONG* pceltFetched)
5425 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5427 DirEntry currentEntry;
5428 STATSTG* currentReturnStruct = rgelt;
5429 ULONG objectFetched = 0;
5430 DirRef currentSearchNode;
5431 HRESULT hr=S_OK;
5433 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5434 return E_INVALIDARG;
5436 if (This->parentStorage->reverted)
5437 return STG_E_REVERTED;
5440 * To avoid the special case, get another pointer to a ULONG value if
5441 * the caller didn't supply one.
5443 if (pceltFetched==0)
5444 pceltFetched = &objectFetched;
5447 * Start the iteration, we will iterate until we hit the end of the
5448 * linked list or until we hit the number of items to iterate through
5450 *pceltFetched = 0;
5452 while ( *pceltFetched < celt )
5454 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5456 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5457 break;
5460 * Read the entry from the storage.
5462 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5463 currentSearchNode,
5464 &currentEntry);
5467 * Copy the information to the return buffer.
5469 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5470 currentReturnStruct,
5471 &currentEntry,
5472 STATFLAG_DEFAULT);
5475 * Step to the next item in the iteration
5477 (*pceltFetched)++;
5478 currentReturnStruct++;
5481 if (SUCCEEDED(hr) && *pceltFetched != celt)
5482 hr = S_FALSE;
5484 return hr;
5488 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5489 IEnumSTATSTG* iface,
5490 ULONG celt)
5492 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5494 ULONG objectFetched = 0;
5495 DirRef currentSearchNode;
5496 HRESULT hr=S_OK;
5498 if (This->parentStorage->reverted)
5499 return STG_E_REVERTED;
5501 while ( (objectFetched < celt) )
5503 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5505 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5506 break;
5508 objectFetched++;
5511 if (SUCCEEDED(hr) && objectFetched != celt)
5512 return S_FALSE;
5514 return hr;
5517 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5518 IEnumSTATSTG* iface)
5520 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5522 if (This->parentStorage->reverted)
5523 return STG_E_REVERTED;
5525 This->name[0] = 0;
5527 return S_OK;
5530 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5531 IEnumSTATSTG* iface,
5532 IEnumSTATSTG** ppenum)
5534 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5536 IEnumSTATSTGImpl* newClone;
5538 if (This->parentStorage->reverted)
5539 return STG_E_REVERTED;
5542 * Perform a sanity check on the parameters.
5544 if (ppenum==0)
5545 return E_INVALIDARG;
5547 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5548 This->storageDirEntry);
5552 * The new clone enumeration must point to the same current node as
5553 * the ole one.
5555 memcpy(newClone->name, This->name, sizeof(newClone->name));
5557 *ppenum = &newClone->IEnumSTATSTG_iface;
5560 * Don't forget to nail down a reference to the clone before
5561 * returning it.
5563 IEnumSTATSTGImpl_AddRef(*ppenum);
5565 return S_OK;
5569 * Virtual function table for the IEnumSTATSTGImpl class.
5571 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5573 IEnumSTATSTGImpl_QueryInterface,
5574 IEnumSTATSTGImpl_AddRef,
5575 IEnumSTATSTGImpl_Release,
5576 IEnumSTATSTGImpl_Next,
5577 IEnumSTATSTGImpl_Skip,
5578 IEnumSTATSTGImpl_Reset,
5579 IEnumSTATSTGImpl_Clone
5582 /******************************************************************************
5583 ** IEnumSTATSTGImpl implementation
5586 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5587 StorageBaseImpl* parentStorage,
5588 DirRef storageDirEntry)
5590 IEnumSTATSTGImpl* newEnumeration;
5592 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5594 if (newEnumeration!=0)
5597 * Set-up the virtual function table and reference count.
5599 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5600 newEnumeration->ref = 0;
5603 * We want to nail-down the reference to the storage in case the
5604 * enumeration out-lives the storage in the client application.
5606 newEnumeration->parentStorage = parentStorage;
5607 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5609 newEnumeration->storageDirEntry = storageDirEntry;
5612 * Make sure the current node of the iterator is the first one.
5614 IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface);
5617 return newEnumeration;
5621 * Virtual function table for the Storage32InternalImpl class.
5623 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5625 StorageBaseImpl_QueryInterface,
5626 StorageBaseImpl_AddRef,
5627 StorageBaseImpl_Release,
5628 StorageBaseImpl_CreateStream,
5629 StorageBaseImpl_OpenStream,
5630 StorageBaseImpl_CreateStorage,
5631 StorageBaseImpl_OpenStorage,
5632 StorageBaseImpl_CopyTo,
5633 StorageBaseImpl_MoveElementTo,
5634 StorageInternalImpl_Commit,
5635 StorageInternalImpl_Revert,
5636 StorageBaseImpl_EnumElements,
5637 StorageBaseImpl_DestroyElement,
5638 StorageBaseImpl_RenameElement,
5639 StorageBaseImpl_SetElementTimes,
5640 StorageBaseImpl_SetClass,
5641 StorageBaseImpl_SetStateBits,
5642 StorageBaseImpl_Stat
5645 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5647 StorageInternalImpl_Destroy,
5648 StorageInternalImpl_Invalidate,
5649 StorageInternalImpl_Flush,
5650 StorageInternalImpl_GetFilename,
5651 StorageInternalImpl_CreateDirEntry,
5652 StorageInternalImpl_WriteDirEntry,
5653 StorageInternalImpl_ReadDirEntry,
5654 StorageInternalImpl_DestroyDirEntry,
5655 StorageInternalImpl_StreamReadAt,
5656 StorageInternalImpl_StreamWriteAt,
5657 StorageInternalImpl_StreamSetSize,
5658 StorageInternalImpl_StreamLink
5661 /******************************************************************************
5662 ** Storage32InternalImpl implementation
5665 static StorageInternalImpl* StorageInternalImpl_Construct(
5666 StorageBaseImpl* parentStorage,
5667 DWORD openFlags,
5668 DirRef storageDirEntry)
5670 StorageInternalImpl* newStorage;
5672 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5674 if (newStorage!=0)
5676 list_init(&newStorage->base.strmHead);
5678 list_init(&newStorage->base.storageHead);
5681 * Initialize the virtual function table.
5683 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5684 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5685 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5686 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5688 newStorage->base.reverted = 0;
5690 newStorage->base.ref = 1;
5692 newStorage->parentStorage = parentStorage;
5695 * Keep a reference to the directory entry of this storage
5697 newStorage->base.storageDirEntry = storageDirEntry;
5699 newStorage->base.create = 0;
5701 return newStorage;
5704 return 0;
5707 /******************************************************************************
5708 ** StorageUtl implementation
5711 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5713 WORD tmp;
5715 memcpy(&tmp, buffer+offset, sizeof(WORD));
5716 *value = lendian16toh(tmp);
5719 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5721 value = htole16(value);
5722 memcpy(buffer+offset, &value, sizeof(WORD));
5725 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5727 DWORD tmp;
5729 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5730 *value = lendian32toh(tmp);
5733 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5735 value = htole32(value);
5736 memcpy(buffer+offset, &value, sizeof(DWORD));
5739 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5740 ULARGE_INTEGER* value)
5742 #ifdef WORDS_BIGENDIAN
5743 ULARGE_INTEGER tmp;
5745 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5746 value->u.LowPart = htole32(tmp.u.HighPart);
5747 value->u.HighPart = htole32(tmp.u.LowPart);
5748 #else
5749 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5750 #endif
5753 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5754 const ULARGE_INTEGER *value)
5756 #ifdef WORDS_BIGENDIAN
5757 ULARGE_INTEGER tmp;
5759 tmp.u.LowPart = htole32(value->u.HighPart);
5760 tmp.u.HighPart = htole32(value->u.LowPart);
5761 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5762 #else
5763 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5764 #endif
5767 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5769 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5770 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5771 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5773 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5776 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5778 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5779 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5780 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5782 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5785 void StorageUtl_CopyDirEntryToSTATSTG(
5786 StorageBaseImpl* storage,
5787 STATSTG* destination,
5788 const DirEntry* source,
5789 int statFlags)
5792 * The copy of the string occurs only when the flag is not set
5794 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5796 /* Use the filename for the root storage. */
5797 destination->pwcsName = 0;
5798 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5800 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5801 (source->name[0] == 0) )
5803 destination->pwcsName = 0;
5805 else
5807 destination->pwcsName =
5808 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5810 strcpyW(destination->pwcsName, source->name);
5813 switch (source->stgType)
5815 case STGTY_STORAGE:
5816 case STGTY_ROOT:
5817 destination->type = STGTY_STORAGE;
5818 break;
5819 case STGTY_STREAM:
5820 destination->type = STGTY_STREAM;
5821 break;
5822 default:
5823 destination->type = STGTY_STREAM;
5824 break;
5827 destination->cbSize = source->size;
5829 currentReturnStruct->mtime = {0}; TODO
5830 currentReturnStruct->ctime = {0};
5831 currentReturnStruct->atime = {0};
5833 destination->grfMode = 0;
5834 destination->grfLocksSupported = 0;
5835 destination->clsid = source->clsid;
5836 destination->grfStateBits = 0;
5837 destination->reserved = 0;
5840 /******************************************************************************
5841 ** BlockChainStream implementation
5844 /* Read and save the index of all blocks in this stream. */
5845 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5847 ULONG next_sector, next_offset;
5848 HRESULT hr;
5849 struct BlockChainRun *last_run;
5851 if (This->indexCacheLen == 0)
5853 last_run = NULL;
5854 next_offset = 0;
5855 next_sector = BlockChainStream_GetHeadOfChain(This);
5857 else
5859 last_run = &This->indexCache[This->indexCacheLen-1];
5860 next_offset = last_run->lastOffset+1;
5861 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5862 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5863 &next_sector);
5864 if (FAILED(hr)) return hr;
5867 while (next_sector != BLOCK_END_OF_CHAIN)
5869 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5871 /* Add the current block to the cache. */
5872 if (This->indexCacheSize == 0)
5874 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5875 if (!This->indexCache) return E_OUTOFMEMORY;
5876 This->indexCacheSize = 16;
5878 else if (This->indexCacheSize == This->indexCacheLen)
5880 struct BlockChainRun *new_cache;
5881 ULONG new_size;
5883 new_size = This->indexCacheSize * 2;
5884 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5885 if (!new_cache) return E_OUTOFMEMORY;
5886 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5888 HeapFree(GetProcessHeap(), 0, This->indexCache);
5889 This->indexCache = new_cache;
5890 This->indexCacheSize = new_size;
5893 This->indexCacheLen++;
5894 last_run = &This->indexCache[This->indexCacheLen-1];
5895 last_run->firstSector = next_sector;
5896 last_run->firstOffset = next_offset;
5899 last_run->lastOffset = next_offset;
5901 /* Find the next block. */
5902 next_offset++;
5903 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5904 if (FAILED(hr)) return hr;
5907 if (This->indexCacheLen)
5909 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5910 This->numBlocks = last_run->lastOffset+1;
5912 else
5914 This->tailIndex = BLOCK_END_OF_CHAIN;
5915 This->numBlocks = 0;
5918 return S_OK;
5921 /* Locate the nth block in this stream. */
5922 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5924 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5925 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5927 if (offset >= This->numBlocks)
5928 return BLOCK_END_OF_CHAIN;
5930 while (min_run < max_run)
5932 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5933 if (offset < This->indexCache[run_to_check].firstOffset)
5935 max_offset = This->indexCache[run_to_check].firstOffset-1;
5936 max_run = run_to_check-1;
5938 else if (offset > This->indexCache[run_to_check].lastOffset)
5940 min_offset = This->indexCache[run_to_check].lastOffset+1;
5941 min_run = run_to_check+1;
5943 else
5944 /* Block is in this run. */
5945 min_run = max_run = run_to_check;
5948 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5951 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5952 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5954 BlockChainBlock *result=NULL;
5955 int i;
5957 for (i=0; i<2; i++)
5958 if (This->cachedBlocks[i].index == index)
5960 *sector = This->cachedBlocks[i].sector;
5961 *block = &This->cachedBlocks[i];
5962 return S_OK;
5965 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5966 if (*sector == BLOCK_END_OF_CHAIN)
5967 return STG_E_DOCFILECORRUPT;
5969 if (create)
5971 if (This->cachedBlocks[0].index == 0xffffffff)
5972 result = &This->cachedBlocks[0];
5973 else if (This->cachedBlocks[1].index == 0xffffffff)
5974 result = &This->cachedBlocks[1];
5975 else
5977 result = &This->cachedBlocks[This->blockToEvict++];
5978 if (This->blockToEvict == 2)
5979 This->blockToEvict = 0;
5982 if (result->dirty)
5984 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5985 return STG_E_WRITEFAULT;
5986 result->dirty = 0;
5989 result->read = 0;
5990 result->index = index;
5991 result->sector = *sector;
5994 *block = result;
5995 return S_OK;
5998 BlockChainStream* BlockChainStream_Construct(
5999 StorageImpl* parentStorage,
6000 ULONG* headOfStreamPlaceHolder,
6001 DirRef dirEntry)
6003 BlockChainStream* newStream;
6005 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6007 newStream->parentStorage = parentStorage;
6008 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6009 newStream->ownerDirEntry = dirEntry;
6010 newStream->indexCache = NULL;
6011 newStream->indexCacheLen = 0;
6012 newStream->indexCacheSize = 0;
6013 newStream->cachedBlocks[0].index = 0xffffffff;
6014 newStream->cachedBlocks[0].dirty = 0;
6015 newStream->cachedBlocks[1].index = 0xffffffff;
6016 newStream->cachedBlocks[1].dirty = 0;
6017 newStream->blockToEvict = 0;
6019 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6021 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6022 HeapFree(GetProcessHeap(), 0, newStream);
6023 return NULL;
6026 return newStream;
6029 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6031 int i;
6032 if (!This) return S_OK;
6033 for (i=0; i<2; i++)
6035 if (This->cachedBlocks[i].dirty)
6037 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6038 This->cachedBlocks[i].dirty = 0;
6039 else
6040 return STG_E_WRITEFAULT;
6043 return S_OK;
6046 void BlockChainStream_Destroy(BlockChainStream* This)
6048 if (This)
6050 BlockChainStream_Flush(This);
6051 HeapFree(GetProcessHeap(), 0, This->indexCache);
6053 HeapFree(GetProcessHeap(), 0, This);
6056 /******************************************************************************
6057 * BlockChainStream_GetHeadOfChain
6059 * Returns the head of this stream chain.
6060 * Some special chains don't have directory entries, their heads are kept in
6061 * This->headOfStreamPlaceHolder.
6064 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6066 DirEntry chainEntry;
6067 HRESULT hr;
6069 if (This->headOfStreamPlaceHolder != 0)
6070 return *(This->headOfStreamPlaceHolder);
6072 if (This->ownerDirEntry != DIRENTRY_NULL)
6074 hr = StorageImpl_ReadDirEntry(
6075 This->parentStorage,
6076 This->ownerDirEntry,
6077 &chainEntry);
6079 if (SUCCEEDED(hr))
6081 return chainEntry.startingBlock;
6085 return BLOCK_END_OF_CHAIN;
6088 /******************************************************************************
6089 * BlockChainStream_GetCount
6091 * Returns the number of blocks that comprises this chain.
6092 * This is not the size of the stream as the last block may not be full!
6094 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6096 return This->numBlocks;
6099 /******************************************************************************
6100 * BlockChainStream_ReadAt
6102 * Reads a specified number of bytes from this chain at the specified offset.
6103 * bytesRead may be NULL.
6104 * Failure will be returned if the specified number of bytes has not been read.
6106 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6107 ULARGE_INTEGER offset,
6108 ULONG size,
6109 void* buffer,
6110 ULONG* bytesRead)
6112 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6113 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6114 ULONG bytesToReadInBuffer;
6115 ULONG blockIndex;
6116 BYTE* bufferWalker;
6117 ULARGE_INTEGER stream_size;
6118 HRESULT hr;
6119 BlockChainBlock *cachedBlock;
6121 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6124 * Find the first block in the stream that contains part of the buffer.
6126 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6128 *bytesRead = 0;
6130 stream_size = BlockChainStream_GetSize(This);
6131 if (stream_size.QuadPart > offset.QuadPart)
6132 size = min(stream_size.QuadPart - offset.QuadPart, size);
6133 else
6134 return S_OK;
6137 * Start reading the buffer.
6139 bufferWalker = buffer;
6141 while (size > 0)
6143 ULARGE_INTEGER ulOffset;
6144 DWORD bytesReadAt;
6147 * Calculate how many bytes we can copy from this big block.
6149 bytesToReadInBuffer =
6150 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6152 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6154 if (FAILED(hr))
6155 return hr;
6157 if (!cachedBlock)
6159 /* Not in cache, and we're going to read past the end of the block. */
6160 ulOffset.u.HighPart = 0;
6161 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6162 offsetInBlock;
6164 StorageImpl_ReadAt(This->parentStorage,
6165 ulOffset,
6166 bufferWalker,
6167 bytesToReadInBuffer,
6168 &bytesReadAt);
6170 else
6172 if (!cachedBlock->read)
6174 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6175 return STG_E_READFAULT;
6177 cachedBlock->read = 1;
6180 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6181 bytesReadAt = bytesToReadInBuffer;
6184 blockNoInSequence++;
6185 bufferWalker += bytesReadAt;
6186 size -= bytesReadAt;
6187 *bytesRead += bytesReadAt;
6188 offsetInBlock = 0; /* There is no offset on the next block */
6190 if (bytesToReadInBuffer != bytesReadAt)
6191 break;
6194 return S_OK;
6197 /******************************************************************************
6198 * BlockChainStream_WriteAt
6200 * Writes the specified number of bytes to this chain at the specified offset.
6201 * Will fail if not all specified number of bytes have been written.
6203 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6204 ULARGE_INTEGER offset,
6205 ULONG size,
6206 const void* buffer,
6207 ULONG* bytesWritten)
6209 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6210 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6211 ULONG bytesToWrite;
6212 ULONG blockIndex;
6213 const BYTE* bufferWalker;
6214 HRESULT hr;
6215 BlockChainBlock *cachedBlock;
6217 *bytesWritten = 0;
6218 bufferWalker = buffer;
6220 while (size > 0)
6222 ULARGE_INTEGER ulOffset;
6223 DWORD bytesWrittenAt;
6226 * Calculate how many bytes we can copy to this big block.
6228 bytesToWrite =
6229 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6231 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6233 /* BlockChainStream_SetSize should have already been called to ensure we have
6234 * enough blocks in the chain to write into */
6235 if (FAILED(hr))
6237 ERR("not enough blocks in chain to write data\n");
6238 return hr;
6241 if (!cachedBlock)
6243 /* Not in cache, and we're going to write past the end of the block. */
6244 ulOffset.u.HighPart = 0;
6245 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6246 offsetInBlock;
6248 StorageImpl_WriteAt(This->parentStorage,
6249 ulOffset,
6250 bufferWalker,
6251 bytesToWrite,
6252 &bytesWrittenAt);
6254 else
6256 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6258 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6259 return STG_E_READFAULT;
6262 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6263 bytesWrittenAt = bytesToWrite;
6264 cachedBlock->read = 1;
6265 cachedBlock->dirty = 1;
6268 blockNoInSequence++;
6269 bufferWalker += bytesWrittenAt;
6270 size -= bytesWrittenAt;
6271 *bytesWritten += bytesWrittenAt;
6272 offsetInBlock = 0; /* There is no offset on the next block */
6274 if (bytesWrittenAt != bytesToWrite)
6275 break;
6278 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6281 /******************************************************************************
6282 * BlockChainStream_Shrink
6284 * Shrinks this chain in the big block depot.
6286 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6287 ULARGE_INTEGER newSize)
6289 ULONG blockIndex;
6290 ULONG numBlocks;
6291 int i;
6294 * Figure out how many blocks are needed to contain the new size
6296 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6298 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6299 numBlocks++;
6301 if (numBlocks)
6304 * Go to the new end of chain
6306 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6308 /* Mark the new end of chain */
6309 StorageImpl_SetNextBlockInChain(
6310 This->parentStorage,
6311 blockIndex,
6312 BLOCK_END_OF_CHAIN);
6314 This->tailIndex = blockIndex;
6316 else
6318 if (This->headOfStreamPlaceHolder != 0)
6320 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6322 else
6324 DirEntry chainEntry;
6325 assert(This->ownerDirEntry != DIRENTRY_NULL);
6327 StorageImpl_ReadDirEntry(
6328 This->parentStorage,
6329 This->ownerDirEntry,
6330 &chainEntry);
6332 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6334 StorageImpl_WriteDirEntry(
6335 This->parentStorage,
6336 This->ownerDirEntry,
6337 &chainEntry);
6340 This->tailIndex = BLOCK_END_OF_CHAIN;
6343 This->numBlocks = numBlocks;
6346 * Mark the extra blocks as free
6348 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6350 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6351 StorageImpl_FreeBigBlock(This->parentStorage,
6352 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6353 if (last_run->lastOffset == last_run->firstOffset)
6354 This->indexCacheLen--;
6355 else
6356 last_run->lastOffset--;
6360 * Reset the last accessed block cache.
6362 for (i=0; i<2; i++)
6364 if (This->cachedBlocks[i].index >= numBlocks)
6366 This->cachedBlocks[i].index = 0xffffffff;
6367 This->cachedBlocks[i].dirty = 0;
6371 return TRUE;
6374 /******************************************************************************
6375 * BlockChainStream_Enlarge
6377 * Grows this chain in the big block depot.
6379 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6380 ULARGE_INTEGER newSize)
6382 ULONG blockIndex, currentBlock;
6383 ULONG newNumBlocks;
6384 ULONG oldNumBlocks = 0;
6386 blockIndex = BlockChainStream_GetHeadOfChain(This);
6389 * Empty chain. Create the head.
6391 if (blockIndex == BLOCK_END_OF_CHAIN)
6393 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6394 StorageImpl_SetNextBlockInChain(This->parentStorage,
6395 blockIndex,
6396 BLOCK_END_OF_CHAIN);
6398 if (This->headOfStreamPlaceHolder != 0)
6400 *(This->headOfStreamPlaceHolder) = blockIndex;
6402 else
6404 DirEntry chainEntry;
6405 assert(This->ownerDirEntry != DIRENTRY_NULL);
6407 StorageImpl_ReadDirEntry(
6408 This->parentStorage,
6409 This->ownerDirEntry,
6410 &chainEntry);
6412 chainEntry.startingBlock = blockIndex;
6414 StorageImpl_WriteDirEntry(
6415 This->parentStorage,
6416 This->ownerDirEntry,
6417 &chainEntry);
6420 This->tailIndex = blockIndex;
6421 This->numBlocks = 1;
6425 * Figure out how many blocks are needed to contain this stream
6427 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6429 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6430 newNumBlocks++;
6433 * Go to the current end of chain
6435 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6437 currentBlock = blockIndex;
6439 while (blockIndex != BLOCK_END_OF_CHAIN)
6441 This->numBlocks++;
6442 currentBlock = blockIndex;
6444 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6445 &blockIndex)))
6446 return FALSE;
6449 This->tailIndex = currentBlock;
6452 currentBlock = This->tailIndex;
6453 oldNumBlocks = This->numBlocks;
6456 * Add new blocks to the chain
6458 if (oldNumBlocks < newNumBlocks)
6460 while (oldNumBlocks < newNumBlocks)
6462 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6464 StorageImpl_SetNextBlockInChain(
6465 This->parentStorage,
6466 currentBlock,
6467 blockIndex);
6469 StorageImpl_SetNextBlockInChain(
6470 This->parentStorage,
6471 blockIndex,
6472 BLOCK_END_OF_CHAIN);
6474 currentBlock = blockIndex;
6475 oldNumBlocks++;
6478 This->tailIndex = blockIndex;
6479 This->numBlocks = newNumBlocks;
6482 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6483 return FALSE;
6485 return TRUE;
6488 /******************************************************************************
6489 * BlockChainStream_SetSize
6491 * Sets the size of this stream. The big block depot will be updated.
6492 * The file will grow if we grow the chain.
6494 * TODO: Free the actual blocks in the file when we shrink the chain.
6495 * Currently, the blocks are still in the file. So the file size
6496 * doesn't shrink even if we shrink streams.
6498 BOOL BlockChainStream_SetSize(
6499 BlockChainStream* This,
6500 ULARGE_INTEGER newSize)
6502 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6504 if (newSize.u.LowPart == size.u.LowPart)
6505 return TRUE;
6507 if (newSize.u.LowPart < size.u.LowPart)
6509 BlockChainStream_Shrink(This, newSize);
6511 else
6513 BlockChainStream_Enlarge(This, newSize);
6516 return TRUE;
6519 /******************************************************************************
6520 * BlockChainStream_GetSize
6522 * Returns the size of this chain.
6523 * Will return the block count if this chain doesn't have a directory entry.
6525 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6527 DirEntry chainEntry;
6529 if(This->headOfStreamPlaceHolder == NULL)
6532 * This chain has a directory entry so use the size value from there.
6534 StorageImpl_ReadDirEntry(
6535 This->parentStorage,
6536 This->ownerDirEntry,
6537 &chainEntry);
6539 return chainEntry.size;
6541 else
6544 * this chain is a chain that does not have a directory entry, figure out the
6545 * size by making the product number of used blocks times the
6546 * size of them
6548 ULARGE_INTEGER result;
6549 result.u.HighPart = 0;
6551 result.u.LowPart =
6552 BlockChainStream_GetCount(This) *
6553 This->parentStorage->bigBlockSize;
6555 return result;
6559 /******************************************************************************
6560 ** SmallBlockChainStream implementation
6563 SmallBlockChainStream* SmallBlockChainStream_Construct(
6564 StorageImpl* parentStorage,
6565 ULONG* headOfStreamPlaceHolder,
6566 DirRef dirEntry)
6568 SmallBlockChainStream* newStream;
6570 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6572 newStream->parentStorage = parentStorage;
6573 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6574 newStream->ownerDirEntry = dirEntry;
6576 return newStream;
6579 void SmallBlockChainStream_Destroy(
6580 SmallBlockChainStream* This)
6582 HeapFree(GetProcessHeap(), 0, This);
6585 /******************************************************************************
6586 * SmallBlockChainStream_GetHeadOfChain
6588 * Returns the head of this chain of small blocks.
6590 static ULONG SmallBlockChainStream_GetHeadOfChain(
6591 SmallBlockChainStream* This)
6593 DirEntry chainEntry;
6594 HRESULT hr;
6596 if (This->headOfStreamPlaceHolder != NULL)
6597 return *(This->headOfStreamPlaceHolder);
6599 if (This->ownerDirEntry)
6601 hr = StorageImpl_ReadDirEntry(
6602 This->parentStorage,
6603 This->ownerDirEntry,
6604 &chainEntry);
6606 if (SUCCEEDED(hr))
6608 return chainEntry.startingBlock;
6613 return BLOCK_END_OF_CHAIN;
6616 /******************************************************************************
6617 * SmallBlockChainStream_GetNextBlockInChain
6619 * Returns the index of the next small block in this chain.
6621 * Return Values:
6622 * - BLOCK_END_OF_CHAIN: end of this chain
6623 * - BLOCK_UNUSED: small block 'blockIndex' is free
6625 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6626 SmallBlockChainStream* This,
6627 ULONG blockIndex,
6628 ULONG* nextBlockInChain)
6630 ULARGE_INTEGER offsetOfBlockInDepot;
6631 DWORD buffer;
6632 ULONG bytesRead;
6633 HRESULT res;
6635 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6637 offsetOfBlockInDepot.u.HighPart = 0;
6638 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6641 * Read those bytes in the buffer from the small block file.
6643 res = BlockChainStream_ReadAt(
6644 This->parentStorage->smallBlockDepotChain,
6645 offsetOfBlockInDepot,
6646 sizeof(DWORD),
6647 &buffer,
6648 &bytesRead);
6650 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6651 res = STG_E_READFAULT;
6653 if (SUCCEEDED(res))
6655 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6656 return S_OK;
6659 return res;
6662 /******************************************************************************
6663 * SmallBlockChainStream_SetNextBlockInChain
6665 * Writes the index of the next block of the specified block in the small
6666 * block depot.
6667 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6668 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6670 static void SmallBlockChainStream_SetNextBlockInChain(
6671 SmallBlockChainStream* This,
6672 ULONG blockIndex,
6673 ULONG nextBlock)
6675 ULARGE_INTEGER offsetOfBlockInDepot;
6676 DWORD buffer;
6677 ULONG bytesWritten;
6679 offsetOfBlockInDepot.u.HighPart = 0;
6680 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6682 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6685 * Read those bytes in the buffer from the small block file.
6687 BlockChainStream_WriteAt(
6688 This->parentStorage->smallBlockDepotChain,
6689 offsetOfBlockInDepot,
6690 sizeof(DWORD),
6691 &buffer,
6692 &bytesWritten);
6695 /******************************************************************************
6696 * SmallBlockChainStream_FreeBlock
6698 * Flag small block 'blockIndex' as free in the small block depot.
6700 static void SmallBlockChainStream_FreeBlock(
6701 SmallBlockChainStream* This,
6702 ULONG blockIndex)
6704 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6707 /******************************************************************************
6708 * SmallBlockChainStream_GetNextFreeBlock
6710 * Returns the index of a free small block. The small block depot will be
6711 * enlarged if necessary. The small block chain will also be enlarged if
6712 * necessary.
6714 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6715 SmallBlockChainStream* This)
6717 ULARGE_INTEGER offsetOfBlockInDepot;
6718 DWORD buffer;
6719 ULONG bytesRead;
6720 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6721 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6722 HRESULT res = S_OK;
6723 ULONG smallBlocksPerBigBlock;
6724 DirEntry rootEntry;
6725 ULONG blocksRequired;
6726 ULARGE_INTEGER old_size, size_required;
6728 offsetOfBlockInDepot.u.HighPart = 0;
6731 * Scan the small block depot for a free block
6733 while (nextBlockIndex != BLOCK_UNUSED)
6735 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6737 res = BlockChainStream_ReadAt(
6738 This->parentStorage->smallBlockDepotChain,
6739 offsetOfBlockInDepot,
6740 sizeof(DWORD),
6741 &buffer,
6742 &bytesRead);
6745 * If we run out of space for the small block depot, enlarge it
6747 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6749 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6751 if (nextBlockIndex != BLOCK_UNUSED)
6752 blockIndex++;
6754 else
6756 ULONG count =
6757 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6759 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6760 ULARGE_INTEGER newSize, offset;
6761 ULONG bytesWritten;
6763 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6764 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6767 * Initialize all the small blocks to free
6769 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6770 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6771 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6772 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6774 StorageImpl_SaveFileHeader(This->parentStorage);
6778 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6780 smallBlocksPerBigBlock =
6781 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6784 * Verify if we have to allocate big blocks to contain small blocks
6786 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6788 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6790 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6792 if (size_required.QuadPart > old_size.QuadPart)
6794 BlockChainStream_SetSize(
6795 This->parentStorage->smallBlockRootChain,
6796 size_required);
6798 StorageImpl_ReadDirEntry(
6799 This->parentStorage,
6800 This->parentStorage->base.storageDirEntry,
6801 &rootEntry);
6803 rootEntry.size = size_required;
6805 StorageImpl_WriteDirEntry(
6806 This->parentStorage,
6807 This->parentStorage->base.storageDirEntry,
6808 &rootEntry);
6811 return blockIndex;
6814 /******************************************************************************
6815 * SmallBlockChainStream_ReadAt
6817 * Reads a specified number of bytes from this chain at the specified offset.
6818 * bytesRead may be NULL.
6819 * Failure will be returned if the specified number of bytes has not been read.
6821 HRESULT SmallBlockChainStream_ReadAt(
6822 SmallBlockChainStream* This,
6823 ULARGE_INTEGER offset,
6824 ULONG size,
6825 void* buffer,
6826 ULONG* bytesRead)
6828 HRESULT rc = S_OK;
6829 ULARGE_INTEGER offsetInBigBlockFile;
6830 ULONG blockNoInSequence =
6831 offset.u.LowPart / This->parentStorage->smallBlockSize;
6833 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6834 ULONG bytesToReadInBuffer;
6835 ULONG blockIndex;
6836 ULONG bytesReadFromBigBlockFile;
6837 BYTE* bufferWalker;
6838 ULARGE_INTEGER stream_size;
6841 * This should never happen on a small block file.
6843 assert(offset.u.HighPart==0);
6845 *bytesRead = 0;
6847 stream_size = SmallBlockChainStream_GetSize(This);
6848 if (stream_size.QuadPart > offset.QuadPart)
6849 size = min(stream_size.QuadPart - offset.QuadPart, size);
6850 else
6851 return S_OK;
6854 * Find the first block in the stream that contains part of the buffer.
6856 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6858 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6860 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6861 if(FAILED(rc))
6862 return rc;
6863 blockNoInSequence--;
6867 * Start reading the buffer.
6869 bufferWalker = buffer;
6871 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6874 * Calculate how many bytes we can copy from this small block.
6876 bytesToReadInBuffer =
6877 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6880 * Calculate the offset of the small block in the small block file.
6882 offsetInBigBlockFile.u.HighPart = 0;
6883 offsetInBigBlockFile.u.LowPart =
6884 blockIndex * This->parentStorage->smallBlockSize;
6886 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6889 * Read those bytes in the buffer from the small block file.
6890 * The small block has already been identified so it shouldn't fail
6891 * unless the file is corrupt.
6893 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6894 offsetInBigBlockFile,
6895 bytesToReadInBuffer,
6896 bufferWalker,
6897 &bytesReadFromBigBlockFile);
6899 if (FAILED(rc))
6900 return rc;
6902 if (!bytesReadFromBigBlockFile)
6903 return STG_E_DOCFILECORRUPT;
6906 * Step to the next big block.
6908 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6909 if(FAILED(rc))
6910 return STG_E_DOCFILECORRUPT;
6912 bufferWalker += bytesReadFromBigBlockFile;
6913 size -= bytesReadFromBigBlockFile;
6914 *bytesRead += bytesReadFromBigBlockFile;
6915 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6918 return S_OK;
6921 /******************************************************************************
6922 * SmallBlockChainStream_WriteAt
6924 * Writes the specified number of bytes to this chain at the specified offset.
6925 * Will fail if not all specified number of bytes have been written.
6927 HRESULT SmallBlockChainStream_WriteAt(
6928 SmallBlockChainStream* This,
6929 ULARGE_INTEGER offset,
6930 ULONG size,
6931 const void* buffer,
6932 ULONG* bytesWritten)
6934 ULARGE_INTEGER offsetInBigBlockFile;
6935 ULONG blockNoInSequence =
6936 offset.u.LowPart / This->parentStorage->smallBlockSize;
6938 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6939 ULONG bytesToWriteInBuffer;
6940 ULONG blockIndex;
6941 ULONG bytesWrittenToBigBlockFile;
6942 const BYTE* bufferWalker;
6943 HRESULT res;
6946 * This should never happen on a small block file.
6948 assert(offset.u.HighPart==0);
6951 * Find the first block in the stream that contains part of the buffer.
6953 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6955 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6957 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6958 return STG_E_DOCFILECORRUPT;
6959 blockNoInSequence--;
6963 * Start writing the buffer.
6965 *bytesWritten = 0;
6966 bufferWalker = buffer;
6967 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6970 * Calculate how many bytes we can copy to this small block.
6972 bytesToWriteInBuffer =
6973 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6976 * Calculate the offset of the small block in the small block file.
6978 offsetInBigBlockFile.u.HighPart = 0;
6979 offsetInBigBlockFile.u.LowPart =
6980 blockIndex * This->parentStorage->smallBlockSize;
6982 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6985 * Write those bytes in the buffer to the small block file.
6987 res = BlockChainStream_WriteAt(
6988 This->parentStorage->smallBlockRootChain,
6989 offsetInBigBlockFile,
6990 bytesToWriteInBuffer,
6991 bufferWalker,
6992 &bytesWrittenToBigBlockFile);
6993 if (FAILED(res))
6994 return res;
6997 * Step to the next big block.
6999 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7000 &blockIndex)))
7001 return FALSE;
7002 bufferWalker += bytesWrittenToBigBlockFile;
7003 size -= bytesWrittenToBigBlockFile;
7004 *bytesWritten += bytesWrittenToBigBlockFile;
7005 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7008 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7011 /******************************************************************************
7012 * SmallBlockChainStream_Shrink
7014 * Shrinks this chain in the small block depot.
7016 static BOOL SmallBlockChainStream_Shrink(
7017 SmallBlockChainStream* This,
7018 ULARGE_INTEGER newSize)
7020 ULONG blockIndex, extraBlock;
7021 ULONG numBlocks;
7022 ULONG count = 0;
7024 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7026 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7027 numBlocks++;
7029 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7032 * Go to the new end of chain
7034 while (count < numBlocks)
7036 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7037 &blockIndex)))
7038 return FALSE;
7039 count++;
7043 * If the count is 0, we have a special case, the head of the chain was
7044 * just freed.
7046 if (count == 0)
7048 DirEntry chainEntry;
7050 StorageImpl_ReadDirEntry(This->parentStorage,
7051 This->ownerDirEntry,
7052 &chainEntry);
7054 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7056 StorageImpl_WriteDirEntry(This->parentStorage,
7057 This->ownerDirEntry,
7058 &chainEntry);
7061 * We start freeing the chain at the head block.
7063 extraBlock = blockIndex;
7065 else
7067 /* Get the next block before marking the new end */
7068 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7069 &extraBlock)))
7070 return FALSE;
7072 /* Mark the new end of chain */
7073 SmallBlockChainStream_SetNextBlockInChain(
7074 This,
7075 blockIndex,
7076 BLOCK_END_OF_CHAIN);
7080 * Mark the extra blocks as free
7082 while (extraBlock != BLOCK_END_OF_CHAIN)
7084 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7085 &blockIndex)))
7086 return FALSE;
7087 SmallBlockChainStream_FreeBlock(This, extraBlock);
7088 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7089 extraBlock = blockIndex;
7092 return TRUE;
7095 /******************************************************************************
7096 * SmallBlockChainStream_Enlarge
7098 * Grows this chain in the small block depot.
7100 static BOOL SmallBlockChainStream_Enlarge(
7101 SmallBlockChainStream* This,
7102 ULARGE_INTEGER newSize)
7104 ULONG blockIndex, currentBlock;
7105 ULONG newNumBlocks;
7106 ULONG oldNumBlocks = 0;
7108 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7111 * Empty chain. Create the head.
7113 if (blockIndex == BLOCK_END_OF_CHAIN)
7115 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7116 SmallBlockChainStream_SetNextBlockInChain(
7117 This,
7118 blockIndex,
7119 BLOCK_END_OF_CHAIN);
7121 if (This->headOfStreamPlaceHolder != NULL)
7123 *(This->headOfStreamPlaceHolder) = blockIndex;
7125 else
7127 DirEntry chainEntry;
7129 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7130 &chainEntry);
7132 chainEntry.startingBlock = blockIndex;
7134 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7135 &chainEntry);
7139 currentBlock = blockIndex;
7142 * Figure out how many blocks are needed to contain this stream
7144 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7146 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7147 newNumBlocks++;
7150 * Go to the current end of chain
7152 while (blockIndex != BLOCK_END_OF_CHAIN)
7154 oldNumBlocks++;
7155 currentBlock = blockIndex;
7156 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7157 return FALSE;
7161 * Add new blocks to the chain
7163 while (oldNumBlocks < newNumBlocks)
7165 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7166 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7168 SmallBlockChainStream_SetNextBlockInChain(
7169 This,
7170 blockIndex,
7171 BLOCK_END_OF_CHAIN);
7173 currentBlock = blockIndex;
7174 oldNumBlocks++;
7177 return TRUE;
7180 /******************************************************************************
7181 * SmallBlockChainStream_SetSize
7183 * Sets the size of this stream.
7184 * The file will grow if we grow the chain.
7186 * TODO: Free the actual blocks in the file when we shrink the chain.
7187 * Currently, the blocks are still in the file. So the file size
7188 * doesn't shrink even if we shrink streams.
7190 BOOL SmallBlockChainStream_SetSize(
7191 SmallBlockChainStream* This,
7192 ULARGE_INTEGER newSize)
7194 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7196 if (newSize.u.LowPart == size.u.LowPart)
7197 return TRUE;
7199 if (newSize.u.LowPart < size.u.LowPart)
7201 SmallBlockChainStream_Shrink(This, newSize);
7203 else
7205 SmallBlockChainStream_Enlarge(This, newSize);
7208 return TRUE;
7211 /******************************************************************************
7212 * SmallBlockChainStream_GetCount
7214 * Returns the number of small blocks that comprises this chain.
7215 * This is not the size of the stream as the last block may not be full!
7218 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7220 ULONG blockIndex;
7221 ULONG count = 0;
7223 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7225 while(blockIndex != BLOCK_END_OF_CHAIN)
7227 count++;
7229 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7230 blockIndex, &blockIndex)))
7231 return 0;
7234 return count;
7237 /******************************************************************************
7238 * SmallBlockChainStream_GetSize
7240 * Returns the size of this chain.
7242 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7244 DirEntry chainEntry;
7246 if(This->headOfStreamPlaceHolder != NULL)
7248 ULARGE_INTEGER result;
7249 result.u.HighPart = 0;
7251 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7252 This->parentStorage->smallBlockSize;
7254 return result;
7257 StorageImpl_ReadDirEntry(
7258 This->parentStorage,
7259 This->ownerDirEntry,
7260 &chainEntry);
7262 return chainEntry.size;
7265 static HRESULT create_storagefile(
7266 LPCOLESTR pwcsName,
7267 DWORD grfMode,
7268 DWORD grfAttrs,
7269 STGOPTIONS* pStgOptions,
7270 REFIID riid,
7271 void** ppstgOpen)
7273 StorageBaseImpl* newStorage = 0;
7274 HANDLE hFile = INVALID_HANDLE_VALUE;
7275 HRESULT hr = STG_E_INVALIDFLAG;
7276 DWORD shareMode;
7277 DWORD accessMode;
7278 DWORD creationMode;
7279 DWORD fileAttributes;
7280 WCHAR tempFileName[MAX_PATH];
7282 if (ppstgOpen == 0)
7283 return STG_E_INVALIDPOINTER;
7285 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7286 return STG_E_INVALIDPARAMETER;
7288 /* if no share mode given then DENY_NONE is the default */
7289 if (STGM_SHARE_MODE(grfMode) == 0)
7290 grfMode |= STGM_SHARE_DENY_NONE;
7292 if ( FAILED( validateSTGM(grfMode) ))
7293 goto end;
7295 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7296 switch(STGM_ACCESS_MODE(grfMode))
7298 case STGM_WRITE:
7299 case STGM_READWRITE:
7300 break;
7301 default:
7302 goto end;
7305 /* in direct mode, can only use SHARE_EXCLUSIVE */
7306 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7307 goto end;
7309 /* but in transacted mode, any share mode is valid */
7312 * Generate a unique name.
7314 if (pwcsName == 0)
7316 WCHAR tempPath[MAX_PATH];
7317 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7319 memset(tempPath, 0, sizeof(tempPath));
7320 memset(tempFileName, 0, sizeof(tempFileName));
7322 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7323 tempPath[0] = '.';
7325 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7326 pwcsName = tempFileName;
7327 else
7329 hr = STG_E_INSUFFICIENTMEMORY;
7330 goto end;
7333 creationMode = TRUNCATE_EXISTING;
7335 else
7337 creationMode = GetCreationModeFromSTGM(grfMode);
7341 * Interpret the STGM value grfMode
7343 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7344 accessMode = GetAccessModeFromSTGM(grfMode);
7346 if (grfMode & STGM_DELETEONRELEASE)
7347 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7348 else
7349 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7351 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7353 static int fixme;
7354 if (!fixme++)
7355 FIXME("Storage share mode not implemented.\n");
7358 *ppstgOpen = 0;
7360 hFile = CreateFileW(pwcsName,
7361 accessMode,
7362 shareMode,
7363 NULL,
7364 creationMode,
7365 fileAttributes,
7368 if (hFile == INVALID_HANDLE_VALUE)
7370 if(GetLastError() == ERROR_FILE_EXISTS)
7371 hr = STG_E_FILEALREADYEXISTS;
7372 else
7373 hr = E_FAIL;
7374 goto end;
7378 * Allocate and initialize the new IStorage32object.
7380 hr = Storage_Construct(
7381 hFile,
7382 pwcsName,
7383 NULL,
7384 grfMode,
7385 TRUE,
7386 TRUE,
7387 pStgOptions->ulSectorSize,
7388 &newStorage);
7390 if (FAILED(hr))
7392 goto end;
7395 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7397 IStorage_Release((IStorage*)newStorage);
7399 end:
7400 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7402 return hr;
7405 /******************************************************************************
7406 * StgCreateDocfile [OLE32.@]
7407 * Creates a new compound file storage object
7409 * PARAMS
7410 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7411 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7412 * reserved [ ?] unused?, usually 0
7413 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7415 * RETURNS
7416 * S_OK if the file was successfully created
7417 * some STG_E_ value if error
7418 * NOTES
7419 * if pwcsName is NULL, create file with new unique name
7420 * the function can returns
7421 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7422 * (unrealized now)
7424 HRESULT WINAPI StgCreateDocfile(
7425 LPCOLESTR pwcsName,
7426 DWORD grfMode,
7427 DWORD reserved,
7428 IStorage **ppstgOpen)
7430 STGOPTIONS stgoptions = {1, 0, 512};
7432 TRACE("(%s, %x, %d, %p)\n",
7433 debugstr_w(pwcsName), grfMode,
7434 reserved, ppstgOpen);
7436 if (ppstgOpen == 0)
7437 return STG_E_INVALIDPOINTER;
7438 if (reserved != 0)
7439 return STG_E_INVALIDPARAMETER;
7441 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7444 /******************************************************************************
7445 * StgCreateStorageEx [OLE32.@]
7447 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7449 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7450 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7452 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7454 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7455 return STG_E_INVALIDPARAMETER;
7458 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7460 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7461 return STG_E_INVALIDPARAMETER;
7464 if (stgfmt == STGFMT_FILE)
7466 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7467 return STG_E_INVALIDPARAMETER;
7470 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7472 STGOPTIONS defaultOptions = {1, 0, 512};
7474 if (!pStgOptions) pStgOptions = &defaultOptions;
7475 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7479 ERR("Invalid stgfmt argument\n");
7480 return STG_E_INVALIDPARAMETER;
7483 /******************************************************************************
7484 * StgCreatePropSetStg [OLE32.@]
7486 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7487 IPropertySetStorage **ppPropSetStg)
7489 HRESULT hr;
7491 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7492 if (reserved)
7493 hr = STG_E_INVALIDPARAMETER;
7494 else
7495 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7496 (void**)ppPropSetStg);
7497 return hr;
7500 /******************************************************************************
7501 * StgOpenStorageEx [OLE32.@]
7503 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7505 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7506 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7508 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7510 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7511 return STG_E_INVALIDPARAMETER;
7514 switch (stgfmt)
7516 case STGFMT_FILE:
7517 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7518 return STG_E_INVALIDPARAMETER;
7520 case STGFMT_STORAGE:
7521 break;
7523 case STGFMT_DOCFILE:
7524 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7526 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7527 return STG_E_INVALIDPARAMETER;
7529 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7530 break;
7532 case STGFMT_ANY:
7533 WARN("STGFMT_ANY assuming storage\n");
7534 break;
7536 default:
7537 return STG_E_INVALIDPARAMETER;
7540 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7544 /******************************************************************************
7545 * StgOpenStorage [OLE32.@]
7547 HRESULT WINAPI StgOpenStorage(
7548 const OLECHAR *pwcsName,
7549 IStorage *pstgPriority,
7550 DWORD grfMode,
7551 SNB snbExclude,
7552 DWORD reserved,
7553 IStorage **ppstgOpen)
7555 StorageBaseImpl* newStorage = 0;
7556 HRESULT hr = S_OK;
7557 HANDLE hFile = 0;
7558 DWORD shareMode;
7559 DWORD accessMode;
7561 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7562 debugstr_w(pwcsName), pstgPriority, grfMode,
7563 snbExclude, reserved, ppstgOpen);
7565 if (pwcsName == 0)
7567 hr = STG_E_INVALIDNAME;
7568 goto end;
7571 if (ppstgOpen == 0)
7573 hr = STG_E_INVALIDPOINTER;
7574 goto end;
7577 if (reserved)
7579 hr = STG_E_INVALIDPARAMETER;
7580 goto end;
7583 if (grfMode & STGM_PRIORITY)
7585 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7586 return STG_E_INVALIDFLAG;
7587 if (grfMode & STGM_DELETEONRELEASE)
7588 return STG_E_INVALIDFUNCTION;
7589 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7590 return STG_E_INVALIDFLAG;
7591 grfMode &= ~0xf0; /* remove the existing sharing mode */
7592 grfMode |= STGM_SHARE_DENY_NONE;
7594 /* STGM_PRIORITY stops other IStorage objects on the same file from
7595 * committing until the STGM_PRIORITY IStorage is closed. it also
7596 * stops non-transacted mode StgOpenStorage calls with write access from
7597 * succeeding. obviously, both of these cannot be achieved through just
7598 * file share flags */
7599 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7603 * Validate the sharing mode
7605 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7606 switch(STGM_SHARE_MODE(grfMode))
7608 case STGM_SHARE_EXCLUSIVE:
7609 case STGM_SHARE_DENY_WRITE:
7610 break;
7611 default:
7612 hr = STG_E_INVALIDFLAG;
7613 goto end;
7616 if ( FAILED( validateSTGM(grfMode) ) ||
7617 (grfMode&STGM_CREATE))
7619 hr = STG_E_INVALIDFLAG;
7620 goto end;
7623 /* shared reading requires transacted mode */
7624 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7625 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7626 !(grfMode&STGM_TRANSACTED) )
7628 hr = STG_E_INVALIDFLAG;
7629 goto end;
7633 * Interpret the STGM value grfMode
7635 shareMode = GetShareModeFromSTGM(grfMode);
7636 accessMode = GetAccessModeFromSTGM(grfMode);
7638 *ppstgOpen = 0;
7640 hFile = CreateFileW( pwcsName,
7641 accessMode,
7642 shareMode,
7643 NULL,
7644 OPEN_EXISTING,
7645 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7648 if (hFile==INVALID_HANDLE_VALUE)
7650 DWORD last_error = GetLastError();
7652 hr = E_FAIL;
7654 switch (last_error)
7656 case ERROR_FILE_NOT_FOUND:
7657 hr = STG_E_FILENOTFOUND;
7658 break;
7660 case ERROR_PATH_NOT_FOUND:
7661 hr = STG_E_PATHNOTFOUND;
7662 break;
7664 case ERROR_ACCESS_DENIED:
7665 case ERROR_WRITE_PROTECT:
7666 hr = STG_E_ACCESSDENIED;
7667 break;
7669 case ERROR_SHARING_VIOLATION:
7670 hr = STG_E_SHAREVIOLATION;
7671 break;
7673 default:
7674 hr = E_FAIL;
7677 goto end;
7681 * Refuse to open the file if it's too small to be a structured storage file
7682 * FIXME: verify the file when reading instead of here
7684 if (GetFileSize(hFile, NULL) < 0x100)
7686 CloseHandle(hFile);
7687 hr = STG_E_FILEALREADYEXISTS;
7688 goto end;
7692 * Allocate and initialize the new IStorage32object.
7694 hr = Storage_Construct(
7695 hFile,
7696 pwcsName,
7697 NULL,
7698 grfMode,
7699 TRUE,
7700 FALSE,
7701 512,
7702 &newStorage);
7704 if (FAILED(hr))
7707 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7709 if(hr == STG_E_INVALIDHEADER)
7710 hr = STG_E_FILEALREADYEXISTS;
7711 goto end;
7715 * Get an "out" pointer for the caller.
7717 *ppstgOpen = (IStorage*)newStorage;
7719 end:
7720 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7721 return hr;
7724 /******************************************************************************
7725 * StgCreateDocfileOnILockBytes [OLE32.@]
7727 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7728 ILockBytes *plkbyt,
7729 DWORD grfMode,
7730 DWORD reserved,
7731 IStorage** ppstgOpen)
7733 StorageBaseImpl* newStorage = 0;
7734 HRESULT hr = S_OK;
7736 if ((ppstgOpen == 0) || (plkbyt == 0))
7737 return STG_E_INVALIDPOINTER;
7740 * Allocate and initialize the new IStorage object.
7742 hr = Storage_Construct(
7745 plkbyt,
7746 grfMode,
7747 FALSE,
7748 TRUE,
7749 512,
7750 &newStorage);
7752 if (FAILED(hr))
7754 return hr;
7758 * Get an "out" pointer for the caller.
7760 *ppstgOpen = (IStorage*)newStorage;
7762 return hr;
7765 /******************************************************************************
7766 * StgOpenStorageOnILockBytes [OLE32.@]
7768 HRESULT WINAPI StgOpenStorageOnILockBytes(
7769 ILockBytes *plkbyt,
7770 IStorage *pstgPriority,
7771 DWORD grfMode,
7772 SNB snbExclude,
7773 DWORD reserved,
7774 IStorage **ppstgOpen)
7776 StorageBaseImpl* newStorage = 0;
7777 HRESULT hr = S_OK;
7779 if ((plkbyt == 0) || (ppstgOpen == 0))
7780 return STG_E_INVALIDPOINTER;
7782 if ( FAILED( validateSTGM(grfMode) ))
7783 return STG_E_INVALIDFLAG;
7785 *ppstgOpen = 0;
7788 * Allocate and initialize the new IStorage object.
7790 hr = Storage_Construct(
7793 plkbyt,
7794 grfMode,
7795 FALSE,
7796 FALSE,
7797 512,
7798 &newStorage);
7800 if (FAILED(hr))
7802 return hr;
7806 * Get an "out" pointer for the caller.
7808 *ppstgOpen = (IStorage*)newStorage;
7810 return hr;
7813 /******************************************************************************
7814 * StgSetTimes [ole32.@]
7815 * StgSetTimes [OLE32.@]
7819 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7820 FILETIME const *patime, FILETIME const *pmtime)
7822 IStorage *stg = NULL;
7823 HRESULT r;
7825 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7827 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7828 0, 0, &stg);
7829 if( SUCCEEDED(r) )
7831 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7832 IStorage_Release(stg);
7835 return r;
7838 /******************************************************************************
7839 * StgIsStorageILockBytes [OLE32.@]
7841 * Determines if the ILockBytes contains a storage object.
7843 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7845 BYTE sig[8];
7846 ULARGE_INTEGER offset;
7848 offset.u.HighPart = 0;
7849 offset.u.LowPart = 0;
7851 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7853 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7854 return S_OK;
7856 return S_FALSE;
7859 /******************************************************************************
7860 * WriteClassStg [OLE32.@]
7862 * This method will store the specified CLSID in the specified storage object
7864 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7866 HRESULT hRes;
7868 if(!pStg)
7869 return E_INVALIDARG;
7871 if(!rclsid)
7872 return STG_E_INVALIDPOINTER;
7874 hRes = IStorage_SetClass(pStg, rclsid);
7876 return hRes;
7879 /***********************************************************************
7880 * ReadClassStg (OLE32.@)
7882 * This method reads the CLSID previously written to a storage object with
7883 * the WriteClassStg.
7885 * PARAMS
7886 * pstg [I] IStorage pointer
7887 * pclsid [O] Pointer to where the CLSID is written
7889 * RETURNS
7890 * Success: S_OK.
7891 * Failure: HRESULT code.
7893 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7895 STATSTG pstatstg;
7896 HRESULT hRes;
7898 TRACE("(%p, %p)\n", pstg, pclsid);
7900 if(!pstg || !pclsid)
7901 return E_INVALIDARG;
7904 * read a STATSTG structure (contains the clsid) from the storage
7906 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7908 if(SUCCEEDED(hRes))
7909 *pclsid=pstatstg.clsid;
7911 return hRes;
7914 /***********************************************************************
7915 * OleLoadFromStream (OLE32.@)
7917 * This function loads an object from stream
7919 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7921 CLSID clsid;
7922 HRESULT res;
7923 LPPERSISTSTREAM xstm;
7925 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7927 res=ReadClassStm(pStm,&clsid);
7928 if (FAILED(res))
7929 return res;
7930 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7931 if (FAILED(res))
7932 return res;
7933 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7934 if (FAILED(res)) {
7935 IUnknown_Release((IUnknown*)*ppvObj);
7936 return res;
7938 res=IPersistStream_Load(xstm,pStm);
7939 IPersistStream_Release(xstm);
7940 /* FIXME: all refcounts ok at this point? I think they should be:
7941 * pStm : unchanged
7942 * ppvObj : 1
7943 * xstm : 0 (released)
7945 return res;
7948 /***********************************************************************
7949 * OleSaveToStream (OLE32.@)
7951 * This function saves an object with the IPersistStream interface on it
7952 * to the specified stream.
7954 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7957 CLSID clsid;
7958 HRESULT res;
7960 TRACE("(%p,%p)\n",pPStm,pStm);
7962 res=IPersistStream_GetClassID(pPStm,&clsid);
7964 if (SUCCEEDED(res)){
7966 res=WriteClassStm(pStm,&clsid);
7968 if (SUCCEEDED(res))
7970 res=IPersistStream_Save(pPStm,pStm,TRUE);
7973 TRACE("Finished Save\n");
7974 return res;
7977 /****************************************************************************
7978 * This method validate a STGM parameter that can contain the values below
7980 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7981 * The stgm values contained in 0xffff0000 are bitmasks.
7983 * STGM_DIRECT 0x00000000
7984 * STGM_TRANSACTED 0x00010000
7985 * STGM_SIMPLE 0x08000000
7987 * STGM_READ 0x00000000
7988 * STGM_WRITE 0x00000001
7989 * STGM_READWRITE 0x00000002
7991 * STGM_SHARE_DENY_NONE 0x00000040
7992 * STGM_SHARE_DENY_READ 0x00000030
7993 * STGM_SHARE_DENY_WRITE 0x00000020
7994 * STGM_SHARE_EXCLUSIVE 0x00000010
7996 * STGM_PRIORITY 0x00040000
7997 * STGM_DELETEONRELEASE 0x04000000
7999 * STGM_CREATE 0x00001000
8000 * STGM_CONVERT 0x00020000
8001 * STGM_FAILIFTHERE 0x00000000
8003 * STGM_NOSCRATCH 0x00100000
8004 * STGM_NOSNAPSHOT 0x00200000
8006 static HRESULT validateSTGM(DWORD stgm)
8008 DWORD access = STGM_ACCESS_MODE(stgm);
8009 DWORD share = STGM_SHARE_MODE(stgm);
8010 DWORD create = STGM_CREATE_MODE(stgm);
8012 if (stgm&~STGM_KNOWN_FLAGS)
8014 ERR("unknown flags %08x\n", stgm);
8015 return E_FAIL;
8018 switch (access)
8020 case STGM_READ:
8021 case STGM_WRITE:
8022 case STGM_READWRITE:
8023 break;
8024 default:
8025 return E_FAIL;
8028 switch (share)
8030 case STGM_SHARE_DENY_NONE:
8031 case STGM_SHARE_DENY_READ:
8032 case STGM_SHARE_DENY_WRITE:
8033 case STGM_SHARE_EXCLUSIVE:
8034 break;
8035 default:
8036 return E_FAIL;
8039 switch (create)
8041 case STGM_CREATE:
8042 case STGM_FAILIFTHERE:
8043 break;
8044 default:
8045 return E_FAIL;
8049 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8051 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8052 return E_FAIL;
8055 * STGM_CREATE | STGM_CONVERT
8056 * if both are false, STGM_FAILIFTHERE is set to TRUE
8058 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8059 return E_FAIL;
8062 * STGM_NOSCRATCH requires STGM_TRANSACTED
8064 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8065 return E_FAIL;
8068 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8069 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8071 if ( (stgm & STGM_NOSNAPSHOT) &&
8072 (!(stgm & STGM_TRANSACTED) ||
8073 share == STGM_SHARE_EXCLUSIVE ||
8074 share == STGM_SHARE_DENY_WRITE) )
8075 return E_FAIL;
8077 return S_OK;
8080 /****************************************************************************
8081 * GetShareModeFromSTGM
8083 * This method will return a share mode flag from a STGM value.
8084 * The STGM value is assumed valid.
8086 static DWORD GetShareModeFromSTGM(DWORD stgm)
8088 switch (STGM_SHARE_MODE(stgm))
8090 case STGM_SHARE_DENY_NONE:
8091 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8092 case STGM_SHARE_DENY_READ:
8093 return FILE_SHARE_WRITE;
8094 case STGM_SHARE_DENY_WRITE:
8095 return FILE_SHARE_READ;
8096 case STGM_SHARE_EXCLUSIVE:
8097 return 0;
8099 ERR("Invalid share mode!\n");
8100 assert(0);
8101 return 0;
8104 /****************************************************************************
8105 * GetAccessModeFromSTGM
8107 * This method will return an access mode flag from a STGM value.
8108 * The STGM value is assumed valid.
8110 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8112 switch (STGM_ACCESS_MODE(stgm))
8114 case STGM_READ:
8115 return GENERIC_READ;
8116 case STGM_WRITE:
8117 case STGM_READWRITE:
8118 return GENERIC_READ | GENERIC_WRITE;
8120 ERR("Invalid access mode!\n");
8121 assert(0);
8122 return 0;
8125 /****************************************************************************
8126 * GetCreationModeFromSTGM
8128 * This method will return a creation mode flag from a STGM value.
8129 * The STGM value is assumed valid.
8131 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8133 switch(STGM_CREATE_MODE(stgm))
8135 case STGM_CREATE:
8136 return CREATE_ALWAYS;
8137 case STGM_CONVERT:
8138 FIXME("STGM_CONVERT not implemented!\n");
8139 return CREATE_NEW;
8140 case STGM_FAILIFTHERE:
8141 return CREATE_NEW;
8143 ERR("Invalid create mode!\n");
8144 assert(0);
8145 return 0;
8149 /*************************************************************************
8150 * OLECONVERT_LoadOLE10 [Internal]
8152 * Loads the OLE10 STREAM to memory
8154 * PARAMS
8155 * pOleStream [I] The OLESTREAM
8156 * pData [I] Data Structure for the OLESTREAM Data
8158 * RETURNS
8159 * Success: S_OK
8160 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8161 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8163 * NOTES
8164 * This function is used by OleConvertOLESTREAMToIStorage only.
8166 * Memory allocated for pData must be freed by the caller
8168 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8170 DWORD dwSize;
8171 HRESULT hRes = S_OK;
8172 int nTryCnt=0;
8173 int max_try = 6;
8175 pData->pData = NULL;
8176 pData->pstrOleObjFileName = NULL;
8178 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8180 /* Get the OleID */
8181 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8182 if(dwSize != sizeof(pData->dwOleID))
8184 hRes = CONVERT10_E_OLESTREAM_GET;
8186 else if(pData->dwOleID != OLESTREAM_ID)
8188 hRes = CONVERT10_E_OLESTREAM_FMT;
8190 else
8192 hRes = S_OK;
8193 break;
8197 if(hRes == S_OK)
8199 /* Get the TypeID... more info needed for this field */
8200 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8201 if(dwSize != sizeof(pData->dwTypeID))
8203 hRes = CONVERT10_E_OLESTREAM_GET;
8206 if(hRes == S_OK)
8208 if(pData->dwTypeID != 0)
8210 /* Get the length of the OleTypeName */
8211 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8212 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8214 hRes = CONVERT10_E_OLESTREAM_GET;
8217 if(hRes == S_OK)
8219 if(pData->dwOleTypeNameLength > 0)
8221 /* Get the OleTypeName */
8222 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8223 if(dwSize != pData->dwOleTypeNameLength)
8225 hRes = CONVERT10_E_OLESTREAM_GET;
8229 if(bStrem1)
8231 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8232 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8234 hRes = CONVERT10_E_OLESTREAM_GET;
8236 if(hRes == S_OK)
8238 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8239 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8240 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8241 if(pData->pstrOleObjFileName)
8243 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8244 if(dwSize != pData->dwOleObjFileNameLength)
8246 hRes = CONVERT10_E_OLESTREAM_GET;
8249 else
8250 hRes = CONVERT10_E_OLESTREAM_GET;
8253 else
8255 /* Get the Width of the Metafile */
8256 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8257 if(dwSize != sizeof(pData->dwMetaFileWidth))
8259 hRes = CONVERT10_E_OLESTREAM_GET;
8261 if(hRes == S_OK)
8263 /* Get the Height of the Metafile */
8264 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8265 if(dwSize != sizeof(pData->dwMetaFileHeight))
8267 hRes = CONVERT10_E_OLESTREAM_GET;
8271 if(hRes == S_OK)
8273 /* Get the Length of the Data */
8274 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8275 if(dwSize != sizeof(pData->dwDataLength))
8277 hRes = CONVERT10_E_OLESTREAM_GET;
8281 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8283 if(!bStrem1) /* if it is a second OLE stream data */
8285 pData->dwDataLength -= 8;
8286 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8287 if(dwSize != sizeof(pData->strUnknown))
8289 hRes = CONVERT10_E_OLESTREAM_GET;
8293 if(hRes == S_OK)
8295 if(pData->dwDataLength > 0)
8297 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8299 /* Get Data (ex. IStorage, Metafile, or BMP) */
8300 if(pData->pData)
8302 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8303 if(dwSize != pData->dwDataLength)
8305 hRes = CONVERT10_E_OLESTREAM_GET;
8308 else
8310 hRes = CONVERT10_E_OLESTREAM_GET;
8316 return hRes;
8319 /*************************************************************************
8320 * OLECONVERT_SaveOLE10 [Internal]
8322 * Saves the OLE10 STREAM From memory
8324 * PARAMS
8325 * pData [I] Data Structure for the OLESTREAM Data
8326 * pOleStream [I] The OLESTREAM to save
8328 * RETURNS
8329 * Success: S_OK
8330 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8332 * NOTES
8333 * This function is used by OleConvertIStorageToOLESTREAM only.
8336 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8338 DWORD dwSize;
8339 HRESULT hRes = S_OK;
8342 /* Set the OleID */
8343 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8344 if(dwSize != sizeof(pData->dwOleID))
8346 hRes = CONVERT10_E_OLESTREAM_PUT;
8349 if(hRes == S_OK)
8351 /* Set the TypeID */
8352 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8353 if(dwSize != sizeof(pData->dwTypeID))
8355 hRes = CONVERT10_E_OLESTREAM_PUT;
8359 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8361 /* Set the Length of the OleTypeName */
8362 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8363 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8365 hRes = CONVERT10_E_OLESTREAM_PUT;
8368 if(hRes == S_OK)
8370 if(pData->dwOleTypeNameLength > 0)
8372 /* Set the OleTypeName */
8373 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8374 if(dwSize != pData->dwOleTypeNameLength)
8376 hRes = CONVERT10_E_OLESTREAM_PUT;
8381 if(hRes == S_OK)
8383 /* Set the width of the Metafile */
8384 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8385 if(dwSize != sizeof(pData->dwMetaFileWidth))
8387 hRes = CONVERT10_E_OLESTREAM_PUT;
8391 if(hRes == S_OK)
8393 /* Set the height of the Metafile */
8394 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8395 if(dwSize != sizeof(pData->dwMetaFileHeight))
8397 hRes = CONVERT10_E_OLESTREAM_PUT;
8401 if(hRes == S_OK)
8403 /* Set the length of the Data */
8404 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8405 if(dwSize != sizeof(pData->dwDataLength))
8407 hRes = CONVERT10_E_OLESTREAM_PUT;
8411 if(hRes == S_OK)
8413 if(pData->dwDataLength > 0)
8415 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8416 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8417 if(dwSize != pData->dwDataLength)
8419 hRes = CONVERT10_E_OLESTREAM_PUT;
8424 return hRes;
8427 /*************************************************************************
8428 * OLECONVERT_GetOLE20FromOLE10[Internal]
8430 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8431 * opens it, and copies the content to the dest IStorage for
8432 * OleConvertOLESTREAMToIStorage
8435 * PARAMS
8436 * pDestStorage [I] The IStorage to copy the data to
8437 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8438 * nBufferLength [I] The size of the buffer
8440 * RETURNS
8441 * Nothing
8443 * NOTES
8447 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8449 HRESULT hRes;
8450 HANDLE hFile;
8451 IStorage *pTempStorage;
8452 DWORD dwNumOfBytesWritten;
8453 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8454 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8456 /* Create a temp File */
8457 GetTempPathW(MAX_PATH, wstrTempDir);
8458 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8459 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8461 if(hFile != INVALID_HANDLE_VALUE)
8463 /* Write IStorage Data to File */
8464 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8465 CloseHandle(hFile);
8467 /* Open and copy temp storage to the Dest Storage */
8468 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8469 if(hRes == S_OK)
8471 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8472 IStorage_Release(pTempStorage);
8474 DeleteFileW(wstrTempFile);
8479 /*************************************************************************
8480 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8482 * Saves the OLE10 STREAM From memory
8484 * PARAMS
8485 * pStorage [I] The Src IStorage to copy
8486 * pData [I] The Dest Memory to write to.
8488 * RETURNS
8489 * The size in bytes allocated for pData
8491 * NOTES
8492 * Memory allocated for pData must be freed by the caller
8494 * Used by OleConvertIStorageToOLESTREAM only.
8497 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8499 HANDLE hFile;
8500 HRESULT hRes;
8501 DWORD nDataLength = 0;
8502 IStorage *pTempStorage;
8503 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8504 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8506 *pData = NULL;
8508 /* Create temp Storage */
8509 GetTempPathW(MAX_PATH, wstrTempDir);
8510 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8511 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8513 if(hRes == S_OK)
8515 /* Copy Src Storage to the Temp Storage */
8516 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8517 IStorage_Release(pTempStorage);
8519 /* Open Temp Storage as a file and copy to memory */
8520 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8521 if(hFile != INVALID_HANDLE_VALUE)
8523 nDataLength = GetFileSize(hFile, NULL);
8524 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8525 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8526 CloseHandle(hFile);
8528 DeleteFileW(wstrTempFile);
8530 return nDataLength;
8533 /*************************************************************************
8534 * OLECONVERT_CreateOleStream [Internal]
8536 * Creates the "\001OLE" stream in the IStorage if necessary.
8538 * PARAMS
8539 * pStorage [I] Dest storage to create the stream in
8541 * RETURNS
8542 * Nothing
8544 * NOTES
8545 * This function is used by OleConvertOLESTREAMToIStorage only.
8547 * This stream is still unknown, MS Word seems to have extra data
8548 * but since the data is stored in the OLESTREAM there should be
8549 * no need to recreate the stream. If the stream is manually
8550 * deleted it will create it with this default data.
8553 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8555 HRESULT hRes;
8556 IStream *pStream;
8557 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8558 BYTE pOleStreamHeader [] =
8560 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8561 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8562 0x00, 0x00, 0x00, 0x00
8565 /* Create stream if not present */
8566 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8567 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8569 if(hRes == S_OK)
8571 /* Write default Data */
8572 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8573 IStream_Release(pStream);
8577 /* write a string to a stream, preceded by its length */
8578 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8580 HRESULT r;
8581 LPSTR str;
8582 DWORD len = 0;
8584 if( string )
8585 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8586 r = IStream_Write( stm, &len, sizeof(len), NULL);
8587 if( FAILED( r ) )
8588 return r;
8589 if(len == 0)
8590 return r;
8591 str = CoTaskMemAlloc( len );
8592 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8593 r = IStream_Write( stm, str, len, NULL);
8594 CoTaskMemFree( str );
8595 return r;
8598 /* read a string preceded by its length from a stream */
8599 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8601 HRESULT r;
8602 DWORD len, count = 0;
8603 LPSTR str;
8604 LPWSTR wstr;
8606 r = IStream_Read( stm, &len, sizeof(len), &count );
8607 if( FAILED( r ) )
8608 return r;
8609 if( count != sizeof(len) )
8610 return E_OUTOFMEMORY;
8612 TRACE("%d bytes\n",len);
8614 str = CoTaskMemAlloc( len );
8615 if( !str )
8616 return E_OUTOFMEMORY;
8617 count = 0;
8618 r = IStream_Read( stm, str, len, &count );
8619 if( FAILED( r ) )
8620 return r;
8621 if( count != len )
8623 CoTaskMemFree( str );
8624 return E_OUTOFMEMORY;
8627 TRACE("Read string %s\n",debugstr_an(str,len));
8629 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8630 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8631 if( wstr )
8632 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8633 CoTaskMemFree( str );
8635 *string = wstr;
8637 return r;
8641 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8642 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8644 IStream *pstm;
8645 HRESULT r = S_OK;
8646 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8648 static const BYTE unknown1[12] =
8649 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8650 0xFF, 0xFF, 0xFF, 0xFF};
8651 static const BYTE unknown2[16] =
8652 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8653 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8655 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8656 debugstr_w(lpszUserType), debugstr_w(szClipName),
8657 debugstr_w(szProgIDName));
8659 /* Create a CompObj stream */
8660 r = IStorage_CreateStream(pstg, szwStreamName,
8661 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8662 if( FAILED (r) )
8663 return r;
8665 /* Write CompObj Structure to stream */
8666 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8668 if( SUCCEEDED( r ) )
8669 r = WriteClassStm( pstm, clsid );
8671 if( SUCCEEDED( r ) )
8672 r = STREAM_WriteString( pstm, lpszUserType );
8673 if( SUCCEEDED( r ) )
8674 r = STREAM_WriteString( pstm, szClipName );
8675 if( SUCCEEDED( r ) )
8676 r = STREAM_WriteString( pstm, szProgIDName );
8677 if( SUCCEEDED( r ) )
8678 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8680 IStream_Release( pstm );
8682 return r;
8685 /***********************************************************************
8686 * WriteFmtUserTypeStg (OLE32.@)
8688 HRESULT WINAPI WriteFmtUserTypeStg(
8689 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8691 HRESULT r;
8692 WCHAR szwClipName[0x40];
8693 CLSID clsid = CLSID_NULL;
8694 LPWSTR wstrProgID = NULL;
8695 DWORD n;
8697 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8699 /* get the clipboard format name */
8700 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8701 szwClipName[n]=0;
8703 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8705 /* FIXME: There's room to save a CLSID and its ProgID, but
8706 the CLSID is not looked up in the registry and in all the
8707 tests I wrote it was CLSID_NULL. Where does it come from?
8710 /* get the real program ID. This may fail, but that's fine */
8711 ProgIDFromCLSID(&clsid, &wstrProgID);
8713 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8715 r = STORAGE_WriteCompObj( pstg, &clsid,
8716 lpszUserType, szwClipName, wstrProgID );
8718 CoTaskMemFree(wstrProgID);
8720 return r;
8724 /******************************************************************************
8725 * ReadFmtUserTypeStg [OLE32.@]
8727 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8729 HRESULT r;
8730 IStream *stm = 0;
8731 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8732 unsigned char unknown1[12];
8733 unsigned char unknown2[16];
8734 DWORD count;
8735 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8736 CLSID clsid;
8738 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8740 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8741 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8742 if( FAILED ( r ) )
8744 WARN("Failed to open stream r = %08x\n", r);
8745 return r;
8748 /* read the various parts of the structure */
8749 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8750 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8751 goto end;
8752 r = ReadClassStm( stm, &clsid );
8753 if( FAILED( r ) )
8754 goto end;
8756 r = STREAM_ReadString( stm, &szCLSIDName );
8757 if( FAILED( r ) )
8758 goto end;
8760 r = STREAM_ReadString( stm, &szOleTypeName );
8761 if( FAILED( r ) )
8762 goto end;
8764 r = STREAM_ReadString( stm, &szProgIDName );
8765 if( FAILED( r ) )
8766 goto end;
8768 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8769 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8770 goto end;
8772 /* ok, success... now we just need to store what we found */
8773 if( pcf )
8774 *pcf = RegisterClipboardFormatW( szOleTypeName );
8775 CoTaskMemFree( szOleTypeName );
8777 if( lplpszUserType )
8778 *lplpszUserType = szCLSIDName;
8779 CoTaskMemFree( szProgIDName );
8781 end:
8782 IStream_Release( stm );
8784 return r;
8788 /*************************************************************************
8789 * OLECONVERT_CreateCompObjStream [Internal]
8791 * Creates a "\001CompObj" is the destination IStorage if necessary.
8793 * PARAMS
8794 * pStorage [I] The dest IStorage to create the CompObj Stream
8795 * if necessary.
8796 * strOleTypeName [I] The ProgID
8798 * RETURNS
8799 * Success: S_OK
8800 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8802 * NOTES
8803 * This function is used by OleConvertOLESTREAMToIStorage only.
8805 * The stream data is stored in the OLESTREAM and there should be
8806 * no need to recreate the stream. If the stream is manually
8807 * deleted it will attempt to create it by querying the registry.
8811 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8813 IStream *pStream;
8814 HRESULT hStorageRes, hRes = S_OK;
8815 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8816 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8817 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8819 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8820 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8822 /* Initialize the CompObj structure */
8823 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8824 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8825 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8828 /* Create a CompObj stream if it doesn't exist */
8829 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8830 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8831 if(hStorageRes == S_OK)
8833 /* copy the OleTypeName to the compobj struct */
8834 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8835 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8837 /* copy the OleTypeName to the compobj struct */
8838 /* Note: in the test made, these were Identical */
8839 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8840 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8842 /* Get the CLSID */
8843 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8844 bufferW, OLESTREAM_MAX_STR_LEN );
8845 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8847 if(hRes == S_OK)
8849 HKEY hKey;
8850 LONG hErr;
8851 /* Get the CLSID Default Name from the Registry */
8852 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8853 if(hErr == ERROR_SUCCESS)
8855 char strTemp[OLESTREAM_MAX_STR_LEN];
8856 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8857 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8858 if(hErr == ERROR_SUCCESS)
8860 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8862 RegCloseKey(hKey);
8866 /* Write CompObj Structure to stream */
8867 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8869 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8871 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8872 if(IStorageCompObj.dwCLSIDNameLength > 0)
8874 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8876 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8877 if(IStorageCompObj.dwOleTypeNameLength > 0)
8879 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8881 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8882 if(IStorageCompObj.dwProgIDNameLength > 0)
8884 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8886 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8887 IStream_Release(pStream);
8889 return hRes;
8893 /*************************************************************************
8894 * OLECONVERT_CreateOlePresStream[Internal]
8896 * Creates the "\002OlePres000" Stream with the Metafile data
8898 * PARAMS
8899 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8900 * dwExtentX [I] Width of the Metafile
8901 * dwExtentY [I] Height of the Metafile
8902 * pData [I] Metafile data
8903 * dwDataLength [I] Size of the Metafile data
8905 * RETURNS
8906 * Success: S_OK
8907 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8909 * NOTES
8910 * This function is used by OleConvertOLESTREAMToIStorage only.
8913 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8915 HRESULT hRes;
8916 IStream *pStream;
8917 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8918 BYTE pOlePresStreamHeader [] =
8920 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8921 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8922 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8923 0x00, 0x00, 0x00, 0x00
8926 BYTE pOlePresStreamHeaderEmpty [] =
8928 0x00, 0x00, 0x00, 0x00,
8929 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8930 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8931 0x00, 0x00, 0x00, 0x00
8934 /* Create the OlePres000 Stream */
8935 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8936 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8938 if(hRes == S_OK)
8940 DWORD nHeaderSize;
8941 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8943 memset(&OlePres, 0, sizeof(OlePres));
8944 /* Do we have any metafile data to save */
8945 if(dwDataLength > 0)
8947 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8948 nHeaderSize = sizeof(pOlePresStreamHeader);
8950 else
8952 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8953 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8955 /* Set width and height of the metafile */
8956 OlePres.dwExtentX = dwExtentX;
8957 OlePres.dwExtentY = -dwExtentY;
8959 /* Set Data and Length */
8960 if(dwDataLength > sizeof(METAFILEPICT16))
8962 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8963 OlePres.pData = &(pData[8]);
8965 /* Save OlePres000 Data to Stream */
8966 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8967 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8968 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8969 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8970 if(OlePres.dwSize > 0)
8972 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8974 IStream_Release(pStream);
8978 /*************************************************************************
8979 * OLECONVERT_CreateOle10NativeStream [Internal]
8981 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8983 * PARAMS
8984 * pStorage [I] Dest storage to create the stream in
8985 * pData [I] Ole10 Native Data (ex. bmp)
8986 * dwDataLength [I] Size of the Ole10 Native Data
8988 * RETURNS
8989 * Nothing
8991 * NOTES
8992 * This function is used by OleConvertOLESTREAMToIStorage only.
8994 * Might need to verify the data and return appropriate error message
8997 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8999 HRESULT hRes;
9000 IStream *pStream;
9001 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9003 /* Create the Ole10Native Stream */
9004 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9005 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9007 if(hRes == S_OK)
9009 /* Write info to stream */
9010 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9011 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9012 IStream_Release(pStream);
9017 /*************************************************************************
9018 * OLECONVERT_GetOLE10ProgID [Internal]
9020 * Finds the ProgID (or OleTypeID) from the IStorage
9022 * PARAMS
9023 * pStorage [I] The Src IStorage to get the ProgID
9024 * strProgID [I] the ProgID string to get
9025 * dwSize [I] the size of the string
9027 * RETURNS
9028 * Success: S_OK
9029 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9031 * NOTES
9032 * This function is used by OleConvertIStorageToOLESTREAM only.
9036 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9038 HRESULT hRes;
9039 IStream *pStream;
9040 LARGE_INTEGER iSeekPos;
9041 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9042 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9044 /* Open the CompObj Stream */
9045 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9046 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9047 if(hRes == S_OK)
9050 /*Get the OleType from the CompObj Stream */
9051 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9052 iSeekPos.u.HighPart = 0;
9054 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9055 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9056 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9057 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9058 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9059 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9060 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9062 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9063 if(*dwSize > 0)
9065 IStream_Read(pStream, strProgID, *dwSize, NULL);
9067 IStream_Release(pStream);
9069 else
9071 STATSTG stat;
9072 LPOLESTR wstrProgID;
9074 /* Get the OleType from the registry */
9075 REFCLSID clsid = &(stat.clsid);
9076 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9077 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9078 if(hRes == S_OK)
9080 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9084 return hRes;
9087 /*************************************************************************
9088 * OLECONVERT_GetOle10PresData [Internal]
9090 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9092 * PARAMS
9093 * pStorage [I] Src IStroage
9094 * pOleStream [I] Dest OleStream Mem Struct
9096 * RETURNS
9097 * Nothing
9099 * NOTES
9100 * This function is used by OleConvertIStorageToOLESTREAM only.
9102 * Memory allocated for pData must be freed by the caller
9106 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9109 HRESULT hRes;
9110 IStream *pStream;
9111 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9113 /* Initialize Default data for OLESTREAM */
9114 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9115 pOleStreamData[0].dwTypeID = 2;
9116 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9117 pOleStreamData[1].dwTypeID = 0;
9118 pOleStreamData[0].dwMetaFileWidth = 0;
9119 pOleStreamData[0].dwMetaFileHeight = 0;
9120 pOleStreamData[0].pData = NULL;
9121 pOleStreamData[1].pData = NULL;
9123 /* Open Ole10Native Stream */
9124 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9125 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9126 if(hRes == S_OK)
9129 /* Read Size and Data */
9130 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9131 if(pOleStreamData->dwDataLength > 0)
9133 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9134 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9136 IStream_Release(pStream);
9142 /*************************************************************************
9143 * OLECONVERT_GetOle20PresData[Internal]
9145 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9147 * PARAMS
9148 * pStorage [I] Src IStroage
9149 * pOleStreamData [I] Dest OleStream Mem Struct
9151 * RETURNS
9152 * Nothing
9154 * NOTES
9155 * This function is used by OleConvertIStorageToOLESTREAM only.
9157 * Memory allocated for pData must be freed by the caller
9159 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9161 HRESULT hRes;
9162 IStream *pStream;
9163 OLECONVERT_ISTORAGE_OLEPRES olePress;
9164 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9166 /* Initialize Default data for OLESTREAM */
9167 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9168 pOleStreamData[0].dwTypeID = 2;
9169 pOleStreamData[0].dwMetaFileWidth = 0;
9170 pOleStreamData[0].dwMetaFileHeight = 0;
9171 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9172 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9173 pOleStreamData[1].dwTypeID = 0;
9174 pOleStreamData[1].dwOleTypeNameLength = 0;
9175 pOleStreamData[1].strOleTypeName[0] = 0;
9176 pOleStreamData[1].dwMetaFileWidth = 0;
9177 pOleStreamData[1].dwMetaFileHeight = 0;
9178 pOleStreamData[1].pData = NULL;
9179 pOleStreamData[1].dwDataLength = 0;
9182 /* Open OlePress000 stream */
9183 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9184 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9185 if(hRes == S_OK)
9187 LARGE_INTEGER iSeekPos;
9188 METAFILEPICT16 MetaFilePict;
9189 static const char strMetafilePictName[] = "METAFILEPICT";
9191 /* Set the TypeID for a Metafile */
9192 pOleStreamData[1].dwTypeID = 5;
9194 /* Set the OleTypeName to Metafile */
9195 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9196 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9198 iSeekPos.u.HighPart = 0;
9199 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9201 /* Get Presentation Data */
9202 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9203 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9204 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9205 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9207 /*Set width and Height */
9208 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9209 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9210 if(olePress.dwSize > 0)
9212 /* Set Length */
9213 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9215 /* Set MetaFilePict struct */
9216 MetaFilePict.mm = 8;
9217 MetaFilePict.xExt = olePress.dwExtentX;
9218 MetaFilePict.yExt = olePress.dwExtentY;
9219 MetaFilePict.hMF = 0;
9221 /* Get Metafile Data */
9222 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9223 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9224 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9226 IStream_Release(pStream);
9230 /*************************************************************************
9231 * OleConvertOLESTREAMToIStorage [OLE32.@]
9233 * Read info on MSDN
9235 * TODO
9236 * DVTARGETDEVICE parameter is not handled
9237 * Still unsure of some mem fields for OLE 10 Stream
9238 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9239 * and "\001OLE" streams
9242 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9243 LPOLESTREAM pOleStream,
9244 LPSTORAGE pstg,
9245 const DVTARGETDEVICE* ptd)
9247 int i;
9248 HRESULT hRes=S_OK;
9249 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9251 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9253 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9255 if(ptd != NULL)
9257 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9260 if(pstg == NULL || pOleStream == NULL)
9262 hRes = E_INVALIDARG;
9265 if(hRes == S_OK)
9267 /* Load the OLESTREAM to Memory */
9268 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9271 if(hRes == S_OK)
9273 /* Load the OLESTREAM to Memory (part 2)*/
9274 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9277 if(hRes == S_OK)
9280 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9282 /* Do we have the IStorage Data in the OLESTREAM */
9283 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9285 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9286 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9288 else
9290 /* It must be an original OLE 1.0 source */
9291 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9294 else
9296 /* It must be an original OLE 1.0 source */
9297 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9300 /* Create CompObj Stream if necessary */
9301 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9302 if(hRes == S_OK)
9304 /*Create the Ole Stream if necessary */
9305 OLECONVERT_CreateOleStream(pstg);
9310 /* Free allocated memory */
9311 for(i=0; i < 2; i++)
9313 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9314 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9315 pOleStreamData[i].pstrOleObjFileName = NULL;
9317 return hRes;
9320 /*************************************************************************
9321 * OleConvertIStorageToOLESTREAM [OLE32.@]
9323 * Read info on MSDN
9325 * Read info on MSDN
9327 * TODO
9328 * Still unsure of some mem fields for OLE 10 Stream
9329 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9330 * and "\001OLE" streams.
9333 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9334 LPSTORAGE pstg,
9335 LPOLESTREAM pOleStream)
9337 int i;
9338 HRESULT hRes = S_OK;
9339 IStream *pStream;
9340 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9341 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9343 TRACE("%p %p\n", pstg, pOleStream);
9345 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9347 if(pstg == NULL || pOleStream == NULL)
9349 hRes = E_INVALIDARG;
9351 if(hRes == S_OK)
9353 /* Get the ProgID */
9354 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9355 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9357 if(hRes == S_OK)
9359 /* Was it originally Ole10 */
9360 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9361 if(hRes == S_OK)
9363 IStream_Release(pStream);
9364 /* Get Presentation Data for Ole10Native */
9365 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9367 else
9369 /* Get Presentation Data (OLE20) */
9370 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9373 /* Save OLESTREAM */
9374 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9375 if(hRes == S_OK)
9377 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9382 /* Free allocated memory */
9383 for(i=0; i < 2; i++)
9385 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9388 return hRes;
9391 /***********************************************************************
9392 * GetConvertStg (OLE32.@)
9394 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9395 FIXME("unimplemented stub!\n");
9396 return E_FAIL;
9399 /******************************************************************************
9400 * StgIsStorageFile [OLE32.@]
9401 * Verify if the file contains a storage object
9403 * PARAMS
9404 * fn [ I] Filename
9406 * RETURNS
9407 * S_OK if file has magic bytes as a storage object
9408 * S_FALSE if file is not storage
9410 HRESULT WINAPI
9411 StgIsStorageFile(LPCOLESTR fn)
9413 HANDLE hf;
9414 BYTE magic[8];
9415 DWORD bytes_read;
9417 TRACE("%s\n", debugstr_w(fn));
9418 hf = CreateFileW(fn, GENERIC_READ,
9419 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9420 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9422 if (hf == INVALID_HANDLE_VALUE)
9423 return STG_E_FILENOTFOUND;
9425 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9427 WARN(" unable to read file\n");
9428 CloseHandle(hf);
9429 return S_FALSE;
9432 CloseHandle(hf);
9434 if (bytes_read != 8) {
9435 TRACE(" too short\n");
9436 return S_FALSE;
9439 if (!memcmp(magic,STORAGE_magic,8)) {
9440 TRACE(" -> YES\n");
9441 return S_OK;
9444 TRACE(" -> Invalid header.\n");
9445 return S_FALSE;
9448 /***********************************************************************
9449 * WriteClassStm (OLE32.@)
9451 * Writes a CLSID to a stream.
9453 * PARAMS
9454 * pStm [I] Stream to write to.
9455 * rclsid [I] CLSID to write.
9457 * RETURNS
9458 * Success: S_OK.
9459 * Failure: HRESULT code.
9461 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9463 TRACE("(%p,%p)\n",pStm,rclsid);
9465 if (!pStm || !rclsid)
9466 return E_INVALIDARG;
9468 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9471 /***********************************************************************
9472 * ReadClassStm (OLE32.@)
9474 * Reads a CLSID from a stream.
9476 * PARAMS
9477 * pStm [I] Stream to read from.
9478 * rclsid [O] CLSID to read.
9480 * RETURNS
9481 * Success: S_OK.
9482 * Failure: HRESULT code.
9484 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9486 ULONG nbByte;
9487 HRESULT res;
9489 TRACE("(%p,%p)\n",pStm,pclsid);
9491 if (!pStm || !pclsid)
9492 return E_INVALIDARG;
9494 /* clear the output args */
9495 *pclsid = CLSID_NULL;
9497 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9499 if (FAILED(res))
9500 return res;
9502 if (nbByte != sizeof(CLSID))
9503 return STG_E_READFAULT;
9504 else
9505 return S_OK;