dsound: Add eax properties
[wine/multimedia.git] / dlls / ole32 / storage32.c
blobf233b614974d12e359c86d6868fe48c0d9168661
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];
2743 /* Discard any existing data. */
2744 size.QuadPart = 0;
2745 ILockBytes_SetSize(This->lockBytes, size);
2748 * Initialize all header variables:
2749 * - The big block depot consists of one block and it is at block 0
2750 * - The directory table starts at block 1
2751 * - There is no small block depot
2753 memset( This->bigBlockDepotStart,
2754 BLOCK_UNUSED,
2755 sizeof(This->bigBlockDepotStart));
2757 This->bigBlockDepotCount = 1;
2758 This->bigBlockDepotStart[0] = 0;
2759 This->rootStartBlock = 1;
2760 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2761 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2762 if (sector_size == 4096)
2763 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2764 else
2765 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2766 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2767 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2768 This->extBigBlockDepotCount = 0;
2770 StorageImpl_SaveFileHeader(This);
2773 * Add one block for the big block depot and one block for the directory table
2775 size.u.HighPart = 0;
2776 size.u.LowPart = This->bigBlockSize * 3;
2777 ILockBytes_SetSize(This->lockBytes, size);
2780 * Initialize the big block depot
2782 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2783 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2784 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2785 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2787 else
2790 * Load the header for the file.
2792 hr = StorageImpl_LoadFileHeader(This);
2794 if (FAILED(hr))
2796 goto end;
2801 * There is no block depot cached yet.
2803 This->indexBlockDepotCached = 0xFFFFFFFF;
2804 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2807 * Start searching for free blocks with block 0.
2809 This->prevFreeBlock = 0;
2811 This->firstFreeSmallBlock = 0;
2813 /* Read the extended big block depot locations. */
2814 if (This->extBigBlockDepotCount != 0)
2816 ULONG current_block = This->extBigBlockDepotStart;
2817 ULONG cache_size = This->extBigBlockDepotCount * 2;
2818 int i;
2820 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2821 if (!This->extBigBlockDepotLocations)
2823 hr = E_OUTOFMEMORY;
2824 goto end;
2827 This->extBigBlockDepotLocationsSize = cache_size;
2829 for (i=0; i<This->extBigBlockDepotCount; i++)
2831 if (current_block == BLOCK_END_OF_CHAIN)
2833 WARN("File has too few extended big block depot blocks.\n");
2834 hr = STG_E_DOCFILECORRUPT;
2835 goto end;
2837 This->extBigBlockDepotLocations[i] = current_block;
2838 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2841 else
2843 This->extBigBlockDepotLocations = NULL;
2844 This->extBigBlockDepotLocationsSize = 0;
2848 * Create the block chain abstractions.
2850 if(!(This->rootBlockChain =
2851 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2853 hr = STG_E_READFAULT;
2854 goto end;
2857 if(!(This->smallBlockDepotChain =
2858 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2859 DIRENTRY_NULL)))
2861 hr = STG_E_READFAULT;
2862 goto end;
2866 * Write the root storage entry (memory only)
2868 if (create)
2870 DirEntry rootEntry;
2872 * Initialize the directory table
2874 memset(&rootEntry, 0, sizeof(rootEntry));
2875 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2876 sizeof(rootEntry.name)/sizeof(WCHAR) );
2877 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2878 rootEntry.stgType = STGTY_ROOT;
2879 rootEntry.leftChild = DIRENTRY_NULL;
2880 rootEntry.rightChild = DIRENTRY_NULL;
2881 rootEntry.dirRootEntry = DIRENTRY_NULL;
2882 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2883 rootEntry.size.u.HighPart = 0;
2884 rootEntry.size.u.LowPart = 0;
2886 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2890 * Find the ID of the root storage.
2892 currentEntryRef = 0;
2896 hr = StorageImpl_ReadDirEntry(
2897 This,
2898 currentEntryRef,
2899 &currentEntry);
2901 if (SUCCEEDED(hr))
2903 if ( (currentEntry.sizeOfNameString != 0 ) &&
2904 (currentEntry.stgType == STGTY_ROOT) )
2906 This->base.storageDirEntry = currentEntryRef;
2910 currentEntryRef++;
2912 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2914 if (FAILED(hr))
2916 hr = STG_E_READFAULT;
2917 goto end;
2921 * Create the block chain abstraction for the small block root chain.
2923 if(!(This->smallBlockRootChain =
2924 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2926 hr = STG_E_READFAULT;
2929 end:
2930 if (FAILED(hr))
2932 IStorage_Release((IStorage*)This);
2933 *result = NULL;
2935 else
2937 StorageImpl_Flush((StorageBaseImpl*)This);
2938 *result = This;
2941 return hr;
2944 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2946 StorageImpl *This = (StorageImpl*) iface;
2948 StorageBaseImpl_DeleteAll(&This->base);
2950 This->base.reverted = 1;
2953 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2955 StorageImpl *This = (StorageImpl*) iface;
2956 int i;
2957 TRACE("(%p)\n", This);
2959 StorageImpl_Flush(iface);
2961 StorageImpl_Invalidate(iface);
2963 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
2965 BlockChainStream_Destroy(This->smallBlockRootChain);
2966 BlockChainStream_Destroy(This->rootBlockChain);
2967 BlockChainStream_Destroy(This->smallBlockDepotChain);
2969 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2970 BlockChainStream_Destroy(This->blockChainCache[i]);
2972 if (This->lockBytes)
2973 ILockBytes_Release(This->lockBytes);
2974 HeapFree(GetProcessHeap(), 0, This);
2977 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2979 StorageImpl *This = (StorageImpl*) iface;
2980 int i;
2981 HRESULT hr;
2982 TRACE("(%p)\n", This);
2984 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2986 if (SUCCEEDED(hr))
2987 hr = BlockChainStream_Flush(This->rootBlockChain);
2989 if (SUCCEEDED(hr))
2990 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2992 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
2993 if (This->blockChainCache[i])
2994 hr = BlockChainStream_Flush(This->blockChainCache[i]);
2996 if (SUCCEEDED(hr))
2997 hr = ILockBytes_Flush(This->lockBytes);
2999 return hr;
3002 /******************************************************************************
3003 * Storage32Impl_GetNextFreeBigBlock
3005 * Returns the index of the next free big block.
3006 * If the big block depot is filled, this method will enlarge it.
3009 static ULONG StorageImpl_GetNextFreeBigBlock(
3010 StorageImpl* This)
3012 ULONG depotBlockIndexPos;
3013 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3014 BOOL success;
3015 ULONG depotBlockOffset;
3016 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3017 ULONG nextBlockIndex = BLOCK_SPECIAL;
3018 int depotIndex = 0;
3019 ULONG freeBlock = BLOCK_UNUSED;
3020 ULARGE_INTEGER neededSize;
3021 STATSTG statstg;
3023 depotIndex = This->prevFreeBlock / blocksPerDepot;
3024 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3027 * Scan the entire big block depot until we find a block marked free
3029 while (nextBlockIndex != BLOCK_UNUSED)
3031 if (depotIndex < COUNT_BBDEPOTINHEADER)
3033 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3036 * Grow the primary depot.
3038 if (depotBlockIndexPos == BLOCK_UNUSED)
3040 depotBlockIndexPos = depotIndex*blocksPerDepot;
3043 * Add a block depot.
3045 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3046 This->bigBlockDepotCount++;
3047 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3050 * Flag it as a block depot.
3052 StorageImpl_SetNextBlockInChain(This,
3053 depotBlockIndexPos,
3054 BLOCK_SPECIAL);
3056 /* Save new header information.
3058 StorageImpl_SaveFileHeader(This);
3061 else
3063 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3065 if (depotBlockIndexPos == BLOCK_UNUSED)
3068 * Grow the extended depot.
3070 ULONG extIndex = BLOCK_UNUSED;
3071 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3072 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3074 if (extBlockOffset == 0)
3076 /* We need an extended block.
3078 extIndex = Storage32Impl_AddExtBlockDepot(This);
3079 This->extBigBlockDepotCount++;
3080 depotBlockIndexPos = extIndex + 1;
3082 else
3083 depotBlockIndexPos = depotIndex * blocksPerDepot;
3086 * Add a block depot and mark it in the extended block.
3088 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3089 This->bigBlockDepotCount++;
3090 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3092 /* Flag the block depot.
3094 StorageImpl_SetNextBlockInChain(This,
3095 depotBlockIndexPos,
3096 BLOCK_SPECIAL);
3098 /* If necessary, flag the extended depot block.
3100 if (extIndex != BLOCK_UNUSED)
3101 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3103 /* Save header information.
3105 StorageImpl_SaveFileHeader(This);
3109 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3111 if (success)
3113 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3114 ( nextBlockIndex != BLOCK_UNUSED))
3116 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3118 if (nextBlockIndex == BLOCK_UNUSED)
3120 freeBlock = (depotIndex * blocksPerDepot) +
3121 (depotBlockOffset/sizeof(ULONG));
3124 depotBlockOffset += sizeof(ULONG);
3128 depotIndex++;
3129 depotBlockOffset = 0;
3133 * make sure that the block physically exists before using it
3135 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3137 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3139 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3140 ILockBytes_SetSize(This->lockBytes, neededSize);
3142 This->prevFreeBlock = freeBlock;
3144 return freeBlock;
3147 /******************************************************************************
3148 * Storage32Impl_AddBlockDepot
3150 * This will create a depot block, essentially it is a block initialized
3151 * to BLOCK_UNUSEDs.
3153 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3155 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3158 * Initialize blocks as free
3160 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3161 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3164 /******************************************************************************
3165 * Storage32Impl_GetExtDepotBlock
3167 * Returns the index of the block that corresponds to the specified depot
3168 * index. This method is only for depot indexes equal or greater than
3169 * COUNT_BBDEPOTINHEADER.
3171 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3173 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3174 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3175 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3176 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3177 ULONG blockIndex = BLOCK_UNUSED;
3178 ULONG extBlockIndex;
3179 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3180 int index, num_blocks;
3182 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3184 if (extBlockCount >= This->extBigBlockDepotCount)
3185 return BLOCK_UNUSED;
3187 if (This->indexExtBlockDepotCached != extBlockCount)
3189 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3191 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);
3193 num_blocks = This->bigBlockSize / 4;
3195 for (index = 0; index < num_blocks; index++)
3197 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3198 This->extBlockDepotCached[index] = blockIndex;
3201 This->indexExtBlockDepotCached = extBlockCount;
3204 blockIndex = This->extBlockDepotCached[extBlockOffset];
3206 return blockIndex;
3209 /******************************************************************************
3210 * Storage32Impl_SetExtDepotBlock
3212 * Associates the specified block index to the specified depot index.
3213 * This method is only for depot indexes equal or greater than
3214 * COUNT_BBDEPOTINHEADER.
3216 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3218 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3219 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3220 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3221 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3222 ULONG extBlockIndex;
3224 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3226 assert(extBlockCount < This->extBigBlockDepotCount);
3228 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3230 if (extBlockIndex != BLOCK_UNUSED)
3232 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3233 extBlockOffset * sizeof(ULONG),
3234 blockIndex);
3237 if (This->indexExtBlockDepotCached == extBlockCount)
3239 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3243 /******************************************************************************
3244 * Storage32Impl_AddExtBlockDepot
3246 * Creates an extended depot block.
3248 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3250 ULONG numExtBlocks = This->extBigBlockDepotCount;
3251 ULONG nextExtBlock = This->extBigBlockDepotStart;
3252 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3253 ULONG index = BLOCK_UNUSED;
3254 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3255 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3256 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3258 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3259 blocksPerDepotBlock;
3261 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3264 * The first extended block.
3266 This->extBigBlockDepotStart = index;
3268 else
3271 * Find the last existing extended block.
3273 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3276 * Add the new extended block to the chain.
3278 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3279 index);
3283 * Initialize this block.
3285 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3286 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3288 /* Add the block to our cache. */
3289 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3291 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3292 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3294 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3295 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3297 This->extBigBlockDepotLocations = new_cache;
3298 This->extBigBlockDepotLocationsSize = new_cache_size;
3300 This->extBigBlockDepotLocations[numExtBlocks] = index;
3302 return index;
3305 /******************************************************************************
3306 * Storage32Impl_FreeBigBlock
3308 * This method will flag the specified block as free in the big block depot.
3310 static void StorageImpl_FreeBigBlock(
3311 StorageImpl* This,
3312 ULONG blockIndex)
3314 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3316 if (blockIndex < This->prevFreeBlock)
3317 This->prevFreeBlock = blockIndex;
3320 /************************************************************************
3321 * Storage32Impl_GetNextBlockInChain
3323 * This method will retrieve the block index of the next big block in
3324 * in the chain.
3326 * Params: This - Pointer to the Storage object.
3327 * blockIndex - Index of the block to retrieve the chain
3328 * for.
3329 * nextBlockIndex - receives the return value.
3331 * Returns: This method returns the index of the next block in the chain.
3332 * It will return the constants:
3333 * BLOCK_SPECIAL - If the block given was not part of a
3334 * chain.
3335 * BLOCK_END_OF_CHAIN - If the block given was the last in
3336 * a chain.
3337 * BLOCK_UNUSED - If the block given was not past of a chain
3338 * and is available.
3339 * BLOCK_EXTBBDEPOT - This block is part of the extended
3340 * big block depot.
3342 * See Windows documentation for more details on IStorage methods.
3344 static HRESULT StorageImpl_GetNextBlockInChain(
3345 StorageImpl* This,
3346 ULONG blockIndex,
3347 ULONG* nextBlockIndex)
3349 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3350 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3351 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3352 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3353 BOOL success;
3354 ULONG depotBlockIndexPos;
3355 int index, num_blocks;
3357 *nextBlockIndex = BLOCK_SPECIAL;
3359 if(depotBlockCount >= This->bigBlockDepotCount)
3361 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3362 This->bigBlockDepotCount);
3363 return STG_E_READFAULT;
3367 * Cache the currently accessed depot block.
3369 if (depotBlockCount != This->indexBlockDepotCached)
3371 This->indexBlockDepotCached = depotBlockCount;
3373 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3375 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3377 else
3380 * We have to look in the extended depot.
3382 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3385 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3387 if (!success)
3388 return STG_E_READFAULT;
3390 num_blocks = This->bigBlockSize / 4;
3392 for (index = 0; index < num_blocks; index++)
3394 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3395 This->blockDepotCached[index] = *nextBlockIndex;
3399 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3401 return S_OK;
3404 /******************************************************************************
3405 * Storage32Impl_GetNextExtendedBlock
3407 * Given an extended block this method will return the next extended block.
3409 * NOTES:
3410 * The last ULONG of an extended block is the block index of the next
3411 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3412 * depot.
3414 * Return values:
3415 * - The index of the next extended block
3416 * - BLOCK_UNUSED: there is no next extended block.
3417 * - Any other return values denotes failure.
3419 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3421 ULONG nextBlockIndex = BLOCK_SPECIAL;
3422 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3424 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3425 &nextBlockIndex);
3427 return nextBlockIndex;
3430 /******************************************************************************
3431 * Storage32Impl_SetNextBlockInChain
3433 * This method will write the index of the specified block's next block
3434 * in the big block depot.
3436 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3437 * do the following
3439 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3440 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3441 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3444 static void StorageImpl_SetNextBlockInChain(
3445 StorageImpl* This,
3446 ULONG blockIndex,
3447 ULONG nextBlock)
3449 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3450 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3451 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3452 ULONG depotBlockIndexPos;
3454 assert(depotBlockCount < This->bigBlockDepotCount);
3455 assert(blockIndex != nextBlock);
3457 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3459 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3461 else
3464 * We have to look in the extended depot.
3466 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3469 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3470 nextBlock);
3472 * Update the cached block depot, if necessary.
3474 if (depotBlockCount == This->indexBlockDepotCached)
3476 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3480 /******************************************************************************
3481 * Storage32Impl_LoadFileHeader
3483 * This method will read in the file header
3485 static HRESULT StorageImpl_LoadFileHeader(
3486 StorageImpl* This)
3488 HRESULT hr;
3489 BYTE headerBigBlock[HEADER_SIZE];
3490 int index;
3491 ULARGE_INTEGER offset;
3492 DWORD bytes_read;
3494 TRACE("\n");
3496 * Get a pointer to the big block of data containing the header.
3498 offset.u.HighPart = 0;
3499 offset.u.LowPart = 0;
3500 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3501 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3502 hr = STG_E_FILENOTFOUND;
3505 * Extract the information from the header.
3507 if (SUCCEEDED(hr))
3510 * Check for the "magic number" signature and return an error if it is not
3511 * found.
3513 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3515 return STG_E_OLDFORMAT;
3518 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3520 return STG_E_INVALIDHEADER;
3523 StorageUtl_ReadWord(
3524 headerBigBlock,
3525 OFFSET_BIGBLOCKSIZEBITS,
3526 &This->bigBlockSizeBits);
3528 StorageUtl_ReadWord(
3529 headerBigBlock,
3530 OFFSET_SMALLBLOCKSIZEBITS,
3531 &This->smallBlockSizeBits);
3533 StorageUtl_ReadDWord(
3534 headerBigBlock,
3535 OFFSET_BBDEPOTCOUNT,
3536 &This->bigBlockDepotCount);
3538 StorageUtl_ReadDWord(
3539 headerBigBlock,
3540 OFFSET_ROOTSTARTBLOCK,
3541 &This->rootStartBlock);
3543 StorageUtl_ReadDWord(
3544 headerBigBlock,
3545 OFFSET_SMALLBLOCKLIMIT,
3546 &This->smallBlockLimit);
3548 StorageUtl_ReadDWord(
3549 headerBigBlock,
3550 OFFSET_SBDEPOTSTART,
3551 &This->smallBlockDepotStart);
3553 StorageUtl_ReadDWord(
3554 headerBigBlock,
3555 OFFSET_EXTBBDEPOTSTART,
3556 &This->extBigBlockDepotStart);
3558 StorageUtl_ReadDWord(
3559 headerBigBlock,
3560 OFFSET_EXTBBDEPOTCOUNT,
3561 &This->extBigBlockDepotCount);
3563 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3565 StorageUtl_ReadDWord(
3566 headerBigBlock,
3567 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3568 &(This->bigBlockDepotStart[index]));
3572 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3574 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3575 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3578 * Right now, the code is making some assumptions about the size of the
3579 * blocks, just make sure they are what we're expecting.
3581 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3582 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3583 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3585 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3586 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3587 hr = STG_E_INVALIDHEADER;
3589 else
3590 hr = S_OK;
3593 return hr;
3596 /******************************************************************************
3597 * Storage32Impl_SaveFileHeader
3599 * This method will save to the file the header
3601 static void StorageImpl_SaveFileHeader(
3602 StorageImpl* This)
3604 BYTE headerBigBlock[HEADER_SIZE];
3605 int index;
3606 HRESULT hr;
3607 ULARGE_INTEGER offset;
3608 DWORD bytes_read, bytes_written;
3609 DWORD major_version, dirsectorcount;
3612 * Get a pointer to the big block of data containing the header.
3614 offset.u.HighPart = 0;
3615 offset.u.LowPart = 0;
3616 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3617 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3618 hr = STG_E_FILENOTFOUND;
3620 if (This->bigBlockSizeBits == 0x9)
3621 major_version = 3;
3622 else if (This->bigBlockSizeBits == 0xc)
3623 major_version = 4;
3624 else
3626 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3627 major_version = 4;
3631 * If the block read failed, the file is probably new.
3633 if (FAILED(hr))
3636 * Initialize for all unknown fields.
3638 memset(headerBigBlock, 0, HEADER_SIZE);
3641 * Initialize the magic number.
3643 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3647 * Write the information to the header.
3649 StorageUtl_WriteWord(
3650 headerBigBlock,
3651 OFFSET_MINORVERSION,
3652 0x3e);
3654 StorageUtl_WriteWord(
3655 headerBigBlock,
3656 OFFSET_MAJORVERSION,
3657 major_version);
3659 StorageUtl_WriteWord(
3660 headerBigBlock,
3661 OFFSET_BYTEORDERMARKER,
3662 (WORD)-2);
3664 StorageUtl_WriteWord(
3665 headerBigBlock,
3666 OFFSET_BIGBLOCKSIZEBITS,
3667 This->bigBlockSizeBits);
3669 StorageUtl_WriteWord(
3670 headerBigBlock,
3671 OFFSET_SMALLBLOCKSIZEBITS,
3672 This->smallBlockSizeBits);
3674 if (major_version >= 4)
3676 if (This->rootBlockChain)
3677 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3678 else
3679 /* This file is being created, and it will start out with one block. */
3680 dirsectorcount = 1;
3682 else
3683 /* This field must be 0 in versions older than 4 */
3684 dirsectorcount = 0;
3686 StorageUtl_WriteDWord(
3687 headerBigBlock,
3688 OFFSET_DIRSECTORCOUNT,
3689 dirsectorcount);
3691 StorageUtl_WriteDWord(
3692 headerBigBlock,
3693 OFFSET_BBDEPOTCOUNT,
3694 This->bigBlockDepotCount);
3696 StorageUtl_WriteDWord(
3697 headerBigBlock,
3698 OFFSET_ROOTSTARTBLOCK,
3699 This->rootStartBlock);
3701 StorageUtl_WriteDWord(
3702 headerBigBlock,
3703 OFFSET_SMALLBLOCKLIMIT,
3704 This->smallBlockLimit);
3706 StorageUtl_WriteDWord(
3707 headerBigBlock,
3708 OFFSET_SBDEPOTSTART,
3709 This->smallBlockDepotStart);
3711 StorageUtl_WriteDWord(
3712 headerBigBlock,
3713 OFFSET_SBDEPOTCOUNT,
3714 This->smallBlockDepotChain ?
3715 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3717 StorageUtl_WriteDWord(
3718 headerBigBlock,
3719 OFFSET_EXTBBDEPOTSTART,
3720 This->extBigBlockDepotStart);
3722 StorageUtl_WriteDWord(
3723 headerBigBlock,
3724 OFFSET_EXTBBDEPOTCOUNT,
3725 This->extBigBlockDepotCount);
3727 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3729 StorageUtl_WriteDWord(
3730 headerBigBlock,
3731 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3732 (This->bigBlockDepotStart[index]));
3736 * Write the big block back to the file.
3738 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3741 /******************************************************************************
3742 * StorageImpl_ReadRawDirEntry
3744 * This method will read the raw data from a directory entry in the file.
3746 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3748 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3750 ULARGE_INTEGER offset;
3751 HRESULT hr;
3752 ULONG bytesRead;
3754 offset.u.HighPart = 0;
3755 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3757 hr = BlockChainStream_ReadAt(
3758 This->rootBlockChain,
3759 offset,
3760 RAW_DIRENTRY_SIZE,
3761 buffer,
3762 &bytesRead);
3764 if (bytesRead != RAW_DIRENTRY_SIZE)
3765 return STG_E_READFAULT;
3767 return hr;
3770 /******************************************************************************
3771 * StorageImpl_WriteRawDirEntry
3773 * This method will write the raw data from a directory entry in the file.
3775 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3777 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3779 ULARGE_INTEGER offset;
3780 HRESULT hr;
3781 ULONG bytesRead;
3783 offset.u.HighPart = 0;
3784 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3786 hr = BlockChainStream_WriteAt(
3787 This->rootBlockChain,
3788 offset,
3789 RAW_DIRENTRY_SIZE,
3790 buffer,
3791 &bytesRead);
3793 return hr;
3796 /******************************************************************************
3797 * UpdateRawDirEntry
3799 * Update raw directory entry data from the fields in newData.
3801 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3803 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3805 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3807 memcpy(
3808 buffer + OFFSET_PS_NAME,
3809 newData->name,
3810 DIRENTRY_NAME_BUFFER_LEN );
3812 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3814 StorageUtl_WriteWord(
3815 buffer,
3816 OFFSET_PS_NAMELENGTH,
3817 newData->sizeOfNameString);
3819 StorageUtl_WriteDWord(
3820 buffer,
3821 OFFSET_PS_LEFTCHILD,
3822 newData->leftChild);
3824 StorageUtl_WriteDWord(
3825 buffer,
3826 OFFSET_PS_RIGHTCHILD,
3827 newData->rightChild);
3829 StorageUtl_WriteDWord(
3830 buffer,
3831 OFFSET_PS_DIRROOT,
3832 newData->dirRootEntry);
3834 StorageUtl_WriteGUID(
3835 buffer,
3836 OFFSET_PS_GUID,
3837 &newData->clsid);
3839 StorageUtl_WriteDWord(
3840 buffer,
3841 OFFSET_PS_CTIMELOW,
3842 newData->ctime.dwLowDateTime);
3844 StorageUtl_WriteDWord(
3845 buffer,
3846 OFFSET_PS_CTIMEHIGH,
3847 newData->ctime.dwHighDateTime);
3849 StorageUtl_WriteDWord(
3850 buffer,
3851 OFFSET_PS_MTIMELOW,
3852 newData->mtime.dwLowDateTime);
3854 StorageUtl_WriteDWord(
3855 buffer,
3856 OFFSET_PS_MTIMEHIGH,
3857 newData->ctime.dwHighDateTime);
3859 StorageUtl_WriteDWord(
3860 buffer,
3861 OFFSET_PS_STARTBLOCK,
3862 newData->startingBlock);
3864 StorageUtl_WriteDWord(
3865 buffer,
3866 OFFSET_PS_SIZE,
3867 newData->size.u.LowPart);
3870 /******************************************************************************
3871 * Storage32Impl_ReadDirEntry
3873 * This method will read the specified directory entry.
3875 HRESULT StorageImpl_ReadDirEntry(
3876 StorageImpl* This,
3877 DirRef index,
3878 DirEntry* buffer)
3880 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3881 HRESULT readRes;
3883 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3885 if (SUCCEEDED(readRes))
3887 memset(buffer->name, 0, sizeof(buffer->name));
3888 memcpy(
3889 buffer->name,
3890 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3891 DIRENTRY_NAME_BUFFER_LEN );
3892 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3894 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3896 StorageUtl_ReadWord(
3897 currentEntry,
3898 OFFSET_PS_NAMELENGTH,
3899 &buffer->sizeOfNameString);
3901 StorageUtl_ReadDWord(
3902 currentEntry,
3903 OFFSET_PS_LEFTCHILD,
3904 &buffer->leftChild);
3906 StorageUtl_ReadDWord(
3907 currentEntry,
3908 OFFSET_PS_RIGHTCHILD,
3909 &buffer->rightChild);
3911 StorageUtl_ReadDWord(
3912 currentEntry,
3913 OFFSET_PS_DIRROOT,
3914 &buffer->dirRootEntry);
3916 StorageUtl_ReadGUID(
3917 currentEntry,
3918 OFFSET_PS_GUID,
3919 &buffer->clsid);
3921 StorageUtl_ReadDWord(
3922 currentEntry,
3923 OFFSET_PS_CTIMELOW,
3924 &buffer->ctime.dwLowDateTime);
3926 StorageUtl_ReadDWord(
3927 currentEntry,
3928 OFFSET_PS_CTIMEHIGH,
3929 &buffer->ctime.dwHighDateTime);
3931 StorageUtl_ReadDWord(
3932 currentEntry,
3933 OFFSET_PS_MTIMELOW,
3934 &buffer->mtime.dwLowDateTime);
3936 StorageUtl_ReadDWord(
3937 currentEntry,
3938 OFFSET_PS_MTIMEHIGH,
3939 &buffer->mtime.dwHighDateTime);
3941 StorageUtl_ReadDWord(
3942 currentEntry,
3943 OFFSET_PS_STARTBLOCK,
3944 &buffer->startingBlock);
3946 StorageUtl_ReadDWord(
3947 currentEntry,
3948 OFFSET_PS_SIZE,
3949 &buffer->size.u.LowPart);
3951 buffer->size.u.HighPart = 0;
3954 return readRes;
3957 /*********************************************************************
3958 * Write the specified directory entry to the file
3960 HRESULT StorageImpl_WriteDirEntry(
3961 StorageImpl* This,
3962 DirRef index,
3963 const DirEntry* buffer)
3965 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3966 HRESULT writeRes;
3968 UpdateRawDirEntry(currentEntry, buffer);
3970 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3971 return writeRes;
3974 static BOOL StorageImpl_ReadBigBlock(
3975 StorageImpl* This,
3976 ULONG blockIndex,
3977 void* buffer)
3979 ULARGE_INTEGER ulOffset;
3980 DWORD read=0;
3982 ulOffset.u.HighPart = 0;
3983 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3985 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3987 if (read && read < This->bigBlockSize)
3989 /* File ends during this block; fill the rest with 0's. */
3990 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3993 return (read != 0);
3996 static BOOL StorageImpl_ReadDWordFromBigBlock(
3997 StorageImpl* This,
3998 ULONG blockIndex,
3999 ULONG offset,
4000 DWORD* value)
4002 ULARGE_INTEGER ulOffset;
4003 DWORD read;
4004 DWORD tmp;
4006 ulOffset.u.HighPart = 0;
4007 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4008 ulOffset.u.LowPart += offset;
4010 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4011 *value = lendian32toh(tmp);
4012 return (read == sizeof(DWORD));
4015 static BOOL StorageImpl_WriteBigBlock(
4016 StorageImpl* This,
4017 ULONG blockIndex,
4018 const void* buffer)
4020 ULARGE_INTEGER ulOffset;
4021 DWORD wrote;
4023 ulOffset.u.HighPart = 0;
4024 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4026 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4027 return (wrote == This->bigBlockSize);
4030 static BOOL StorageImpl_WriteDWordToBigBlock(
4031 StorageImpl* This,
4032 ULONG blockIndex,
4033 ULONG offset,
4034 DWORD value)
4036 ULARGE_INTEGER ulOffset;
4037 DWORD wrote;
4039 ulOffset.u.HighPart = 0;
4040 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4041 ulOffset.u.LowPart += offset;
4043 value = htole32(value);
4044 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4045 return (wrote == sizeof(DWORD));
4048 /******************************************************************************
4049 * Storage32Impl_SmallBlocksToBigBlocks
4051 * This method will convert a small block chain to a big block chain.
4052 * The small block chain will be destroyed.
4054 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4055 StorageImpl* This,
4056 SmallBlockChainStream** ppsbChain)
4058 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4059 ULARGE_INTEGER size, offset;
4060 ULONG cbRead, cbWritten;
4061 ULARGE_INTEGER cbTotalRead;
4062 DirRef streamEntryRef;
4063 HRESULT resWrite = S_OK;
4064 HRESULT resRead;
4065 DirEntry streamEntry;
4066 BYTE *buffer;
4067 BlockChainStream *bbTempChain = NULL;
4068 BlockChainStream *bigBlockChain = NULL;
4071 * Create a temporary big block chain that doesn't have
4072 * an associated directory entry. This temporary chain will be
4073 * used to copy data from small blocks to big blocks.
4075 bbTempChain = BlockChainStream_Construct(This,
4076 &bbHeadOfChain,
4077 DIRENTRY_NULL);
4078 if(!bbTempChain) return NULL;
4080 * Grow the big block chain.
4082 size = SmallBlockChainStream_GetSize(*ppsbChain);
4083 BlockChainStream_SetSize(bbTempChain, size);
4086 * Copy the contents of the small block chain to the big block chain
4087 * by small block size increments.
4089 offset.u.LowPart = 0;
4090 offset.u.HighPart = 0;
4091 cbTotalRead.QuadPart = 0;
4093 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4096 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4097 offset,
4098 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4099 buffer,
4100 &cbRead);
4101 if (FAILED(resRead))
4102 break;
4104 if (cbRead > 0)
4106 cbTotalRead.QuadPart += cbRead;
4108 resWrite = BlockChainStream_WriteAt(bbTempChain,
4109 offset,
4110 cbRead,
4111 buffer,
4112 &cbWritten);
4114 if (FAILED(resWrite))
4115 break;
4117 offset.u.LowPart += cbRead;
4119 else
4121 resRead = STG_E_READFAULT;
4122 break;
4124 } while (cbTotalRead.QuadPart < size.QuadPart);
4125 HeapFree(GetProcessHeap(),0,buffer);
4127 size.u.HighPart = 0;
4128 size.u.LowPart = 0;
4130 if (FAILED(resRead) || FAILED(resWrite))
4132 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4133 BlockChainStream_SetSize(bbTempChain, size);
4134 BlockChainStream_Destroy(bbTempChain);
4135 return NULL;
4139 * Destroy the small block chain.
4141 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4142 SmallBlockChainStream_SetSize(*ppsbChain, size);
4143 SmallBlockChainStream_Destroy(*ppsbChain);
4144 *ppsbChain = 0;
4147 * Change the directory entry. This chain is now a big block chain
4148 * and it doesn't reside in the small blocks chain anymore.
4150 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4152 streamEntry.startingBlock = bbHeadOfChain;
4154 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4157 * Destroy the temporary entryless big block chain.
4158 * Create a new big block chain associated with this entry.
4160 BlockChainStream_Destroy(bbTempChain);
4161 bigBlockChain = BlockChainStream_Construct(This,
4162 NULL,
4163 streamEntryRef);
4165 return bigBlockChain;
4168 /******************************************************************************
4169 * Storage32Impl_BigBlocksToSmallBlocks
4171 * This method will convert a big block chain to a small block chain.
4172 * The big block chain will be destroyed on success.
4174 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4175 StorageImpl* This,
4176 BlockChainStream** ppbbChain,
4177 ULARGE_INTEGER newSize)
4179 ULARGE_INTEGER size, offset, cbTotalRead;
4180 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4181 DirRef streamEntryRef;
4182 HRESULT resWrite = S_OK, resRead = S_OK;
4183 DirEntry streamEntry;
4184 BYTE* buffer;
4185 SmallBlockChainStream* sbTempChain;
4187 TRACE("%p %p\n", This, ppbbChain);
4189 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4190 DIRENTRY_NULL);
4192 if(!sbTempChain)
4193 return NULL;
4195 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4196 size = BlockChainStream_GetSize(*ppbbChain);
4197 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4199 offset.u.HighPart = 0;
4200 offset.u.LowPart = 0;
4201 cbTotalRead.QuadPart = 0;
4202 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4203 while(cbTotalRead.QuadPart < size.QuadPart)
4205 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4206 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4207 buffer, &cbRead);
4209 if(FAILED(resRead))
4210 break;
4212 if(cbRead > 0)
4214 cbTotalRead.QuadPart += cbRead;
4216 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4217 cbRead, buffer, &cbWritten);
4219 if(FAILED(resWrite))
4220 break;
4222 offset.u.LowPart += cbRead;
4224 else
4226 resRead = STG_E_READFAULT;
4227 break;
4230 HeapFree(GetProcessHeap(), 0, buffer);
4232 size.u.HighPart = 0;
4233 size.u.LowPart = 0;
4235 if(FAILED(resRead) || FAILED(resWrite))
4237 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4238 SmallBlockChainStream_SetSize(sbTempChain, size);
4239 SmallBlockChainStream_Destroy(sbTempChain);
4240 return NULL;
4243 /* destroy the original big block chain */
4244 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4245 BlockChainStream_SetSize(*ppbbChain, size);
4246 BlockChainStream_Destroy(*ppbbChain);
4247 *ppbbChain = NULL;
4249 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4250 streamEntry.startingBlock = sbHeadOfChain;
4251 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4253 SmallBlockChainStream_Destroy(sbTempChain);
4254 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4257 static HRESULT StorageBaseImpl_CopyStream(
4258 StorageBaseImpl *dst, DirRef dst_entry,
4259 StorageBaseImpl *src, DirRef src_entry)
4261 HRESULT hr;
4262 BYTE data[4096];
4263 DirEntry srcdata;
4264 ULARGE_INTEGER bytes_copied;
4265 ULONG bytestocopy, bytesread, byteswritten;
4267 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4269 if (SUCCEEDED(hr))
4271 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4273 bytes_copied.QuadPart = 0;
4274 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4276 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4278 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4279 data, &bytesread);
4280 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4282 if (SUCCEEDED(hr))
4283 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4284 data, &byteswritten);
4285 if (SUCCEEDED(hr))
4287 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4288 bytes_copied.QuadPart += byteswritten;
4293 return hr;
4296 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4298 DirRef result=This->firstFreeEntry;
4300 while (result < This->entries_size && This->entries[result].inuse)
4301 result++;
4303 if (result == This->entries_size)
4305 ULONG new_size = This->entries_size * 2;
4306 TransactedDirEntry *new_entries;
4308 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4309 if (!new_entries) return DIRENTRY_NULL;
4311 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4312 HeapFree(GetProcessHeap(), 0, This->entries);
4314 This->entries = new_entries;
4315 This->entries_size = new_size;
4318 This->entries[result].inuse = 1;
4320 This->firstFreeEntry = result+1;
4322 return result;
4325 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4326 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4328 DirRef stubEntryRef;
4329 TransactedDirEntry *entry;
4331 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4333 if (stubEntryRef != DIRENTRY_NULL)
4335 entry = &This->entries[stubEntryRef];
4337 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4339 entry->read = 0;
4342 return stubEntryRef;
4345 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4346 TransactedSnapshotImpl *This, DirRef entry)
4348 HRESULT hr=S_OK;
4349 DirEntry data;
4351 if (!This->entries[entry].read)
4353 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4354 This->entries[entry].transactedParentEntry,
4355 &data);
4357 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4359 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4361 if (data.leftChild == DIRENTRY_NULL)
4362 hr = E_OUTOFMEMORY;
4365 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4367 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4369 if (data.rightChild == DIRENTRY_NULL)
4370 hr = E_OUTOFMEMORY;
4373 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4375 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4377 if (data.dirRootEntry == DIRENTRY_NULL)
4378 hr = E_OUTOFMEMORY;
4381 if (SUCCEEDED(hr))
4383 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4384 This->entries[entry].read = 1;
4388 return hr;
4391 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4392 TransactedSnapshotImpl *This, DirRef entry)
4394 HRESULT hr = S_OK;
4396 if (!This->entries[entry].stream_dirty)
4398 DirEntry new_entrydata;
4400 memset(&new_entrydata, 0, sizeof(DirEntry));
4401 new_entrydata.name[0] = 'S';
4402 new_entrydata.sizeOfNameString = 1;
4403 new_entrydata.stgType = STGTY_STREAM;
4404 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4405 new_entrydata.leftChild = DIRENTRY_NULL;
4406 new_entrydata.rightChild = DIRENTRY_NULL;
4407 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4409 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4410 &This->entries[entry].stream_entry);
4412 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4414 hr = StorageBaseImpl_CopyStream(
4415 This->scratch, This->entries[entry].stream_entry,
4416 This->transactedParent, This->entries[entry].transactedParentEntry);
4418 if (FAILED(hr))
4419 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4422 if (SUCCEEDED(hr))
4423 This->entries[entry].stream_dirty = 1;
4425 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4427 /* Since this entry is modified, and we aren't using its stream data, we
4428 * no longer care about the original entry. */
4429 DirRef delete_ref;
4430 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4432 if (delete_ref != DIRENTRY_NULL)
4433 This->entries[delete_ref].deleted = 1;
4435 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4439 return hr;
4442 /* Find the first entry in a depth-first traversal. */
4443 static DirRef TransactedSnapshotImpl_FindFirstChild(
4444 TransactedSnapshotImpl* This, DirRef parent)
4446 DirRef cursor, prev;
4447 TransactedDirEntry *entry;
4449 cursor = parent;
4450 entry = &This->entries[cursor];
4451 while (entry->read)
4453 if (entry->data.leftChild != DIRENTRY_NULL)
4455 prev = cursor;
4456 cursor = entry->data.leftChild;
4457 entry = &This->entries[cursor];
4458 entry->parent = prev;
4460 else if (entry->data.rightChild != DIRENTRY_NULL)
4462 prev = cursor;
4463 cursor = entry->data.rightChild;
4464 entry = &This->entries[cursor];
4465 entry->parent = prev;
4467 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4469 prev = cursor;
4470 cursor = entry->data.dirRootEntry;
4471 entry = &This->entries[cursor];
4472 entry->parent = prev;
4474 else
4475 break;
4478 return cursor;
4481 /* Find the next entry in a depth-first traversal. */
4482 static DirRef TransactedSnapshotImpl_FindNextChild(
4483 TransactedSnapshotImpl* This, DirRef current)
4485 DirRef parent;
4486 TransactedDirEntry *parent_entry;
4488 parent = This->entries[current].parent;
4489 parent_entry = &This->entries[parent];
4491 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4493 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4495 This->entries[parent_entry->data.rightChild].parent = parent;
4496 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4499 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4501 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4502 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4506 return parent;
4509 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4510 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4511 TransactedSnapshotImpl* This, DirRef entry)
4513 return entry != DIRENTRY_NULL &&
4514 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4517 /* Destroy the entries created by CopyTree. */
4518 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4519 TransactedSnapshotImpl* This, DirRef stop)
4521 DirRef cursor;
4522 TransactedDirEntry *entry;
4523 ULARGE_INTEGER zero;
4525 zero.QuadPart = 0;
4527 if (!This->entries[This->base.storageDirEntry].read)
4528 return;
4530 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4532 if (cursor == DIRENTRY_NULL)
4533 return;
4535 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4537 while (cursor != DIRENTRY_NULL && cursor != stop)
4539 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4541 entry = &This->entries[cursor];
4543 if (entry->stream_dirty)
4544 StorageBaseImpl_StreamSetSize(This->transactedParent,
4545 entry->newTransactedParentEntry, zero);
4547 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4548 entry->newTransactedParentEntry);
4550 entry->newTransactedParentEntry = entry->transactedParentEntry;
4553 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4557 /* Make a copy of our edited tree that we can use in the parent. */
4558 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4560 DirRef cursor;
4561 TransactedDirEntry *entry;
4562 HRESULT hr = S_OK;
4564 cursor = This->base.storageDirEntry;
4565 entry = &This->entries[cursor];
4566 entry->parent = DIRENTRY_NULL;
4567 entry->newTransactedParentEntry = entry->transactedParentEntry;
4569 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4570 return S_OK;
4572 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4574 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4575 entry = &This->entries[cursor];
4577 while (cursor != DIRENTRY_NULL)
4579 /* Make a copy of this entry in the transacted parent. */
4580 if (!entry->read ||
4581 (!entry->dirty && !entry->stream_dirty &&
4582 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4583 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4584 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4585 entry->newTransactedParentEntry = entry->transactedParentEntry;
4586 else
4588 DirEntry newData;
4590 memcpy(&newData, &entry->data, sizeof(DirEntry));
4592 newData.size.QuadPart = 0;
4593 newData.startingBlock = BLOCK_END_OF_CHAIN;
4595 if (newData.leftChild != DIRENTRY_NULL)
4596 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4598 if (newData.rightChild != DIRENTRY_NULL)
4599 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4601 if (newData.dirRootEntry != DIRENTRY_NULL)
4602 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4604 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4605 &entry->newTransactedParentEntry);
4606 if (FAILED(hr))
4608 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4609 return hr;
4612 if (entry->stream_dirty)
4614 hr = StorageBaseImpl_CopyStream(
4615 This->transactedParent, entry->newTransactedParentEntry,
4616 This->scratch, entry->stream_entry);
4618 else if (entry->data.size.QuadPart)
4620 hr = StorageBaseImpl_StreamLink(
4621 This->transactedParent, entry->newTransactedParentEntry,
4622 entry->transactedParentEntry);
4625 if (FAILED(hr))
4627 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4628 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4629 return hr;
4633 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4634 entry = &This->entries[cursor];
4637 return hr;
4640 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4641 IStorage* iface,
4642 DWORD grfCommitFlags) /* [in] */
4644 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4645 TransactedDirEntry *root_entry;
4646 DirRef i, dir_root_ref;
4647 DirEntry data;
4648 ULARGE_INTEGER zero;
4649 HRESULT hr;
4651 zero.QuadPart = 0;
4653 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4655 /* Cannot commit a read-only transacted storage */
4656 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4657 return STG_E_ACCESSDENIED;
4659 /* To prevent data loss, we create the new structure in the file before we
4660 * delete the old one, so that in case of errors the old data is intact. We
4661 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4662 * needed in the rare situation where we have just enough free disk space to
4663 * overwrite the existing data. */
4665 root_entry = &This->entries[This->base.storageDirEntry];
4667 if (!root_entry->read)
4668 return S_OK;
4670 hr = TransactedSnapshotImpl_CopyTree(This);
4671 if (FAILED(hr)) return hr;
4673 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4674 dir_root_ref = DIRENTRY_NULL;
4675 else
4676 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4678 hr = StorageBaseImpl_Flush(This->transactedParent);
4680 /* Update the storage to use the new data in one step. */
4681 if (SUCCEEDED(hr))
4682 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4683 root_entry->transactedParentEntry, &data);
4685 if (SUCCEEDED(hr))
4687 data.dirRootEntry = dir_root_ref;
4688 data.clsid = root_entry->data.clsid;
4689 data.ctime = root_entry->data.ctime;
4690 data.mtime = root_entry->data.mtime;
4692 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4693 root_entry->transactedParentEntry, &data);
4696 /* Try to flush after updating the root storage, but if the flush fails, keep
4697 * going, on the theory that it'll either succeed later or the subsequent
4698 * writes will fail. */
4699 StorageBaseImpl_Flush(This->transactedParent);
4701 if (SUCCEEDED(hr))
4703 /* Destroy the old now-orphaned data. */
4704 for (i=0; i<This->entries_size; i++)
4706 TransactedDirEntry *entry = &This->entries[i];
4707 if (entry->inuse)
4709 if (entry->deleted)
4711 StorageBaseImpl_StreamSetSize(This->transactedParent,
4712 entry->transactedParentEntry, zero);
4713 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4714 entry->transactedParentEntry);
4715 memset(entry, 0, sizeof(TransactedDirEntry));
4716 This->firstFreeEntry = min(i, This->firstFreeEntry);
4718 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4720 if (entry->transactedParentEntry != DIRENTRY_NULL)
4721 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4722 entry->transactedParentEntry);
4723 if (entry->stream_dirty)
4725 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4726 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4727 entry->stream_dirty = 0;
4729 entry->dirty = 0;
4730 entry->transactedParentEntry = entry->newTransactedParentEntry;
4735 else
4737 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4740 if (SUCCEEDED(hr))
4741 hr = StorageBaseImpl_Flush(This->transactedParent);
4743 return hr;
4746 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4747 IStorage* iface)
4749 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4750 ULARGE_INTEGER zero;
4751 ULONG i;
4753 TRACE("(%p)\n", iface);
4755 /* Destroy the open objects. */
4756 StorageBaseImpl_DeleteAll(&This->base);
4758 /* Clear out the scratch file. */
4759 zero.QuadPart = 0;
4760 for (i=0; i<This->entries_size; i++)
4762 if (This->entries[i].stream_dirty)
4764 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4765 zero);
4767 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4771 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4773 This->firstFreeEntry = 0;
4774 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4776 return S_OK;
4779 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4781 if (!This->reverted)
4783 TRACE("Storage invalidated (stg=%p)\n", This);
4785 This->reverted = 1;
4787 StorageBaseImpl_DeleteAll(This);
4791 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4793 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4795 TransactedSnapshotImpl_Revert((IStorage*)iface);
4797 IStorage_Release((IStorage*)This->transactedParent);
4799 IStorage_Release((IStorage*)This->scratch);
4801 HeapFree(GetProcessHeap(), 0, This->entries);
4803 HeapFree(GetProcessHeap(), 0, This);
4806 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4808 /* We only need to flush when committing. */
4809 return S_OK;
4812 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4814 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4816 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4819 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4820 const DirEntry *newData, DirRef *index)
4822 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4823 DirRef new_ref;
4824 TransactedDirEntry *new_entry;
4826 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4827 if (new_ref == DIRENTRY_NULL)
4828 return E_OUTOFMEMORY;
4830 new_entry = &This->entries[new_ref];
4832 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4833 new_entry->read = 1;
4834 new_entry->dirty = 1;
4835 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4837 *index = new_ref;
4839 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4841 return S_OK;
4844 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4845 DirRef index, const DirEntry *data)
4847 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4848 HRESULT hr;
4850 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4852 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4853 if (FAILED(hr)) return hr;
4855 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4857 if (index != This->base.storageDirEntry)
4859 This->entries[index].dirty = 1;
4861 if (data->size.QuadPart == 0 &&
4862 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4864 /* Since this entry is modified, and we aren't using its stream data, we
4865 * no longer care about the original entry. */
4866 DirRef delete_ref;
4867 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4869 if (delete_ref != DIRENTRY_NULL)
4870 This->entries[delete_ref].deleted = 1;
4872 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4876 return S_OK;
4879 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4880 DirRef index, DirEntry *data)
4882 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4883 HRESULT hr;
4885 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4886 if (FAILED(hr)) return hr;
4888 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4890 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4892 return S_OK;
4895 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4896 DirRef index)
4898 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4900 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4901 This->entries[index].data.size.QuadPart != 0)
4903 /* If we deleted this entry while it has stream data. We must have left the
4904 * data because some other entry is using it, and we need to leave the
4905 * original entry alone. */
4906 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4907 This->firstFreeEntry = min(index, This->firstFreeEntry);
4909 else
4911 This->entries[index].deleted = 1;
4914 return S_OK;
4917 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4918 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4920 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4922 if (This->entries[index].stream_dirty)
4924 return StorageBaseImpl_StreamReadAt(This->scratch,
4925 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4927 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4929 /* This stream doesn't live in the parent, and we haven't allocated storage
4930 * for it yet */
4931 *bytesRead = 0;
4932 return S_OK;
4934 else
4936 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4937 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4941 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4942 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4944 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4945 HRESULT hr;
4947 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4948 if (FAILED(hr)) return hr;
4950 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4951 if (FAILED(hr)) return hr;
4953 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4954 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4956 if (SUCCEEDED(hr) && size != 0)
4957 This->entries[index].data.size.QuadPart = max(
4958 This->entries[index].data.size.QuadPart,
4959 offset.QuadPart + size);
4961 return hr;
4964 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4965 DirRef index, ULARGE_INTEGER newsize)
4967 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4968 HRESULT hr;
4970 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4971 if (FAILED(hr)) return hr;
4973 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4974 return S_OK;
4976 if (newsize.QuadPart == 0)
4978 /* Destroy any parent references or entries in the scratch file. */
4979 if (This->entries[index].stream_dirty)
4981 ULARGE_INTEGER zero;
4982 zero.QuadPart = 0;
4983 StorageBaseImpl_StreamSetSize(This->scratch,
4984 This->entries[index].stream_entry, zero);
4985 StorageBaseImpl_DestroyDirEntry(This->scratch,
4986 This->entries[index].stream_entry);
4987 This->entries[index].stream_dirty = 0;
4989 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4991 DirRef delete_ref;
4992 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4994 if (delete_ref != DIRENTRY_NULL)
4995 This->entries[delete_ref].deleted = 1;
4997 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5000 else
5002 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5003 if (FAILED(hr)) return hr;
5005 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5006 This->entries[index].stream_entry, newsize);
5009 if (SUCCEEDED(hr))
5010 This->entries[index].data.size = newsize;
5012 return hr;
5015 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5016 DirRef dst, DirRef src)
5018 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5019 HRESULT hr;
5020 TransactedDirEntry *dst_entry, *src_entry;
5022 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5023 if (FAILED(hr)) return hr;
5025 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5026 if (FAILED(hr)) return hr;
5028 dst_entry = &This->entries[dst];
5029 src_entry = &This->entries[src];
5031 dst_entry->stream_dirty = src_entry->stream_dirty;
5032 dst_entry->stream_entry = src_entry->stream_entry;
5033 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5034 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5035 dst_entry->data.size = src_entry->data.size;
5037 return S_OK;
5040 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5042 StorageBaseImpl_QueryInterface,
5043 StorageBaseImpl_AddRef,
5044 StorageBaseImpl_Release,
5045 StorageBaseImpl_CreateStream,
5046 StorageBaseImpl_OpenStream,
5047 StorageBaseImpl_CreateStorage,
5048 StorageBaseImpl_OpenStorage,
5049 StorageBaseImpl_CopyTo,
5050 StorageBaseImpl_MoveElementTo,
5051 TransactedSnapshotImpl_Commit,
5052 TransactedSnapshotImpl_Revert,
5053 StorageBaseImpl_EnumElements,
5054 StorageBaseImpl_DestroyElement,
5055 StorageBaseImpl_RenameElement,
5056 StorageBaseImpl_SetElementTimes,
5057 StorageBaseImpl_SetClass,
5058 StorageBaseImpl_SetStateBits,
5059 StorageBaseImpl_Stat
5062 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5064 TransactedSnapshotImpl_Destroy,
5065 TransactedSnapshotImpl_Invalidate,
5066 TransactedSnapshotImpl_Flush,
5067 TransactedSnapshotImpl_GetFilename,
5068 TransactedSnapshotImpl_CreateDirEntry,
5069 TransactedSnapshotImpl_WriteDirEntry,
5070 TransactedSnapshotImpl_ReadDirEntry,
5071 TransactedSnapshotImpl_DestroyDirEntry,
5072 TransactedSnapshotImpl_StreamReadAt,
5073 TransactedSnapshotImpl_StreamWriteAt,
5074 TransactedSnapshotImpl_StreamSetSize,
5075 TransactedSnapshotImpl_StreamLink
5078 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5079 TransactedSnapshotImpl** result)
5081 HRESULT hr;
5083 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5084 if (*result)
5086 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5088 /* This is OK because the property set storage functions use the IStorage functions. */
5089 (*result)->base.pssVtbl = parentStorage->pssVtbl;
5091 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5093 list_init(&(*result)->base.strmHead);
5095 list_init(&(*result)->base.storageHead);
5097 (*result)->base.ref = 1;
5099 (*result)->base.openFlags = parentStorage->openFlags;
5101 /* Create a new temporary storage to act as the scratch file. */
5102 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5103 0, (IStorage**)&(*result)->scratch);
5105 if (SUCCEEDED(hr))
5107 ULONG num_entries = 20;
5109 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5111 (*result)->entries_size = num_entries;
5113 (*result)->firstFreeEntry = 0;
5115 if ((*result)->entries)
5117 /* parentStorage already has 1 reference, which we take over here. */
5118 (*result)->transactedParent = parentStorage;
5120 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5122 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5124 else
5126 IStorage_Release((IStorage*)(*result)->scratch);
5128 hr = E_OUTOFMEMORY;
5132 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5134 return hr;
5136 else
5137 return E_OUTOFMEMORY;
5140 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5141 StorageBaseImpl** result)
5143 static int fixme=0;
5145 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5147 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5150 return TransactedSnapshotImpl_Construct(parentStorage,
5151 (TransactedSnapshotImpl**)result);
5154 static HRESULT Storage_Construct(
5155 HANDLE hFile,
5156 LPCOLESTR pwcsName,
5157 ILockBytes* pLkbyt,
5158 DWORD openFlags,
5159 BOOL fileBased,
5160 BOOL create,
5161 ULONG sector_size,
5162 StorageBaseImpl** result)
5164 StorageImpl *newStorage;
5165 StorageBaseImpl *newTransactedStorage;
5166 HRESULT hr;
5168 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5169 if (FAILED(hr)) goto end;
5171 if (openFlags & STGM_TRANSACTED)
5173 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5174 if (FAILED(hr))
5175 IStorage_Release((IStorage*)newStorage);
5176 else
5177 *result = newTransactedStorage;
5179 else
5180 *result = &newStorage->base;
5182 end:
5183 return hr;
5186 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5188 StorageInternalImpl* This = (StorageInternalImpl*) base;
5190 if (!This->base.reverted)
5192 TRACE("Storage invalidated (stg=%p)\n", This);
5194 This->base.reverted = 1;
5196 This->parentStorage = NULL;
5198 StorageBaseImpl_DeleteAll(&This->base);
5200 list_remove(&This->ParentListEntry);
5204 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5206 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5208 StorageInternalImpl_Invalidate(&This->base);
5210 HeapFree(GetProcessHeap(), 0, This);
5213 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5215 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5217 return StorageBaseImpl_Flush(This->parentStorage);
5220 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5222 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5224 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5227 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5228 const DirEntry *newData, DirRef *index)
5230 StorageInternalImpl* This = (StorageInternalImpl*) base;
5232 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5233 newData, index);
5236 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5237 DirRef index, const DirEntry *data)
5239 StorageInternalImpl* This = (StorageInternalImpl*) base;
5241 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5242 index, data);
5245 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5246 DirRef index, DirEntry *data)
5248 StorageInternalImpl* This = (StorageInternalImpl*) base;
5250 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5251 index, data);
5254 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5255 DirRef index)
5257 StorageInternalImpl* This = (StorageInternalImpl*) base;
5259 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5260 index);
5263 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5264 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5266 StorageInternalImpl* This = (StorageInternalImpl*) base;
5268 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5269 index, offset, size, buffer, bytesRead);
5272 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5273 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5275 StorageInternalImpl* This = (StorageInternalImpl*) base;
5277 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5278 index, offset, size, buffer, bytesWritten);
5281 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5282 DirRef index, ULARGE_INTEGER newsize)
5284 StorageInternalImpl* This = (StorageInternalImpl*) base;
5286 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5287 index, newsize);
5290 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5291 DirRef dst, DirRef src)
5293 StorageInternalImpl* This = (StorageInternalImpl*) base;
5295 return StorageBaseImpl_StreamLink(This->parentStorage,
5296 dst, src);
5299 /******************************************************************************
5301 ** Storage32InternalImpl_Commit
5304 static HRESULT WINAPI StorageInternalImpl_Commit(
5305 IStorage* iface,
5306 DWORD grfCommitFlags) /* [in] */
5308 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5309 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5310 return StorageBaseImpl_Flush(base);
5313 /******************************************************************************
5315 ** Storage32InternalImpl_Revert
5318 static HRESULT WINAPI StorageInternalImpl_Revert(
5319 IStorage* iface)
5321 FIXME("(%p): stub\n", iface);
5322 return S_OK;
5325 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5327 IStorage_Release((IStorage*)This->parentStorage);
5328 HeapFree(GetProcessHeap(), 0, This);
5331 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5332 IEnumSTATSTG* iface,
5333 REFIID riid,
5334 void** ppvObject)
5336 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5338 if (ppvObject==0)
5339 return E_INVALIDARG;
5341 *ppvObject = 0;
5343 if (IsEqualGUID(&IID_IUnknown, riid) ||
5344 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5346 *ppvObject = This;
5347 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5348 return S_OK;
5351 return E_NOINTERFACE;
5354 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5355 IEnumSTATSTG* iface)
5357 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5358 return InterlockedIncrement(&This->ref);
5361 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5362 IEnumSTATSTG* iface)
5364 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5366 ULONG newRef;
5368 newRef = InterlockedDecrement(&This->ref);
5370 if (newRef==0)
5372 IEnumSTATSTGImpl_Destroy(This);
5375 return newRef;
5378 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5379 IEnumSTATSTGImpl* This,
5380 DirRef *ref)
5382 DirRef result = DIRENTRY_NULL;
5383 DirRef searchNode;
5384 DirEntry entry;
5385 HRESULT hr;
5386 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5388 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5389 This->parentStorage->storageDirEntry, &entry);
5390 searchNode = entry.dirRootEntry;
5392 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5394 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5396 if (SUCCEEDED(hr))
5398 LONG diff = entryNameCmp( entry.name, This->name);
5400 if (diff <= 0)
5402 searchNode = entry.rightChild;
5404 else
5406 result = searchNode;
5407 memcpy(result_name, entry.name, sizeof(result_name));
5408 searchNode = entry.leftChild;
5413 if (SUCCEEDED(hr))
5415 *ref = result;
5416 if (result != DIRENTRY_NULL)
5417 memcpy(This->name, result_name, sizeof(result_name));
5420 return hr;
5423 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5424 IEnumSTATSTG* iface,
5425 ULONG celt,
5426 STATSTG* rgelt,
5427 ULONG* pceltFetched)
5429 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5431 DirEntry currentEntry;
5432 STATSTG* currentReturnStruct = rgelt;
5433 ULONG objectFetched = 0;
5434 DirRef currentSearchNode;
5435 HRESULT hr=S_OK;
5437 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5438 return E_INVALIDARG;
5440 if (This->parentStorage->reverted)
5441 return STG_E_REVERTED;
5444 * To avoid the special case, get another pointer to a ULONG value if
5445 * the caller didn't supply one.
5447 if (pceltFetched==0)
5448 pceltFetched = &objectFetched;
5451 * Start the iteration, we will iterate until we hit the end of the
5452 * linked list or until we hit the number of items to iterate through
5454 *pceltFetched = 0;
5456 while ( *pceltFetched < celt )
5458 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5460 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5461 break;
5464 * Read the entry from the storage.
5466 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5467 currentSearchNode,
5468 &currentEntry);
5471 * Copy the information to the return buffer.
5473 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5474 currentReturnStruct,
5475 &currentEntry,
5476 STATFLAG_DEFAULT);
5479 * Step to the next item in the iteration
5481 (*pceltFetched)++;
5482 currentReturnStruct++;
5485 if (SUCCEEDED(hr) && *pceltFetched != celt)
5486 hr = S_FALSE;
5488 return hr;
5492 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5493 IEnumSTATSTG* iface,
5494 ULONG celt)
5496 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5498 ULONG objectFetched = 0;
5499 DirRef currentSearchNode;
5500 HRESULT hr=S_OK;
5502 if (This->parentStorage->reverted)
5503 return STG_E_REVERTED;
5505 while ( (objectFetched < celt) )
5507 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5509 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5510 break;
5512 objectFetched++;
5515 if (SUCCEEDED(hr) && objectFetched != celt)
5516 return S_FALSE;
5518 return hr;
5521 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5522 IEnumSTATSTG* iface)
5524 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5526 if (This->parentStorage->reverted)
5527 return STG_E_REVERTED;
5529 This->name[0] = 0;
5531 return S_OK;
5534 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5535 IEnumSTATSTG* iface,
5536 IEnumSTATSTG** ppenum)
5538 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5540 IEnumSTATSTGImpl* newClone;
5542 if (This->parentStorage->reverted)
5543 return STG_E_REVERTED;
5546 * Perform a sanity check on the parameters.
5548 if (ppenum==0)
5549 return E_INVALIDARG;
5551 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5552 This->storageDirEntry);
5556 * The new clone enumeration must point to the same current node as
5557 * the ole one.
5559 memcpy(newClone->name, This->name, sizeof(newClone->name));
5561 *ppenum = &newClone->IEnumSTATSTG_iface;
5564 * Don't forget to nail down a reference to the clone before
5565 * returning it.
5567 IEnumSTATSTGImpl_AddRef(*ppenum);
5569 return S_OK;
5573 * Virtual function table for the IEnumSTATSTGImpl class.
5575 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5577 IEnumSTATSTGImpl_QueryInterface,
5578 IEnumSTATSTGImpl_AddRef,
5579 IEnumSTATSTGImpl_Release,
5580 IEnumSTATSTGImpl_Next,
5581 IEnumSTATSTGImpl_Skip,
5582 IEnumSTATSTGImpl_Reset,
5583 IEnumSTATSTGImpl_Clone
5586 /******************************************************************************
5587 ** IEnumSTATSTGImpl implementation
5590 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5591 StorageBaseImpl* parentStorage,
5592 DirRef storageDirEntry)
5594 IEnumSTATSTGImpl* newEnumeration;
5596 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5598 if (newEnumeration!=0)
5601 * Set-up the virtual function table and reference count.
5603 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5604 newEnumeration->ref = 0;
5607 * We want to nail-down the reference to the storage in case the
5608 * enumeration out-lives the storage in the client application.
5610 newEnumeration->parentStorage = parentStorage;
5611 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5613 newEnumeration->storageDirEntry = storageDirEntry;
5616 * Make sure the current node of the iterator is the first one.
5618 IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface);
5621 return newEnumeration;
5625 * Virtual function table for the Storage32InternalImpl class.
5627 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5629 StorageBaseImpl_QueryInterface,
5630 StorageBaseImpl_AddRef,
5631 StorageBaseImpl_Release,
5632 StorageBaseImpl_CreateStream,
5633 StorageBaseImpl_OpenStream,
5634 StorageBaseImpl_CreateStorage,
5635 StorageBaseImpl_OpenStorage,
5636 StorageBaseImpl_CopyTo,
5637 StorageBaseImpl_MoveElementTo,
5638 StorageInternalImpl_Commit,
5639 StorageInternalImpl_Revert,
5640 StorageBaseImpl_EnumElements,
5641 StorageBaseImpl_DestroyElement,
5642 StorageBaseImpl_RenameElement,
5643 StorageBaseImpl_SetElementTimes,
5644 StorageBaseImpl_SetClass,
5645 StorageBaseImpl_SetStateBits,
5646 StorageBaseImpl_Stat
5649 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5651 StorageInternalImpl_Destroy,
5652 StorageInternalImpl_Invalidate,
5653 StorageInternalImpl_Flush,
5654 StorageInternalImpl_GetFilename,
5655 StorageInternalImpl_CreateDirEntry,
5656 StorageInternalImpl_WriteDirEntry,
5657 StorageInternalImpl_ReadDirEntry,
5658 StorageInternalImpl_DestroyDirEntry,
5659 StorageInternalImpl_StreamReadAt,
5660 StorageInternalImpl_StreamWriteAt,
5661 StorageInternalImpl_StreamSetSize,
5662 StorageInternalImpl_StreamLink
5665 /******************************************************************************
5666 ** Storage32InternalImpl implementation
5669 static StorageInternalImpl* StorageInternalImpl_Construct(
5670 StorageBaseImpl* parentStorage,
5671 DWORD openFlags,
5672 DirRef storageDirEntry)
5674 StorageInternalImpl* newStorage;
5676 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5678 if (newStorage!=0)
5680 list_init(&newStorage->base.strmHead);
5682 list_init(&newStorage->base.storageHead);
5685 * Initialize the virtual function table.
5687 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5688 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5689 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5690 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5692 newStorage->base.reverted = 0;
5694 newStorage->base.ref = 1;
5696 newStorage->parentStorage = parentStorage;
5699 * Keep a reference to the directory entry of this storage
5701 newStorage->base.storageDirEntry = storageDirEntry;
5703 newStorage->base.create = 0;
5705 return newStorage;
5708 return 0;
5711 /******************************************************************************
5712 ** StorageUtl implementation
5715 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5717 WORD tmp;
5719 memcpy(&tmp, buffer+offset, sizeof(WORD));
5720 *value = lendian16toh(tmp);
5723 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5725 value = htole16(value);
5726 memcpy(buffer+offset, &value, sizeof(WORD));
5729 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5731 DWORD tmp;
5733 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5734 *value = lendian32toh(tmp);
5737 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5739 value = htole32(value);
5740 memcpy(buffer+offset, &value, sizeof(DWORD));
5743 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5744 ULARGE_INTEGER* value)
5746 #ifdef WORDS_BIGENDIAN
5747 ULARGE_INTEGER tmp;
5749 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5750 value->u.LowPart = htole32(tmp.u.HighPart);
5751 value->u.HighPart = htole32(tmp.u.LowPart);
5752 #else
5753 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5754 #endif
5757 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5758 const ULARGE_INTEGER *value)
5760 #ifdef WORDS_BIGENDIAN
5761 ULARGE_INTEGER tmp;
5763 tmp.u.LowPart = htole32(value->u.HighPart);
5764 tmp.u.HighPart = htole32(value->u.LowPart);
5765 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5766 #else
5767 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5768 #endif
5771 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5773 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5774 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5775 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5777 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5780 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5782 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5783 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5784 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5786 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5789 void StorageUtl_CopyDirEntryToSTATSTG(
5790 StorageBaseImpl* storage,
5791 STATSTG* destination,
5792 const DirEntry* source,
5793 int statFlags)
5796 * The copy of the string occurs only when the flag is not set
5798 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5800 /* Use the filename for the root storage. */
5801 destination->pwcsName = 0;
5802 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5804 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5805 (source->name[0] == 0) )
5807 destination->pwcsName = 0;
5809 else
5811 destination->pwcsName =
5812 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5814 strcpyW(destination->pwcsName, source->name);
5817 switch (source->stgType)
5819 case STGTY_STORAGE:
5820 case STGTY_ROOT:
5821 destination->type = STGTY_STORAGE;
5822 break;
5823 case STGTY_STREAM:
5824 destination->type = STGTY_STREAM;
5825 break;
5826 default:
5827 destination->type = STGTY_STREAM;
5828 break;
5831 destination->cbSize = source->size;
5833 currentReturnStruct->mtime = {0}; TODO
5834 currentReturnStruct->ctime = {0};
5835 currentReturnStruct->atime = {0};
5837 destination->grfMode = 0;
5838 destination->grfLocksSupported = 0;
5839 destination->clsid = source->clsid;
5840 destination->grfStateBits = 0;
5841 destination->reserved = 0;
5844 /******************************************************************************
5845 ** BlockChainStream implementation
5848 /* Read and save the index of all blocks in this stream. */
5849 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5851 ULONG next_sector, next_offset;
5852 HRESULT hr;
5853 struct BlockChainRun *last_run;
5855 if (This->indexCacheLen == 0)
5857 last_run = NULL;
5858 next_offset = 0;
5859 next_sector = BlockChainStream_GetHeadOfChain(This);
5861 else
5863 last_run = &This->indexCache[This->indexCacheLen-1];
5864 next_offset = last_run->lastOffset+1;
5865 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5866 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5867 &next_sector);
5868 if (FAILED(hr)) return hr;
5871 while (next_sector != BLOCK_END_OF_CHAIN)
5873 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5875 /* Add the current block to the cache. */
5876 if (This->indexCacheSize == 0)
5878 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5879 if (!This->indexCache) return E_OUTOFMEMORY;
5880 This->indexCacheSize = 16;
5882 else if (This->indexCacheSize == This->indexCacheLen)
5884 struct BlockChainRun *new_cache;
5885 ULONG new_size;
5887 new_size = This->indexCacheSize * 2;
5888 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5889 if (!new_cache) return E_OUTOFMEMORY;
5890 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5892 HeapFree(GetProcessHeap(), 0, This->indexCache);
5893 This->indexCache = new_cache;
5894 This->indexCacheSize = new_size;
5897 This->indexCacheLen++;
5898 last_run = &This->indexCache[This->indexCacheLen-1];
5899 last_run->firstSector = next_sector;
5900 last_run->firstOffset = next_offset;
5903 last_run->lastOffset = next_offset;
5905 /* Find the next block. */
5906 next_offset++;
5907 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5908 if (FAILED(hr)) return hr;
5911 if (This->indexCacheLen)
5913 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5914 This->numBlocks = last_run->lastOffset+1;
5916 else
5918 This->tailIndex = BLOCK_END_OF_CHAIN;
5919 This->numBlocks = 0;
5922 return S_OK;
5925 /* Locate the nth block in this stream. */
5926 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5928 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5929 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5931 if (offset >= This->numBlocks)
5932 return BLOCK_END_OF_CHAIN;
5934 while (min_run < max_run)
5936 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5937 if (offset < This->indexCache[run_to_check].firstOffset)
5939 max_offset = This->indexCache[run_to_check].firstOffset-1;
5940 max_run = run_to_check-1;
5942 else if (offset > This->indexCache[run_to_check].lastOffset)
5944 min_offset = This->indexCache[run_to_check].lastOffset+1;
5945 min_run = run_to_check+1;
5947 else
5948 /* Block is in this run. */
5949 min_run = max_run = run_to_check;
5952 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5955 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5956 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5958 BlockChainBlock *result=NULL;
5959 int i;
5961 for (i=0; i<2; i++)
5962 if (This->cachedBlocks[i].index == index)
5964 *sector = This->cachedBlocks[i].sector;
5965 *block = &This->cachedBlocks[i];
5966 return S_OK;
5969 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5970 if (*sector == BLOCK_END_OF_CHAIN)
5971 return STG_E_DOCFILECORRUPT;
5973 if (create)
5975 if (This->cachedBlocks[0].index == 0xffffffff)
5976 result = &This->cachedBlocks[0];
5977 else if (This->cachedBlocks[1].index == 0xffffffff)
5978 result = &This->cachedBlocks[1];
5979 else
5981 result = &This->cachedBlocks[This->blockToEvict++];
5982 if (This->blockToEvict == 2)
5983 This->blockToEvict = 0;
5986 if (result->dirty)
5988 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5989 return STG_E_WRITEFAULT;
5990 result->dirty = 0;
5993 result->read = 0;
5994 result->index = index;
5995 result->sector = *sector;
5998 *block = result;
5999 return S_OK;
6002 BlockChainStream* BlockChainStream_Construct(
6003 StorageImpl* parentStorage,
6004 ULONG* headOfStreamPlaceHolder,
6005 DirRef dirEntry)
6007 BlockChainStream* newStream;
6009 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6011 newStream->parentStorage = parentStorage;
6012 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6013 newStream->ownerDirEntry = dirEntry;
6014 newStream->indexCache = NULL;
6015 newStream->indexCacheLen = 0;
6016 newStream->indexCacheSize = 0;
6017 newStream->cachedBlocks[0].index = 0xffffffff;
6018 newStream->cachedBlocks[0].dirty = 0;
6019 newStream->cachedBlocks[1].index = 0xffffffff;
6020 newStream->cachedBlocks[1].dirty = 0;
6021 newStream->blockToEvict = 0;
6023 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6025 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6026 HeapFree(GetProcessHeap(), 0, newStream);
6027 return NULL;
6030 return newStream;
6033 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6035 int i;
6036 if (!This) return S_OK;
6037 for (i=0; i<2; i++)
6039 if (This->cachedBlocks[i].dirty)
6041 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6042 This->cachedBlocks[i].dirty = 0;
6043 else
6044 return STG_E_WRITEFAULT;
6047 return S_OK;
6050 void BlockChainStream_Destroy(BlockChainStream* This)
6052 if (This)
6054 BlockChainStream_Flush(This);
6055 HeapFree(GetProcessHeap(), 0, This->indexCache);
6057 HeapFree(GetProcessHeap(), 0, This);
6060 /******************************************************************************
6061 * BlockChainStream_GetHeadOfChain
6063 * Returns the head of this stream chain.
6064 * Some special chains don't have directory entries, their heads are kept in
6065 * This->headOfStreamPlaceHolder.
6068 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6070 DirEntry chainEntry;
6071 HRESULT hr;
6073 if (This->headOfStreamPlaceHolder != 0)
6074 return *(This->headOfStreamPlaceHolder);
6076 if (This->ownerDirEntry != DIRENTRY_NULL)
6078 hr = StorageImpl_ReadDirEntry(
6079 This->parentStorage,
6080 This->ownerDirEntry,
6081 &chainEntry);
6083 if (SUCCEEDED(hr))
6085 return chainEntry.startingBlock;
6089 return BLOCK_END_OF_CHAIN;
6092 /******************************************************************************
6093 * BlockChainStream_GetCount
6095 * Returns the number of blocks that comprises this chain.
6096 * This is not the size of the stream as the last block may not be full!
6098 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6100 return This->numBlocks;
6103 /******************************************************************************
6104 * BlockChainStream_ReadAt
6106 * Reads a specified number of bytes from this chain at the specified offset.
6107 * bytesRead may be NULL.
6108 * Failure will be returned if the specified number of bytes has not been read.
6110 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6111 ULARGE_INTEGER offset,
6112 ULONG size,
6113 void* buffer,
6114 ULONG* bytesRead)
6116 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6117 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6118 ULONG bytesToReadInBuffer;
6119 ULONG blockIndex;
6120 BYTE* bufferWalker;
6121 ULARGE_INTEGER stream_size;
6122 HRESULT hr;
6123 BlockChainBlock *cachedBlock;
6125 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6128 * Find the first block in the stream that contains part of the buffer.
6130 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6132 *bytesRead = 0;
6134 stream_size = BlockChainStream_GetSize(This);
6135 if (stream_size.QuadPart > offset.QuadPart)
6136 size = min(stream_size.QuadPart - offset.QuadPart, size);
6137 else
6138 return S_OK;
6141 * Start reading the buffer.
6143 bufferWalker = buffer;
6145 while (size > 0)
6147 ULARGE_INTEGER ulOffset;
6148 DWORD bytesReadAt;
6151 * Calculate how many bytes we can copy from this big block.
6153 bytesToReadInBuffer =
6154 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6156 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6158 if (FAILED(hr))
6159 return hr;
6161 if (!cachedBlock)
6163 /* Not in cache, and we're going to read past the end of the block. */
6164 ulOffset.u.HighPart = 0;
6165 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6166 offsetInBlock;
6168 StorageImpl_ReadAt(This->parentStorage,
6169 ulOffset,
6170 bufferWalker,
6171 bytesToReadInBuffer,
6172 &bytesReadAt);
6174 else
6176 if (!cachedBlock->read)
6178 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6179 return STG_E_READFAULT;
6181 cachedBlock->read = 1;
6184 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6185 bytesReadAt = bytesToReadInBuffer;
6188 blockNoInSequence++;
6189 bufferWalker += bytesReadAt;
6190 size -= bytesReadAt;
6191 *bytesRead += bytesReadAt;
6192 offsetInBlock = 0; /* There is no offset on the next block */
6194 if (bytesToReadInBuffer != bytesReadAt)
6195 break;
6198 return S_OK;
6201 /******************************************************************************
6202 * BlockChainStream_WriteAt
6204 * Writes the specified number of bytes to this chain at the specified offset.
6205 * Will fail if not all specified number of bytes have been written.
6207 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6208 ULARGE_INTEGER offset,
6209 ULONG size,
6210 const void* buffer,
6211 ULONG* bytesWritten)
6213 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6214 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6215 ULONG bytesToWrite;
6216 ULONG blockIndex;
6217 const BYTE* bufferWalker;
6218 HRESULT hr;
6219 BlockChainBlock *cachedBlock;
6221 *bytesWritten = 0;
6222 bufferWalker = buffer;
6224 while (size > 0)
6226 ULARGE_INTEGER ulOffset;
6227 DWORD bytesWrittenAt;
6230 * Calculate how many bytes we can copy to this big block.
6232 bytesToWrite =
6233 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6235 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6237 /* BlockChainStream_SetSize should have already been called to ensure we have
6238 * enough blocks in the chain to write into */
6239 if (FAILED(hr))
6241 ERR("not enough blocks in chain to write data\n");
6242 return hr;
6245 if (!cachedBlock)
6247 /* Not in cache, and we're going to write past the end of the block. */
6248 ulOffset.u.HighPart = 0;
6249 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6250 offsetInBlock;
6252 StorageImpl_WriteAt(This->parentStorage,
6253 ulOffset,
6254 bufferWalker,
6255 bytesToWrite,
6256 &bytesWrittenAt);
6258 else
6260 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6262 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6263 return STG_E_READFAULT;
6266 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6267 bytesWrittenAt = bytesToWrite;
6268 cachedBlock->read = 1;
6269 cachedBlock->dirty = 1;
6272 blockNoInSequence++;
6273 bufferWalker += bytesWrittenAt;
6274 size -= bytesWrittenAt;
6275 *bytesWritten += bytesWrittenAt;
6276 offsetInBlock = 0; /* There is no offset on the next block */
6278 if (bytesWrittenAt != bytesToWrite)
6279 break;
6282 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6285 /******************************************************************************
6286 * BlockChainStream_Shrink
6288 * Shrinks this chain in the big block depot.
6290 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6291 ULARGE_INTEGER newSize)
6293 ULONG blockIndex;
6294 ULONG numBlocks;
6295 int i;
6298 * Figure out how many blocks are needed to contain the new size
6300 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6302 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6303 numBlocks++;
6305 if (numBlocks)
6308 * Go to the new end of chain
6310 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6312 /* Mark the new end of chain */
6313 StorageImpl_SetNextBlockInChain(
6314 This->parentStorage,
6315 blockIndex,
6316 BLOCK_END_OF_CHAIN);
6318 This->tailIndex = blockIndex;
6320 else
6322 if (This->headOfStreamPlaceHolder != 0)
6324 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6326 else
6328 DirEntry chainEntry;
6329 assert(This->ownerDirEntry != DIRENTRY_NULL);
6331 StorageImpl_ReadDirEntry(
6332 This->parentStorage,
6333 This->ownerDirEntry,
6334 &chainEntry);
6336 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6338 StorageImpl_WriteDirEntry(
6339 This->parentStorage,
6340 This->ownerDirEntry,
6341 &chainEntry);
6344 This->tailIndex = BLOCK_END_OF_CHAIN;
6347 This->numBlocks = numBlocks;
6350 * Mark the extra blocks as free
6352 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6354 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6355 StorageImpl_FreeBigBlock(This->parentStorage,
6356 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6357 if (last_run->lastOffset == last_run->firstOffset)
6358 This->indexCacheLen--;
6359 else
6360 last_run->lastOffset--;
6364 * Reset the last accessed block cache.
6366 for (i=0; i<2; i++)
6368 if (This->cachedBlocks[i].index >= numBlocks)
6370 This->cachedBlocks[i].index = 0xffffffff;
6371 This->cachedBlocks[i].dirty = 0;
6375 return TRUE;
6378 /******************************************************************************
6379 * BlockChainStream_Enlarge
6381 * Grows this chain in the big block depot.
6383 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6384 ULARGE_INTEGER newSize)
6386 ULONG blockIndex, currentBlock;
6387 ULONG newNumBlocks;
6388 ULONG oldNumBlocks = 0;
6390 blockIndex = BlockChainStream_GetHeadOfChain(This);
6393 * Empty chain. Create the head.
6395 if (blockIndex == BLOCK_END_OF_CHAIN)
6397 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6398 StorageImpl_SetNextBlockInChain(This->parentStorage,
6399 blockIndex,
6400 BLOCK_END_OF_CHAIN);
6402 if (This->headOfStreamPlaceHolder != 0)
6404 *(This->headOfStreamPlaceHolder) = blockIndex;
6406 else
6408 DirEntry chainEntry;
6409 assert(This->ownerDirEntry != DIRENTRY_NULL);
6411 StorageImpl_ReadDirEntry(
6412 This->parentStorage,
6413 This->ownerDirEntry,
6414 &chainEntry);
6416 chainEntry.startingBlock = blockIndex;
6418 StorageImpl_WriteDirEntry(
6419 This->parentStorage,
6420 This->ownerDirEntry,
6421 &chainEntry);
6424 This->tailIndex = blockIndex;
6425 This->numBlocks = 1;
6429 * Figure out how many blocks are needed to contain this stream
6431 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6433 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6434 newNumBlocks++;
6437 * Go to the current end of chain
6439 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6441 currentBlock = blockIndex;
6443 while (blockIndex != BLOCK_END_OF_CHAIN)
6445 This->numBlocks++;
6446 currentBlock = blockIndex;
6448 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6449 &blockIndex)))
6450 return FALSE;
6453 This->tailIndex = currentBlock;
6456 currentBlock = This->tailIndex;
6457 oldNumBlocks = This->numBlocks;
6460 * Add new blocks to the chain
6462 if (oldNumBlocks < newNumBlocks)
6464 while (oldNumBlocks < newNumBlocks)
6466 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6468 StorageImpl_SetNextBlockInChain(
6469 This->parentStorage,
6470 currentBlock,
6471 blockIndex);
6473 StorageImpl_SetNextBlockInChain(
6474 This->parentStorage,
6475 blockIndex,
6476 BLOCK_END_OF_CHAIN);
6478 currentBlock = blockIndex;
6479 oldNumBlocks++;
6482 This->tailIndex = blockIndex;
6483 This->numBlocks = newNumBlocks;
6486 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6487 return FALSE;
6489 return TRUE;
6492 /******************************************************************************
6493 * BlockChainStream_SetSize
6495 * Sets the size of this stream. The big block depot will be updated.
6496 * The file will grow if we grow the chain.
6498 * TODO: Free the actual blocks in the file when we shrink the chain.
6499 * Currently, the blocks are still in the file. So the file size
6500 * doesn't shrink even if we shrink streams.
6502 BOOL BlockChainStream_SetSize(
6503 BlockChainStream* This,
6504 ULARGE_INTEGER newSize)
6506 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6508 if (newSize.u.LowPart == size.u.LowPart)
6509 return TRUE;
6511 if (newSize.u.LowPart < size.u.LowPart)
6513 BlockChainStream_Shrink(This, newSize);
6515 else
6517 BlockChainStream_Enlarge(This, newSize);
6520 return TRUE;
6523 /******************************************************************************
6524 * BlockChainStream_GetSize
6526 * Returns the size of this chain.
6527 * Will return the block count if this chain doesn't have a directory entry.
6529 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6531 DirEntry chainEntry;
6533 if(This->headOfStreamPlaceHolder == NULL)
6536 * This chain has a directory entry so use the size value from there.
6538 StorageImpl_ReadDirEntry(
6539 This->parentStorage,
6540 This->ownerDirEntry,
6541 &chainEntry);
6543 return chainEntry.size;
6545 else
6548 * this chain is a chain that does not have a directory entry, figure out the
6549 * size by making the product number of used blocks times the
6550 * size of them
6552 ULARGE_INTEGER result;
6553 result.u.HighPart = 0;
6555 result.u.LowPart =
6556 BlockChainStream_GetCount(This) *
6557 This->parentStorage->bigBlockSize;
6559 return result;
6563 /******************************************************************************
6564 ** SmallBlockChainStream implementation
6567 SmallBlockChainStream* SmallBlockChainStream_Construct(
6568 StorageImpl* parentStorage,
6569 ULONG* headOfStreamPlaceHolder,
6570 DirRef dirEntry)
6572 SmallBlockChainStream* newStream;
6574 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6576 newStream->parentStorage = parentStorage;
6577 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6578 newStream->ownerDirEntry = dirEntry;
6580 return newStream;
6583 void SmallBlockChainStream_Destroy(
6584 SmallBlockChainStream* This)
6586 HeapFree(GetProcessHeap(), 0, This);
6589 /******************************************************************************
6590 * SmallBlockChainStream_GetHeadOfChain
6592 * Returns the head of this chain of small blocks.
6594 static ULONG SmallBlockChainStream_GetHeadOfChain(
6595 SmallBlockChainStream* This)
6597 DirEntry chainEntry;
6598 HRESULT hr;
6600 if (This->headOfStreamPlaceHolder != NULL)
6601 return *(This->headOfStreamPlaceHolder);
6603 if (This->ownerDirEntry)
6605 hr = StorageImpl_ReadDirEntry(
6606 This->parentStorage,
6607 This->ownerDirEntry,
6608 &chainEntry);
6610 if (SUCCEEDED(hr))
6612 return chainEntry.startingBlock;
6617 return BLOCK_END_OF_CHAIN;
6620 /******************************************************************************
6621 * SmallBlockChainStream_GetNextBlockInChain
6623 * Returns the index of the next small block in this chain.
6625 * Return Values:
6626 * - BLOCK_END_OF_CHAIN: end of this chain
6627 * - BLOCK_UNUSED: small block 'blockIndex' is free
6629 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6630 SmallBlockChainStream* This,
6631 ULONG blockIndex,
6632 ULONG* nextBlockInChain)
6634 ULARGE_INTEGER offsetOfBlockInDepot;
6635 DWORD buffer;
6636 ULONG bytesRead;
6637 HRESULT res;
6639 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6641 offsetOfBlockInDepot.u.HighPart = 0;
6642 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6645 * Read those bytes in the buffer from the small block file.
6647 res = BlockChainStream_ReadAt(
6648 This->parentStorage->smallBlockDepotChain,
6649 offsetOfBlockInDepot,
6650 sizeof(DWORD),
6651 &buffer,
6652 &bytesRead);
6654 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6655 res = STG_E_READFAULT;
6657 if (SUCCEEDED(res))
6659 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6660 return S_OK;
6663 return res;
6666 /******************************************************************************
6667 * SmallBlockChainStream_SetNextBlockInChain
6669 * Writes the index of the next block of the specified block in the small
6670 * block depot.
6671 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6672 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6674 static void SmallBlockChainStream_SetNextBlockInChain(
6675 SmallBlockChainStream* This,
6676 ULONG blockIndex,
6677 ULONG nextBlock)
6679 ULARGE_INTEGER offsetOfBlockInDepot;
6680 DWORD buffer;
6681 ULONG bytesWritten;
6683 offsetOfBlockInDepot.u.HighPart = 0;
6684 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6686 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6689 * Read those bytes in the buffer from the small block file.
6691 BlockChainStream_WriteAt(
6692 This->parentStorage->smallBlockDepotChain,
6693 offsetOfBlockInDepot,
6694 sizeof(DWORD),
6695 &buffer,
6696 &bytesWritten);
6699 /******************************************************************************
6700 * SmallBlockChainStream_FreeBlock
6702 * Flag small block 'blockIndex' as free in the small block depot.
6704 static void SmallBlockChainStream_FreeBlock(
6705 SmallBlockChainStream* This,
6706 ULONG blockIndex)
6708 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6711 /******************************************************************************
6712 * SmallBlockChainStream_GetNextFreeBlock
6714 * Returns the index of a free small block. The small block depot will be
6715 * enlarged if necessary. The small block chain will also be enlarged if
6716 * necessary.
6718 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6719 SmallBlockChainStream* This)
6721 ULARGE_INTEGER offsetOfBlockInDepot;
6722 DWORD buffer;
6723 ULONG bytesRead;
6724 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6725 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6726 HRESULT res = S_OK;
6727 ULONG smallBlocksPerBigBlock;
6728 DirEntry rootEntry;
6729 ULONG blocksRequired;
6730 ULARGE_INTEGER old_size, size_required;
6732 offsetOfBlockInDepot.u.HighPart = 0;
6735 * Scan the small block depot for a free block
6737 while (nextBlockIndex != BLOCK_UNUSED)
6739 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6741 res = BlockChainStream_ReadAt(
6742 This->parentStorage->smallBlockDepotChain,
6743 offsetOfBlockInDepot,
6744 sizeof(DWORD),
6745 &buffer,
6746 &bytesRead);
6749 * If we run out of space for the small block depot, enlarge it
6751 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6753 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6755 if (nextBlockIndex != BLOCK_UNUSED)
6756 blockIndex++;
6758 else
6760 ULONG count =
6761 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6763 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6764 ULARGE_INTEGER newSize, offset;
6765 ULONG bytesWritten;
6767 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6768 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6771 * Initialize all the small blocks to free
6773 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6774 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6775 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6776 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6778 StorageImpl_SaveFileHeader(This->parentStorage);
6782 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6784 smallBlocksPerBigBlock =
6785 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6788 * Verify if we have to allocate big blocks to contain small blocks
6790 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6792 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6794 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6796 if (size_required.QuadPart > old_size.QuadPart)
6798 BlockChainStream_SetSize(
6799 This->parentStorage->smallBlockRootChain,
6800 size_required);
6802 StorageImpl_ReadDirEntry(
6803 This->parentStorage,
6804 This->parentStorage->base.storageDirEntry,
6805 &rootEntry);
6807 rootEntry.size = size_required;
6809 StorageImpl_WriteDirEntry(
6810 This->parentStorage,
6811 This->parentStorage->base.storageDirEntry,
6812 &rootEntry);
6815 return blockIndex;
6818 /******************************************************************************
6819 * SmallBlockChainStream_ReadAt
6821 * Reads a specified number of bytes from this chain at the specified offset.
6822 * bytesRead may be NULL.
6823 * Failure will be returned if the specified number of bytes has not been read.
6825 HRESULT SmallBlockChainStream_ReadAt(
6826 SmallBlockChainStream* This,
6827 ULARGE_INTEGER offset,
6828 ULONG size,
6829 void* buffer,
6830 ULONG* bytesRead)
6832 HRESULT rc = S_OK;
6833 ULARGE_INTEGER offsetInBigBlockFile;
6834 ULONG blockNoInSequence =
6835 offset.u.LowPart / This->parentStorage->smallBlockSize;
6837 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6838 ULONG bytesToReadInBuffer;
6839 ULONG blockIndex;
6840 ULONG bytesReadFromBigBlockFile;
6841 BYTE* bufferWalker;
6842 ULARGE_INTEGER stream_size;
6845 * This should never happen on a small block file.
6847 assert(offset.u.HighPart==0);
6849 *bytesRead = 0;
6851 stream_size = SmallBlockChainStream_GetSize(This);
6852 if (stream_size.QuadPart > offset.QuadPart)
6853 size = min(stream_size.QuadPart - offset.QuadPart, size);
6854 else
6855 return S_OK;
6858 * Find the first block in the stream that contains part of the buffer.
6860 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6862 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6864 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6865 if(FAILED(rc))
6866 return rc;
6867 blockNoInSequence--;
6871 * Start reading the buffer.
6873 bufferWalker = buffer;
6875 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6878 * Calculate how many bytes we can copy from this small block.
6880 bytesToReadInBuffer =
6881 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6884 * Calculate the offset of the small block in the small block file.
6886 offsetInBigBlockFile.u.HighPart = 0;
6887 offsetInBigBlockFile.u.LowPart =
6888 blockIndex * This->parentStorage->smallBlockSize;
6890 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6893 * Read those bytes in the buffer from the small block file.
6894 * The small block has already been identified so it shouldn't fail
6895 * unless the file is corrupt.
6897 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6898 offsetInBigBlockFile,
6899 bytesToReadInBuffer,
6900 bufferWalker,
6901 &bytesReadFromBigBlockFile);
6903 if (FAILED(rc))
6904 return rc;
6906 if (!bytesReadFromBigBlockFile)
6907 return STG_E_DOCFILECORRUPT;
6910 * Step to the next big block.
6912 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6913 if(FAILED(rc))
6914 return STG_E_DOCFILECORRUPT;
6916 bufferWalker += bytesReadFromBigBlockFile;
6917 size -= bytesReadFromBigBlockFile;
6918 *bytesRead += bytesReadFromBigBlockFile;
6919 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6922 return S_OK;
6925 /******************************************************************************
6926 * SmallBlockChainStream_WriteAt
6928 * Writes the specified number of bytes to this chain at the specified offset.
6929 * Will fail if not all specified number of bytes have been written.
6931 HRESULT SmallBlockChainStream_WriteAt(
6932 SmallBlockChainStream* This,
6933 ULARGE_INTEGER offset,
6934 ULONG size,
6935 const void* buffer,
6936 ULONG* bytesWritten)
6938 ULARGE_INTEGER offsetInBigBlockFile;
6939 ULONG blockNoInSequence =
6940 offset.u.LowPart / This->parentStorage->smallBlockSize;
6942 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6943 ULONG bytesToWriteInBuffer;
6944 ULONG blockIndex;
6945 ULONG bytesWrittenToBigBlockFile;
6946 const BYTE* bufferWalker;
6947 HRESULT res;
6950 * This should never happen on a small block file.
6952 assert(offset.u.HighPart==0);
6955 * Find the first block in the stream that contains part of the buffer.
6957 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6959 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6961 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6962 return STG_E_DOCFILECORRUPT;
6963 blockNoInSequence--;
6967 * Start writing the buffer.
6969 *bytesWritten = 0;
6970 bufferWalker = buffer;
6971 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6974 * Calculate how many bytes we can copy to this small block.
6976 bytesToWriteInBuffer =
6977 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6980 * Calculate the offset of the small block in the small block file.
6982 offsetInBigBlockFile.u.HighPart = 0;
6983 offsetInBigBlockFile.u.LowPart =
6984 blockIndex * This->parentStorage->smallBlockSize;
6986 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6989 * Write those bytes in the buffer to the small block file.
6991 res = BlockChainStream_WriteAt(
6992 This->parentStorage->smallBlockRootChain,
6993 offsetInBigBlockFile,
6994 bytesToWriteInBuffer,
6995 bufferWalker,
6996 &bytesWrittenToBigBlockFile);
6997 if (FAILED(res))
6998 return res;
7001 * Step to the next big block.
7003 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7004 &blockIndex)))
7005 return FALSE;
7006 bufferWalker += bytesWrittenToBigBlockFile;
7007 size -= bytesWrittenToBigBlockFile;
7008 *bytesWritten += bytesWrittenToBigBlockFile;
7009 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7012 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7015 /******************************************************************************
7016 * SmallBlockChainStream_Shrink
7018 * Shrinks this chain in the small block depot.
7020 static BOOL SmallBlockChainStream_Shrink(
7021 SmallBlockChainStream* This,
7022 ULARGE_INTEGER newSize)
7024 ULONG blockIndex, extraBlock;
7025 ULONG numBlocks;
7026 ULONG count = 0;
7028 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7030 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7031 numBlocks++;
7033 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7036 * Go to the new end of chain
7038 while (count < numBlocks)
7040 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7041 &blockIndex)))
7042 return FALSE;
7043 count++;
7047 * If the count is 0, we have a special case, the head of the chain was
7048 * just freed.
7050 if (count == 0)
7052 DirEntry chainEntry;
7054 StorageImpl_ReadDirEntry(This->parentStorage,
7055 This->ownerDirEntry,
7056 &chainEntry);
7058 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7060 StorageImpl_WriteDirEntry(This->parentStorage,
7061 This->ownerDirEntry,
7062 &chainEntry);
7065 * We start freeing the chain at the head block.
7067 extraBlock = blockIndex;
7069 else
7071 /* Get the next block before marking the new end */
7072 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7073 &extraBlock)))
7074 return FALSE;
7076 /* Mark the new end of chain */
7077 SmallBlockChainStream_SetNextBlockInChain(
7078 This,
7079 blockIndex,
7080 BLOCK_END_OF_CHAIN);
7084 * Mark the extra blocks as free
7086 while (extraBlock != BLOCK_END_OF_CHAIN)
7088 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7089 &blockIndex)))
7090 return FALSE;
7091 SmallBlockChainStream_FreeBlock(This, extraBlock);
7092 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7093 extraBlock = blockIndex;
7096 return TRUE;
7099 /******************************************************************************
7100 * SmallBlockChainStream_Enlarge
7102 * Grows this chain in the small block depot.
7104 static BOOL SmallBlockChainStream_Enlarge(
7105 SmallBlockChainStream* This,
7106 ULARGE_INTEGER newSize)
7108 ULONG blockIndex, currentBlock;
7109 ULONG newNumBlocks;
7110 ULONG oldNumBlocks = 0;
7112 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7115 * Empty chain. Create the head.
7117 if (blockIndex == BLOCK_END_OF_CHAIN)
7119 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7120 SmallBlockChainStream_SetNextBlockInChain(
7121 This,
7122 blockIndex,
7123 BLOCK_END_OF_CHAIN);
7125 if (This->headOfStreamPlaceHolder != NULL)
7127 *(This->headOfStreamPlaceHolder) = blockIndex;
7129 else
7131 DirEntry chainEntry;
7133 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7134 &chainEntry);
7136 chainEntry.startingBlock = blockIndex;
7138 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7139 &chainEntry);
7143 currentBlock = blockIndex;
7146 * Figure out how many blocks are needed to contain this stream
7148 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7150 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7151 newNumBlocks++;
7154 * Go to the current end of chain
7156 while (blockIndex != BLOCK_END_OF_CHAIN)
7158 oldNumBlocks++;
7159 currentBlock = blockIndex;
7160 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7161 return FALSE;
7165 * Add new blocks to the chain
7167 while (oldNumBlocks < newNumBlocks)
7169 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7170 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7172 SmallBlockChainStream_SetNextBlockInChain(
7173 This,
7174 blockIndex,
7175 BLOCK_END_OF_CHAIN);
7177 currentBlock = blockIndex;
7178 oldNumBlocks++;
7181 return TRUE;
7184 /******************************************************************************
7185 * SmallBlockChainStream_SetSize
7187 * Sets the size of this stream.
7188 * The file will grow if we grow the chain.
7190 * TODO: Free the actual blocks in the file when we shrink the chain.
7191 * Currently, the blocks are still in the file. So the file size
7192 * doesn't shrink even if we shrink streams.
7194 BOOL SmallBlockChainStream_SetSize(
7195 SmallBlockChainStream* This,
7196 ULARGE_INTEGER newSize)
7198 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7200 if (newSize.u.LowPart == size.u.LowPart)
7201 return TRUE;
7203 if (newSize.u.LowPart < size.u.LowPart)
7205 SmallBlockChainStream_Shrink(This, newSize);
7207 else
7209 SmallBlockChainStream_Enlarge(This, newSize);
7212 return TRUE;
7215 /******************************************************************************
7216 * SmallBlockChainStream_GetCount
7218 * Returns the number of small blocks that comprises this chain.
7219 * This is not the size of the stream as the last block may not be full!
7222 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7224 ULONG blockIndex;
7225 ULONG count = 0;
7227 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7229 while(blockIndex != BLOCK_END_OF_CHAIN)
7231 count++;
7233 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7234 blockIndex, &blockIndex)))
7235 return 0;
7238 return count;
7241 /******************************************************************************
7242 * SmallBlockChainStream_GetSize
7244 * Returns the size of this chain.
7246 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7248 DirEntry chainEntry;
7250 if(This->headOfStreamPlaceHolder != NULL)
7252 ULARGE_INTEGER result;
7253 result.u.HighPart = 0;
7255 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7256 This->parentStorage->smallBlockSize;
7258 return result;
7261 StorageImpl_ReadDirEntry(
7262 This->parentStorage,
7263 This->ownerDirEntry,
7264 &chainEntry);
7266 return chainEntry.size;
7269 static HRESULT create_storagefile(
7270 LPCOLESTR pwcsName,
7271 DWORD grfMode,
7272 DWORD grfAttrs,
7273 STGOPTIONS* pStgOptions,
7274 REFIID riid,
7275 void** ppstgOpen)
7277 StorageBaseImpl* newStorage = 0;
7278 HANDLE hFile = INVALID_HANDLE_VALUE;
7279 HRESULT hr = STG_E_INVALIDFLAG;
7280 DWORD shareMode;
7281 DWORD accessMode;
7282 DWORD creationMode;
7283 DWORD fileAttributes;
7284 WCHAR tempFileName[MAX_PATH];
7286 if (ppstgOpen == 0)
7287 return STG_E_INVALIDPOINTER;
7289 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7290 return STG_E_INVALIDPARAMETER;
7292 /* if no share mode given then DENY_NONE is the default */
7293 if (STGM_SHARE_MODE(grfMode) == 0)
7294 grfMode |= STGM_SHARE_DENY_NONE;
7296 if ( FAILED( validateSTGM(grfMode) ))
7297 goto end;
7299 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7300 switch(STGM_ACCESS_MODE(grfMode))
7302 case STGM_WRITE:
7303 case STGM_READWRITE:
7304 break;
7305 default:
7306 goto end;
7309 /* in direct mode, can only use SHARE_EXCLUSIVE */
7310 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7311 goto end;
7313 /* but in transacted mode, any share mode is valid */
7316 * Generate a unique name.
7318 if (pwcsName == 0)
7320 WCHAR tempPath[MAX_PATH];
7321 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7323 memset(tempPath, 0, sizeof(tempPath));
7324 memset(tempFileName, 0, sizeof(tempFileName));
7326 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7327 tempPath[0] = '.';
7329 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7330 pwcsName = tempFileName;
7331 else
7333 hr = STG_E_INSUFFICIENTMEMORY;
7334 goto end;
7337 creationMode = TRUNCATE_EXISTING;
7339 else
7341 creationMode = GetCreationModeFromSTGM(grfMode);
7345 * Interpret the STGM value grfMode
7347 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7348 accessMode = GetAccessModeFromSTGM(grfMode);
7350 if (grfMode & STGM_DELETEONRELEASE)
7351 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7352 else
7353 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7355 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7357 static int fixme;
7358 if (!fixme++)
7359 FIXME("Storage share mode not implemented.\n");
7362 *ppstgOpen = 0;
7364 hFile = CreateFileW(pwcsName,
7365 accessMode,
7366 shareMode,
7367 NULL,
7368 creationMode,
7369 fileAttributes,
7372 if (hFile == INVALID_HANDLE_VALUE)
7374 if(GetLastError() == ERROR_FILE_EXISTS)
7375 hr = STG_E_FILEALREADYEXISTS;
7376 else
7377 hr = E_FAIL;
7378 goto end;
7382 * Allocate and initialize the new IStorage32object.
7384 hr = Storage_Construct(
7385 hFile,
7386 pwcsName,
7387 NULL,
7388 grfMode,
7389 TRUE,
7390 TRUE,
7391 pStgOptions->ulSectorSize,
7392 &newStorage);
7394 if (FAILED(hr))
7396 goto end;
7399 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7401 IStorage_Release((IStorage*)newStorage);
7403 end:
7404 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7406 return hr;
7409 /******************************************************************************
7410 * StgCreateDocfile [OLE32.@]
7411 * Creates a new compound file storage object
7413 * PARAMS
7414 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7415 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7416 * reserved [ ?] unused?, usually 0
7417 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7419 * RETURNS
7420 * S_OK if the file was successfully created
7421 * some STG_E_ value if error
7422 * NOTES
7423 * if pwcsName is NULL, create file with new unique name
7424 * the function can returns
7425 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7426 * (unrealized now)
7428 HRESULT WINAPI StgCreateDocfile(
7429 LPCOLESTR pwcsName,
7430 DWORD grfMode,
7431 DWORD reserved,
7432 IStorage **ppstgOpen)
7434 STGOPTIONS stgoptions = {1, 0, 512};
7436 TRACE("(%s, %x, %d, %p)\n",
7437 debugstr_w(pwcsName), grfMode,
7438 reserved, ppstgOpen);
7440 if (ppstgOpen == 0)
7441 return STG_E_INVALIDPOINTER;
7442 if (reserved != 0)
7443 return STG_E_INVALIDPARAMETER;
7445 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7448 /******************************************************************************
7449 * StgCreateStorageEx [OLE32.@]
7451 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7453 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7454 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7456 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7458 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7459 return STG_E_INVALIDPARAMETER;
7462 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7464 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7465 return STG_E_INVALIDPARAMETER;
7468 if (stgfmt == STGFMT_FILE)
7470 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7471 return STG_E_INVALIDPARAMETER;
7474 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7476 STGOPTIONS defaultOptions = {1, 0, 512};
7478 if (!pStgOptions) pStgOptions = &defaultOptions;
7479 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7483 ERR("Invalid stgfmt argument\n");
7484 return STG_E_INVALIDPARAMETER;
7487 /******************************************************************************
7488 * StgCreatePropSetStg [OLE32.@]
7490 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7491 IPropertySetStorage **ppPropSetStg)
7493 HRESULT hr;
7495 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7496 if (reserved)
7497 hr = STG_E_INVALIDPARAMETER;
7498 else
7499 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7500 (void**)ppPropSetStg);
7501 return hr;
7504 /******************************************************************************
7505 * StgOpenStorageEx [OLE32.@]
7507 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7509 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7510 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7512 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7514 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7515 return STG_E_INVALIDPARAMETER;
7518 switch (stgfmt)
7520 case STGFMT_FILE:
7521 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7522 return STG_E_INVALIDPARAMETER;
7524 case STGFMT_STORAGE:
7525 break;
7527 case STGFMT_DOCFILE:
7528 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7530 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7531 return STG_E_INVALIDPARAMETER;
7533 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7534 break;
7536 case STGFMT_ANY:
7537 WARN("STGFMT_ANY assuming storage\n");
7538 break;
7540 default:
7541 return STG_E_INVALIDPARAMETER;
7544 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7548 /******************************************************************************
7549 * StgOpenStorage [OLE32.@]
7551 HRESULT WINAPI StgOpenStorage(
7552 const OLECHAR *pwcsName,
7553 IStorage *pstgPriority,
7554 DWORD grfMode,
7555 SNB snbExclude,
7556 DWORD reserved,
7557 IStorage **ppstgOpen)
7559 StorageBaseImpl* newStorage = 0;
7560 HRESULT hr = S_OK;
7561 HANDLE hFile = 0;
7562 DWORD shareMode;
7563 DWORD accessMode;
7565 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7566 debugstr_w(pwcsName), pstgPriority, grfMode,
7567 snbExclude, reserved, ppstgOpen);
7569 if (pwcsName == 0)
7571 hr = STG_E_INVALIDNAME;
7572 goto end;
7575 if (ppstgOpen == 0)
7577 hr = STG_E_INVALIDPOINTER;
7578 goto end;
7581 if (reserved)
7583 hr = STG_E_INVALIDPARAMETER;
7584 goto end;
7587 if (grfMode & STGM_PRIORITY)
7589 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7590 return STG_E_INVALIDFLAG;
7591 if (grfMode & STGM_DELETEONRELEASE)
7592 return STG_E_INVALIDFUNCTION;
7593 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7594 return STG_E_INVALIDFLAG;
7595 grfMode &= ~0xf0; /* remove the existing sharing mode */
7596 grfMode |= STGM_SHARE_DENY_NONE;
7598 /* STGM_PRIORITY stops other IStorage objects on the same file from
7599 * committing until the STGM_PRIORITY IStorage is closed. it also
7600 * stops non-transacted mode StgOpenStorage calls with write access from
7601 * succeeding. obviously, both of these cannot be achieved through just
7602 * file share flags */
7603 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7607 * Validate the sharing mode
7609 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7610 switch(STGM_SHARE_MODE(grfMode))
7612 case STGM_SHARE_EXCLUSIVE:
7613 case STGM_SHARE_DENY_WRITE:
7614 break;
7615 default:
7616 hr = STG_E_INVALIDFLAG;
7617 goto end;
7620 if ( FAILED( validateSTGM(grfMode) ) ||
7621 (grfMode&STGM_CREATE))
7623 hr = STG_E_INVALIDFLAG;
7624 goto end;
7627 /* shared reading requires transacted mode */
7628 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7629 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7630 !(grfMode&STGM_TRANSACTED) )
7632 hr = STG_E_INVALIDFLAG;
7633 goto end;
7637 * Interpret the STGM value grfMode
7639 shareMode = GetShareModeFromSTGM(grfMode);
7640 accessMode = GetAccessModeFromSTGM(grfMode);
7642 *ppstgOpen = 0;
7644 hFile = CreateFileW( pwcsName,
7645 accessMode,
7646 shareMode,
7647 NULL,
7648 OPEN_EXISTING,
7649 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7652 if (hFile==INVALID_HANDLE_VALUE)
7654 DWORD last_error = GetLastError();
7656 hr = E_FAIL;
7658 switch (last_error)
7660 case ERROR_FILE_NOT_FOUND:
7661 hr = STG_E_FILENOTFOUND;
7662 break;
7664 case ERROR_PATH_NOT_FOUND:
7665 hr = STG_E_PATHNOTFOUND;
7666 break;
7668 case ERROR_ACCESS_DENIED:
7669 case ERROR_WRITE_PROTECT:
7670 hr = STG_E_ACCESSDENIED;
7671 break;
7673 case ERROR_SHARING_VIOLATION:
7674 hr = STG_E_SHAREVIOLATION;
7675 break;
7677 default:
7678 hr = E_FAIL;
7681 goto end;
7685 * Refuse to open the file if it's too small to be a structured storage file
7686 * FIXME: verify the file when reading instead of here
7688 if (GetFileSize(hFile, NULL) < 0x100)
7690 CloseHandle(hFile);
7691 hr = STG_E_FILEALREADYEXISTS;
7692 goto end;
7696 * Allocate and initialize the new IStorage32object.
7698 hr = Storage_Construct(
7699 hFile,
7700 pwcsName,
7701 NULL,
7702 grfMode,
7703 TRUE,
7704 FALSE,
7705 512,
7706 &newStorage);
7708 if (FAILED(hr))
7711 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7713 if(hr == STG_E_INVALIDHEADER)
7714 hr = STG_E_FILEALREADYEXISTS;
7715 goto end;
7719 * Get an "out" pointer for the caller.
7721 *ppstgOpen = (IStorage*)newStorage;
7723 end:
7724 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7725 return hr;
7728 /******************************************************************************
7729 * StgCreateDocfileOnILockBytes [OLE32.@]
7731 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7732 ILockBytes *plkbyt,
7733 DWORD grfMode,
7734 DWORD reserved,
7735 IStorage** ppstgOpen)
7737 StorageBaseImpl* newStorage = 0;
7738 HRESULT hr = S_OK;
7740 if ((ppstgOpen == 0) || (plkbyt == 0))
7741 return STG_E_INVALIDPOINTER;
7744 * Allocate and initialize the new IStorage object.
7746 hr = Storage_Construct(
7749 plkbyt,
7750 grfMode,
7751 FALSE,
7752 TRUE,
7753 512,
7754 &newStorage);
7756 if (FAILED(hr))
7758 return hr;
7762 * Get an "out" pointer for the caller.
7764 *ppstgOpen = (IStorage*)newStorage;
7766 return hr;
7769 /******************************************************************************
7770 * StgOpenStorageOnILockBytes [OLE32.@]
7772 HRESULT WINAPI StgOpenStorageOnILockBytes(
7773 ILockBytes *plkbyt,
7774 IStorage *pstgPriority,
7775 DWORD grfMode,
7776 SNB snbExclude,
7777 DWORD reserved,
7778 IStorage **ppstgOpen)
7780 StorageBaseImpl* newStorage = 0;
7781 HRESULT hr = S_OK;
7783 if ((plkbyt == 0) || (ppstgOpen == 0))
7784 return STG_E_INVALIDPOINTER;
7786 if ( FAILED( validateSTGM(grfMode) ))
7787 return STG_E_INVALIDFLAG;
7789 *ppstgOpen = 0;
7792 * Allocate and initialize the new IStorage object.
7794 hr = Storage_Construct(
7797 plkbyt,
7798 grfMode,
7799 FALSE,
7800 FALSE,
7801 512,
7802 &newStorage);
7804 if (FAILED(hr))
7806 return hr;
7810 * Get an "out" pointer for the caller.
7812 *ppstgOpen = (IStorage*)newStorage;
7814 return hr;
7817 /******************************************************************************
7818 * StgSetTimes [ole32.@]
7819 * StgSetTimes [OLE32.@]
7823 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7824 FILETIME const *patime, FILETIME const *pmtime)
7826 IStorage *stg = NULL;
7827 HRESULT r;
7829 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7831 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7832 0, 0, &stg);
7833 if( SUCCEEDED(r) )
7835 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7836 IStorage_Release(stg);
7839 return r;
7842 /******************************************************************************
7843 * StgIsStorageILockBytes [OLE32.@]
7845 * Determines if the ILockBytes contains a storage object.
7847 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7849 BYTE sig[sizeof(STORAGE_magic)];
7850 ULARGE_INTEGER offset;
7851 ULONG read = 0;
7853 offset.u.HighPart = 0;
7854 offset.u.LowPart = 0;
7856 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
7858 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
7859 return S_OK;
7861 return S_FALSE;
7864 /******************************************************************************
7865 * WriteClassStg [OLE32.@]
7867 * This method will store the specified CLSID in the specified storage object
7869 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7871 HRESULT hRes;
7873 if(!pStg)
7874 return E_INVALIDARG;
7876 if(!rclsid)
7877 return STG_E_INVALIDPOINTER;
7879 hRes = IStorage_SetClass(pStg, rclsid);
7881 return hRes;
7884 /***********************************************************************
7885 * ReadClassStg (OLE32.@)
7887 * This method reads the CLSID previously written to a storage object with
7888 * the WriteClassStg.
7890 * PARAMS
7891 * pstg [I] IStorage pointer
7892 * pclsid [O] Pointer to where the CLSID is written
7894 * RETURNS
7895 * Success: S_OK.
7896 * Failure: HRESULT code.
7898 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7900 STATSTG pstatstg;
7901 HRESULT hRes;
7903 TRACE("(%p, %p)\n", pstg, pclsid);
7905 if(!pstg || !pclsid)
7906 return E_INVALIDARG;
7909 * read a STATSTG structure (contains the clsid) from the storage
7911 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7913 if(SUCCEEDED(hRes))
7914 *pclsid=pstatstg.clsid;
7916 return hRes;
7919 /***********************************************************************
7920 * OleLoadFromStream (OLE32.@)
7922 * This function loads an object from stream
7924 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7926 CLSID clsid;
7927 HRESULT res;
7928 LPPERSISTSTREAM xstm;
7930 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7932 res=ReadClassStm(pStm,&clsid);
7933 if (FAILED(res))
7934 return res;
7935 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7936 if (FAILED(res))
7937 return res;
7938 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7939 if (FAILED(res)) {
7940 IUnknown_Release((IUnknown*)*ppvObj);
7941 return res;
7943 res=IPersistStream_Load(xstm,pStm);
7944 IPersistStream_Release(xstm);
7945 /* FIXME: all refcounts ok at this point? I think they should be:
7946 * pStm : unchanged
7947 * ppvObj : 1
7948 * xstm : 0 (released)
7950 return res;
7953 /***********************************************************************
7954 * OleSaveToStream (OLE32.@)
7956 * This function saves an object with the IPersistStream interface on it
7957 * to the specified stream.
7959 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7962 CLSID clsid;
7963 HRESULT res;
7965 TRACE("(%p,%p)\n",pPStm,pStm);
7967 res=IPersistStream_GetClassID(pPStm,&clsid);
7969 if (SUCCEEDED(res)){
7971 res=WriteClassStm(pStm,&clsid);
7973 if (SUCCEEDED(res))
7975 res=IPersistStream_Save(pPStm,pStm,TRUE);
7978 TRACE("Finished Save\n");
7979 return res;
7982 /****************************************************************************
7983 * This method validate a STGM parameter that can contain the values below
7985 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7986 * The stgm values contained in 0xffff0000 are bitmasks.
7988 * STGM_DIRECT 0x00000000
7989 * STGM_TRANSACTED 0x00010000
7990 * STGM_SIMPLE 0x08000000
7992 * STGM_READ 0x00000000
7993 * STGM_WRITE 0x00000001
7994 * STGM_READWRITE 0x00000002
7996 * STGM_SHARE_DENY_NONE 0x00000040
7997 * STGM_SHARE_DENY_READ 0x00000030
7998 * STGM_SHARE_DENY_WRITE 0x00000020
7999 * STGM_SHARE_EXCLUSIVE 0x00000010
8001 * STGM_PRIORITY 0x00040000
8002 * STGM_DELETEONRELEASE 0x04000000
8004 * STGM_CREATE 0x00001000
8005 * STGM_CONVERT 0x00020000
8006 * STGM_FAILIFTHERE 0x00000000
8008 * STGM_NOSCRATCH 0x00100000
8009 * STGM_NOSNAPSHOT 0x00200000
8011 static HRESULT validateSTGM(DWORD stgm)
8013 DWORD access = STGM_ACCESS_MODE(stgm);
8014 DWORD share = STGM_SHARE_MODE(stgm);
8015 DWORD create = STGM_CREATE_MODE(stgm);
8017 if (stgm&~STGM_KNOWN_FLAGS)
8019 ERR("unknown flags %08x\n", stgm);
8020 return E_FAIL;
8023 switch (access)
8025 case STGM_READ:
8026 case STGM_WRITE:
8027 case STGM_READWRITE:
8028 break;
8029 default:
8030 return E_FAIL;
8033 switch (share)
8035 case STGM_SHARE_DENY_NONE:
8036 case STGM_SHARE_DENY_READ:
8037 case STGM_SHARE_DENY_WRITE:
8038 case STGM_SHARE_EXCLUSIVE:
8039 break;
8040 default:
8041 return E_FAIL;
8044 switch (create)
8046 case STGM_CREATE:
8047 case STGM_FAILIFTHERE:
8048 break;
8049 default:
8050 return E_FAIL;
8054 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8056 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8057 return E_FAIL;
8060 * STGM_CREATE | STGM_CONVERT
8061 * if both are false, STGM_FAILIFTHERE is set to TRUE
8063 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8064 return E_FAIL;
8067 * STGM_NOSCRATCH requires STGM_TRANSACTED
8069 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8070 return E_FAIL;
8073 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8074 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8076 if ( (stgm & STGM_NOSNAPSHOT) &&
8077 (!(stgm & STGM_TRANSACTED) ||
8078 share == STGM_SHARE_EXCLUSIVE ||
8079 share == STGM_SHARE_DENY_WRITE) )
8080 return E_FAIL;
8082 return S_OK;
8085 /****************************************************************************
8086 * GetShareModeFromSTGM
8088 * This method will return a share mode flag from a STGM value.
8089 * The STGM value is assumed valid.
8091 static DWORD GetShareModeFromSTGM(DWORD stgm)
8093 switch (STGM_SHARE_MODE(stgm))
8095 case STGM_SHARE_DENY_NONE:
8096 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8097 case STGM_SHARE_DENY_READ:
8098 return FILE_SHARE_WRITE;
8099 case STGM_SHARE_DENY_WRITE:
8100 return FILE_SHARE_READ;
8101 case STGM_SHARE_EXCLUSIVE:
8102 return 0;
8104 ERR("Invalid share mode!\n");
8105 assert(0);
8106 return 0;
8109 /****************************************************************************
8110 * GetAccessModeFromSTGM
8112 * This method will return an access mode flag from a STGM value.
8113 * The STGM value is assumed valid.
8115 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8117 switch (STGM_ACCESS_MODE(stgm))
8119 case STGM_READ:
8120 return GENERIC_READ;
8121 case STGM_WRITE:
8122 case STGM_READWRITE:
8123 return GENERIC_READ | GENERIC_WRITE;
8125 ERR("Invalid access mode!\n");
8126 assert(0);
8127 return 0;
8130 /****************************************************************************
8131 * GetCreationModeFromSTGM
8133 * This method will return a creation mode flag from a STGM value.
8134 * The STGM value is assumed valid.
8136 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8138 switch(STGM_CREATE_MODE(stgm))
8140 case STGM_CREATE:
8141 return CREATE_ALWAYS;
8142 case STGM_CONVERT:
8143 FIXME("STGM_CONVERT not implemented!\n");
8144 return CREATE_NEW;
8145 case STGM_FAILIFTHERE:
8146 return CREATE_NEW;
8148 ERR("Invalid create mode!\n");
8149 assert(0);
8150 return 0;
8154 /*************************************************************************
8155 * OLECONVERT_LoadOLE10 [Internal]
8157 * Loads the OLE10 STREAM to memory
8159 * PARAMS
8160 * pOleStream [I] The OLESTREAM
8161 * pData [I] Data Structure for the OLESTREAM Data
8163 * RETURNS
8164 * Success: S_OK
8165 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8166 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8168 * NOTES
8169 * This function is used by OleConvertOLESTREAMToIStorage only.
8171 * Memory allocated for pData must be freed by the caller
8173 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8175 DWORD dwSize;
8176 HRESULT hRes = S_OK;
8177 int nTryCnt=0;
8178 int max_try = 6;
8180 pData->pData = NULL;
8181 pData->pstrOleObjFileName = NULL;
8183 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8185 /* Get the OleID */
8186 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8187 if(dwSize != sizeof(pData->dwOleID))
8189 hRes = CONVERT10_E_OLESTREAM_GET;
8191 else if(pData->dwOleID != OLESTREAM_ID)
8193 hRes = CONVERT10_E_OLESTREAM_FMT;
8195 else
8197 hRes = S_OK;
8198 break;
8202 if(hRes == S_OK)
8204 /* Get the TypeID... more info needed for this field */
8205 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8206 if(dwSize != sizeof(pData->dwTypeID))
8208 hRes = CONVERT10_E_OLESTREAM_GET;
8211 if(hRes == S_OK)
8213 if(pData->dwTypeID != 0)
8215 /* Get the length of the OleTypeName */
8216 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8217 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8219 hRes = CONVERT10_E_OLESTREAM_GET;
8222 if(hRes == S_OK)
8224 if(pData->dwOleTypeNameLength > 0)
8226 /* Get the OleTypeName */
8227 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8228 if(dwSize != pData->dwOleTypeNameLength)
8230 hRes = CONVERT10_E_OLESTREAM_GET;
8234 if(bStrem1)
8236 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8237 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8239 hRes = CONVERT10_E_OLESTREAM_GET;
8241 if(hRes == S_OK)
8243 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8244 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8245 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8246 if(pData->pstrOleObjFileName)
8248 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8249 if(dwSize != pData->dwOleObjFileNameLength)
8251 hRes = CONVERT10_E_OLESTREAM_GET;
8254 else
8255 hRes = CONVERT10_E_OLESTREAM_GET;
8258 else
8260 /* Get the Width of the Metafile */
8261 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8262 if(dwSize != sizeof(pData->dwMetaFileWidth))
8264 hRes = CONVERT10_E_OLESTREAM_GET;
8266 if(hRes == S_OK)
8268 /* Get the Height of the Metafile */
8269 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8270 if(dwSize != sizeof(pData->dwMetaFileHeight))
8272 hRes = CONVERT10_E_OLESTREAM_GET;
8276 if(hRes == S_OK)
8278 /* Get the Length of the Data */
8279 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8280 if(dwSize != sizeof(pData->dwDataLength))
8282 hRes = CONVERT10_E_OLESTREAM_GET;
8286 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8288 if(!bStrem1) /* if it is a second OLE stream data */
8290 pData->dwDataLength -= 8;
8291 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8292 if(dwSize != sizeof(pData->strUnknown))
8294 hRes = CONVERT10_E_OLESTREAM_GET;
8298 if(hRes == S_OK)
8300 if(pData->dwDataLength > 0)
8302 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8304 /* Get Data (ex. IStorage, Metafile, or BMP) */
8305 if(pData->pData)
8307 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8308 if(dwSize != pData->dwDataLength)
8310 hRes = CONVERT10_E_OLESTREAM_GET;
8313 else
8315 hRes = CONVERT10_E_OLESTREAM_GET;
8321 return hRes;
8324 /*************************************************************************
8325 * OLECONVERT_SaveOLE10 [Internal]
8327 * Saves the OLE10 STREAM From memory
8329 * PARAMS
8330 * pData [I] Data Structure for the OLESTREAM Data
8331 * pOleStream [I] The OLESTREAM to save
8333 * RETURNS
8334 * Success: S_OK
8335 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8337 * NOTES
8338 * This function is used by OleConvertIStorageToOLESTREAM only.
8341 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8343 DWORD dwSize;
8344 HRESULT hRes = S_OK;
8347 /* Set the OleID */
8348 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8349 if(dwSize != sizeof(pData->dwOleID))
8351 hRes = CONVERT10_E_OLESTREAM_PUT;
8354 if(hRes == S_OK)
8356 /* Set the TypeID */
8357 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8358 if(dwSize != sizeof(pData->dwTypeID))
8360 hRes = CONVERT10_E_OLESTREAM_PUT;
8364 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8366 /* Set the Length of the OleTypeName */
8367 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8368 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8370 hRes = CONVERT10_E_OLESTREAM_PUT;
8373 if(hRes == S_OK)
8375 if(pData->dwOleTypeNameLength > 0)
8377 /* Set the OleTypeName */
8378 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8379 if(dwSize != pData->dwOleTypeNameLength)
8381 hRes = CONVERT10_E_OLESTREAM_PUT;
8386 if(hRes == S_OK)
8388 /* Set the width of the Metafile */
8389 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8390 if(dwSize != sizeof(pData->dwMetaFileWidth))
8392 hRes = CONVERT10_E_OLESTREAM_PUT;
8396 if(hRes == S_OK)
8398 /* Set the height of the Metafile */
8399 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8400 if(dwSize != sizeof(pData->dwMetaFileHeight))
8402 hRes = CONVERT10_E_OLESTREAM_PUT;
8406 if(hRes == S_OK)
8408 /* Set the length of the Data */
8409 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8410 if(dwSize != sizeof(pData->dwDataLength))
8412 hRes = CONVERT10_E_OLESTREAM_PUT;
8416 if(hRes == S_OK)
8418 if(pData->dwDataLength > 0)
8420 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8421 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8422 if(dwSize != pData->dwDataLength)
8424 hRes = CONVERT10_E_OLESTREAM_PUT;
8429 return hRes;
8432 /*************************************************************************
8433 * OLECONVERT_GetOLE20FromOLE10[Internal]
8435 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8436 * opens it, and copies the content to the dest IStorage for
8437 * OleConvertOLESTREAMToIStorage
8440 * PARAMS
8441 * pDestStorage [I] The IStorage to copy the data to
8442 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8443 * nBufferLength [I] The size of the buffer
8445 * RETURNS
8446 * Nothing
8448 * NOTES
8452 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8454 HRESULT hRes;
8455 HANDLE hFile;
8456 IStorage *pTempStorage;
8457 DWORD dwNumOfBytesWritten;
8458 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8459 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8461 /* Create a temp File */
8462 GetTempPathW(MAX_PATH, wstrTempDir);
8463 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8464 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8466 if(hFile != INVALID_HANDLE_VALUE)
8468 /* Write IStorage Data to File */
8469 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8470 CloseHandle(hFile);
8472 /* Open and copy temp storage to the Dest Storage */
8473 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8474 if(hRes == S_OK)
8476 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8477 IStorage_Release(pTempStorage);
8479 DeleteFileW(wstrTempFile);
8484 /*************************************************************************
8485 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8487 * Saves the OLE10 STREAM From memory
8489 * PARAMS
8490 * pStorage [I] The Src IStorage to copy
8491 * pData [I] The Dest Memory to write to.
8493 * RETURNS
8494 * The size in bytes allocated for pData
8496 * NOTES
8497 * Memory allocated for pData must be freed by the caller
8499 * Used by OleConvertIStorageToOLESTREAM only.
8502 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8504 HANDLE hFile;
8505 HRESULT hRes;
8506 DWORD nDataLength = 0;
8507 IStorage *pTempStorage;
8508 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8509 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8511 *pData = NULL;
8513 /* Create temp Storage */
8514 GetTempPathW(MAX_PATH, wstrTempDir);
8515 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8516 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8518 if(hRes == S_OK)
8520 /* Copy Src Storage to the Temp Storage */
8521 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8522 IStorage_Release(pTempStorage);
8524 /* Open Temp Storage as a file and copy to memory */
8525 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8526 if(hFile != INVALID_HANDLE_VALUE)
8528 nDataLength = GetFileSize(hFile, NULL);
8529 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8530 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8531 CloseHandle(hFile);
8533 DeleteFileW(wstrTempFile);
8535 return nDataLength;
8538 /*************************************************************************
8539 * OLECONVERT_CreateOleStream [Internal]
8541 * Creates the "\001OLE" stream in the IStorage if necessary.
8543 * PARAMS
8544 * pStorage [I] Dest storage to create the stream in
8546 * RETURNS
8547 * Nothing
8549 * NOTES
8550 * This function is used by OleConvertOLESTREAMToIStorage only.
8552 * This stream is still unknown, MS Word seems to have extra data
8553 * but since the data is stored in the OLESTREAM there should be
8554 * no need to recreate the stream. If the stream is manually
8555 * deleted it will create it with this default data.
8558 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8560 HRESULT hRes;
8561 IStream *pStream;
8562 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8563 BYTE pOleStreamHeader [] =
8565 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8566 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8567 0x00, 0x00, 0x00, 0x00
8570 /* Create stream if not present */
8571 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8572 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8574 if(hRes == S_OK)
8576 /* Write default Data */
8577 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8578 IStream_Release(pStream);
8582 /* write a string to a stream, preceded by its length */
8583 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8585 HRESULT r;
8586 LPSTR str;
8587 DWORD len = 0;
8589 if( string )
8590 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8591 r = IStream_Write( stm, &len, sizeof(len), NULL);
8592 if( FAILED( r ) )
8593 return r;
8594 if(len == 0)
8595 return r;
8596 str = CoTaskMemAlloc( len );
8597 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8598 r = IStream_Write( stm, str, len, NULL);
8599 CoTaskMemFree( str );
8600 return r;
8603 /* read a string preceded by its length from a stream */
8604 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8606 HRESULT r;
8607 DWORD len, count = 0;
8608 LPSTR str;
8609 LPWSTR wstr;
8611 r = IStream_Read( stm, &len, sizeof(len), &count );
8612 if( FAILED( r ) )
8613 return r;
8614 if( count != sizeof(len) )
8615 return E_OUTOFMEMORY;
8617 TRACE("%d bytes\n",len);
8619 str = CoTaskMemAlloc( len );
8620 if( !str )
8621 return E_OUTOFMEMORY;
8622 count = 0;
8623 r = IStream_Read( stm, str, len, &count );
8624 if( FAILED( r ) )
8625 return r;
8626 if( count != len )
8628 CoTaskMemFree( str );
8629 return E_OUTOFMEMORY;
8632 TRACE("Read string %s\n",debugstr_an(str,len));
8634 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8635 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8636 if( wstr )
8637 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8638 CoTaskMemFree( str );
8640 *string = wstr;
8642 return r;
8646 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8647 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8649 IStream *pstm;
8650 HRESULT r = S_OK;
8651 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8653 static const BYTE unknown1[12] =
8654 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8655 0xFF, 0xFF, 0xFF, 0xFF};
8656 static const BYTE unknown2[16] =
8657 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8658 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8660 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8661 debugstr_w(lpszUserType), debugstr_w(szClipName),
8662 debugstr_w(szProgIDName));
8664 /* Create a CompObj stream */
8665 r = IStorage_CreateStream(pstg, szwStreamName,
8666 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8667 if( FAILED (r) )
8668 return r;
8670 /* Write CompObj Structure to stream */
8671 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8673 if( SUCCEEDED( r ) )
8674 r = WriteClassStm( pstm, clsid );
8676 if( SUCCEEDED( r ) )
8677 r = STREAM_WriteString( pstm, lpszUserType );
8678 if( SUCCEEDED( r ) )
8679 r = STREAM_WriteString( pstm, szClipName );
8680 if( SUCCEEDED( r ) )
8681 r = STREAM_WriteString( pstm, szProgIDName );
8682 if( SUCCEEDED( r ) )
8683 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8685 IStream_Release( pstm );
8687 return r;
8690 /***********************************************************************
8691 * WriteFmtUserTypeStg (OLE32.@)
8693 HRESULT WINAPI WriteFmtUserTypeStg(
8694 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8696 HRESULT r;
8697 WCHAR szwClipName[0x40];
8698 CLSID clsid = CLSID_NULL;
8699 LPWSTR wstrProgID = NULL;
8700 DWORD n;
8702 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8704 /* get the clipboard format name */
8705 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8706 szwClipName[n]=0;
8708 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8710 /* FIXME: There's room to save a CLSID and its ProgID, but
8711 the CLSID is not looked up in the registry and in all the
8712 tests I wrote it was CLSID_NULL. Where does it come from?
8715 /* get the real program ID. This may fail, but that's fine */
8716 ProgIDFromCLSID(&clsid, &wstrProgID);
8718 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8720 r = STORAGE_WriteCompObj( pstg, &clsid,
8721 lpszUserType, szwClipName, wstrProgID );
8723 CoTaskMemFree(wstrProgID);
8725 return r;
8729 /******************************************************************************
8730 * ReadFmtUserTypeStg [OLE32.@]
8732 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8734 HRESULT r;
8735 IStream *stm = 0;
8736 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8737 unsigned char unknown1[12];
8738 unsigned char unknown2[16];
8739 DWORD count;
8740 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8741 CLSID clsid;
8743 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8745 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8746 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8747 if( FAILED ( r ) )
8749 WARN("Failed to open stream r = %08x\n", r);
8750 return r;
8753 /* read the various parts of the structure */
8754 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8755 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8756 goto end;
8757 r = ReadClassStm( stm, &clsid );
8758 if( FAILED( r ) )
8759 goto end;
8761 r = STREAM_ReadString( stm, &szCLSIDName );
8762 if( FAILED( r ) )
8763 goto end;
8765 r = STREAM_ReadString( stm, &szOleTypeName );
8766 if( FAILED( r ) )
8767 goto end;
8769 r = STREAM_ReadString( stm, &szProgIDName );
8770 if( FAILED( r ) )
8771 goto end;
8773 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8774 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8775 goto end;
8777 /* ok, success... now we just need to store what we found */
8778 if( pcf )
8779 *pcf = RegisterClipboardFormatW( szOleTypeName );
8780 CoTaskMemFree( szOleTypeName );
8782 if( lplpszUserType )
8783 *lplpszUserType = szCLSIDName;
8784 CoTaskMemFree( szProgIDName );
8786 end:
8787 IStream_Release( stm );
8789 return r;
8793 /*************************************************************************
8794 * OLECONVERT_CreateCompObjStream [Internal]
8796 * Creates a "\001CompObj" is the destination IStorage if necessary.
8798 * PARAMS
8799 * pStorage [I] The dest IStorage to create the CompObj Stream
8800 * if necessary.
8801 * strOleTypeName [I] The ProgID
8803 * RETURNS
8804 * Success: S_OK
8805 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8807 * NOTES
8808 * This function is used by OleConvertOLESTREAMToIStorage only.
8810 * The stream data is stored in the OLESTREAM and there should be
8811 * no need to recreate the stream. If the stream is manually
8812 * deleted it will attempt to create it by querying the registry.
8816 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8818 IStream *pStream;
8819 HRESULT hStorageRes, hRes = S_OK;
8820 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8821 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8822 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8824 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8825 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8827 /* Initialize the CompObj structure */
8828 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8829 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8830 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8833 /* Create a CompObj stream if it doesn't exist */
8834 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8835 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8836 if(hStorageRes == S_OK)
8838 /* copy the OleTypeName to the compobj struct */
8839 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8840 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8842 /* copy the OleTypeName to the compobj struct */
8843 /* Note: in the test made, these were Identical */
8844 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8845 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8847 /* Get the CLSID */
8848 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8849 bufferW, OLESTREAM_MAX_STR_LEN );
8850 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8852 if(hRes == S_OK)
8854 HKEY hKey;
8855 LONG hErr;
8856 /* Get the CLSID Default Name from the Registry */
8857 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8858 if(hErr == ERROR_SUCCESS)
8860 char strTemp[OLESTREAM_MAX_STR_LEN];
8861 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8862 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8863 if(hErr == ERROR_SUCCESS)
8865 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8867 RegCloseKey(hKey);
8871 /* Write CompObj Structure to stream */
8872 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8874 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8876 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8877 if(IStorageCompObj.dwCLSIDNameLength > 0)
8879 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8881 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8882 if(IStorageCompObj.dwOleTypeNameLength > 0)
8884 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8886 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8887 if(IStorageCompObj.dwProgIDNameLength > 0)
8889 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8891 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8892 IStream_Release(pStream);
8894 return hRes;
8898 /*************************************************************************
8899 * OLECONVERT_CreateOlePresStream[Internal]
8901 * Creates the "\002OlePres000" Stream with the Metafile data
8903 * PARAMS
8904 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8905 * dwExtentX [I] Width of the Metafile
8906 * dwExtentY [I] Height of the Metafile
8907 * pData [I] Metafile data
8908 * dwDataLength [I] Size of the Metafile data
8910 * RETURNS
8911 * Success: S_OK
8912 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8914 * NOTES
8915 * This function is used by OleConvertOLESTREAMToIStorage only.
8918 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8920 HRESULT hRes;
8921 IStream *pStream;
8922 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8923 BYTE pOlePresStreamHeader [] =
8925 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8926 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8927 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8928 0x00, 0x00, 0x00, 0x00
8931 BYTE pOlePresStreamHeaderEmpty [] =
8933 0x00, 0x00, 0x00, 0x00,
8934 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8935 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8936 0x00, 0x00, 0x00, 0x00
8939 /* Create the OlePres000 Stream */
8940 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8941 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8943 if(hRes == S_OK)
8945 DWORD nHeaderSize;
8946 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8948 memset(&OlePres, 0, sizeof(OlePres));
8949 /* Do we have any metafile data to save */
8950 if(dwDataLength > 0)
8952 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8953 nHeaderSize = sizeof(pOlePresStreamHeader);
8955 else
8957 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8958 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8960 /* Set width and height of the metafile */
8961 OlePres.dwExtentX = dwExtentX;
8962 OlePres.dwExtentY = -dwExtentY;
8964 /* Set Data and Length */
8965 if(dwDataLength > sizeof(METAFILEPICT16))
8967 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8968 OlePres.pData = &(pData[8]);
8970 /* Save OlePres000 Data to Stream */
8971 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8972 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8973 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8974 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8975 if(OlePres.dwSize > 0)
8977 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8979 IStream_Release(pStream);
8983 /*************************************************************************
8984 * OLECONVERT_CreateOle10NativeStream [Internal]
8986 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8988 * PARAMS
8989 * pStorage [I] Dest storage to create the stream in
8990 * pData [I] Ole10 Native Data (ex. bmp)
8991 * dwDataLength [I] Size of the Ole10 Native Data
8993 * RETURNS
8994 * Nothing
8996 * NOTES
8997 * This function is used by OleConvertOLESTREAMToIStorage only.
8999 * Might need to verify the data and return appropriate error message
9002 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9004 HRESULT hRes;
9005 IStream *pStream;
9006 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9008 /* Create the Ole10Native Stream */
9009 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9010 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9012 if(hRes == S_OK)
9014 /* Write info to stream */
9015 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9016 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9017 IStream_Release(pStream);
9022 /*************************************************************************
9023 * OLECONVERT_GetOLE10ProgID [Internal]
9025 * Finds the ProgID (or OleTypeID) from the IStorage
9027 * PARAMS
9028 * pStorage [I] The Src IStorage to get the ProgID
9029 * strProgID [I] the ProgID string to get
9030 * dwSize [I] the size of the string
9032 * RETURNS
9033 * Success: S_OK
9034 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9036 * NOTES
9037 * This function is used by OleConvertIStorageToOLESTREAM only.
9041 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9043 HRESULT hRes;
9044 IStream *pStream;
9045 LARGE_INTEGER iSeekPos;
9046 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9047 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9049 /* Open the CompObj Stream */
9050 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9051 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9052 if(hRes == S_OK)
9055 /*Get the OleType from the CompObj Stream */
9056 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9057 iSeekPos.u.HighPart = 0;
9059 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9060 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9061 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9062 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9063 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9064 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9065 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9067 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9068 if(*dwSize > 0)
9070 IStream_Read(pStream, strProgID, *dwSize, NULL);
9072 IStream_Release(pStream);
9074 else
9076 STATSTG stat;
9077 LPOLESTR wstrProgID;
9079 /* Get the OleType from the registry */
9080 REFCLSID clsid = &(stat.clsid);
9081 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9082 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9083 if(hRes == S_OK)
9085 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9089 return hRes;
9092 /*************************************************************************
9093 * OLECONVERT_GetOle10PresData [Internal]
9095 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9097 * PARAMS
9098 * pStorage [I] Src IStroage
9099 * pOleStream [I] Dest OleStream Mem Struct
9101 * RETURNS
9102 * Nothing
9104 * NOTES
9105 * This function is used by OleConvertIStorageToOLESTREAM only.
9107 * Memory allocated for pData must be freed by the caller
9111 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9114 HRESULT hRes;
9115 IStream *pStream;
9116 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9118 /* Initialize Default data for OLESTREAM */
9119 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9120 pOleStreamData[0].dwTypeID = 2;
9121 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9122 pOleStreamData[1].dwTypeID = 0;
9123 pOleStreamData[0].dwMetaFileWidth = 0;
9124 pOleStreamData[0].dwMetaFileHeight = 0;
9125 pOleStreamData[0].pData = NULL;
9126 pOleStreamData[1].pData = NULL;
9128 /* Open Ole10Native Stream */
9129 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9130 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9131 if(hRes == S_OK)
9134 /* Read Size and Data */
9135 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9136 if(pOleStreamData->dwDataLength > 0)
9138 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9139 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9141 IStream_Release(pStream);
9147 /*************************************************************************
9148 * OLECONVERT_GetOle20PresData[Internal]
9150 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9152 * PARAMS
9153 * pStorage [I] Src IStroage
9154 * pOleStreamData [I] Dest OleStream Mem Struct
9156 * RETURNS
9157 * Nothing
9159 * NOTES
9160 * This function is used by OleConvertIStorageToOLESTREAM only.
9162 * Memory allocated for pData must be freed by the caller
9164 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9166 HRESULT hRes;
9167 IStream *pStream;
9168 OLECONVERT_ISTORAGE_OLEPRES olePress;
9169 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9171 /* Initialize Default data for OLESTREAM */
9172 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9173 pOleStreamData[0].dwTypeID = 2;
9174 pOleStreamData[0].dwMetaFileWidth = 0;
9175 pOleStreamData[0].dwMetaFileHeight = 0;
9176 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9177 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9178 pOleStreamData[1].dwTypeID = 0;
9179 pOleStreamData[1].dwOleTypeNameLength = 0;
9180 pOleStreamData[1].strOleTypeName[0] = 0;
9181 pOleStreamData[1].dwMetaFileWidth = 0;
9182 pOleStreamData[1].dwMetaFileHeight = 0;
9183 pOleStreamData[1].pData = NULL;
9184 pOleStreamData[1].dwDataLength = 0;
9187 /* Open OlePress000 stream */
9188 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9189 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9190 if(hRes == S_OK)
9192 LARGE_INTEGER iSeekPos;
9193 METAFILEPICT16 MetaFilePict;
9194 static const char strMetafilePictName[] = "METAFILEPICT";
9196 /* Set the TypeID for a Metafile */
9197 pOleStreamData[1].dwTypeID = 5;
9199 /* Set the OleTypeName to Metafile */
9200 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9201 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9203 iSeekPos.u.HighPart = 0;
9204 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9206 /* Get Presentation Data */
9207 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9208 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9209 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9210 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9212 /*Set width and Height */
9213 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9214 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9215 if(olePress.dwSize > 0)
9217 /* Set Length */
9218 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9220 /* Set MetaFilePict struct */
9221 MetaFilePict.mm = 8;
9222 MetaFilePict.xExt = olePress.dwExtentX;
9223 MetaFilePict.yExt = olePress.dwExtentY;
9224 MetaFilePict.hMF = 0;
9226 /* Get Metafile Data */
9227 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9228 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9229 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9231 IStream_Release(pStream);
9235 /*************************************************************************
9236 * OleConvertOLESTREAMToIStorage [OLE32.@]
9238 * Read info on MSDN
9240 * TODO
9241 * DVTARGETDEVICE parameter is not handled
9242 * Still unsure of some mem fields for OLE 10 Stream
9243 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9244 * and "\001OLE" streams
9247 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9248 LPOLESTREAM pOleStream,
9249 LPSTORAGE pstg,
9250 const DVTARGETDEVICE* ptd)
9252 int i;
9253 HRESULT hRes=S_OK;
9254 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9256 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9258 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9260 if(ptd != NULL)
9262 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9265 if(pstg == NULL || pOleStream == NULL)
9267 hRes = E_INVALIDARG;
9270 if(hRes == S_OK)
9272 /* Load the OLESTREAM to Memory */
9273 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9276 if(hRes == S_OK)
9278 /* Load the OLESTREAM to Memory (part 2)*/
9279 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9282 if(hRes == S_OK)
9285 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9287 /* Do we have the IStorage Data in the OLESTREAM */
9288 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9290 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9291 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9293 else
9295 /* It must be an original OLE 1.0 source */
9296 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9299 else
9301 /* It must be an original OLE 1.0 source */
9302 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9305 /* Create CompObj Stream if necessary */
9306 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9307 if(hRes == S_OK)
9309 /*Create the Ole Stream if necessary */
9310 OLECONVERT_CreateOleStream(pstg);
9315 /* Free allocated memory */
9316 for(i=0; i < 2; i++)
9318 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9319 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9320 pOleStreamData[i].pstrOleObjFileName = NULL;
9322 return hRes;
9325 /*************************************************************************
9326 * OleConvertIStorageToOLESTREAM [OLE32.@]
9328 * Read info on MSDN
9330 * Read info on MSDN
9332 * TODO
9333 * Still unsure of some mem fields for OLE 10 Stream
9334 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9335 * and "\001OLE" streams.
9338 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9339 LPSTORAGE pstg,
9340 LPOLESTREAM pOleStream)
9342 int i;
9343 HRESULT hRes = S_OK;
9344 IStream *pStream;
9345 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9346 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9348 TRACE("%p %p\n", pstg, pOleStream);
9350 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9352 if(pstg == NULL || pOleStream == NULL)
9354 hRes = E_INVALIDARG;
9356 if(hRes == S_OK)
9358 /* Get the ProgID */
9359 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9360 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9362 if(hRes == S_OK)
9364 /* Was it originally Ole10 */
9365 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9366 if(hRes == S_OK)
9368 IStream_Release(pStream);
9369 /* Get Presentation Data for Ole10Native */
9370 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9372 else
9374 /* Get Presentation Data (OLE20) */
9375 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9378 /* Save OLESTREAM */
9379 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9380 if(hRes == S_OK)
9382 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9387 /* Free allocated memory */
9388 for(i=0; i < 2; i++)
9390 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9393 return hRes;
9396 /***********************************************************************
9397 * GetConvertStg (OLE32.@)
9399 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9400 FIXME("unimplemented stub!\n");
9401 return E_FAIL;
9404 /******************************************************************************
9405 * StgIsStorageFile [OLE32.@]
9406 * Verify if the file contains a storage object
9408 * PARAMS
9409 * fn [ I] Filename
9411 * RETURNS
9412 * S_OK if file has magic bytes as a storage object
9413 * S_FALSE if file is not storage
9415 HRESULT WINAPI
9416 StgIsStorageFile(LPCOLESTR fn)
9418 HANDLE hf;
9419 BYTE magic[8];
9420 DWORD bytes_read;
9422 TRACE("%s\n", debugstr_w(fn));
9423 hf = CreateFileW(fn, GENERIC_READ,
9424 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9425 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9427 if (hf == INVALID_HANDLE_VALUE)
9428 return STG_E_FILENOTFOUND;
9430 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9432 WARN(" unable to read file\n");
9433 CloseHandle(hf);
9434 return S_FALSE;
9437 CloseHandle(hf);
9439 if (bytes_read != 8) {
9440 TRACE(" too short\n");
9441 return S_FALSE;
9444 if (!memcmp(magic,STORAGE_magic,8)) {
9445 TRACE(" -> YES\n");
9446 return S_OK;
9449 TRACE(" -> Invalid header.\n");
9450 return S_FALSE;
9453 /***********************************************************************
9454 * WriteClassStm (OLE32.@)
9456 * Writes a CLSID to a stream.
9458 * PARAMS
9459 * pStm [I] Stream to write to.
9460 * rclsid [I] CLSID to write.
9462 * RETURNS
9463 * Success: S_OK.
9464 * Failure: HRESULT code.
9466 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9468 TRACE("(%p,%p)\n",pStm,rclsid);
9470 if (!pStm || !rclsid)
9471 return E_INVALIDARG;
9473 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9476 /***********************************************************************
9477 * ReadClassStm (OLE32.@)
9479 * Reads a CLSID from a stream.
9481 * PARAMS
9482 * pStm [I] Stream to read from.
9483 * rclsid [O] CLSID to read.
9485 * RETURNS
9486 * Success: S_OK.
9487 * Failure: HRESULT code.
9489 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9491 ULONG nbByte;
9492 HRESULT res;
9494 TRACE("(%p,%p)\n",pStm,pclsid);
9496 if (!pStm || !pclsid)
9497 return E_INVALIDARG;
9499 /* clear the output args */
9500 *pclsid = CLSID_NULL;
9502 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9504 if (FAILED(res))
9505 return res;
9507 if (nbByte != sizeof(CLSID))
9508 return STG_E_READFAULT;
9509 else
9510 return S_OK;