po: Update Lithuanian translation.
[wine.git] / dlls / ole32 / storage32.c
blobf259d246d700ffb4213c7a495b34c632759e0805
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"
55 #include "compobj_private.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(storage);
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
60 #define OLESTREAM_ID 0x501
61 #define OLESTREAM_MAX_STR_LEN 255
64 * These are signatures to detect the type of Document file.
66 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
67 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
69 static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
71 return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
74 /****************************************************************************
75 * Storage32InternalImpl definitions.
77 * Definition of the implementation structure for the IStorage32 interface.
78 * This one implements the IStorage32 interface for storage that are
79 * inside another storage.
81 struct StorageInternalImpl
83 struct StorageBaseImpl base;
86 * Entry in the parent's stream tracking list
88 struct list ParentListEntry;
90 StorageBaseImpl *parentStorage;
92 typedef struct StorageInternalImpl StorageInternalImpl;
94 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
95 static const IStorageVtbl Storage32InternalImpl_Vtbl;
97 /* Method definitions for the Storage32InternalImpl class. */
98 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
99 DWORD openFlags, DirRef storageDirEntry);
100 static void StorageImpl_Destroy(StorageBaseImpl* iface);
101 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
102 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
103 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
104 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
105 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
106 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
107 static void StorageImpl_SaveFileHeader(StorageImpl* This);
109 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
110 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
111 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
112 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
113 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
115 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
116 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
117 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
119 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
120 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
121 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
122 ULONG blockIndex, ULONG offset, DWORD value);
123 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
124 ULONG blockIndex, ULONG offset, DWORD* value);
126 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
127 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
129 typedef struct TransactedDirEntry
131 /* If applicable, a reference to the original DirEntry in the transacted
132 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
133 DirRef transactedParentEntry;
135 /* True if this entry is being used. */
136 int inuse;
138 /* True if data is up to date. */
139 int read;
141 /* True if this entry has been modified. */
142 int dirty;
144 /* True if this entry's stream has been modified. */
145 int stream_dirty;
147 /* True if this entry has been deleted in the transacted storage, but the
148 * delete has not yet been committed. */
149 int deleted;
151 /* If this entry's stream has been modified, a reference to where the stream
152 * is stored in the snapshot file. */
153 DirRef stream_entry;
155 /* This directory entry's data, including any changes that have been made. */
156 DirEntry data;
158 /* A reference to the parent of this node. This is only valid while we are
159 * committing changes. */
160 DirRef parent;
162 /* A reference to a newly-created entry in the transacted parent. This is
163 * always equal to transactedParentEntry except when committing changes. */
164 DirRef newTransactedParentEntry;
165 } TransactedDirEntry;
167 /****************************************************************************
168 * Transacted storage object.
170 typedef struct TransactedSnapshotImpl
172 struct StorageBaseImpl base;
175 * Modified streams are temporarily saved to the scratch file.
177 StorageBaseImpl *scratch;
179 /* The directory structure is kept here, so that we can track how these
180 * entries relate to those in the parent storage. */
181 TransactedDirEntry *entries;
182 ULONG entries_size;
183 ULONG firstFreeEntry;
186 * Changes are committed to the transacted parent.
188 StorageBaseImpl *transactedParent;
189 } TransactedSnapshotImpl;
191 /* Generic function to create a transacted wrapper for a direct storage object. */
192 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
194 /* OLESTREAM memory structure to use for Get and Put Routines */
195 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
196 typedef struct
198 DWORD dwOleID;
199 DWORD dwTypeID;
200 DWORD dwOleTypeNameLength;
201 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
202 CHAR *pstrOleObjFileName;
203 DWORD dwOleObjFileNameLength;
204 DWORD dwMetaFileWidth;
205 DWORD dwMetaFileHeight;
206 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
207 DWORD dwDataLength;
208 BYTE *pData;
209 }OLECONVERT_OLESTREAM_DATA;
211 /* CompObj Stream structure */
212 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
213 typedef struct
215 BYTE byUnknown1[12];
216 CLSID clsid;
217 DWORD dwCLSIDNameLength;
218 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
219 DWORD dwOleTypeNameLength;
220 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
221 DWORD dwProgIDNameLength;
222 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
223 BYTE byUnknown2[16];
224 }OLECONVERT_ISTORAGE_COMPOBJ;
227 /* Ole Presentation Stream structure */
228 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
229 typedef struct
231 BYTE byUnknown1[28];
232 DWORD dwExtentX;
233 DWORD dwExtentY;
234 DWORD dwSize;
235 BYTE *pData;
236 }OLECONVERT_ISTORAGE_OLEPRES;
240 /***********************************************************************
241 * Forward declaration of internal functions used by the method DestroyElement
243 static HRESULT deleteStorageContents(
244 StorageBaseImpl *parentStorage,
245 DirRef indexToDelete,
246 DirEntry entryDataToDelete);
248 static HRESULT deleteStreamContents(
249 StorageBaseImpl *parentStorage,
250 DirRef indexToDelete,
251 DirEntry entryDataToDelete);
253 static HRESULT removeFromTree(
254 StorageBaseImpl *This,
255 DirRef parentStorageIndex,
256 DirRef deletedIndex);
258 /***********************************************************************
259 * Declaration of the functions used to manipulate DirEntry
262 static HRESULT insertIntoTree(
263 StorageBaseImpl *This,
264 DirRef parentStorageIndex,
265 DirRef newEntryIndex);
267 static LONG entryNameCmp(
268 const OLECHAR *name1,
269 const OLECHAR *name2);
271 static DirRef findElement(
272 StorageBaseImpl *storage,
273 DirRef storageEntry,
274 const OLECHAR *name,
275 DirEntry *data);
277 static HRESULT findTreeParent(
278 StorageBaseImpl *storage,
279 DirRef storageEntry,
280 const OLECHAR *childName,
281 DirEntry *parentData,
282 DirRef *parentEntry,
283 ULONG *relation);
285 /***********************************************************************
286 * Declaration of miscellaneous functions...
288 static HRESULT validateSTGM(DWORD stgmValue);
290 static DWORD GetShareModeFromSTGM(DWORD stgm);
291 static DWORD GetAccessModeFromSTGM(DWORD stgm);
292 static DWORD GetCreationModeFromSTGM(DWORD stgm);
294 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
297 /****************************************************************************
298 * IEnumSTATSTGImpl definitions.
300 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
301 * This class allows iterating through the content of a storage and to find
302 * specific items inside it.
304 struct IEnumSTATSTGImpl
306 IEnumSTATSTG IEnumSTATSTG_iface;
308 LONG ref; /* Reference count */
309 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
310 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
312 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
315 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
317 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
321 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
322 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
324 /************************************************************************
325 ** Block Functions
328 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
330 return (index+1) * This->bigBlockSize;
333 /************************************************************************
334 ** Storage32BaseImpl implementation
336 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
337 ULARGE_INTEGER offset,
338 void* buffer,
339 ULONG size,
340 ULONG* bytesRead)
342 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
345 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
346 ULARGE_INTEGER offset,
347 const void* buffer,
348 const ULONG size,
349 ULONG* bytesWritten)
351 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
354 /************************************************************************
355 * Storage32BaseImpl_QueryInterface (IUnknown)
357 * This method implements the common QueryInterface for all IStorage32
358 * implementations contained in this file.
360 * See Windows documentation for more details on IUnknown methods.
362 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
363 IStorage* iface,
364 REFIID riid,
365 void** ppvObject)
367 StorageBaseImpl *This = impl_from_IStorage(iface);
369 if (!ppvObject)
370 return E_INVALIDARG;
372 *ppvObject = 0;
374 if (IsEqualGUID(&IID_IUnknown, riid) ||
375 IsEqualGUID(&IID_IStorage, riid))
377 *ppvObject = &This->IStorage_iface;
379 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
381 *ppvObject = &This->IPropertySetStorage_iface;
383 else
384 return E_NOINTERFACE;
386 IStorage_AddRef(iface);
388 return S_OK;
391 /************************************************************************
392 * Storage32BaseImpl_AddRef (IUnknown)
394 * This method implements the common AddRef for all IStorage32
395 * implementations contained in this file.
397 * See Windows documentation for more details on IUnknown methods.
399 static ULONG WINAPI StorageBaseImpl_AddRef(
400 IStorage* iface)
402 StorageBaseImpl *This = impl_from_IStorage(iface);
403 ULONG ref = InterlockedIncrement(&This->ref);
405 TRACE("(%p) AddRef to %d\n", This, ref);
407 return ref;
410 /************************************************************************
411 * Storage32BaseImpl_Release (IUnknown)
413 * This method implements the common Release for all IStorage32
414 * implementations contained in this file.
416 * See Windows documentation for more details on IUnknown methods.
418 static ULONG WINAPI StorageBaseImpl_Release(
419 IStorage* iface)
421 StorageBaseImpl *This = impl_from_IStorage(iface);
423 ULONG ref = InterlockedDecrement(&This->ref);
425 TRACE("(%p) ReleaseRef to %d\n", This, ref);
427 if (ref == 0)
430 * Since we are using a system of base-classes, we want to call the
431 * destructor of the appropriate derived class. To do this, we are
432 * using virtual functions to implement the destructor.
434 StorageBaseImpl_Destroy(This);
437 return ref;
440 /************************************************************************
441 * Storage32BaseImpl_OpenStream (IStorage)
443 * This method will open the specified stream object from the current storage.
445 * See Windows documentation for more details on IStorage methods.
447 static HRESULT WINAPI StorageBaseImpl_OpenStream(
448 IStorage* iface,
449 const OLECHAR* pwcsName, /* [string][in] */
450 void* reserved1, /* [unique][in] */
451 DWORD grfMode, /* [in] */
452 DWORD reserved2, /* [in] */
453 IStream** ppstm) /* [out] */
455 StorageBaseImpl *This = impl_from_IStorage(iface);
456 StgStreamImpl* newStream;
457 DirEntry currentEntry;
458 DirRef streamEntryRef;
459 HRESULT res = STG_E_UNKNOWN;
461 TRACE("(%p, %s, %p, %x, %d, %p)\n",
462 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
464 if ( (pwcsName==NULL) || (ppstm==0) )
466 res = E_INVALIDARG;
467 goto end;
470 *ppstm = NULL;
472 if ( FAILED( validateSTGM(grfMode) ) ||
473 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
475 res = STG_E_INVALIDFLAG;
476 goto end;
480 * As documented.
482 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
484 res = STG_E_INVALIDFUNCTION;
485 goto end;
488 if (This->reverted)
490 res = STG_E_REVERTED;
491 goto end;
495 * Check that we're compatible with the parent's storage mode, but
496 * only if we are not in transacted mode
498 if(!(This->openFlags & STGM_TRANSACTED)) {
499 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
501 res = STG_E_INVALIDFLAG;
502 goto end;
507 * Search for the element with the given name
509 streamEntryRef = findElement(
510 This,
511 This->storageDirEntry,
512 pwcsName,
513 &currentEntry);
516 * If it was found, construct the stream object and return a pointer to it.
518 if ( (streamEntryRef!=DIRENTRY_NULL) &&
519 (currentEntry.stgType==STGTY_STREAM) )
521 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
523 /* A single stream cannot be opened a second time. */
524 res = STG_E_ACCESSDENIED;
525 goto end;
528 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
530 if (newStream)
532 newStream->grfMode = grfMode;
533 *ppstm = &newStream->IStream_iface;
535 IStream_AddRef(*ppstm);
537 res = S_OK;
538 goto end;
541 res = E_OUTOFMEMORY;
542 goto end;
545 res = STG_E_FILENOTFOUND;
547 end:
548 if (res == S_OK)
549 TRACE("<-- IStream %p\n", *ppstm);
550 TRACE("<-- %08x\n", res);
551 return res;
554 /************************************************************************
555 * Storage32BaseImpl_OpenStorage (IStorage)
557 * This method will open a new storage object from the current storage.
559 * See Windows documentation for more details on IStorage methods.
561 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
562 IStorage* iface,
563 const OLECHAR* pwcsName, /* [string][unique][in] */
564 IStorage* pstgPriority, /* [unique][in] */
565 DWORD grfMode, /* [in] */
566 SNB snbExclude, /* [unique][in] */
567 DWORD reserved, /* [in] */
568 IStorage** ppstg) /* [out] */
570 StorageBaseImpl *This = impl_from_IStorage(iface);
571 StorageInternalImpl* newStorage;
572 StorageBaseImpl* newTransactedStorage;
573 DirEntry currentEntry;
574 DirRef storageEntryRef;
575 HRESULT res = STG_E_UNKNOWN;
577 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
578 iface, debugstr_w(pwcsName), pstgPriority,
579 grfMode, snbExclude, reserved, ppstg);
581 if ((pwcsName==NULL) || (ppstg==0) )
583 res = E_INVALIDARG;
584 goto end;
587 if (This->openFlags & STGM_SIMPLE)
589 res = STG_E_INVALIDFUNCTION;
590 goto end;
593 /* as documented */
594 if (snbExclude != NULL)
596 res = STG_E_INVALIDPARAMETER;
597 goto end;
600 if ( FAILED( validateSTGM(grfMode) ))
602 res = STG_E_INVALIDFLAG;
603 goto end;
607 * As documented.
609 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
610 (grfMode & STGM_DELETEONRELEASE) ||
611 (grfMode & STGM_PRIORITY) )
613 res = STG_E_INVALIDFUNCTION;
614 goto end;
617 if (This->reverted)
618 return STG_E_REVERTED;
621 * Check that we're compatible with the parent's storage mode,
622 * but only if we are not transacted
624 if(!(This->openFlags & STGM_TRANSACTED)) {
625 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
627 res = STG_E_ACCESSDENIED;
628 goto end;
632 *ppstg = NULL;
634 storageEntryRef = findElement(
635 This,
636 This->storageDirEntry,
637 pwcsName,
638 &currentEntry);
640 if ( (storageEntryRef!=DIRENTRY_NULL) &&
641 (currentEntry.stgType==STGTY_STORAGE) )
643 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
645 /* A single storage cannot be opened a second time. */
646 res = STG_E_ACCESSDENIED;
647 goto end;
650 newStorage = StorageInternalImpl_Construct(
651 This,
652 grfMode,
653 storageEntryRef);
655 if (newStorage != 0)
657 if (grfMode & STGM_TRANSACTED)
659 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
661 if (FAILED(res))
663 HeapFree(GetProcessHeap(), 0, newStorage);
664 goto end;
667 *ppstg = &newTransactedStorage->IStorage_iface;
669 else
671 *ppstg = &newStorage->base.IStorage_iface;
674 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
676 res = S_OK;
677 goto end;
680 res = STG_E_INSUFFICIENTMEMORY;
681 goto end;
684 res = STG_E_FILENOTFOUND;
686 end:
687 TRACE("<-- %08x\n", res);
688 return res;
691 /************************************************************************
692 * Storage32BaseImpl_EnumElements (IStorage)
694 * This method will create an enumerator object that can be used to
695 * retrieve information about all the elements in the storage object.
697 * See Windows documentation for more details on IStorage methods.
699 static HRESULT WINAPI StorageBaseImpl_EnumElements(
700 IStorage* iface,
701 DWORD reserved1, /* [in] */
702 void* reserved2, /* [size_is][unique][in] */
703 DWORD reserved3, /* [in] */
704 IEnumSTATSTG** ppenum) /* [out] */
706 StorageBaseImpl *This = impl_from_IStorage(iface);
707 IEnumSTATSTGImpl* newEnum;
709 TRACE("(%p, %d, %p, %d, %p)\n",
710 iface, reserved1, reserved2, reserved3, ppenum);
712 if (!ppenum)
713 return E_INVALIDARG;
715 if (This->reverted)
716 return STG_E_REVERTED;
718 newEnum = IEnumSTATSTGImpl_Construct(
719 This,
720 This->storageDirEntry);
722 if (newEnum)
724 *ppenum = &newEnum->IEnumSTATSTG_iface;
726 IEnumSTATSTG_AddRef(*ppenum);
728 return S_OK;
731 return E_OUTOFMEMORY;
734 /************************************************************************
735 * Storage32BaseImpl_Stat (IStorage)
737 * This method will retrieve information about this storage object.
739 * See Windows documentation for more details on IStorage methods.
741 static HRESULT WINAPI StorageBaseImpl_Stat(
742 IStorage* iface,
743 STATSTG* pstatstg, /* [out] */
744 DWORD grfStatFlag) /* [in] */
746 StorageBaseImpl *This = impl_from_IStorage(iface);
747 DirEntry currentEntry;
748 HRESULT res = STG_E_UNKNOWN;
750 TRACE("(%p, %p, %x)\n",
751 iface, pstatstg, grfStatFlag);
753 if (!pstatstg)
755 res = E_INVALIDARG;
756 goto end;
759 if (This->reverted)
761 res = STG_E_REVERTED;
762 goto end;
765 res = StorageBaseImpl_ReadDirEntry(
766 This,
767 This->storageDirEntry,
768 &currentEntry);
770 if (SUCCEEDED(res))
772 StorageUtl_CopyDirEntryToSTATSTG(
773 This,
774 pstatstg,
775 &currentEntry,
776 grfStatFlag);
778 pstatstg->grfMode = This->openFlags;
779 pstatstg->grfStateBits = This->stateBits;
782 end:
783 if (res == S_OK)
785 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);
787 TRACE("<-- %08x\n", res);
788 return res;
791 /************************************************************************
792 * Storage32BaseImpl_RenameElement (IStorage)
794 * This method will rename the specified element.
796 * See Windows documentation for more details on IStorage methods.
798 static HRESULT WINAPI StorageBaseImpl_RenameElement(
799 IStorage* iface,
800 const OLECHAR* pwcsOldName, /* [in] */
801 const OLECHAR* pwcsNewName) /* [in] */
803 StorageBaseImpl *This = impl_from_IStorage(iface);
804 DirEntry currentEntry;
805 DirRef currentEntryRef;
807 TRACE("(%p, %s, %s)\n",
808 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
810 if (This->reverted)
811 return STG_E_REVERTED;
813 currentEntryRef = findElement(This,
814 This->storageDirEntry,
815 pwcsNewName,
816 &currentEntry);
818 if (currentEntryRef != DIRENTRY_NULL)
821 * There is already an element with the new name
823 return STG_E_FILEALREADYEXISTS;
827 * Search for the old element name
829 currentEntryRef = findElement(This,
830 This->storageDirEntry,
831 pwcsOldName,
832 &currentEntry);
834 if (currentEntryRef != DIRENTRY_NULL)
836 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
837 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
839 WARN("Element is already open; cannot rename.\n");
840 return STG_E_ACCESSDENIED;
843 /* Remove the element from its current position in the tree */
844 removeFromTree(This, This->storageDirEntry,
845 currentEntryRef);
847 /* Change the name of the element */
848 strcpyW(currentEntry.name, pwcsNewName);
850 /* Delete any sibling links */
851 currentEntry.leftChild = DIRENTRY_NULL;
852 currentEntry.rightChild = DIRENTRY_NULL;
854 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
855 &currentEntry);
857 /* Insert the element in a new position in the tree */
858 insertIntoTree(This, This->storageDirEntry,
859 currentEntryRef);
861 else
864 * There is no element with the old name
866 return STG_E_FILENOTFOUND;
869 return StorageBaseImpl_Flush(This);
872 /************************************************************************
873 * Storage32BaseImpl_CreateStream (IStorage)
875 * This method will create a stream object within this storage
877 * See Windows documentation for more details on IStorage methods.
879 static HRESULT WINAPI StorageBaseImpl_CreateStream(
880 IStorage* iface,
881 const OLECHAR* pwcsName, /* [string][in] */
882 DWORD grfMode, /* [in] */
883 DWORD reserved1, /* [in] */
884 DWORD reserved2, /* [in] */
885 IStream** ppstm) /* [out] */
887 StorageBaseImpl *This = impl_from_IStorage(iface);
888 StgStreamImpl* newStream;
889 DirEntry currentEntry, newStreamEntry;
890 DirRef currentEntryRef, newStreamEntryRef;
891 HRESULT hr;
893 TRACE("(%p, %s, %x, %d, %d, %p)\n",
894 iface, debugstr_w(pwcsName), grfMode,
895 reserved1, reserved2, ppstm);
897 if (ppstm == 0)
898 return STG_E_INVALIDPOINTER;
900 if (pwcsName == 0)
901 return STG_E_INVALIDNAME;
903 if (reserved1 || reserved2)
904 return STG_E_INVALIDPARAMETER;
906 if ( FAILED( validateSTGM(grfMode) ))
907 return STG_E_INVALIDFLAG;
909 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
910 return STG_E_INVALIDFLAG;
912 if (This->reverted)
913 return STG_E_REVERTED;
916 * As documented.
918 if ((grfMode & STGM_DELETEONRELEASE) ||
919 (grfMode & STGM_TRANSACTED))
920 return STG_E_INVALIDFUNCTION;
923 * Don't worry about permissions in transacted mode, as we can always write
924 * changes; we just can't always commit them.
926 if(!(This->openFlags & STGM_TRANSACTED)) {
927 /* Can't create a stream on read-only storage */
928 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
929 return STG_E_ACCESSDENIED;
931 /* Can't create a stream with greater access than the parent. */
932 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
933 return STG_E_ACCESSDENIED;
936 if(This->openFlags & STGM_SIMPLE)
937 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
939 *ppstm = 0;
941 currentEntryRef = findElement(This,
942 This->storageDirEntry,
943 pwcsName,
944 &currentEntry);
946 if (currentEntryRef != DIRENTRY_NULL)
949 * An element with this name already exists
951 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
953 IStorage_DestroyElement(iface, pwcsName);
955 else
956 return STG_E_FILEALREADYEXISTS;
960 * memset the empty entry
962 memset(&newStreamEntry, 0, sizeof(DirEntry));
964 newStreamEntry.sizeOfNameString =
965 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
967 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
968 return STG_E_INVALIDNAME;
970 strcpyW(newStreamEntry.name, pwcsName);
972 newStreamEntry.stgType = STGTY_STREAM;
973 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
974 newStreamEntry.size.u.LowPart = 0;
975 newStreamEntry.size.u.HighPart = 0;
977 newStreamEntry.leftChild = DIRENTRY_NULL;
978 newStreamEntry.rightChild = DIRENTRY_NULL;
979 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
981 /* call CoFileTime to get the current time
982 newStreamEntry.ctime
983 newStreamEntry.mtime
986 /* newStreamEntry.clsid */
989 * Create an entry with the new data
991 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
992 if (FAILED(hr))
993 return hr;
996 * Insert the new entry in the parent storage's tree.
998 hr = insertIntoTree(
999 This,
1000 This->storageDirEntry,
1001 newStreamEntryRef);
1002 if (FAILED(hr))
1004 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
1005 return hr;
1009 * Open the stream to return it.
1011 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1013 if (newStream)
1015 *ppstm = &newStream->IStream_iface;
1016 IStream_AddRef(*ppstm);
1018 else
1020 return STG_E_INSUFFICIENTMEMORY;
1023 return StorageBaseImpl_Flush(This);
1026 /************************************************************************
1027 * Storage32BaseImpl_SetClass (IStorage)
1029 * This method will write the specified CLSID in the directory entry of this
1030 * storage.
1032 * See Windows documentation for more details on IStorage methods.
1034 static HRESULT WINAPI StorageBaseImpl_SetClass(
1035 IStorage* iface,
1036 REFCLSID clsid) /* [in] */
1038 StorageBaseImpl *This = impl_from_IStorage(iface);
1039 HRESULT hRes;
1040 DirEntry currentEntry;
1042 TRACE("(%p, %p)\n", iface, clsid);
1044 if (This->reverted)
1045 return STG_E_REVERTED;
1047 hRes = StorageBaseImpl_ReadDirEntry(This,
1048 This->storageDirEntry,
1049 &currentEntry);
1050 if (SUCCEEDED(hRes))
1052 currentEntry.clsid = *clsid;
1054 hRes = StorageBaseImpl_WriteDirEntry(This,
1055 This->storageDirEntry,
1056 &currentEntry);
1059 if (SUCCEEDED(hRes))
1060 hRes = StorageBaseImpl_Flush(This);
1062 return hRes;
1065 /************************************************************************
1066 ** Storage32Impl implementation
1069 /************************************************************************
1070 * Storage32BaseImpl_CreateStorage (IStorage)
1072 * This method will create the storage object within the provided storage.
1074 * See Windows documentation for more details on IStorage methods.
1076 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1077 IStorage* iface,
1078 const OLECHAR *pwcsName, /* [string][in] */
1079 DWORD grfMode, /* [in] */
1080 DWORD reserved1, /* [in] */
1081 DWORD reserved2, /* [in] */
1082 IStorage **ppstg) /* [out] */
1084 StorageBaseImpl* This = impl_from_IStorage(iface);
1086 DirEntry currentEntry;
1087 DirEntry newEntry;
1088 DirRef currentEntryRef;
1089 DirRef newEntryRef;
1090 HRESULT hr;
1092 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1093 iface, debugstr_w(pwcsName), grfMode,
1094 reserved1, reserved2, ppstg);
1096 if (ppstg == 0)
1097 return STG_E_INVALIDPOINTER;
1099 if (This->openFlags & STGM_SIMPLE)
1101 return STG_E_INVALIDFUNCTION;
1104 if (pwcsName == 0)
1105 return STG_E_INVALIDNAME;
1107 *ppstg = NULL;
1109 if ( FAILED( validateSTGM(grfMode) ) ||
1110 (grfMode & STGM_DELETEONRELEASE) )
1112 WARN("bad grfMode: 0x%x\n", grfMode);
1113 return STG_E_INVALIDFLAG;
1116 if (This->reverted)
1117 return STG_E_REVERTED;
1120 * Check that we're compatible with the parent's storage mode
1122 if ( !(This->openFlags & STGM_TRANSACTED) &&
1123 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1125 WARN("access denied\n");
1126 return STG_E_ACCESSDENIED;
1129 currentEntryRef = findElement(This,
1130 This->storageDirEntry,
1131 pwcsName,
1132 &currentEntry);
1134 if (currentEntryRef != DIRENTRY_NULL)
1137 * An element with this name already exists
1139 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1140 ((This->openFlags & STGM_TRANSACTED) ||
1141 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1143 hr = IStorage_DestroyElement(iface, pwcsName);
1144 if (FAILED(hr))
1145 return hr;
1147 else
1149 WARN("file already exists\n");
1150 return STG_E_FILEALREADYEXISTS;
1153 else if (!(This->openFlags & STGM_TRANSACTED) &&
1154 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1156 WARN("read-only storage\n");
1157 return STG_E_ACCESSDENIED;
1160 memset(&newEntry, 0, sizeof(DirEntry));
1162 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1164 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1166 FIXME("name too long\n");
1167 return STG_E_INVALIDNAME;
1170 strcpyW(newEntry.name, pwcsName);
1172 newEntry.stgType = STGTY_STORAGE;
1173 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1174 newEntry.size.u.LowPart = 0;
1175 newEntry.size.u.HighPart = 0;
1177 newEntry.leftChild = DIRENTRY_NULL;
1178 newEntry.rightChild = DIRENTRY_NULL;
1179 newEntry.dirRootEntry = DIRENTRY_NULL;
1181 /* call CoFileTime to get the current time
1182 newEntry.ctime
1183 newEntry.mtime
1186 /* newEntry.clsid */
1189 * Create a new directory entry for the storage
1191 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1192 if (FAILED(hr))
1193 return hr;
1196 * Insert the new directory entry into the parent storage's tree
1198 hr = insertIntoTree(
1199 This,
1200 This->storageDirEntry,
1201 newEntryRef);
1202 if (FAILED(hr))
1204 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1205 return hr;
1209 * Open it to get a pointer to return.
1211 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1213 if( (hr != S_OK) || (*ppstg == NULL))
1215 return hr;
1218 if (SUCCEEDED(hr))
1219 hr = StorageBaseImpl_Flush(This);
1221 return S_OK;
1225 /***************************************************************************
1227 * Internal Method
1229 * Reserve a directory entry in the file and initialize it.
1231 static HRESULT StorageImpl_CreateDirEntry(
1232 StorageBaseImpl *base,
1233 const DirEntry *newData,
1234 DirRef *index)
1236 StorageImpl *storage = (StorageImpl*)base;
1237 ULONG currentEntryIndex = 0;
1238 ULONG newEntryIndex = DIRENTRY_NULL;
1239 HRESULT hr = S_OK;
1240 BYTE currentData[RAW_DIRENTRY_SIZE];
1241 WORD sizeOfNameString;
1245 hr = StorageImpl_ReadRawDirEntry(storage,
1246 currentEntryIndex,
1247 currentData);
1249 if (SUCCEEDED(hr))
1251 StorageUtl_ReadWord(
1252 currentData,
1253 OFFSET_PS_NAMELENGTH,
1254 &sizeOfNameString);
1256 if (sizeOfNameString == 0)
1259 * The entry exists and is available, we found it.
1261 newEntryIndex = currentEntryIndex;
1264 else
1267 * We exhausted the directory entries, we will create more space below
1269 newEntryIndex = currentEntryIndex;
1271 currentEntryIndex++;
1273 } while (newEntryIndex == DIRENTRY_NULL);
1276 * grow the directory stream
1278 if (FAILED(hr))
1280 BYTE emptyData[RAW_DIRENTRY_SIZE];
1281 ULARGE_INTEGER newSize;
1282 ULONG entryIndex;
1283 ULONG lastEntry = 0;
1284 ULONG blockCount = 0;
1287 * obtain the new count of blocks in the directory stream
1289 blockCount = BlockChainStream_GetCount(
1290 storage->rootBlockChain)+1;
1293 * initialize the size used by the directory stream
1295 newSize.u.HighPart = 0;
1296 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1299 * add a block to the directory stream
1301 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1304 * memset the empty entry in order to initialize the unused newly
1305 * created entries
1307 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1310 * initialize them
1312 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1314 for(
1315 entryIndex = newEntryIndex + 1;
1316 entryIndex < lastEntry;
1317 entryIndex++)
1319 StorageImpl_WriteRawDirEntry(
1320 storage,
1321 entryIndex,
1322 emptyData);
1325 StorageImpl_SaveFileHeader(storage);
1328 UpdateRawDirEntry(currentData, newData);
1330 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1332 if (SUCCEEDED(hr))
1333 *index = newEntryIndex;
1335 return hr;
1338 /***************************************************************************
1340 * Internal Method
1342 * Mark a directory entry in the file as free.
1344 static HRESULT StorageImpl_DestroyDirEntry(
1345 StorageBaseImpl *base,
1346 DirRef index)
1348 BYTE emptyData[RAW_DIRENTRY_SIZE];
1349 StorageImpl *storage = (StorageImpl*)base;
1351 memset(emptyData, 0, RAW_DIRENTRY_SIZE);
1353 return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1357 /****************************************************************************
1359 * Internal Method
1361 * Case insensitive comparison of DirEntry.name by first considering
1362 * their size.
1364 * Returns <0 when name1 < name2
1365 * >0 when name1 > name2
1366 * 0 when name1 == name2
1368 static LONG entryNameCmp(
1369 const OLECHAR *name1,
1370 const OLECHAR *name2)
1372 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1374 while (diff == 0 && *name1 != 0)
1377 * We compare the string themselves only when they are of the same length
1379 diff = toupperW(*name1++) - toupperW(*name2++);
1382 return diff;
1385 /****************************************************************************
1387 * Internal Method
1389 * Add a directory entry to a storage
1391 static HRESULT insertIntoTree(
1392 StorageBaseImpl *This,
1393 DirRef parentStorageIndex,
1394 DirRef newEntryIndex)
1396 DirEntry currentEntry;
1397 DirEntry newEntry;
1400 * Read the inserted entry
1402 StorageBaseImpl_ReadDirEntry(This,
1403 newEntryIndex,
1404 &newEntry);
1407 * Read the storage entry
1409 StorageBaseImpl_ReadDirEntry(This,
1410 parentStorageIndex,
1411 &currentEntry);
1413 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1416 * The root storage contains some element, therefore, start the research
1417 * for the appropriate location.
1419 BOOL found = 0;
1420 DirRef current, next, previous, currentEntryId;
1423 * Keep a reference to the root of the storage's element tree
1425 currentEntryId = currentEntry.dirRootEntry;
1428 * Read
1430 StorageBaseImpl_ReadDirEntry(This,
1431 currentEntry.dirRootEntry,
1432 &currentEntry);
1434 previous = currentEntry.leftChild;
1435 next = currentEntry.rightChild;
1436 current = currentEntryId;
1438 while (found == 0)
1440 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1442 if (diff < 0)
1444 if (previous != DIRENTRY_NULL)
1446 StorageBaseImpl_ReadDirEntry(This,
1447 previous,
1448 &currentEntry);
1449 current = previous;
1451 else
1453 currentEntry.leftChild = newEntryIndex;
1454 StorageBaseImpl_WriteDirEntry(This,
1455 current,
1456 &currentEntry);
1457 found = 1;
1460 else if (diff > 0)
1462 if (next != DIRENTRY_NULL)
1464 StorageBaseImpl_ReadDirEntry(This,
1465 next,
1466 &currentEntry);
1467 current = next;
1469 else
1471 currentEntry.rightChild = newEntryIndex;
1472 StorageBaseImpl_WriteDirEntry(This,
1473 current,
1474 &currentEntry);
1475 found = 1;
1478 else
1481 * Trying to insert an item with the same name in the
1482 * subtree structure.
1484 return STG_E_FILEALREADYEXISTS;
1487 previous = currentEntry.leftChild;
1488 next = currentEntry.rightChild;
1491 else
1494 * The storage is empty, make the new entry the root of its element tree
1496 currentEntry.dirRootEntry = newEntryIndex;
1497 StorageBaseImpl_WriteDirEntry(This,
1498 parentStorageIndex,
1499 &currentEntry);
1502 return S_OK;
1505 /****************************************************************************
1507 * Internal Method
1509 * Find and read the element of a storage with the given name.
1511 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1512 const OLECHAR *name, DirEntry *data)
1514 DirRef currentEntry;
1516 /* Read the storage entry to find the root of the tree. */
1517 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1519 currentEntry = data->dirRootEntry;
1521 while (currentEntry != DIRENTRY_NULL)
1523 LONG cmp;
1525 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1527 cmp = entryNameCmp(name, data->name);
1529 if (cmp == 0)
1530 /* found it */
1531 break;
1533 else if (cmp < 0)
1534 currentEntry = data->leftChild;
1536 else if (cmp > 0)
1537 currentEntry = data->rightChild;
1540 return currentEntry;
1543 /****************************************************************************
1545 * Internal Method
1547 * Find and read the binary tree parent of the element with the given name.
1549 * If there is no such element, find a place where it could be inserted and
1550 * return STG_E_FILENOTFOUND.
1552 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1553 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1554 ULONG *relation)
1556 DirRef childEntry;
1557 DirEntry childData;
1559 /* Read the storage entry to find the root of the tree. */
1560 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1562 *parentEntry = storageEntry;
1563 *relation = DIRENTRY_RELATION_DIR;
1565 childEntry = parentData->dirRootEntry;
1567 while (childEntry != DIRENTRY_NULL)
1569 LONG cmp;
1571 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1573 cmp = entryNameCmp(childName, childData.name);
1575 if (cmp == 0)
1576 /* found it */
1577 break;
1579 else if (cmp < 0)
1581 *parentData = childData;
1582 *parentEntry = childEntry;
1583 *relation = DIRENTRY_RELATION_PREVIOUS;
1585 childEntry = parentData->leftChild;
1588 else if (cmp > 0)
1590 *parentData = childData;
1591 *parentEntry = childEntry;
1592 *relation = DIRENTRY_RELATION_NEXT;
1594 childEntry = parentData->rightChild;
1598 if (childEntry == DIRENTRY_NULL)
1599 return STG_E_FILENOTFOUND;
1600 else
1601 return S_OK;
1605 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1606 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1607 SNB snbExclude, IStorage *pstgDest);
1609 static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
1610 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1611 SNB snbExclude, IStorage *pstgDest)
1613 DirEntry data;
1614 HRESULT hr;
1615 BOOL skip = FALSE;
1616 IStorage *pstgTmp;
1617 IStream *pstrChild, *pstrTmp;
1618 STATSTG strStat;
1620 if (srcEntry == DIRENTRY_NULL)
1621 return S_OK;
1623 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1625 if (FAILED(hr))
1626 return hr;
1628 if ( snbExclude )
1630 WCHAR **snb = snbExclude;
1632 while ( *snb != NULL && !skip )
1634 if ( lstrcmpW(data.name, *snb) == 0 )
1635 skip = TRUE;
1636 ++snb;
1640 if (!skip)
1642 if (data.stgType == STGTY_STORAGE && !skip_storage)
1645 * create a new storage in destination storage
1647 hr = IStorage_CreateStorage( pstgDest, data.name,
1648 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1649 0, 0,
1650 &pstgTmp );
1653 * if it already exist, don't create a new one use this one
1655 if (hr == STG_E_FILEALREADYEXISTS)
1657 hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1658 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1659 NULL, 0, &pstgTmp );
1662 if (SUCCEEDED(hr))
1664 hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1665 skip_stream, NULL, pstgTmp );
1667 IStorage_Release(pstgTmp);
1670 else if (data.stgType == STGTY_STREAM && !skip_stream)
1673 * create a new stream in destination storage. If the stream already
1674 * exist, it will be deleted and a new one will be created.
1676 hr = IStorage_CreateStream( pstgDest, data.name,
1677 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1678 0, 0, &pstrTmp );
1681 * open child stream storage. This operation must succeed even if the
1682 * stream is already open, so we use internal functions to do it.
1684 if (hr == S_OK)
1686 StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
1688 if (streamimpl)
1690 pstrChild = &streamimpl->IStream_iface;
1691 if (pstrChild)
1692 IStream_AddRef(pstrChild);
1694 else
1696 pstrChild = NULL;
1697 hr = E_OUTOFMEMORY;
1701 if (hr == S_OK)
1704 * Get the size of the source stream
1706 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1709 * Set the size of the destination stream.
1711 IStream_SetSize(pstrTmp, strStat.cbSize);
1714 * do the copy
1716 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1717 NULL, NULL );
1719 IStream_Release( pstrChild );
1722 IStream_Release( pstrTmp );
1726 /* copy siblings */
1727 if (SUCCEEDED(hr))
1728 hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1729 skip_stream, snbExclude, pstgDest );
1731 if (SUCCEEDED(hr))
1732 hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1733 skip_stream, snbExclude, pstgDest );
1735 return hr;
1738 static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
1739 DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1740 SNB snbExclude, IStorage *pstgDest)
1742 DirEntry data;
1743 HRESULT hr;
1745 hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1747 if (SUCCEEDED(hr))
1748 hr = IStorage_SetClass( pstgDest, &data.clsid );
1750 if (SUCCEEDED(hr))
1751 hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
1752 skip_stream, snbExclude, pstgDest );
1754 return hr;
1757 /*************************************************************************
1758 * CopyTo (IStorage)
1760 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1761 IStorage* iface,
1762 DWORD ciidExclude, /* [in] */
1763 const IID* rgiidExclude, /* [size_is][unique][in] */
1764 SNB snbExclude, /* [unique][in] */
1765 IStorage* pstgDest) /* [unique][in] */
1767 StorageBaseImpl *This = impl_from_IStorage(iface);
1769 BOOL skip_storage = FALSE, skip_stream = FALSE;
1770 int i;
1772 TRACE("(%p, %d, %p, %p, %p)\n",
1773 iface, ciidExclude, rgiidExclude,
1774 snbExclude, pstgDest);
1776 if ( pstgDest == 0 )
1777 return STG_E_INVALIDPOINTER;
1779 for(i = 0; i < ciidExclude; ++i)
1781 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1782 skip_storage = TRUE;
1783 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1784 skip_stream = TRUE;
1785 else
1786 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1789 if (!skip_storage)
1791 /* Give up early if it looks like this would be infinitely recursive.
1792 * Oddly enough, this includes some cases that aren't really recursive, like
1793 * copying to a transacted child. */
1794 IStorage *pstgDestAncestor = pstgDest;
1795 IStorage *pstgDestAncestorChild = NULL;
1797 /* Go up the chain from the destination until we find the source storage. */
1798 while (pstgDestAncestor != iface) {
1799 pstgDestAncestorChild = pstgDest;
1801 if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
1803 TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
1805 pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
1807 else if (pstgDestAncestor->lpVtbl == &Storage32InternalImpl_Vtbl)
1809 StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
1811 pstgDestAncestor = &internal->parentStorage->IStorage_iface;
1813 else
1814 break;
1817 if (pstgDestAncestor == iface)
1819 BOOL fail = TRUE;
1821 if (pstgDestAncestorChild && snbExclude)
1823 StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
1824 DirEntry data;
1825 WCHAR **snb = snbExclude;
1827 StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
1829 while ( *snb != NULL && fail )
1831 if ( lstrcmpW(data.name, *snb) == 0 )
1832 fail = FALSE;
1833 ++snb;
1837 if (fail)
1838 return STG_E_ACCESSDENIED;
1842 return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
1843 skip_storage, skip_stream, snbExclude, pstgDest );
1846 /*************************************************************************
1847 * MoveElementTo (IStorage)
1849 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1850 IStorage* iface,
1851 const OLECHAR *pwcsName, /* [string][in] */
1852 IStorage *pstgDest, /* [unique][in] */
1853 const OLECHAR *pwcsNewName,/* [string][in] */
1854 DWORD grfFlags) /* [in] */
1856 FIXME("(%p %s %p %s %u): stub\n", iface,
1857 debugstr_w(pwcsName), pstgDest,
1858 debugstr_w(pwcsNewName), grfFlags);
1859 return E_NOTIMPL;
1862 /*************************************************************************
1863 * Commit (IStorage)
1865 * Ensures that any changes made to a storage object open in transacted mode
1866 * are reflected in the parent storage
1868 * In a non-transacted mode, this ensures all cached writes are completed.
1870 static HRESULT WINAPI StorageImpl_Commit(
1871 IStorage* iface,
1872 DWORD grfCommitFlags)/* [in] */
1874 StorageBaseImpl* This = impl_from_IStorage(iface);
1875 TRACE("(%p %d)\n", iface, grfCommitFlags);
1876 return StorageBaseImpl_Flush(This);
1879 /*************************************************************************
1880 * Revert (IStorage)
1882 * Discard all changes that have been made since the last commit operation
1884 static HRESULT WINAPI StorageImpl_Revert(
1885 IStorage* iface)
1887 TRACE("(%p)\n", iface);
1888 return S_OK;
1891 /*************************************************************************
1892 * DestroyElement (IStorage)
1894 * Strategy: This implementation is built this way for simplicity not for speed.
1895 * I always delete the topmost element of the enumeration and adjust
1896 * the deleted element pointer all the time. This takes longer to
1897 * do but allow to reinvoke DestroyElement whenever we encounter a
1898 * storage object. The optimisation resides in the usage of another
1899 * enumeration strategy that would give all the leaves of a storage
1900 * first. (postfix order)
1902 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1903 IStorage* iface,
1904 const OLECHAR *pwcsName)/* [string][in] */
1906 StorageBaseImpl *This = impl_from_IStorage(iface);
1908 HRESULT hr = S_OK;
1909 DirEntry entryToDelete;
1910 DirRef entryToDeleteRef;
1912 TRACE("(%p, %s)\n",
1913 iface, debugstr_w(pwcsName));
1915 if (pwcsName==NULL)
1916 return STG_E_INVALIDPOINTER;
1918 if (This->reverted)
1919 return STG_E_REVERTED;
1921 if ( !(This->openFlags & STGM_TRANSACTED) &&
1922 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1923 return STG_E_ACCESSDENIED;
1925 entryToDeleteRef = findElement(
1926 This,
1927 This->storageDirEntry,
1928 pwcsName,
1929 &entryToDelete);
1931 if ( entryToDeleteRef == DIRENTRY_NULL )
1933 return STG_E_FILENOTFOUND;
1936 if ( entryToDelete.stgType == STGTY_STORAGE )
1938 hr = deleteStorageContents(
1939 This,
1940 entryToDeleteRef,
1941 entryToDelete);
1943 else if ( entryToDelete.stgType == STGTY_STREAM )
1945 hr = deleteStreamContents(
1946 This,
1947 entryToDeleteRef,
1948 entryToDelete);
1951 if (hr!=S_OK)
1952 return hr;
1955 * Remove the entry from its parent storage
1957 hr = removeFromTree(
1958 This,
1959 This->storageDirEntry,
1960 entryToDeleteRef);
1963 * Invalidate the entry
1965 if (SUCCEEDED(hr))
1966 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1968 if (SUCCEEDED(hr))
1969 hr = StorageBaseImpl_Flush(This);
1971 return hr;
1975 /******************************************************************************
1976 * Internal stream list handlers
1979 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1981 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1982 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1985 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1987 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1988 list_remove(&(strm->StrmListEntry));
1991 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1993 StgStreamImpl *strm;
1995 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1997 if (strm->dirEntry == streamEntry)
1999 return TRUE;
2003 return FALSE;
2006 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
2008 StorageInternalImpl *childstg;
2010 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
2012 if (childstg->base.storageDirEntry == storageEntry)
2014 return TRUE;
2018 return FALSE;
2021 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
2023 struct list *cur, *cur2;
2024 StgStreamImpl *strm=NULL;
2025 StorageInternalImpl *childstg=NULL;
2027 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2028 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2029 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2030 strm->parentStorage = NULL;
2031 list_remove(cur);
2034 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2035 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2036 StorageBaseImpl_Invalidate( &childstg->base );
2039 if (stg->transactedChild)
2041 StorageBaseImpl_Invalidate(stg->transactedChild);
2043 stg->transactedChild = NULL;
2048 /*********************************************************************
2050 * Internal Method
2052 * Delete the contents of a storage entry.
2055 static HRESULT deleteStorageContents(
2056 StorageBaseImpl *parentStorage,
2057 DirRef indexToDelete,
2058 DirEntry entryDataToDelete)
2060 IEnumSTATSTG *elements = 0;
2061 IStorage *childStorage = 0;
2062 STATSTG currentElement;
2063 HRESULT hr;
2064 HRESULT destroyHr = S_OK;
2065 StorageInternalImpl *stg, *stg2;
2067 /* Invalidate any open storage objects. */
2068 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2070 if (stg->base.storageDirEntry == indexToDelete)
2072 StorageBaseImpl_Invalidate(&stg->base);
2077 * Open the storage and enumerate it
2079 hr = IStorage_OpenStorage(
2080 &parentStorage->IStorage_iface,
2081 entryDataToDelete.name,
2083 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2086 &childStorage);
2088 if (hr != S_OK)
2090 return hr;
2094 * Enumerate the elements
2096 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2101 * Obtain the next element
2103 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2104 if (hr==S_OK)
2106 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2108 CoTaskMemFree(currentElement.pwcsName);
2112 * We need to Reset the enumeration every time because we delete elements
2113 * and the enumeration could be invalid
2115 IEnumSTATSTG_Reset(elements);
2117 } while ((hr == S_OK) && (destroyHr == S_OK));
2119 IStorage_Release(childStorage);
2120 IEnumSTATSTG_Release(elements);
2122 return destroyHr;
2125 /*********************************************************************
2127 * Internal Method
2129 * Perform the deletion of a stream's data
2132 static HRESULT deleteStreamContents(
2133 StorageBaseImpl *parentStorage,
2134 DirRef indexToDelete,
2135 DirEntry entryDataToDelete)
2137 IStream *pis;
2138 HRESULT hr;
2139 ULARGE_INTEGER size;
2140 StgStreamImpl *strm, *strm2;
2142 /* Invalidate any open stream objects. */
2143 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2145 if (strm->dirEntry == indexToDelete)
2147 TRACE("Stream deleted %p\n", strm);
2148 strm->parentStorage = NULL;
2149 list_remove(&strm->StrmListEntry);
2153 size.u.HighPart = 0;
2154 size.u.LowPart = 0;
2156 hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2157 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2159 if (hr!=S_OK)
2161 return(hr);
2165 * Zap the stream
2167 hr = IStream_SetSize(pis, size);
2169 if(hr != S_OK)
2171 return hr;
2175 * Release the stream object.
2177 IStream_Release(pis);
2179 return S_OK;
2182 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2184 switch (relation)
2186 case DIRENTRY_RELATION_PREVIOUS:
2187 entry->leftChild = new_target;
2188 break;
2189 case DIRENTRY_RELATION_NEXT:
2190 entry->rightChild = new_target;
2191 break;
2192 case DIRENTRY_RELATION_DIR:
2193 entry->dirRootEntry = new_target;
2194 break;
2195 default:
2196 assert(0);
2200 /*************************************************************************
2202 * Internal Method
2204 * This method removes a directory entry from its parent storage tree without
2205 * freeing any resources attached to it.
2207 static HRESULT removeFromTree(
2208 StorageBaseImpl *This,
2209 DirRef parentStorageIndex,
2210 DirRef deletedIndex)
2212 DirEntry entryToDelete;
2213 DirEntry parentEntry;
2214 DirRef parentEntryRef;
2215 ULONG typeOfRelation;
2216 HRESULT hr;
2218 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2220 if (hr != S_OK)
2221 return hr;
2224 * Find the element that links to the one we want to delete.
2226 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2227 &parentEntry, &parentEntryRef, &typeOfRelation);
2229 if (hr != S_OK)
2230 return hr;
2232 if (entryToDelete.leftChild != DIRENTRY_NULL)
2235 * Replace the deleted entry with its left child
2237 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2239 hr = StorageBaseImpl_WriteDirEntry(
2240 This,
2241 parentEntryRef,
2242 &parentEntry);
2243 if(FAILED(hr))
2245 return hr;
2248 if (entryToDelete.rightChild != DIRENTRY_NULL)
2251 * We need to reinsert the right child somewhere. We already know it and
2252 * its children are greater than everything in the left tree, so we
2253 * insert it at the rightmost point in the left tree.
2255 DirRef newRightChildParent = entryToDelete.leftChild;
2256 DirEntry newRightChildParentEntry;
2260 hr = StorageBaseImpl_ReadDirEntry(
2261 This,
2262 newRightChildParent,
2263 &newRightChildParentEntry);
2264 if (FAILED(hr))
2266 return hr;
2269 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2270 newRightChildParent = newRightChildParentEntry.rightChild;
2271 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2273 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2275 hr = StorageBaseImpl_WriteDirEntry(
2276 This,
2277 newRightChildParent,
2278 &newRightChildParentEntry);
2279 if (FAILED(hr))
2281 return hr;
2285 else
2288 * Replace the deleted entry with its right child
2290 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2292 hr = StorageBaseImpl_WriteDirEntry(
2293 This,
2294 parentEntryRef,
2295 &parentEntry);
2296 if(FAILED(hr))
2298 return hr;
2302 return hr;
2306 /******************************************************************************
2307 * SetElementTimes (IStorage)
2309 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2310 IStorage* iface,
2311 const OLECHAR *pwcsName,/* [string][in] */
2312 const FILETIME *pctime, /* [in] */
2313 const FILETIME *patime, /* [in] */
2314 const FILETIME *pmtime) /* [in] */
2316 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2317 return S_OK;
2320 /******************************************************************************
2321 * SetStateBits (IStorage)
2323 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2324 IStorage* iface,
2325 DWORD grfStateBits,/* [in] */
2326 DWORD grfMask) /* [in] */
2328 StorageBaseImpl *This = impl_from_IStorage(iface);
2330 if (This->reverted)
2331 return STG_E_REVERTED;
2333 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2334 return S_OK;
2337 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2338 DirRef index, const DirEntry *data)
2340 StorageImpl *This = (StorageImpl*)base;
2341 return StorageImpl_WriteDirEntry(This, index, data);
2344 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2345 DirRef index, DirEntry *data)
2347 StorageImpl *This = (StorageImpl*)base;
2348 return StorageImpl_ReadDirEntry(This, index, data);
2351 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2353 int i;
2355 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2357 if (!This->blockChainCache[i])
2359 return &This->blockChainCache[i];
2363 i = This->blockChainToEvict;
2365 BlockChainStream_Destroy(This->blockChainCache[i]);
2366 This->blockChainCache[i] = NULL;
2368 This->blockChainToEvict++;
2369 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2370 This->blockChainToEvict = 0;
2372 return &This->blockChainCache[i];
2375 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2376 DirRef index)
2378 int i, free_index=-1;
2380 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2382 if (!This->blockChainCache[i])
2384 if (free_index == -1) free_index = i;
2386 else if (This->blockChainCache[i]->ownerDirEntry == index)
2388 return &This->blockChainCache[i];
2392 if (free_index == -1)
2394 free_index = This->blockChainToEvict;
2396 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2397 This->blockChainCache[free_index] = NULL;
2399 This->blockChainToEvict++;
2400 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2401 This->blockChainToEvict = 0;
2404 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2405 return &This->blockChainCache[free_index];
2408 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2410 int i;
2412 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2414 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2416 BlockChainStream_Destroy(This->blockChainCache[i]);
2417 This->blockChainCache[i] = NULL;
2418 return;
2423 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2424 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2426 StorageImpl *This = (StorageImpl*)base;
2427 DirEntry data;
2428 HRESULT hr;
2429 ULONG bytesToRead;
2431 hr = StorageImpl_ReadDirEntry(This, index, &data);
2432 if (FAILED(hr)) return hr;
2434 if (data.size.QuadPart == 0)
2436 *bytesRead = 0;
2437 return S_OK;
2440 if (offset.QuadPart + size > data.size.QuadPart)
2442 bytesToRead = data.size.QuadPart - offset.QuadPart;
2444 else
2446 bytesToRead = size;
2449 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2451 SmallBlockChainStream *stream;
2453 stream = SmallBlockChainStream_Construct(This, NULL, index);
2454 if (!stream) return E_OUTOFMEMORY;
2456 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2458 SmallBlockChainStream_Destroy(stream);
2460 return hr;
2462 else
2464 BlockChainStream *stream = NULL;
2466 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2467 if (!stream) return E_OUTOFMEMORY;
2469 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2471 return hr;
2475 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2476 ULARGE_INTEGER newsize)
2478 StorageImpl *This = (StorageImpl*)base;
2479 DirEntry data;
2480 HRESULT hr;
2481 SmallBlockChainStream *smallblock=NULL;
2482 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2484 hr = StorageImpl_ReadDirEntry(This, index, &data);
2485 if (FAILED(hr)) return hr;
2487 /* In simple mode keep the stream size above the small block limit */
2488 if (This->base.openFlags & STGM_SIMPLE)
2489 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2491 if (data.size.QuadPart == newsize.QuadPart)
2492 return S_OK;
2494 /* Create a block chain object of the appropriate type */
2495 if (data.size.QuadPart == 0)
2497 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2499 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2500 if (!smallblock) return E_OUTOFMEMORY;
2502 else
2504 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2505 bigblock = *pbigblock;
2506 if (!bigblock) return E_OUTOFMEMORY;
2509 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2511 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2512 if (!smallblock) return E_OUTOFMEMORY;
2514 else
2516 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2517 bigblock = *pbigblock;
2518 if (!bigblock) return E_OUTOFMEMORY;
2521 /* Change the block chain type if necessary. */
2522 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2524 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2525 if (!bigblock)
2527 SmallBlockChainStream_Destroy(smallblock);
2528 return E_FAIL;
2531 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2532 *pbigblock = bigblock;
2534 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2536 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2537 if (!smallblock)
2538 return E_FAIL;
2541 /* Set the size of the block chain. */
2542 if (smallblock)
2544 SmallBlockChainStream_SetSize(smallblock, newsize);
2545 SmallBlockChainStream_Destroy(smallblock);
2547 else
2549 BlockChainStream_SetSize(bigblock, newsize);
2552 /* Set the size in the directory entry. */
2553 hr = StorageImpl_ReadDirEntry(This, index, &data);
2554 if (SUCCEEDED(hr))
2556 data.size = newsize;
2558 hr = StorageImpl_WriteDirEntry(This, index, &data);
2560 return hr;
2563 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2564 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2566 StorageImpl *This = (StorageImpl*)base;
2567 DirEntry data;
2568 HRESULT hr;
2569 ULARGE_INTEGER newSize;
2571 hr = StorageImpl_ReadDirEntry(This, index, &data);
2572 if (FAILED(hr)) return hr;
2574 /* Grow the stream if necessary */
2575 newSize.QuadPart = 0;
2576 newSize.QuadPart = offset.QuadPart + size;
2578 if (newSize.QuadPart > data.size.QuadPart)
2580 hr = StorageImpl_StreamSetSize(base, index, newSize);
2581 if (FAILED(hr))
2582 return hr;
2584 hr = StorageImpl_ReadDirEntry(This, index, &data);
2585 if (FAILED(hr)) return hr;
2588 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2590 SmallBlockChainStream *stream;
2592 stream = SmallBlockChainStream_Construct(This, NULL, index);
2593 if (!stream) return E_OUTOFMEMORY;
2595 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2597 SmallBlockChainStream_Destroy(stream);
2599 return hr;
2601 else
2603 BlockChainStream *stream;
2605 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2606 if (!stream) return E_OUTOFMEMORY;
2608 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2610 return hr;
2614 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2615 DirRef src)
2617 StorageImpl *This = (StorageImpl*)base;
2618 DirEntry dst_data, src_data;
2619 HRESULT hr;
2621 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2623 if (SUCCEEDED(hr))
2624 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2626 if (SUCCEEDED(hr))
2628 StorageImpl_DeleteCachedBlockChainStream(This, src);
2629 dst_data.startingBlock = src_data.startingBlock;
2630 dst_data.size = src_data.size;
2632 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2635 return hr;
2638 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2640 StorageImpl *This = (StorageImpl*) iface;
2641 STATSTG statstg;
2642 HRESULT hr;
2644 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2646 *result = statstg.pwcsName;
2648 return hr;
2652 * Virtual function table for the IStorage32Impl class.
2654 static const IStorageVtbl Storage32Impl_Vtbl =
2656 StorageBaseImpl_QueryInterface,
2657 StorageBaseImpl_AddRef,
2658 StorageBaseImpl_Release,
2659 StorageBaseImpl_CreateStream,
2660 StorageBaseImpl_OpenStream,
2661 StorageBaseImpl_CreateStorage,
2662 StorageBaseImpl_OpenStorage,
2663 StorageBaseImpl_CopyTo,
2664 StorageBaseImpl_MoveElementTo,
2665 StorageImpl_Commit,
2666 StorageImpl_Revert,
2667 StorageBaseImpl_EnumElements,
2668 StorageBaseImpl_DestroyElement,
2669 StorageBaseImpl_RenameElement,
2670 StorageBaseImpl_SetElementTimes,
2671 StorageBaseImpl_SetClass,
2672 StorageBaseImpl_SetStateBits,
2673 StorageBaseImpl_Stat
2676 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2678 StorageImpl_Destroy,
2679 StorageImpl_Invalidate,
2680 StorageImpl_Flush,
2681 StorageImpl_GetFilename,
2682 StorageImpl_CreateDirEntry,
2683 StorageImpl_BaseWriteDirEntry,
2684 StorageImpl_BaseReadDirEntry,
2685 StorageImpl_DestroyDirEntry,
2686 StorageImpl_StreamReadAt,
2687 StorageImpl_StreamWriteAt,
2688 StorageImpl_StreamSetSize,
2689 StorageImpl_StreamLink
2692 static HRESULT StorageImpl_Construct(
2693 HANDLE hFile,
2694 LPCOLESTR pwcsName,
2695 ILockBytes* pLkbyt,
2696 DWORD openFlags,
2697 BOOL fileBased,
2698 BOOL create,
2699 ULONG sector_size,
2700 StorageImpl** result)
2702 StorageImpl* This;
2703 HRESULT hr = S_OK;
2704 DirEntry currentEntry;
2705 DirRef currentEntryRef;
2707 if ( FAILED( validateSTGM(openFlags) ))
2708 return STG_E_INVALIDFLAG;
2710 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2711 if (!This)
2712 return E_OUTOFMEMORY;
2714 memset(This, 0, sizeof(StorageImpl));
2716 list_init(&This->base.strmHead);
2718 list_init(&This->base.storageHead);
2720 This->base.IStorage_iface.lpVtbl = &Storage32Impl_Vtbl;
2721 This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
2722 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2723 This->base.openFlags = (openFlags & ~STGM_CREATE);
2724 This->base.ref = 1;
2725 This->base.create = create;
2727 This->base.reverted = 0;
2730 * Initialize the big block cache.
2732 This->bigBlockSize = sector_size;
2733 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2734 if (hFile)
2735 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2736 else
2738 This->lockBytes = pLkbyt;
2739 ILockBytes_AddRef(pLkbyt);
2742 if (FAILED(hr))
2743 goto end;
2745 if (create)
2747 ULARGE_INTEGER size;
2748 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2750 /* Discard any existing data. */
2751 size.QuadPart = 0;
2752 ILockBytes_SetSize(This->lockBytes, size);
2755 * Initialize all header variables:
2756 * - The big block depot consists of one block and it is at block 0
2757 * - The directory table starts at block 1
2758 * - There is no small block depot
2760 memset( This->bigBlockDepotStart,
2761 BLOCK_UNUSED,
2762 sizeof(This->bigBlockDepotStart));
2764 This->bigBlockDepotCount = 1;
2765 This->bigBlockDepotStart[0] = 0;
2766 This->rootStartBlock = 1;
2767 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2768 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2769 if (sector_size == 4096)
2770 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2771 else
2772 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2773 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2774 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2775 This->extBigBlockDepotCount = 0;
2777 StorageImpl_SaveFileHeader(This);
2780 * Add one block for the big block depot and one block for the directory table
2782 size.u.HighPart = 0;
2783 size.u.LowPart = This->bigBlockSize * 3;
2784 ILockBytes_SetSize(This->lockBytes, size);
2787 * Initialize the big block depot
2789 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2790 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2791 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2792 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2794 else
2797 * Load the header for the file.
2799 hr = StorageImpl_LoadFileHeader(This);
2801 if (FAILED(hr))
2803 goto end;
2808 * There is no block depot cached yet.
2810 This->indexBlockDepotCached = 0xFFFFFFFF;
2811 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2814 * Start searching for free blocks with block 0.
2816 This->prevFreeBlock = 0;
2818 This->firstFreeSmallBlock = 0;
2820 /* Read the extended big block depot locations. */
2821 if (This->extBigBlockDepotCount != 0)
2823 ULONG current_block = This->extBigBlockDepotStart;
2824 ULONG cache_size = This->extBigBlockDepotCount * 2;
2825 int i;
2827 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2828 if (!This->extBigBlockDepotLocations)
2830 hr = E_OUTOFMEMORY;
2831 goto end;
2834 This->extBigBlockDepotLocationsSize = cache_size;
2836 for (i=0; i<This->extBigBlockDepotCount; i++)
2838 if (current_block == BLOCK_END_OF_CHAIN)
2840 WARN("File has too few extended big block depot blocks.\n");
2841 hr = STG_E_DOCFILECORRUPT;
2842 goto end;
2844 This->extBigBlockDepotLocations[i] = current_block;
2845 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2848 else
2850 This->extBigBlockDepotLocations = NULL;
2851 This->extBigBlockDepotLocationsSize = 0;
2855 * Create the block chain abstractions.
2857 if(!(This->rootBlockChain =
2858 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2860 hr = STG_E_READFAULT;
2861 goto end;
2864 if(!(This->smallBlockDepotChain =
2865 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2866 DIRENTRY_NULL)))
2868 hr = STG_E_READFAULT;
2869 goto end;
2873 * Write the root storage entry (memory only)
2875 if (create)
2877 static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
2878 DirEntry rootEntry;
2880 * Initialize the directory table
2882 memset(&rootEntry, 0, sizeof(rootEntry));
2883 strcpyW(rootEntry.name, rootentryW);
2884 rootEntry.sizeOfNameString = sizeof(rootentryW);
2885 rootEntry.stgType = STGTY_ROOT;
2886 rootEntry.leftChild = DIRENTRY_NULL;
2887 rootEntry.rightChild = DIRENTRY_NULL;
2888 rootEntry.dirRootEntry = DIRENTRY_NULL;
2889 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2890 rootEntry.size.u.HighPart = 0;
2891 rootEntry.size.u.LowPart = 0;
2893 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2897 * Find the ID of the root storage.
2899 currentEntryRef = 0;
2903 hr = StorageImpl_ReadDirEntry(
2904 This,
2905 currentEntryRef,
2906 &currentEntry);
2908 if (SUCCEEDED(hr))
2910 if ( (currentEntry.sizeOfNameString != 0 ) &&
2911 (currentEntry.stgType == STGTY_ROOT) )
2913 This->base.storageDirEntry = currentEntryRef;
2917 currentEntryRef++;
2919 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2921 if (FAILED(hr))
2923 hr = STG_E_READFAULT;
2924 goto end;
2928 * Create the block chain abstraction for the small block root chain.
2930 if(!(This->smallBlockRootChain =
2931 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2933 hr = STG_E_READFAULT;
2936 end:
2937 if (FAILED(hr))
2939 IStorage_Release(&This->base.IStorage_iface);
2940 *result = NULL;
2942 else
2944 StorageImpl_Flush((StorageBaseImpl*)This);
2945 *result = This;
2948 return hr;
2951 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2953 StorageImpl *This = (StorageImpl*) iface;
2955 StorageBaseImpl_DeleteAll(&This->base);
2957 This->base.reverted = 1;
2960 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2962 StorageImpl *This = (StorageImpl*) iface;
2963 int i;
2964 TRACE("(%p)\n", This);
2966 StorageImpl_Flush(iface);
2968 StorageImpl_Invalidate(iface);
2970 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
2972 BlockChainStream_Destroy(This->smallBlockRootChain);
2973 BlockChainStream_Destroy(This->rootBlockChain);
2974 BlockChainStream_Destroy(This->smallBlockDepotChain);
2976 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2977 BlockChainStream_Destroy(This->blockChainCache[i]);
2979 if (This->lockBytes)
2980 ILockBytes_Release(This->lockBytes);
2981 HeapFree(GetProcessHeap(), 0, This);
2984 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2986 StorageImpl *This = (StorageImpl*) iface;
2987 int i;
2988 HRESULT hr;
2989 TRACE("(%p)\n", This);
2991 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2993 if (SUCCEEDED(hr))
2994 hr = BlockChainStream_Flush(This->rootBlockChain);
2996 if (SUCCEEDED(hr))
2997 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2999 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
3000 if (This->blockChainCache[i])
3001 hr = BlockChainStream_Flush(This->blockChainCache[i]);
3003 if (SUCCEEDED(hr))
3004 hr = ILockBytes_Flush(This->lockBytes);
3006 return hr;
3009 /******************************************************************************
3010 * Storage32Impl_GetNextFreeBigBlock
3012 * Returns the index of the next free big block.
3013 * If the big block depot is filled, this method will enlarge it.
3016 static ULONG StorageImpl_GetNextFreeBigBlock(
3017 StorageImpl* This)
3019 ULONG depotBlockIndexPos;
3020 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3021 BOOL success;
3022 ULONG depotBlockOffset;
3023 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3024 ULONG nextBlockIndex = BLOCK_SPECIAL;
3025 int depotIndex = 0;
3026 ULONG freeBlock = BLOCK_UNUSED;
3027 ULARGE_INTEGER neededSize;
3028 STATSTG statstg;
3030 depotIndex = This->prevFreeBlock / blocksPerDepot;
3031 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
3034 * Scan the entire big block depot until we find a block marked free
3036 while (nextBlockIndex != BLOCK_UNUSED)
3038 if (depotIndex < COUNT_BBDEPOTINHEADER)
3040 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
3043 * Grow the primary depot.
3045 if (depotBlockIndexPos == BLOCK_UNUSED)
3047 depotBlockIndexPos = depotIndex*blocksPerDepot;
3050 * Add a block depot.
3052 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3053 This->bigBlockDepotCount++;
3054 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3057 * Flag it as a block depot.
3059 StorageImpl_SetNextBlockInChain(This,
3060 depotBlockIndexPos,
3061 BLOCK_SPECIAL);
3063 /* Save new header information.
3065 StorageImpl_SaveFileHeader(This);
3068 else
3070 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3072 if (depotBlockIndexPos == BLOCK_UNUSED)
3075 * Grow the extended depot.
3077 ULONG extIndex = BLOCK_UNUSED;
3078 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3079 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3081 if (extBlockOffset == 0)
3083 /* We need an extended block.
3085 extIndex = Storage32Impl_AddExtBlockDepot(This);
3086 This->extBigBlockDepotCount++;
3087 depotBlockIndexPos = extIndex + 1;
3089 else
3090 depotBlockIndexPos = depotIndex * blocksPerDepot;
3093 * Add a block depot and mark it in the extended block.
3095 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3096 This->bigBlockDepotCount++;
3097 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3099 /* Flag the block depot.
3101 StorageImpl_SetNextBlockInChain(This,
3102 depotBlockIndexPos,
3103 BLOCK_SPECIAL);
3105 /* If necessary, flag the extended depot block.
3107 if (extIndex != BLOCK_UNUSED)
3108 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3110 /* Save header information.
3112 StorageImpl_SaveFileHeader(This);
3116 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3118 if (success)
3120 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3121 ( nextBlockIndex != BLOCK_UNUSED))
3123 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3125 if (nextBlockIndex == BLOCK_UNUSED)
3127 freeBlock = (depotIndex * blocksPerDepot) +
3128 (depotBlockOffset/sizeof(ULONG));
3131 depotBlockOffset += sizeof(ULONG);
3135 depotIndex++;
3136 depotBlockOffset = 0;
3140 * make sure that the block physically exists before using it
3142 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3144 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3146 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3147 ILockBytes_SetSize(This->lockBytes, neededSize);
3149 This->prevFreeBlock = freeBlock;
3151 return freeBlock;
3154 /******************************************************************************
3155 * Storage32Impl_AddBlockDepot
3157 * This will create a depot block, essentially it is a block initialized
3158 * to BLOCK_UNUSEDs.
3160 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3162 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3165 * Initialize blocks as free
3167 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3168 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3171 /******************************************************************************
3172 * Storage32Impl_GetExtDepotBlock
3174 * Returns the index of the block that corresponds to the specified depot
3175 * index. This method is only for depot indexes equal or greater than
3176 * COUNT_BBDEPOTINHEADER.
3178 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3180 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3181 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3182 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3183 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3184 ULONG blockIndex = BLOCK_UNUSED;
3185 ULONG extBlockIndex;
3186 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3187 int index, num_blocks;
3189 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3191 if (extBlockCount >= This->extBigBlockDepotCount)
3192 return BLOCK_UNUSED;
3194 if (This->indexExtBlockDepotCached != extBlockCount)
3196 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3198 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);
3200 num_blocks = This->bigBlockSize / 4;
3202 for (index = 0; index < num_blocks; index++)
3204 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3205 This->extBlockDepotCached[index] = blockIndex;
3208 This->indexExtBlockDepotCached = extBlockCount;
3211 blockIndex = This->extBlockDepotCached[extBlockOffset];
3213 return blockIndex;
3216 /******************************************************************************
3217 * Storage32Impl_SetExtDepotBlock
3219 * Associates the specified block index to the specified depot index.
3220 * This method is only for depot indexes equal or greater than
3221 * COUNT_BBDEPOTINHEADER.
3223 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3225 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3226 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3227 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3228 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3229 ULONG extBlockIndex;
3231 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3233 assert(extBlockCount < This->extBigBlockDepotCount);
3235 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3237 if (extBlockIndex != BLOCK_UNUSED)
3239 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3240 extBlockOffset * sizeof(ULONG),
3241 blockIndex);
3244 if (This->indexExtBlockDepotCached == extBlockCount)
3246 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3250 /******************************************************************************
3251 * Storage32Impl_AddExtBlockDepot
3253 * Creates an extended depot block.
3255 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3257 ULONG numExtBlocks = This->extBigBlockDepotCount;
3258 ULONG nextExtBlock = This->extBigBlockDepotStart;
3259 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3260 ULONG index = BLOCK_UNUSED;
3261 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3262 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3263 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3265 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3266 blocksPerDepotBlock;
3268 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3271 * The first extended block.
3273 This->extBigBlockDepotStart = index;
3275 else
3278 * Find the last existing extended block.
3280 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3283 * Add the new extended block to the chain.
3285 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3286 index);
3290 * Initialize this block.
3292 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3293 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3295 /* Add the block to our cache. */
3296 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3298 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3299 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3301 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3302 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3304 This->extBigBlockDepotLocations = new_cache;
3305 This->extBigBlockDepotLocationsSize = new_cache_size;
3307 This->extBigBlockDepotLocations[numExtBlocks] = index;
3309 return index;
3312 /******************************************************************************
3313 * Storage32Impl_FreeBigBlock
3315 * This method will flag the specified block as free in the big block depot.
3317 static void StorageImpl_FreeBigBlock(
3318 StorageImpl* This,
3319 ULONG blockIndex)
3321 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3323 if (blockIndex < This->prevFreeBlock)
3324 This->prevFreeBlock = blockIndex;
3327 /************************************************************************
3328 * Storage32Impl_GetNextBlockInChain
3330 * This method will retrieve the block index of the next big block in
3331 * in the chain.
3333 * Params: This - Pointer to the Storage object.
3334 * blockIndex - Index of the block to retrieve the chain
3335 * for.
3336 * nextBlockIndex - receives the return value.
3338 * Returns: This method returns the index of the next block in the chain.
3339 * It will return the constants:
3340 * BLOCK_SPECIAL - If the block given was not part of a
3341 * chain.
3342 * BLOCK_END_OF_CHAIN - If the block given was the last in
3343 * a chain.
3344 * BLOCK_UNUSED - If the block given was not past of a chain
3345 * and is available.
3346 * BLOCK_EXTBBDEPOT - This block is part of the extended
3347 * big block depot.
3349 * See Windows documentation for more details on IStorage methods.
3351 static HRESULT StorageImpl_GetNextBlockInChain(
3352 StorageImpl* This,
3353 ULONG blockIndex,
3354 ULONG* nextBlockIndex)
3356 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3357 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3358 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3359 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3360 BOOL success;
3361 ULONG depotBlockIndexPos;
3362 int index, num_blocks;
3364 *nextBlockIndex = BLOCK_SPECIAL;
3366 if(depotBlockCount >= This->bigBlockDepotCount)
3368 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3369 This->bigBlockDepotCount);
3370 return STG_E_READFAULT;
3374 * Cache the currently accessed depot block.
3376 if (depotBlockCount != This->indexBlockDepotCached)
3378 This->indexBlockDepotCached = depotBlockCount;
3380 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3382 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3384 else
3387 * We have to look in the extended depot.
3389 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3392 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3394 if (!success)
3395 return STG_E_READFAULT;
3397 num_blocks = This->bigBlockSize / 4;
3399 for (index = 0; index < num_blocks; index++)
3401 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3402 This->blockDepotCached[index] = *nextBlockIndex;
3406 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3408 return S_OK;
3411 /******************************************************************************
3412 * Storage32Impl_GetNextExtendedBlock
3414 * Given an extended block this method will return the next extended block.
3416 * NOTES:
3417 * The last ULONG of an extended block is the block index of the next
3418 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3419 * depot.
3421 * Return values:
3422 * - The index of the next extended block
3423 * - BLOCK_UNUSED: there is no next extended block.
3424 * - Any other return values denotes failure.
3426 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3428 ULONG nextBlockIndex = BLOCK_SPECIAL;
3429 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3431 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3432 &nextBlockIndex);
3434 return nextBlockIndex;
3437 /******************************************************************************
3438 * Storage32Impl_SetNextBlockInChain
3440 * This method will write the index of the specified block's next block
3441 * in the big block depot.
3443 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3444 * do the following
3446 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3447 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3448 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3451 static void StorageImpl_SetNextBlockInChain(
3452 StorageImpl* This,
3453 ULONG blockIndex,
3454 ULONG nextBlock)
3456 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3457 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3458 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3459 ULONG depotBlockIndexPos;
3461 assert(depotBlockCount < This->bigBlockDepotCount);
3462 assert(blockIndex != nextBlock);
3464 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3466 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3468 else
3471 * We have to look in the extended depot.
3473 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3476 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3477 nextBlock);
3479 * Update the cached block depot, if necessary.
3481 if (depotBlockCount == This->indexBlockDepotCached)
3483 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3487 /******************************************************************************
3488 * Storage32Impl_LoadFileHeader
3490 * This method will read in the file header
3492 static HRESULT StorageImpl_LoadFileHeader(
3493 StorageImpl* This)
3495 HRESULT hr;
3496 BYTE headerBigBlock[HEADER_SIZE];
3497 int index;
3498 ULARGE_INTEGER offset;
3499 DWORD bytes_read;
3501 TRACE("\n");
3503 * Get a pointer to the big block of data containing the header.
3505 offset.u.HighPart = 0;
3506 offset.u.LowPart = 0;
3507 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3508 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3509 hr = STG_E_FILENOTFOUND;
3512 * Extract the information from the header.
3514 if (SUCCEEDED(hr))
3517 * Check for the "magic number" signature and return an error if it is not
3518 * found.
3520 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3522 return STG_E_OLDFORMAT;
3525 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3527 return STG_E_INVALIDHEADER;
3530 StorageUtl_ReadWord(
3531 headerBigBlock,
3532 OFFSET_BIGBLOCKSIZEBITS,
3533 &This->bigBlockSizeBits);
3535 StorageUtl_ReadWord(
3536 headerBigBlock,
3537 OFFSET_SMALLBLOCKSIZEBITS,
3538 &This->smallBlockSizeBits);
3540 StorageUtl_ReadDWord(
3541 headerBigBlock,
3542 OFFSET_BBDEPOTCOUNT,
3543 &This->bigBlockDepotCount);
3545 StorageUtl_ReadDWord(
3546 headerBigBlock,
3547 OFFSET_ROOTSTARTBLOCK,
3548 &This->rootStartBlock);
3550 StorageUtl_ReadDWord(
3551 headerBigBlock,
3552 OFFSET_SMALLBLOCKLIMIT,
3553 &This->smallBlockLimit);
3555 StorageUtl_ReadDWord(
3556 headerBigBlock,
3557 OFFSET_SBDEPOTSTART,
3558 &This->smallBlockDepotStart);
3560 StorageUtl_ReadDWord(
3561 headerBigBlock,
3562 OFFSET_EXTBBDEPOTSTART,
3563 &This->extBigBlockDepotStart);
3565 StorageUtl_ReadDWord(
3566 headerBigBlock,
3567 OFFSET_EXTBBDEPOTCOUNT,
3568 &This->extBigBlockDepotCount);
3570 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3572 StorageUtl_ReadDWord(
3573 headerBigBlock,
3574 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3575 &(This->bigBlockDepotStart[index]));
3579 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3581 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3582 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3585 * Right now, the code is making some assumptions about the size of the
3586 * blocks, just make sure they are what we're expecting.
3588 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3589 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3590 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3592 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3593 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3594 hr = STG_E_INVALIDHEADER;
3596 else
3597 hr = S_OK;
3600 return hr;
3603 /******************************************************************************
3604 * Storage32Impl_SaveFileHeader
3606 * This method will save to the file the header
3608 static void StorageImpl_SaveFileHeader(
3609 StorageImpl* This)
3611 BYTE headerBigBlock[HEADER_SIZE];
3612 int index;
3613 HRESULT hr;
3614 ULARGE_INTEGER offset;
3615 DWORD bytes_read, bytes_written;
3616 DWORD major_version, dirsectorcount;
3619 * Get a pointer to the big block of data containing the header.
3621 offset.u.HighPart = 0;
3622 offset.u.LowPart = 0;
3623 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3624 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3625 hr = STG_E_FILENOTFOUND;
3627 if (This->bigBlockSizeBits == 0x9)
3628 major_version = 3;
3629 else if (This->bigBlockSizeBits == 0xc)
3630 major_version = 4;
3631 else
3633 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3634 major_version = 4;
3638 * If the block read failed, the file is probably new.
3640 if (FAILED(hr))
3643 * Initialize for all unknown fields.
3645 memset(headerBigBlock, 0, HEADER_SIZE);
3648 * Initialize the magic number.
3650 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3654 * Write the information to the header.
3656 StorageUtl_WriteWord(
3657 headerBigBlock,
3658 OFFSET_MINORVERSION,
3659 0x3e);
3661 StorageUtl_WriteWord(
3662 headerBigBlock,
3663 OFFSET_MAJORVERSION,
3664 major_version);
3666 StorageUtl_WriteWord(
3667 headerBigBlock,
3668 OFFSET_BYTEORDERMARKER,
3669 (WORD)-2);
3671 StorageUtl_WriteWord(
3672 headerBigBlock,
3673 OFFSET_BIGBLOCKSIZEBITS,
3674 This->bigBlockSizeBits);
3676 StorageUtl_WriteWord(
3677 headerBigBlock,
3678 OFFSET_SMALLBLOCKSIZEBITS,
3679 This->smallBlockSizeBits);
3681 if (major_version >= 4)
3683 if (This->rootBlockChain)
3684 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3685 else
3686 /* This file is being created, and it will start out with one block. */
3687 dirsectorcount = 1;
3689 else
3690 /* This field must be 0 in versions older than 4 */
3691 dirsectorcount = 0;
3693 StorageUtl_WriteDWord(
3694 headerBigBlock,
3695 OFFSET_DIRSECTORCOUNT,
3696 dirsectorcount);
3698 StorageUtl_WriteDWord(
3699 headerBigBlock,
3700 OFFSET_BBDEPOTCOUNT,
3701 This->bigBlockDepotCount);
3703 StorageUtl_WriteDWord(
3704 headerBigBlock,
3705 OFFSET_ROOTSTARTBLOCK,
3706 This->rootStartBlock);
3708 StorageUtl_WriteDWord(
3709 headerBigBlock,
3710 OFFSET_SMALLBLOCKLIMIT,
3711 This->smallBlockLimit);
3713 StorageUtl_WriteDWord(
3714 headerBigBlock,
3715 OFFSET_SBDEPOTSTART,
3716 This->smallBlockDepotStart);
3718 StorageUtl_WriteDWord(
3719 headerBigBlock,
3720 OFFSET_SBDEPOTCOUNT,
3721 This->smallBlockDepotChain ?
3722 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3724 StorageUtl_WriteDWord(
3725 headerBigBlock,
3726 OFFSET_EXTBBDEPOTSTART,
3727 This->extBigBlockDepotStart);
3729 StorageUtl_WriteDWord(
3730 headerBigBlock,
3731 OFFSET_EXTBBDEPOTCOUNT,
3732 This->extBigBlockDepotCount);
3734 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3736 StorageUtl_WriteDWord(
3737 headerBigBlock,
3738 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3739 (This->bigBlockDepotStart[index]));
3743 * Write the big block back to the file.
3745 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3748 /******************************************************************************
3749 * StorageImpl_ReadRawDirEntry
3751 * This method will read the raw data from a directory entry in the file.
3753 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3755 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3757 ULARGE_INTEGER offset;
3758 HRESULT hr;
3759 ULONG bytesRead;
3761 offset.u.HighPart = 0;
3762 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3764 hr = BlockChainStream_ReadAt(
3765 This->rootBlockChain,
3766 offset,
3767 RAW_DIRENTRY_SIZE,
3768 buffer,
3769 &bytesRead);
3771 if (bytesRead != RAW_DIRENTRY_SIZE)
3772 return STG_E_READFAULT;
3774 return hr;
3777 /******************************************************************************
3778 * StorageImpl_WriteRawDirEntry
3780 * This method will write the raw data from a directory entry in the file.
3782 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3784 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3786 ULARGE_INTEGER offset;
3787 HRESULT hr;
3788 ULONG bytesRead;
3790 offset.u.HighPart = 0;
3791 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3793 hr = BlockChainStream_WriteAt(
3794 This->rootBlockChain,
3795 offset,
3796 RAW_DIRENTRY_SIZE,
3797 buffer,
3798 &bytesRead);
3800 return hr;
3803 /******************************************************************************
3804 * UpdateRawDirEntry
3806 * Update raw directory entry data from the fields in newData.
3808 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3810 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3812 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3814 memcpy(
3815 buffer + OFFSET_PS_NAME,
3816 newData->name,
3817 DIRENTRY_NAME_BUFFER_LEN );
3819 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3821 StorageUtl_WriteWord(
3822 buffer,
3823 OFFSET_PS_NAMELENGTH,
3824 newData->sizeOfNameString);
3826 StorageUtl_WriteDWord(
3827 buffer,
3828 OFFSET_PS_LEFTCHILD,
3829 newData->leftChild);
3831 StorageUtl_WriteDWord(
3832 buffer,
3833 OFFSET_PS_RIGHTCHILD,
3834 newData->rightChild);
3836 StorageUtl_WriteDWord(
3837 buffer,
3838 OFFSET_PS_DIRROOT,
3839 newData->dirRootEntry);
3841 StorageUtl_WriteGUID(
3842 buffer,
3843 OFFSET_PS_GUID,
3844 &newData->clsid);
3846 StorageUtl_WriteDWord(
3847 buffer,
3848 OFFSET_PS_CTIMELOW,
3849 newData->ctime.dwLowDateTime);
3851 StorageUtl_WriteDWord(
3852 buffer,
3853 OFFSET_PS_CTIMEHIGH,
3854 newData->ctime.dwHighDateTime);
3856 StorageUtl_WriteDWord(
3857 buffer,
3858 OFFSET_PS_MTIMELOW,
3859 newData->mtime.dwLowDateTime);
3861 StorageUtl_WriteDWord(
3862 buffer,
3863 OFFSET_PS_MTIMEHIGH,
3864 newData->ctime.dwHighDateTime);
3866 StorageUtl_WriteDWord(
3867 buffer,
3868 OFFSET_PS_STARTBLOCK,
3869 newData->startingBlock);
3871 StorageUtl_WriteDWord(
3872 buffer,
3873 OFFSET_PS_SIZE,
3874 newData->size.u.LowPart);
3877 /******************************************************************************
3878 * Storage32Impl_ReadDirEntry
3880 * This method will read the specified directory entry.
3882 HRESULT StorageImpl_ReadDirEntry(
3883 StorageImpl* This,
3884 DirRef index,
3885 DirEntry* buffer)
3887 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3888 HRESULT readRes;
3890 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3892 if (SUCCEEDED(readRes))
3894 memset(buffer->name, 0, sizeof(buffer->name));
3895 memcpy(
3896 buffer->name,
3897 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3898 DIRENTRY_NAME_BUFFER_LEN );
3899 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3901 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3903 StorageUtl_ReadWord(
3904 currentEntry,
3905 OFFSET_PS_NAMELENGTH,
3906 &buffer->sizeOfNameString);
3908 StorageUtl_ReadDWord(
3909 currentEntry,
3910 OFFSET_PS_LEFTCHILD,
3911 &buffer->leftChild);
3913 StorageUtl_ReadDWord(
3914 currentEntry,
3915 OFFSET_PS_RIGHTCHILD,
3916 &buffer->rightChild);
3918 StorageUtl_ReadDWord(
3919 currentEntry,
3920 OFFSET_PS_DIRROOT,
3921 &buffer->dirRootEntry);
3923 StorageUtl_ReadGUID(
3924 currentEntry,
3925 OFFSET_PS_GUID,
3926 &buffer->clsid);
3928 StorageUtl_ReadDWord(
3929 currentEntry,
3930 OFFSET_PS_CTIMELOW,
3931 &buffer->ctime.dwLowDateTime);
3933 StorageUtl_ReadDWord(
3934 currentEntry,
3935 OFFSET_PS_CTIMEHIGH,
3936 &buffer->ctime.dwHighDateTime);
3938 StorageUtl_ReadDWord(
3939 currentEntry,
3940 OFFSET_PS_MTIMELOW,
3941 &buffer->mtime.dwLowDateTime);
3943 StorageUtl_ReadDWord(
3944 currentEntry,
3945 OFFSET_PS_MTIMEHIGH,
3946 &buffer->mtime.dwHighDateTime);
3948 StorageUtl_ReadDWord(
3949 currentEntry,
3950 OFFSET_PS_STARTBLOCK,
3951 &buffer->startingBlock);
3953 StorageUtl_ReadDWord(
3954 currentEntry,
3955 OFFSET_PS_SIZE,
3956 &buffer->size.u.LowPart);
3958 buffer->size.u.HighPart = 0;
3961 return readRes;
3964 /*********************************************************************
3965 * Write the specified directory entry to the file
3967 HRESULT StorageImpl_WriteDirEntry(
3968 StorageImpl* This,
3969 DirRef index,
3970 const DirEntry* buffer)
3972 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3973 HRESULT writeRes;
3975 UpdateRawDirEntry(currentEntry, buffer);
3977 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3978 return writeRes;
3981 static BOOL StorageImpl_ReadBigBlock(
3982 StorageImpl* This,
3983 ULONG blockIndex,
3984 void* buffer)
3986 ULARGE_INTEGER ulOffset;
3987 DWORD read=0;
3989 ulOffset.u.HighPart = 0;
3990 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3992 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3994 if (read && read < This->bigBlockSize)
3996 /* File ends during this block; fill the rest with 0's. */
3997 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
4000 return (read != 0);
4003 static BOOL StorageImpl_ReadDWordFromBigBlock(
4004 StorageImpl* This,
4005 ULONG blockIndex,
4006 ULONG offset,
4007 DWORD* value)
4009 ULARGE_INTEGER ulOffset;
4010 DWORD read;
4011 DWORD tmp;
4013 ulOffset.u.HighPart = 0;
4014 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4015 ulOffset.u.LowPart += offset;
4017 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
4018 *value = lendian32toh(tmp);
4019 return (read == sizeof(DWORD));
4022 static BOOL StorageImpl_WriteBigBlock(
4023 StorageImpl* This,
4024 ULONG blockIndex,
4025 const void* buffer)
4027 ULARGE_INTEGER ulOffset;
4028 DWORD wrote;
4030 ulOffset.u.HighPart = 0;
4031 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4033 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
4034 return (wrote == This->bigBlockSize);
4037 static BOOL StorageImpl_WriteDWordToBigBlock(
4038 StorageImpl* This,
4039 ULONG blockIndex,
4040 ULONG offset,
4041 DWORD value)
4043 ULARGE_INTEGER ulOffset;
4044 DWORD wrote;
4046 ulOffset.u.HighPart = 0;
4047 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4048 ulOffset.u.LowPart += offset;
4050 value = htole32(value);
4051 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4052 return (wrote == sizeof(DWORD));
4055 /******************************************************************************
4056 * Storage32Impl_SmallBlocksToBigBlocks
4058 * This method will convert a small block chain to a big block chain.
4059 * The small block chain will be destroyed.
4061 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4062 StorageImpl* This,
4063 SmallBlockChainStream** ppsbChain)
4065 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4066 ULARGE_INTEGER size, offset;
4067 ULONG cbRead, cbWritten;
4068 ULARGE_INTEGER cbTotalRead;
4069 DirRef streamEntryRef;
4070 HRESULT resWrite = S_OK;
4071 HRESULT resRead;
4072 DirEntry streamEntry;
4073 BYTE *buffer;
4074 BlockChainStream *bbTempChain = NULL;
4075 BlockChainStream *bigBlockChain = NULL;
4078 * Create a temporary big block chain that doesn't have
4079 * an associated directory entry. This temporary chain will be
4080 * used to copy data from small blocks to big blocks.
4082 bbTempChain = BlockChainStream_Construct(This,
4083 &bbHeadOfChain,
4084 DIRENTRY_NULL);
4085 if(!bbTempChain) return NULL;
4087 * Grow the big block chain.
4089 size = SmallBlockChainStream_GetSize(*ppsbChain);
4090 BlockChainStream_SetSize(bbTempChain, size);
4093 * Copy the contents of the small block chain to the big block chain
4094 * by small block size increments.
4096 offset.u.LowPart = 0;
4097 offset.u.HighPart = 0;
4098 cbTotalRead.QuadPart = 0;
4100 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4103 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4104 offset,
4105 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4106 buffer,
4107 &cbRead);
4108 if (FAILED(resRead))
4109 break;
4111 if (cbRead > 0)
4113 cbTotalRead.QuadPart += cbRead;
4115 resWrite = BlockChainStream_WriteAt(bbTempChain,
4116 offset,
4117 cbRead,
4118 buffer,
4119 &cbWritten);
4121 if (FAILED(resWrite))
4122 break;
4124 offset.u.LowPart += cbRead;
4126 else
4128 resRead = STG_E_READFAULT;
4129 break;
4131 } while (cbTotalRead.QuadPart < size.QuadPart);
4132 HeapFree(GetProcessHeap(),0,buffer);
4134 size.u.HighPart = 0;
4135 size.u.LowPart = 0;
4137 if (FAILED(resRead) || FAILED(resWrite))
4139 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4140 BlockChainStream_SetSize(bbTempChain, size);
4141 BlockChainStream_Destroy(bbTempChain);
4142 return NULL;
4146 * Destroy the small block chain.
4148 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4149 SmallBlockChainStream_SetSize(*ppsbChain, size);
4150 SmallBlockChainStream_Destroy(*ppsbChain);
4151 *ppsbChain = 0;
4154 * Change the directory entry. This chain is now a big block chain
4155 * and it doesn't reside in the small blocks chain anymore.
4157 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4159 streamEntry.startingBlock = bbHeadOfChain;
4161 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4164 * Destroy the temporary entryless big block chain.
4165 * Create a new big block chain associated with this entry.
4167 BlockChainStream_Destroy(bbTempChain);
4168 bigBlockChain = BlockChainStream_Construct(This,
4169 NULL,
4170 streamEntryRef);
4172 return bigBlockChain;
4175 /******************************************************************************
4176 * Storage32Impl_BigBlocksToSmallBlocks
4178 * This method will convert a big block chain to a small block chain.
4179 * The big block chain will be destroyed on success.
4181 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4182 StorageImpl* This,
4183 BlockChainStream** ppbbChain,
4184 ULARGE_INTEGER newSize)
4186 ULARGE_INTEGER size, offset, cbTotalRead;
4187 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4188 DirRef streamEntryRef;
4189 HRESULT resWrite = S_OK, resRead = S_OK;
4190 DirEntry streamEntry;
4191 BYTE* buffer;
4192 SmallBlockChainStream* sbTempChain;
4194 TRACE("%p %p\n", This, ppbbChain);
4196 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4197 DIRENTRY_NULL);
4199 if(!sbTempChain)
4200 return NULL;
4202 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4203 size = BlockChainStream_GetSize(*ppbbChain);
4204 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4206 offset.u.HighPart = 0;
4207 offset.u.LowPart = 0;
4208 cbTotalRead.QuadPart = 0;
4209 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4210 while(cbTotalRead.QuadPart < size.QuadPart)
4212 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4213 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4214 buffer, &cbRead);
4216 if(FAILED(resRead))
4217 break;
4219 if(cbRead > 0)
4221 cbTotalRead.QuadPart += cbRead;
4223 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4224 cbRead, buffer, &cbWritten);
4226 if(FAILED(resWrite))
4227 break;
4229 offset.u.LowPart += cbRead;
4231 else
4233 resRead = STG_E_READFAULT;
4234 break;
4237 HeapFree(GetProcessHeap(), 0, buffer);
4239 size.u.HighPart = 0;
4240 size.u.LowPart = 0;
4242 if(FAILED(resRead) || FAILED(resWrite))
4244 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4245 SmallBlockChainStream_SetSize(sbTempChain, size);
4246 SmallBlockChainStream_Destroy(sbTempChain);
4247 return NULL;
4250 /* destroy the original big block chain */
4251 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4252 BlockChainStream_SetSize(*ppbbChain, size);
4253 BlockChainStream_Destroy(*ppbbChain);
4254 *ppbbChain = NULL;
4256 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4257 streamEntry.startingBlock = sbHeadOfChain;
4258 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4260 SmallBlockChainStream_Destroy(sbTempChain);
4261 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4264 static HRESULT StorageBaseImpl_CopyStream(
4265 StorageBaseImpl *dst, DirRef dst_entry,
4266 StorageBaseImpl *src, DirRef src_entry)
4268 HRESULT hr;
4269 BYTE data[4096];
4270 DirEntry srcdata;
4271 ULARGE_INTEGER bytes_copied;
4272 ULONG bytestocopy, bytesread, byteswritten;
4274 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4276 if (SUCCEEDED(hr))
4278 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4280 bytes_copied.QuadPart = 0;
4281 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4283 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4285 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4286 data, &bytesread);
4287 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4289 if (SUCCEEDED(hr))
4290 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4291 data, &byteswritten);
4292 if (SUCCEEDED(hr))
4294 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4295 bytes_copied.QuadPart += byteswritten;
4300 return hr;
4303 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4305 DirRef result=This->firstFreeEntry;
4307 while (result < This->entries_size && This->entries[result].inuse)
4308 result++;
4310 if (result == This->entries_size)
4312 ULONG new_size = This->entries_size * 2;
4313 TransactedDirEntry *new_entries;
4315 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4316 if (!new_entries) return DIRENTRY_NULL;
4318 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4319 HeapFree(GetProcessHeap(), 0, This->entries);
4321 This->entries = new_entries;
4322 This->entries_size = new_size;
4325 This->entries[result].inuse = 1;
4327 This->firstFreeEntry = result+1;
4329 return result;
4332 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4333 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4335 DirRef stubEntryRef;
4336 TransactedDirEntry *entry;
4338 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4340 if (stubEntryRef != DIRENTRY_NULL)
4342 entry = &This->entries[stubEntryRef];
4344 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4346 entry->read = 0;
4349 return stubEntryRef;
4352 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4353 TransactedSnapshotImpl *This, DirRef entry)
4355 HRESULT hr=S_OK;
4356 DirEntry data;
4358 if (!This->entries[entry].read)
4360 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4361 This->entries[entry].transactedParentEntry,
4362 &data);
4364 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4366 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4368 if (data.leftChild == DIRENTRY_NULL)
4369 hr = E_OUTOFMEMORY;
4372 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4374 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4376 if (data.rightChild == DIRENTRY_NULL)
4377 hr = E_OUTOFMEMORY;
4380 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4382 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4384 if (data.dirRootEntry == DIRENTRY_NULL)
4385 hr = E_OUTOFMEMORY;
4388 if (SUCCEEDED(hr))
4390 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4391 This->entries[entry].read = 1;
4395 return hr;
4398 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4399 TransactedSnapshotImpl *This, DirRef entry)
4401 HRESULT hr = S_OK;
4403 if (!This->entries[entry].stream_dirty)
4405 DirEntry new_entrydata;
4407 memset(&new_entrydata, 0, sizeof(DirEntry));
4408 new_entrydata.name[0] = 'S';
4409 new_entrydata.sizeOfNameString = 1;
4410 new_entrydata.stgType = STGTY_STREAM;
4411 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4412 new_entrydata.leftChild = DIRENTRY_NULL;
4413 new_entrydata.rightChild = DIRENTRY_NULL;
4414 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4416 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4417 &This->entries[entry].stream_entry);
4419 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4421 hr = StorageBaseImpl_CopyStream(
4422 This->scratch, This->entries[entry].stream_entry,
4423 This->transactedParent, This->entries[entry].transactedParentEntry);
4425 if (FAILED(hr))
4426 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4429 if (SUCCEEDED(hr))
4430 This->entries[entry].stream_dirty = 1;
4432 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4434 /* Since this entry is modified, and we aren't using its stream data, we
4435 * no longer care about the original entry. */
4436 DirRef delete_ref;
4437 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4439 if (delete_ref != DIRENTRY_NULL)
4440 This->entries[delete_ref].deleted = 1;
4442 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4446 return hr;
4449 /* Find the first entry in a depth-first traversal. */
4450 static DirRef TransactedSnapshotImpl_FindFirstChild(
4451 TransactedSnapshotImpl* This, DirRef parent)
4453 DirRef cursor, prev;
4454 TransactedDirEntry *entry;
4456 cursor = parent;
4457 entry = &This->entries[cursor];
4458 while (entry->read)
4460 if (entry->data.leftChild != DIRENTRY_NULL)
4462 prev = cursor;
4463 cursor = entry->data.leftChild;
4464 entry = &This->entries[cursor];
4465 entry->parent = prev;
4467 else if (entry->data.rightChild != DIRENTRY_NULL)
4469 prev = cursor;
4470 cursor = entry->data.rightChild;
4471 entry = &This->entries[cursor];
4472 entry->parent = prev;
4474 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4476 prev = cursor;
4477 cursor = entry->data.dirRootEntry;
4478 entry = &This->entries[cursor];
4479 entry->parent = prev;
4481 else
4482 break;
4485 return cursor;
4488 /* Find the next entry in a depth-first traversal. */
4489 static DirRef TransactedSnapshotImpl_FindNextChild(
4490 TransactedSnapshotImpl* This, DirRef current)
4492 DirRef parent;
4493 TransactedDirEntry *parent_entry;
4495 parent = This->entries[current].parent;
4496 parent_entry = &This->entries[parent];
4498 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4500 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4502 This->entries[parent_entry->data.rightChild].parent = parent;
4503 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4506 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4508 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4509 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4513 return parent;
4516 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4517 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4518 TransactedSnapshotImpl* This, DirRef entry)
4520 return entry != DIRENTRY_NULL &&
4521 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4524 /* Destroy the entries created by CopyTree. */
4525 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4526 TransactedSnapshotImpl* This, DirRef stop)
4528 DirRef cursor;
4529 TransactedDirEntry *entry;
4530 ULARGE_INTEGER zero;
4532 zero.QuadPart = 0;
4534 if (!This->entries[This->base.storageDirEntry].read)
4535 return;
4537 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4539 if (cursor == DIRENTRY_NULL)
4540 return;
4542 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4544 while (cursor != DIRENTRY_NULL && cursor != stop)
4546 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4548 entry = &This->entries[cursor];
4550 if (entry->stream_dirty)
4551 StorageBaseImpl_StreamSetSize(This->transactedParent,
4552 entry->newTransactedParentEntry, zero);
4554 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4555 entry->newTransactedParentEntry);
4557 entry->newTransactedParentEntry = entry->transactedParentEntry;
4560 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4564 /* Make a copy of our edited tree that we can use in the parent. */
4565 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4567 DirRef cursor;
4568 TransactedDirEntry *entry;
4569 HRESULT hr = S_OK;
4571 cursor = This->base.storageDirEntry;
4572 entry = &This->entries[cursor];
4573 entry->parent = DIRENTRY_NULL;
4574 entry->newTransactedParentEntry = entry->transactedParentEntry;
4576 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4577 return S_OK;
4579 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4581 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4582 entry = &This->entries[cursor];
4584 while (cursor != DIRENTRY_NULL)
4586 /* Make a copy of this entry in the transacted parent. */
4587 if (!entry->read ||
4588 (!entry->dirty && !entry->stream_dirty &&
4589 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4590 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4591 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4592 entry->newTransactedParentEntry = entry->transactedParentEntry;
4593 else
4595 DirEntry newData;
4597 memcpy(&newData, &entry->data, sizeof(DirEntry));
4599 newData.size.QuadPart = 0;
4600 newData.startingBlock = BLOCK_END_OF_CHAIN;
4602 if (newData.leftChild != DIRENTRY_NULL)
4603 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4605 if (newData.rightChild != DIRENTRY_NULL)
4606 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4608 if (newData.dirRootEntry != DIRENTRY_NULL)
4609 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4611 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4612 &entry->newTransactedParentEntry);
4613 if (FAILED(hr))
4615 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4616 return hr;
4619 if (entry->stream_dirty)
4621 hr = StorageBaseImpl_CopyStream(
4622 This->transactedParent, entry->newTransactedParentEntry,
4623 This->scratch, entry->stream_entry);
4625 else if (entry->data.size.QuadPart)
4627 hr = StorageBaseImpl_StreamLink(
4628 This->transactedParent, entry->newTransactedParentEntry,
4629 entry->transactedParentEntry);
4632 if (FAILED(hr))
4634 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4635 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4636 return hr;
4640 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4641 entry = &This->entries[cursor];
4644 return hr;
4647 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4648 IStorage* iface,
4649 DWORD grfCommitFlags) /* [in] */
4651 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4652 TransactedDirEntry *root_entry;
4653 DirRef i, dir_root_ref;
4654 DirEntry data;
4655 ULARGE_INTEGER zero;
4656 HRESULT hr;
4658 zero.QuadPart = 0;
4660 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4662 /* Cannot commit a read-only transacted storage */
4663 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4664 return STG_E_ACCESSDENIED;
4666 /* To prevent data loss, we create the new structure in the file before we
4667 * delete the old one, so that in case of errors the old data is intact. We
4668 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4669 * needed in the rare situation where we have just enough free disk space to
4670 * overwrite the existing data. */
4672 root_entry = &This->entries[This->base.storageDirEntry];
4674 if (!root_entry->read)
4675 return S_OK;
4677 hr = TransactedSnapshotImpl_CopyTree(This);
4678 if (FAILED(hr)) return hr;
4680 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4681 dir_root_ref = DIRENTRY_NULL;
4682 else
4683 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4685 hr = StorageBaseImpl_Flush(This->transactedParent);
4687 /* Update the storage to use the new data in one step. */
4688 if (SUCCEEDED(hr))
4689 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4690 root_entry->transactedParentEntry, &data);
4692 if (SUCCEEDED(hr))
4694 data.dirRootEntry = dir_root_ref;
4695 data.clsid = root_entry->data.clsid;
4696 data.ctime = root_entry->data.ctime;
4697 data.mtime = root_entry->data.mtime;
4699 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4700 root_entry->transactedParentEntry, &data);
4703 /* Try to flush after updating the root storage, but if the flush fails, keep
4704 * going, on the theory that it'll either succeed later or the subsequent
4705 * writes will fail. */
4706 StorageBaseImpl_Flush(This->transactedParent);
4708 if (SUCCEEDED(hr))
4710 /* Destroy the old now-orphaned data. */
4711 for (i=0; i<This->entries_size; i++)
4713 TransactedDirEntry *entry = &This->entries[i];
4714 if (entry->inuse)
4716 if (entry->deleted)
4718 StorageBaseImpl_StreamSetSize(This->transactedParent,
4719 entry->transactedParentEntry, zero);
4720 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4721 entry->transactedParentEntry);
4722 memset(entry, 0, sizeof(TransactedDirEntry));
4723 This->firstFreeEntry = min(i, This->firstFreeEntry);
4725 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4727 if (entry->transactedParentEntry != DIRENTRY_NULL)
4728 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4729 entry->transactedParentEntry);
4730 if (entry->stream_dirty)
4732 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4733 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4734 entry->stream_dirty = 0;
4736 entry->dirty = 0;
4737 entry->transactedParentEntry = entry->newTransactedParentEntry;
4742 else
4744 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4747 if (SUCCEEDED(hr))
4748 hr = StorageBaseImpl_Flush(This->transactedParent);
4750 return hr;
4753 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4754 IStorage* iface)
4756 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
4757 ULARGE_INTEGER zero;
4758 ULONG i;
4760 TRACE("(%p)\n", iface);
4762 /* Destroy the open objects. */
4763 StorageBaseImpl_DeleteAll(&This->base);
4765 /* Clear out the scratch file. */
4766 zero.QuadPart = 0;
4767 for (i=0; i<This->entries_size; i++)
4769 if (This->entries[i].stream_dirty)
4771 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4772 zero);
4774 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4778 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4780 This->firstFreeEntry = 0;
4781 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4783 return S_OK;
4786 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4788 if (!This->reverted)
4790 TRACE("Storage invalidated (stg=%p)\n", This);
4792 This->reverted = 1;
4794 StorageBaseImpl_DeleteAll(This);
4798 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4800 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4802 IStorage_Revert(&This->base.IStorage_iface);
4803 IStorage_Release(&This->transactedParent->IStorage_iface);
4804 IStorage_Release(&This->scratch->IStorage_iface);
4805 HeapFree(GetProcessHeap(), 0, This->entries);
4806 HeapFree(GetProcessHeap(), 0, This);
4809 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4811 /* We only need to flush when committing. */
4812 return S_OK;
4815 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4817 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4819 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4822 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4823 const DirEntry *newData, DirRef *index)
4825 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4826 DirRef new_ref;
4827 TransactedDirEntry *new_entry;
4829 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4830 if (new_ref == DIRENTRY_NULL)
4831 return E_OUTOFMEMORY;
4833 new_entry = &This->entries[new_ref];
4835 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4836 new_entry->read = 1;
4837 new_entry->dirty = 1;
4838 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4840 *index = new_ref;
4842 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4844 return S_OK;
4847 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4848 DirRef index, const DirEntry *data)
4850 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4851 HRESULT hr;
4853 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4855 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4856 if (FAILED(hr)) return hr;
4858 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4860 if (index != This->base.storageDirEntry)
4862 This->entries[index].dirty = 1;
4864 if (data->size.QuadPart == 0 &&
4865 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4867 /* Since this entry is modified, and we aren't using its stream data, we
4868 * no longer care about the original entry. */
4869 DirRef delete_ref;
4870 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4872 if (delete_ref != DIRENTRY_NULL)
4873 This->entries[delete_ref].deleted = 1;
4875 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4879 return S_OK;
4882 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4883 DirRef index, DirEntry *data)
4885 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4886 HRESULT hr;
4888 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4889 if (FAILED(hr)) return hr;
4891 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4893 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4895 return S_OK;
4898 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4899 DirRef index)
4901 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4903 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4904 This->entries[index].data.size.QuadPart != 0)
4906 /* If we deleted this entry while it has stream data. We must have left the
4907 * data because some other entry is using it, and we need to leave the
4908 * original entry alone. */
4909 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4910 This->firstFreeEntry = min(index, This->firstFreeEntry);
4912 else
4914 This->entries[index].deleted = 1;
4917 return S_OK;
4920 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4921 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4923 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4925 if (This->entries[index].stream_dirty)
4927 return StorageBaseImpl_StreamReadAt(This->scratch,
4928 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4930 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4932 /* This stream doesn't live in the parent, and we haven't allocated storage
4933 * for it yet */
4934 *bytesRead = 0;
4935 return S_OK;
4937 else
4939 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4940 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4944 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4945 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4947 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4948 HRESULT hr;
4950 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4951 if (FAILED(hr)) return hr;
4953 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4954 if (FAILED(hr)) return hr;
4956 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4957 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4959 if (SUCCEEDED(hr) && size != 0)
4960 This->entries[index].data.size.QuadPart = max(
4961 This->entries[index].data.size.QuadPart,
4962 offset.QuadPart + size);
4964 return hr;
4967 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4968 DirRef index, ULARGE_INTEGER newsize)
4970 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4971 HRESULT hr;
4973 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4974 if (FAILED(hr)) return hr;
4976 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4977 return S_OK;
4979 if (newsize.QuadPart == 0)
4981 /* Destroy any parent references or entries in the scratch file. */
4982 if (This->entries[index].stream_dirty)
4984 ULARGE_INTEGER zero;
4985 zero.QuadPart = 0;
4986 StorageBaseImpl_StreamSetSize(This->scratch,
4987 This->entries[index].stream_entry, zero);
4988 StorageBaseImpl_DestroyDirEntry(This->scratch,
4989 This->entries[index].stream_entry);
4990 This->entries[index].stream_dirty = 0;
4992 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4994 DirRef delete_ref;
4995 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4997 if (delete_ref != DIRENTRY_NULL)
4998 This->entries[delete_ref].deleted = 1;
5000 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
5003 else
5005 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
5006 if (FAILED(hr)) return hr;
5008 hr = StorageBaseImpl_StreamSetSize(This->scratch,
5009 This->entries[index].stream_entry, newsize);
5012 if (SUCCEEDED(hr))
5013 This->entries[index].data.size = newsize;
5015 return hr;
5018 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
5019 DirRef dst, DirRef src)
5021 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
5022 HRESULT hr;
5023 TransactedDirEntry *dst_entry, *src_entry;
5025 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
5026 if (FAILED(hr)) return hr;
5028 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
5029 if (FAILED(hr)) return hr;
5031 dst_entry = &This->entries[dst];
5032 src_entry = &This->entries[src];
5034 dst_entry->stream_dirty = src_entry->stream_dirty;
5035 dst_entry->stream_entry = src_entry->stream_entry;
5036 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
5037 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
5038 dst_entry->data.size = src_entry->data.size;
5040 return S_OK;
5043 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5045 StorageBaseImpl_QueryInterface,
5046 StorageBaseImpl_AddRef,
5047 StorageBaseImpl_Release,
5048 StorageBaseImpl_CreateStream,
5049 StorageBaseImpl_OpenStream,
5050 StorageBaseImpl_CreateStorage,
5051 StorageBaseImpl_OpenStorage,
5052 StorageBaseImpl_CopyTo,
5053 StorageBaseImpl_MoveElementTo,
5054 TransactedSnapshotImpl_Commit,
5055 TransactedSnapshotImpl_Revert,
5056 StorageBaseImpl_EnumElements,
5057 StorageBaseImpl_DestroyElement,
5058 StorageBaseImpl_RenameElement,
5059 StorageBaseImpl_SetElementTimes,
5060 StorageBaseImpl_SetClass,
5061 StorageBaseImpl_SetStateBits,
5062 StorageBaseImpl_Stat
5065 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5067 TransactedSnapshotImpl_Destroy,
5068 TransactedSnapshotImpl_Invalidate,
5069 TransactedSnapshotImpl_Flush,
5070 TransactedSnapshotImpl_GetFilename,
5071 TransactedSnapshotImpl_CreateDirEntry,
5072 TransactedSnapshotImpl_WriteDirEntry,
5073 TransactedSnapshotImpl_ReadDirEntry,
5074 TransactedSnapshotImpl_DestroyDirEntry,
5075 TransactedSnapshotImpl_StreamReadAt,
5076 TransactedSnapshotImpl_StreamWriteAt,
5077 TransactedSnapshotImpl_StreamSetSize,
5078 TransactedSnapshotImpl_StreamLink
5081 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5082 TransactedSnapshotImpl** result)
5084 HRESULT hr;
5086 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5087 if (*result)
5089 IStorage *scratch;
5091 (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5093 /* This is OK because the property set storage functions use the IStorage functions. */
5094 (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
5095 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5097 list_init(&(*result)->base.strmHead);
5099 list_init(&(*result)->base.storageHead);
5101 (*result)->base.ref = 1;
5103 (*result)->base.openFlags = parentStorage->openFlags;
5105 /* Create a new temporary storage to act as the scratch file. */
5106 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
5107 0, &scratch);
5108 (*result)->scratch = impl_from_IStorage(scratch);
5110 if (SUCCEEDED(hr))
5112 ULONG num_entries = 20;
5114 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5115 (*result)->entries_size = num_entries;
5116 (*result)->firstFreeEntry = 0;
5118 if ((*result)->entries)
5120 /* parentStorage already has 1 reference, which we take over here. */
5121 (*result)->transactedParent = parentStorage;
5123 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5125 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5127 else
5129 IStorage_Release(scratch);
5131 hr = E_OUTOFMEMORY;
5135 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5137 return hr;
5139 else
5140 return E_OUTOFMEMORY;
5143 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5144 StorageBaseImpl** result)
5146 static int fixme=0;
5148 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5150 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5153 return TransactedSnapshotImpl_Construct(parentStorage,
5154 (TransactedSnapshotImpl**)result);
5157 static HRESULT Storage_Construct(
5158 HANDLE hFile,
5159 LPCOLESTR pwcsName,
5160 ILockBytes* pLkbyt,
5161 DWORD openFlags,
5162 BOOL fileBased,
5163 BOOL create,
5164 ULONG sector_size,
5165 StorageBaseImpl** result)
5167 StorageImpl *newStorage;
5168 StorageBaseImpl *newTransactedStorage;
5169 HRESULT hr;
5171 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5172 if (FAILED(hr)) goto end;
5174 if (openFlags & STGM_TRANSACTED)
5176 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5177 if (FAILED(hr))
5178 IStorage_Release(&newStorage->base.IStorage_iface);
5179 else
5180 *result = newTransactedStorage;
5182 else
5183 *result = &newStorage->base;
5185 end:
5186 return hr;
5189 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5191 StorageInternalImpl* This = (StorageInternalImpl*) base;
5193 if (!This->base.reverted)
5195 TRACE("Storage invalidated (stg=%p)\n", This);
5197 This->base.reverted = 1;
5199 This->parentStorage = NULL;
5201 StorageBaseImpl_DeleteAll(&This->base);
5203 list_remove(&This->ParentListEntry);
5207 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5209 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5211 StorageInternalImpl_Invalidate(&This->base);
5213 HeapFree(GetProcessHeap(), 0, This);
5216 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5218 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5220 return StorageBaseImpl_Flush(This->parentStorage);
5223 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5225 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5227 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5230 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5231 const DirEntry *newData, DirRef *index)
5233 StorageInternalImpl* This = (StorageInternalImpl*) base;
5235 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5236 newData, index);
5239 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5240 DirRef index, const DirEntry *data)
5242 StorageInternalImpl* This = (StorageInternalImpl*) base;
5244 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5245 index, data);
5248 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5249 DirRef index, DirEntry *data)
5251 StorageInternalImpl* This = (StorageInternalImpl*) base;
5253 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5254 index, data);
5257 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5258 DirRef index)
5260 StorageInternalImpl* This = (StorageInternalImpl*) base;
5262 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5263 index);
5266 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5267 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5269 StorageInternalImpl* This = (StorageInternalImpl*) base;
5271 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5272 index, offset, size, buffer, bytesRead);
5275 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5276 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5278 StorageInternalImpl* This = (StorageInternalImpl*) base;
5280 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5281 index, offset, size, buffer, bytesWritten);
5284 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5285 DirRef index, ULARGE_INTEGER newsize)
5287 StorageInternalImpl* This = (StorageInternalImpl*) base;
5289 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5290 index, newsize);
5293 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5294 DirRef dst, DirRef src)
5296 StorageInternalImpl* This = (StorageInternalImpl*) base;
5298 return StorageBaseImpl_StreamLink(This->parentStorage,
5299 dst, src);
5302 /******************************************************************************
5304 ** Storage32InternalImpl_Commit
5307 static HRESULT WINAPI StorageInternalImpl_Commit(
5308 IStorage* iface,
5309 DWORD grfCommitFlags) /* [in] */
5311 StorageBaseImpl* This = impl_from_IStorage(iface);
5312 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5313 return StorageBaseImpl_Flush(This);
5316 /******************************************************************************
5318 ** Storage32InternalImpl_Revert
5321 static HRESULT WINAPI StorageInternalImpl_Revert(
5322 IStorage* iface)
5324 FIXME("(%p): stub\n", iface);
5325 return S_OK;
5328 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5330 IStorage_Release(&This->parentStorage->IStorage_iface);
5331 HeapFree(GetProcessHeap(), 0, This);
5334 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5335 IEnumSTATSTG* iface,
5336 REFIID riid,
5337 void** ppvObject)
5339 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5341 if (ppvObject==0)
5342 return E_INVALIDARG;
5344 *ppvObject = 0;
5346 if (IsEqualGUID(&IID_IUnknown, riid) ||
5347 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5349 *ppvObject = This;
5350 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5351 return S_OK;
5354 return E_NOINTERFACE;
5357 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5358 IEnumSTATSTG* iface)
5360 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5361 return InterlockedIncrement(&This->ref);
5364 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5365 IEnumSTATSTG* iface)
5367 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5369 ULONG newRef;
5371 newRef = InterlockedDecrement(&This->ref);
5373 if (newRef==0)
5375 IEnumSTATSTGImpl_Destroy(This);
5378 return newRef;
5381 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5382 IEnumSTATSTGImpl* This,
5383 DirRef *ref)
5385 DirRef result = DIRENTRY_NULL;
5386 DirRef searchNode;
5387 DirEntry entry;
5388 HRESULT hr;
5389 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5391 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5392 This->parentStorage->storageDirEntry, &entry);
5393 searchNode = entry.dirRootEntry;
5395 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5397 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5399 if (SUCCEEDED(hr))
5401 LONG diff = entryNameCmp( entry.name, This->name);
5403 if (diff <= 0)
5405 searchNode = entry.rightChild;
5407 else
5409 result = searchNode;
5410 memcpy(result_name, entry.name, sizeof(result_name));
5411 searchNode = entry.leftChild;
5416 if (SUCCEEDED(hr))
5418 *ref = result;
5419 if (result != DIRENTRY_NULL)
5420 memcpy(This->name, result_name, sizeof(result_name));
5423 return hr;
5426 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5427 IEnumSTATSTG* iface,
5428 ULONG celt,
5429 STATSTG* rgelt,
5430 ULONG* pceltFetched)
5432 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5434 DirEntry currentEntry;
5435 STATSTG* currentReturnStruct = rgelt;
5436 ULONG objectFetched = 0;
5437 DirRef currentSearchNode;
5438 HRESULT hr=S_OK;
5440 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5441 return E_INVALIDARG;
5443 if (This->parentStorage->reverted)
5444 return STG_E_REVERTED;
5447 * To avoid the special case, get another pointer to a ULONG value if
5448 * the caller didn't supply one.
5450 if (pceltFetched==0)
5451 pceltFetched = &objectFetched;
5454 * Start the iteration, we will iterate until we hit the end of the
5455 * linked list or until we hit the number of items to iterate through
5457 *pceltFetched = 0;
5459 while ( *pceltFetched < celt )
5461 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5463 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5464 break;
5467 * Read the entry from the storage.
5469 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5470 currentSearchNode,
5471 &currentEntry);
5474 * Copy the information to the return buffer.
5476 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5477 currentReturnStruct,
5478 &currentEntry,
5479 STATFLAG_DEFAULT);
5482 * Step to the next item in the iteration
5484 (*pceltFetched)++;
5485 currentReturnStruct++;
5488 if (SUCCEEDED(hr) && *pceltFetched != celt)
5489 hr = S_FALSE;
5491 return hr;
5495 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5496 IEnumSTATSTG* iface,
5497 ULONG celt)
5499 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5501 ULONG objectFetched = 0;
5502 DirRef currentSearchNode;
5503 HRESULT hr=S_OK;
5505 if (This->parentStorage->reverted)
5506 return STG_E_REVERTED;
5508 while ( (objectFetched < celt) )
5510 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5512 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5513 break;
5515 objectFetched++;
5518 if (SUCCEEDED(hr) && objectFetched != celt)
5519 return S_FALSE;
5521 return hr;
5524 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5525 IEnumSTATSTG* iface)
5527 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5529 if (This->parentStorage->reverted)
5530 return STG_E_REVERTED;
5532 This->name[0] = 0;
5534 return S_OK;
5537 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5538 IEnumSTATSTG* iface,
5539 IEnumSTATSTG** ppenum)
5541 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5543 IEnumSTATSTGImpl* newClone;
5545 if (This->parentStorage->reverted)
5546 return STG_E_REVERTED;
5549 * Perform a sanity check on the parameters.
5551 if (ppenum==0)
5552 return E_INVALIDARG;
5554 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5555 This->storageDirEntry);
5559 * The new clone enumeration must point to the same current node as
5560 * the ole one.
5562 memcpy(newClone->name, This->name, sizeof(newClone->name));
5564 *ppenum = &newClone->IEnumSTATSTG_iface;
5567 * Don't forget to nail down a reference to the clone before
5568 * returning it.
5570 IEnumSTATSTGImpl_AddRef(*ppenum);
5572 return S_OK;
5576 * Virtual function table for the IEnumSTATSTGImpl class.
5578 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5580 IEnumSTATSTGImpl_QueryInterface,
5581 IEnumSTATSTGImpl_AddRef,
5582 IEnumSTATSTGImpl_Release,
5583 IEnumSTATSTGImpl_Next,
5584 IEnumSTATSTGImpl_Skip,
5585 IEnumSTATSTGImpl_Reset,
5586 IEnumSTATSTGImpl_Clone
5589 /******************************************************************************
5590 ** IEnumSTATSTGImpl implementation
5593 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5594 StorageBaseImpl* parentStorage,
5595 DirRef storageDirEntry)
5597 IEnumSTATSTGImpl* newEnumeration;
5599 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5601 if (newEnumeration!=0)
5604 * Set-up the virtual function table and reference count.
5606 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5607 newEnumeration->ref = 0;
5610 * We want to nail-down the reference to the storage in case the
5611 * enumeration out-lives the storage in the client application.
5613 newEnumeration->parentStorage = parentStorage;
5614 IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
5616 newEnumeration->storageDirEntry = storageDirEntry;
5619 * Make sure the current node of the iterator is the first one.
5621 IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface);
5624 return newEnumeration;
5628 * Virtual function table for the Storage32InternalImpl class.
5630 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5632 StorageBaseImpl_QueryInterface,
5633 StorageBaseImpl_AddRef,
5634 StorageBaseImpl_Release,
5635 StorageBaseImpl_CreateStream,
5636 StorageBaseImpl_OpenStream,
5637 StorageBaseImpl_CreateStorage,
5638 StorageBaseImpl_OpenStorage,
5639 StorageBaseImpl_CopyTo,
5640 StorageBaseImpl_MoveElementTo,
5641 StorageInternalImpl_Commit,
5642 StorageInternalImpl_Revert,
5643 StorageBaseImpl_EnumElements,
5644 StorageBaseImpl_DestroyElement,
5645 StorageBaseImpl_RenameElement,
5646 StorageBaseImpl_SetElementTimes,
5647 StorageBaseImpl_SetClass,
5648 StorageBaseImpl_SetStateBits,
5649 StorageBaseImpl_Stat
5652 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5654 StorageInternalImpl_Destroy,
5655 StorageInternalImpl_Invalidate,
5656 StorageInternalImpl_Flush,
5657 StorageInternalImpl_GetFilename,
5658 StorageInternalImpl_CreateDirEntry,
5659 StorageInternalImpl_WriteDirEntry,
5660 StorageInternalImpl_ReadDirEntry,
5661 StorageInternalImpl_DestroyDirEntry,
5662 StorageInternalImpl_StreamReadAt,
5663 StorageInternalImpl_StreamWriteAt,
5664 StorageInternalImpl_StreamSetSize,
5665 StorageInternalImpl_StreamLink
5668 /******************************************************************************
5669 ** Storage32InternalImpl implementation
5672 static StorageInternalImpl* StorageInternalImpl_Construct(
5673 StorageBaseImpl* parentStorage,
5674 DWORD openFlags,
5675 DirRef storageDirEntry)
5677 StorageInternalImpl* newStorage;
5679 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5681 if (newStorage!=0)
5683 list_init(&newStorage->base.strmHead);
5685 list_init(&newStorage->base.storageHead);
5688 * Initialize the virtual function table.
5690 newStorage->base.IStorage_iface.lpVtbl = &Storage32InternalImpl_Vtbl;
5691 newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
5692 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5693 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5695 newStorage->base.reverted = 0;
5697 newStorage->base.ref = 1;
5699 newStorage->parentStorage = parentStorage;
5702 * Keep a reference to the directory entry of this storage
5704 newStorage->base.storageDirEntry = storageDirEntry;
5706 newStorage->base.create = 0;
5708 return newStorage;
5711 return 0;
5714 /******************************************************************************
5715 ** StorageUtl implementation
5718 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5720 WORD tmp;
5722 memcpy(&tmp, buffer+offset, sizeof(WORD));
5723 *value = lendian16toh(tmp);
5726 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5728 value = htole16(value);
5729 memcpy(buffer+offset, &value, sizeof(WORD));
5732 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5734 DWORD tmp;
5736 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5737 *value = lendian32toh(tmp);
5740 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5742 value = htole32(value);
5743 memcpy(buffer+offset, &value, sizeof(DWORD));
5746 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5747 ULARGE_INTEGER* value)
5749 #ifdef WORDS_BIGENDIAN
5750 ULARGE_INTEGER tmp;
5752 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5753 value->u.LowPart = htole32(tmp.u.HighPart);
5754 value->u.HighPart = htole32(tmp.u.LowPart);
5755 #else
5756 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5757 #endif
5760 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5761 const ULARGE_INTEGER *value)
5763 #ifdef WORDS_BIGENDIAN
5764 ULARGE_INTEGER tmp;
5766 tmp.u.LowPart = htole32(value->u.HighPart);
5767 tmp.u.HighPart = htole32(value->u.LowPart);
5768 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5769 #else
5770 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5771 #endif
5774 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5776 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5777 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5778 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5780 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5783 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5785 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5786 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5787 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5789 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5792 void StorageUtl_CopyDirEntryToSTATSTG(
5793 StorageBaseImpl* storage,
5794 STATSTG* destination,
5795 const DirEntry* source,
5796 int statFlags)
5799 * The copy of the string occurs only when the flag is not set
5801 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5803 /* Use the filename for the root storage. */
5804 destination->pwcsName = 0;
5805 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5807 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5808 (source->name[0] == 0) )
5810 destination->pwcsName = 0;
5812 else
5814 destination->pwcsName =
5815 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5817 strcpyW(destination->pwcsName, source->name);
5820 switch (source->stgType)
5822 case STGTY_STORAGE:
5823 case STGTY_ROOT:
5824 destination->type = STGTY_STORAGE;
5825 break;
5826 case STGTY_STREAM:
5827 destination->type = STGTY_STREAM;
5828 break;
5829 default:
5830 destination->type = STGTY_STREAM;
5831 break;
5834 destination->cbSize = source->size;
5836 currentReturnStruct->mtime = {0}; TODO
5837 currentReturnStruct->ctime = {0};
5838 currentReturnStruct->atime = {0};
5840 destination->grfMode = 0;
5841 destination->grfLocksSupported = 0;
5842 destination->clsid = source->clsid;
5843 destination->grfStateBits = 0;
5844 destination->reserved = 0;
5847 /******************************************************************************
5848 ** BlockChainStream implementation
5851 /* Read and save the index of all blocks in this stream. */
5852 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5854 ULONG next_sector, next_offset;
5855 HRESULT hr;
5856 struct BlockChainRun *last_run;
5858 if (This->indexCacheLen == 0)
5860 last_run = NULL;
5861 next_offset = 0;
5862 next_sector = BlockChainStream_GetHeadOfChain(This);
5864 else
5866 last_run = &This->indexCache[This->indexCacheLen-1];
5867 next_offset = last_run->lastOffset+1;
5868 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5869 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5870 &next_sector);
5871 if (FAILED(hr)) return hr;
5874 while (next_sector != BLOCK_END_OF_CHAIN)
5876 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5878 /* Add the current block to the cache. */
5879 if (This->indexCacheSize == 0)
5881 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5882 if (!This->indexCache) return E_OUTOFMEMORY;
5883 This->indexCacheSize = 16;
5885 else if (This->indexCacheSize == This->indexCacheLen)
5887 struct BlockChainRun *new_cache;
5888 ULONG new_size;
5890 new_size = This->indexCacheSize * 2;
5891 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5892 if (!new_cache) return E_OUTOFMEMORY;
5893 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5895 HeapFree(GetProcessHeap(), 0, This->indexCache);
5896 This->indexCache = new_cache;
5897 This->indexCacheSize = new_size;
5900 This->indexCacheLen++;
5901 last_run = &This->indexCache[This->indexCacheLen-1];
5902 last_run->firstSector = next_sector;
5903 last_run->firstOffset = next_offset;
5906 last_run->lastOffset = next_offset;
5908 /* Find the next block. */
5909 next_offset++;
5910 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5911 if (FAILED(hr)) return hr;
5914 if (This->indexCacheLen)
5916 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5917 This->numBlocks = last_run->lastOffset+1;
5919 else
5921 This->tailIndex = BLOCK_END_OF_CHAIN;
5922 This->numBlocks = 0;
5925 return S_OK;
5928 /* Locate the nth block in this stream. */
5929 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5931 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5932 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5934 if (offset >= This->numBlocks)
5935 return BLOCK_END_OF_CHAIN;
5937 while (min_run < max_run)
5939 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5940 if (offset < This->indexCache[run_to_check].firstOffset)
5942 max_offset = This->indexCache[run_to_check].firstOffset-1;
5943 max_run = run_to_check-1;
5945 else if (offset > This->indexCache[run_to_check].lastOffset)
5947 min_offset = This->indexCache[run_to_check].lastOffset+1;
5948 min_run = run_to_check+1;
5950 else
5951 /* Block is in this run. */
5952 min_run = max_run = run_to_check;
5955 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5958 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5959 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5961 BlockChainBlock *result=NULL;
5962 int i;
5964 for (i=0; i<2; i++)
5965 if (This->cachedBlocks[i].index == index)
5967 *sector = This->cachedBlocks[i].sector;
5968 *block = &This->cachedBlocks[i];
5969 return S_OK;
5972 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5973 if (*sector == BLOCK_END_OF_CHAIN)
5974 return STG_E_DOCFILECORRUPT;
5976 if (create)
5978 if (This->cachedBlocks[0].index == 0xffffffff)
5979 result = &This->cachedBlocks[0];
5980 else if (This->cachedBlocks[1].index == 0xffffffff)
5981 result = &This->cachedBlocks[1];
5982 else
5984 result = &This->cachedBlocks[This->blockToEvict++];
5985 if (This->blockToEvict == 2)
5986 This->blockToEvict = 0;
5989 if (result->dirty)
5991 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5992 return STG_E_WRITEFAULT;
5993 result->dirty = 0;
5996 result->read = 0;
5997 result->index = index;
5998 result->sector = *sector;
6001 *block = result;
6002 return S_OK;
6005 BlockChainStream* BlockChainStream_Construct(
6006 StorageImpl* parentStorage,
6007 ULONG* headOfStreamPlaceHolder,
6008 DirRef dirEntry)
6010 BlockChainStream* newStream;
6012 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
6014 newStream->parentStorage = parentStorage;
6015 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6016 newStream->ownerDirEntry = dirEntry;
6017 newStream->indexCache = NULL;
6018 newStream->indexCacheLen = 0;
6019 newStream->indexCacheSize = 0;
6020 newStream->cachedBlocks[0].index = 0xffffffff;
6021 newStream->cachedBlocks[0].dirty = 0;
6022 newStream->cachedBlocks[1].index = 0xffffffff;
6023 newStream->cachedBlocks[1].dirty = 0;
6024 newStream->blockToEvict = 0;
6026 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
6028 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
6029 HeapFree(GetProcessHeap(), 0, newStream);
6030 return NULL;
6033 return newStream;
6036 HRESULT BlockChainStream_Flush(BlockChainStream* This)
6038 int i;
6039 if (!This) return S_OK;
6040 for (i=0; i<2; i++)
6042 if (This->cachedBlocks[i].dirty)
6044 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6045 This->cachedBlocks[i].dirty = 0;
6046 else
6047 return STG_E_WRITEFAULT;
6050 return S_OK;
6053 void BlockChainStream_Destroy(BlockChainStream* This)
6055 if (This)
6057 BlockChainStream_Flush(This);
6058 HeapFree(GetProcessHeap(), 0, This->indexCache);
6060 HeapFree(GetProcessHeap(), 0, This);
6063 /******************************************************************************
6064 * BlockChainStream_GetHeadOfChain
6066 * Returns the head of this stream chain.
6067 * Some special chains don't have directory entries, their heads are kept in
6068 * This->headOfStreamPlaceHolder.
6071 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6073 DirEntry chainEntry;
6074 HRESULT hr;
6076 if (This->headOfStreamPlaceHolder != 0)
6077 return *(This->headOfStreamPlaceHolder);
6079 if (This->ownerDirEntry != DIRENTRY_NULL)
6081 hr = StorageImpl_ReadDirEntry(
6082 This->parentStorage,
6083 This->ownerDirEntry,
6084 &chainEntry);
6086 if (SUCCEEDED(hr))
6088 return chainEntry.startingBlock;
6092 return BLOCK_END_OF_CHAIN;
6095 /******************************************************************************
6096 * BlockChainStream_GetCount
6098 * Returns the number of blocks that comprises this chain.
6099 * This is not the size of the stream as the last block may not be full!
6101 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6103 return This->numBlocks;
6106 /******************************************************************************
6107 * BlockChainStream_ReadAt
6109 * Reads a specified number of bytes from this chain at the specified offset.
6110 * bytesRead may be NULL.
6111 * Failure will be returned if the specified number of bytes has not been read.
6113 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6114 ULARGE_INTEGER offset,
6115 ULONG size,
6116 void* buffer,
6117 ULONG* bytesRead)
6119 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6120 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6121 ULONG bytesToReadInBuffer;
6122 ULONG blockIndex;
6123 BYTE* bufferWalker;
6124 ULARGE_INTEGER stream_size;
6125 HRESULT hr;
6126 BlockChainBlock *cachedBlock;
6128 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6131 * Find the first block in the stream that contains part of the buffer.
6133 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6135 *bytesRead = 0;
6137 stream_size = BlockChainStream_GetSize(This);
6138 if (stream_size.QuadPart > offset.QuadPart)
6139 size = min(stream_size.QuadPart - offset.QuadPart, size);
6140 else
6141 return S_OK;
6144 * Start reading the buffer.
6146 bufferWalker = buffer;
6148 while (size > 0)
6150 ULARGE_INTEGER ulOffset;
6151 DWORD bytesReadAt;
6154 * Calculate how many bytes we can copy from this big block.
6156 bytesToReadInBuffer =
6157 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6159 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6161 if (FAILED(hr))
6162 return hr;
6164 if (!cachedBlock)
6166 /* Not in cache, and we're going to read past the end of the block. */
6167 ulOffset.u.HighPart = 0;
6168 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6169 offsetInBlock;
6171 StorageImpl_ReadAt(This->parentStorage,
6172 ulOffset,
6173 bufferWalker,
6174 bytesToReadInBuffer,
6175 &bytesReadAt);
6177 else
6179 if (!cachedBlock->read)
6181 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6182 return STG_E_READFAULT;
6184 cachedBlock->read = 1;
6187 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6188 bytesReadAt = bytesToReadInBuffer;
6191 blockNoInSequence++;
6192 bufferWalker += bytesReadAt;
6193 size -= bytesReadAt;
6194 *bytesRead += bytesReadAt;
6195 offsetInBlock = 0; /* There is no offset on the next block */
6197 if (bytesToReadInBuffer != bytesReadAt)
6198 break;
6201 return S_OK;
6204 /******************************************************************************
6205 * BlockChainStream_WriteAt
6207 * Writes the specified number of bytes to this chain at the specified offset.
6208 * Will fail if not all specified number of bytes have been written.
6210 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6211 ULARGE_INTEGER offset,
6212 ULONG size,
6213 const void* buffer,
6214 ULONG* bytesWritten)
6216 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6217 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6218 ULONG bytesToWrite;
6219 ULONG blockIndex;
6220 const BYTE* bufferWalker;
6221 HRESULT hr;
6222 BlockChainBlock *cachedBlock;
6224 *bytesWritten = 0;
6225 bufferWalker = buffer;
6227 while (size > 0)
6229 ULARGE_INTEGER ulOffset;
6230 DWORD bytesWrittenAt;
6233 * Calculate how many bytes we can copy to this big block.
6235 bytesToWrite =
6236 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6238 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6240 /* BlockChainStream_SetSize should have already been called to ensure we have
6241 * enough blocks in the chain to write into */
6242 if (FAILED(hr))
6244 ERR("not enough blocks in chain to write data\n");
6245 return hr;
6248 if (!cachedBlock)
6250 /* Not in cache, and we're going to write past the end of the block. */
6251 ulOffset.u.HighPart = 0;
6252 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6253 offsetInBlock;
6255 StorageImpl_WriteAt(This->parentStorage,
6256 ulOffset,
6257 bufferWalker,
6258 bytesToWrite,
6259 &bytesWrittenAt);
6261 else
6263 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6265 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6266 return STG_E_READFAULT;
6269 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6270 bytesWrittenAt = bytesToWrite;
6271 cachedBlock->read = 1;
6272 cachedBlock->dirty = 1;
6275 blockNoInSequence++;
6276 bufferWalker += bytesWrittenAt;
6277 size -= bytesWrittenAt;
6278 *bytesWritten += bytesWrittenAt;
6279 offsetInBlock = 0; /* There is no offset on the next block */
6281 if (bytesWrittenAt != bytesToWrite)
6282 break;
6285 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6288 /******************************************************************************
6289 * BlockChainStream_Shrink
6291 * Shrinks this chain in the big block depot.
6293 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6294 ULARGE_INTEGER newSize)
6296 ULONG blockIndex;
6297 ULONG numBlocks;
6298 int i;
6301 * Figure out how many blocks are needed to contain the new size
6303 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6305 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6306 numBlocks++;
6308 if (numBlocks)
6311 * Go to the new end of chain
6313 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6315 /* Mark the new end of chain */
6316 StorageImpl_SetNextBlockInChain(
6317 This->parentStorage,
6318 blockIndex,
6319 BLOCK_END_OF_CHAIN);
6321 This->tailIndex = blockIndex;
6323 else
6325 if (This->headOfStreamPlaceHolder != 0)
6327 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6329 else
6331 DirEntry chainEntry;
6332 assert(This->ownerDirEntry != DIRENTRY_NULL);
6334 StorageImpl_ReadDirEntry(
6335 This->parentStorage,
6336 This->ownerDirEntry,
6337 &chainEntry);
6339 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6341 StorageImpl_WriteDirEntry(
6342 This->parentStorage,
6343 This->ownerDirEntry,
6344 &chainEntry);
6347 This->tailIndex = BLOCK_END_OF_CHAIN;
6350 This->numBlocks = numBlocks;
6353 * Mark the extra blocks as free
6355 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6357 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6358 StorageImpl_FreeBigBlock(This->parentStorage,
6359 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6360 if (last_run->lastOffset == last_run->firstOffset)
6361 This->indexCacheLen--;
6362 else
6363 last_run->lastOffset--;
6367 * Reset the last accessed block cache.
6369 for (i=0; i<2; i++)
6371 if (This->cachedBlocks[i].index >= numBlocks)
6373 This->cachedBlocks[i].index = 0xffffffff;
6374 This->cachedBlocks[i].dirty = 0;
6378 return TRUE;
6381 /******************************************************************************
6382 * BlockChainStream_Enlarge
6384 * Grows this chain in the big block depot.
6386 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6387 ULARGE_INTEGER newSize)
6389 ULONG blockIndex, currentBlock;
6390 ULONG newNumBlocks;
6391 ULONG oldNumBlocks = 0;
6393 blockIndex = BlockChainStream_GetHeadOfChain(This);
6396 * Empty chain. Create the head.
6398 if (blockIndex == BLOCK_END_OF_CHAIN)
6400 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6401 StorageImpl_SetNextBlockInChain(This->parentStorage,
6402 blockIndex,
6403 BLOCK_END_OF_CHAIN);
6405 if (This->headOfStreamPlaceHolder != 0)
6407 *(This->headOfStreamPlaceHolder) = blockIndex;
6409 else
6411 DirEntry chainEntry;
6412 assert(This->ownerDirEntry != DIRENTRY_NULL);
6414 StorageImpl_ReadDirEntry(
6415 This->parentStorage,
6416 This->ownerDirEntry,
6417 &chainEntry);
6419 chainEntry.startingBlock = blockIndex;
6421 StorageImpl_WriteDirEntry(
6422 This->parentStorage,
6423 This->ownerDirEntry,
6424 &chainEntry);
6427 This->tailIndex = blockIndex;
6428 This->numBlocks = 1;
6432 * Figure out how many blocks are needed to contain this stream
6434 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6436 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6437 newNumBlocks++;
6440 * Go to the current end of chain
6442 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6444 currentBlock = blockIndex;
6446 while (blockIndex != BLOCK_END_OF_CHAIN)
6448 This->numBlocks++;
6449 currentBlock = blockIndex;
6451 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6452 &blockIndex)))
6453 return FALSE;
6456 This->tailIndex = currentBlock;
6459 currentBlock = This->tailIndex;
6460 oldNumBlocks = This->numBlocks;
6463 * Add new blocks to the chain
6465 if (oldNumBlocks < newNumBlocks)
6467 while (oldNumBlocks < newNumBlocks)
6469 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6471 StorageImpl_SetNextBlockInChain(
6472 This->parentStorage,
6473 currentBlock,
6474 blockIndex);
6476 StorageImpl_SetNextBlockInChain(
6477 This->parentStorage,
6478 blockIndex,
6479 BLOCK_END_OF_CHAIN);
6481 currentBlock = blockIndex;
6482 oldNumBlocks++;
6485 This->tailIndex = blockIndex;
6486 This->numBlocks = newNumBlocks;
6489 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6490 return FALSE;
6492 return TRUE;
6495 /******************************************************************************
6496 * BlockChainStream_SetSize
6498 * Sets the size of this stream. The big block depot will be updated.
6499 * The file will grow if we grow the chain.
6501 * TODO: Free the actual blocks in the file when we shrink the chain.
6502 * Currently, the blocks are still in the file. So the file size
6503 * doesn't shrink even if we shrink streams.
6505 BOOL BlockChainStream_SetSize(
6506 BlockChainStream* This,
6507 ULARGE_INTEGER newSize)
6509 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6511 if (newSize.u.LowPart == size.u.LowPart)
6512 return TRUE;
6514 if (newSize.u.LowPart < size.u.LowPart)
6516 BlockChainStream_Shrink(This, newSize);
6518 else
6520 BlockChainStream_Enlarge(This, newSize);
6523 return TRUE;
6526 /******************************************************************************
6527 * BlockChainStream_GetSize
6529 * Returns the size of this chain.
6530 * Will return the block count if this chain doesn't have a directory entry.
6532 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6534 DirEntry chainEntry;
6536 if(This->headOfStreamPlaceHolder == NULL)
6539 * This chain has a directory entry so use the size value from there.
6541 StorageImpl_ReadDirEntry(
6542 This->parentStorage,
6543 This->ownerDirEntry,
6544 &chainEntry);
6546 return chainEntry.size;
6548 else
6551 * this chain is a chain that does not have a directory entry, figure out the
6552 * size by making the product number of used blocks times the
6553 * size of them
6555 ULARGE_INTEGER result;
6556 result.u.HighPart = 0;
6558 result.u.LowPart =
6559 BlockChainStream_GetCount(This) *
6560 This->parentStorage->bigBlockSize;
6562 return result;
6566 /******************************************************************************
6567 ** SmallBlockChainStream implementation
6570 SmallBlockChainStream* SmallBlockChainStream_Construct(
6571 StorageImpl* parentStorage,
6572 ULONG* headOfStreamPlaceHolder,
6573 DirRef dirEntry)
6575 SmallBlockChainStream* newStream;
6577 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6579 newStream->parentStorage = parentStorage;
6580 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6581 newStream->ownerDirEntry = dirEntry;
6583 return newStream;
6586 void SmallBlockChainStream_Destroy(
6587 SmallBlockChainStream* This)
6589 HeapFree(GetProcessHeap(), 0, This);
6592 /******************************************************************************
6593 * SmallBlockChainStream_GetHeadOfChain
6595 * Returns the head of this chain of small blocks.
6597 static ULONG SmallBlockChainStream_GetHeadOfChain(
6598 SmallBlockChainStream* This)
6600 DirEntry chainEntry;
6601 HRESULT hr;
6603 if (This->headOfStreamPlaceHolder != NULL)
6604 return *(This->headOfStreamPlaceHolder);
6606 if (This->ownerDirEntry)
6608 hr = StorageImpl_ReadDirEntry(
6609 This->parentStorage,
6610 This->ownerDirEntry,
6611 &chainEntry);
6613 if (SUCCEEDED(hr))
6615 return chainEntry.startingBlock;
6620 return BLOCK_END_OF_CHAIN;
6623 /******************************************************************************
6624 * SmallBlockChainStream_GetNextBlockInChain
6626 * Returns the index of the next small block in this chain.
6628 * Return Values:
6629 * - BLOCK_END_OF_CHAIN: end of this chain
6630 * - BLOCK_UNUSED: small block 'blockIndex' is free
6632 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6633 SmallBlockChainStream* This,
6634 ULONG blockIndex,
6635 ULONG* nextBlockInChain)
6637 ULARGE_INTEGER offsetOfBlockInDepot;
6638 DWORD buffer;
6639 ULONG bytesRead;
6640 HRESULT res;
6642 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6644 offsetOfBlockInDepot.u.HighPart = 0;
6645 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6648 * Read those bytes in the buffer from the small block file.
6650 res = BlockChainStream_ReadAt(
6651 This->parentStorage->smallBlockDepotChain,
6652 offsetOfBlockInDepot,
6653 sizeof(DWORD),
6654 &buffer,
6655 &bytesRead);
6657 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6658 res = STG_E_READFAULT;
6660 if (SUCCEEDED(res))
6662 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6663 return S_OK;
6666 return res;
6669 /******************************************************************************
6670 * SmallBlockChainStream_SetNextBlockInChain
6672 * Writes the index of the next block of the specified block in the small
6673 * block depot.
6674 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6675 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6677 static void SmallBlockChainStream_SetNextBlockInChain(
6678 SmallBlockChainStream* This,
6679 ULONG blockIndex,
6680 ULONG nextBlock)
6682 ULARGE_INTEGER offsetOfBlockInDepot;
6683 DWORD buffer;
6684 ULONG bytesWritten;
6686 offsetOfBlockInDepot.u.HighPart = 0;
6687 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6689 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6692 * Read those bytes in the buffer from the small block file.
6694 BlockChainStream_WriteAt(
6695 This->parentStorage->smallBlockDepotChain,
6696 offsetOfBlockInDepot,
6697 sizeof(DWORD),
6698 &buffer,
6699 &bytesWritten);
6702 /******************************************************************************
6703 * SmallBlockChainStream_FreeBlock
6705 * Flag small block 'blockIndex' as free in the small block depot.
6707 static void SmallBlockChainStream_FreeBlock(
6708 SmallBlockChainStream* This,
6709 ULONG blockIndex)
6711 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6714 /******************************************************************************
6715 * SmallBlockChainStream_GetNextFreeBlock
6717 * Returns the index of a free small block. The small block depot will be
6718 * enlarged if necessary. The small block chain will also be enlarged if
6719 * necessary.
6721 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6722 SmallBlockChainStream* This)
6724 ULARGE_INTEGER offsetOfBlockInDepot;
6725 DWORD buffer;
6726 ULONG bytesRead;
6727 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6728 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6729 HRESULT res = S_OK;
6730 ULONG smallBlocksPerBigBlock;
6731 DirEntry rootEntry;
6732 ULONG blocksRequired;
6733 ULARGE_INTEGER old_size, size_required;
6735 offsetOfBlockInDepot.u.HighPart = 0;
6738 * Scan the small block depot for a free block
6740 while (nextBlockIndex != BLOCK_UNUSED)
6742 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6744 res = BlockChainStream_ReadAt(
6745 This->parentStorage->smallBlockDepotChain,
6746 offsetOfBlockInDepot,
6747 sizeof(DWORD),
6748 &buffer,
6749 &bytesRead);
6752 * If we run out of space for the small block depot, enlarge it
6754 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6756 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6758 if (nextBlockIndex != BLOCK_UNUSED)
6759 blockIndex++;
6761 else
6763 ULONG count =
6764 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6766 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6767 ULARGE_INTEGER newSize, offset;
6768 ULONG bytesWritten;
6770 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6771 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6774 * Initialize all the small blocks to free
6776 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6777 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6778 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6779 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6781 StorageImpl_SaveFileHeader(This->parentStorage);
6785 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6787 smallBlocksPerBigBlock =
6788 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6791 * Verify if we have to allocate big blocks to contain small blocks
6793 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6795 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6797 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6799 if (size_required.QuadPart > old_size.QuadPart)
6801 BlockChainStream_SetSize(
6802 This->parentStorage->smallBlockRootChain,
6803 size_required);
6805 StorageImpl_ReadDirEntry(
6806 This->parentStorage,
6807 This->parentStorage->base.storageDirEntry,
6808 &rootEntry);
6810 rootEntry.size = size_required;
6812 StorageImpl_WriteDirEntry(
6813 This->parentStorage,
6814 This->parentStorage->base.storageDirEntry,
6815 &rootEntry);
6818 return blockIndex;
6821 /******************************************************************************
6822 * SmallBlockChainStream_ReadAt
6824 * Reads a specified number of bytes from this chain at the specified offset.
6825 * bytesRead may be NULL.
6826 * Failure will be returned if the specified number of bytes has not been read.
6828 HRESULT SmallBlockChainStream_ReadAt(
6829 SmallBlockChainStream* This,
6830 ULARGE_INTEGER offset,
6831 ULONG size,
6832 void* buffer,
6833 ULONG* bytesRead)
6835 HRESULT rc = S_OK;
6836 ULARGE_INTEGER offsetInBigBlockFile;
6837 ULONG blockNoInSequence =
6838 offset.u.LowPart / This->parentStorage->smallBlockSize;
6840 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6841 ULONG bytesToReadInBuffer;
6842 ULONG blockIndex;
6843 ULONG bytesReadFromBigBlockFile;
6844 BYTE* bufferWalker;
6845 ULARGE_INTEGER stream_size;
6848 * This should never happen on a small block file.
6850 assert(offset.u.HighPart==0);
6852 *bytesRead = 0;
6854 stream_size = SmallBlockChainStream_GetSize(This);
6855 if (stream_size.QuadPart > offset.QuadPart)
6856 size = min(stream_size.QuadPart - offset.QuadPart, size);
6857 else
6858 return S_OK;
6861 * Find the first block in the stream that contains part of the buffer.
6863 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6865 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6867 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6868 if(FAILED(rc))
6869 return rc;
6870 blockNoInSequence--;
6874 * Start reading the buffer.
6876 bufferWalker = buffer;
6878 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6881 * Calculate how many bytes we can copy from this small block.
6883 bytesToReadInBuffer =
6884 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6887 * Calculate the offset of the small block in the small block file.
6889 offsetInBigBlockFile.u.HighPart = 0;
6890 offsetInBigBlockFile.u.LowPart =
6891 blockIndex * This->parentStorage->smallBlockSize;
6893 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6896 * Read those bytes in the buffer from the small block file.
6897 * The small block has already been identified so it shouldn't fail
6898 * unless the file is corrupt.
6900 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6901 offsetInBigBlockFile,
6902 bytesToReadInBuffer,
6903 bufferWalker,
6904 &bytesReadFromBigBlockFile);
6906 if (FAILED(rc))
6907 return rc;
6909 if (!bytesReadFromBigBlockFile)
6910 return STG_E_DOCFILECORRUPT;
6913 * Step to the next big block.
6915 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6916 if(FAILED(rc))
6917 return STG_E_DOCFILECORRUPT;
6919 bufferWalker += bytesReadFromBigBlockFile;
6920 size -= bytesReadFromBigBlockFile;
6921 *bytesRead += bytesReadFromBigBlockFile;
6922 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6925 return S_OK;
6928 /******************************************************************************
6929 * SmallBlockChainStream_WriteAt
6931 * Writes the specified number of bytes to this chain at the specified offset.
6932 * Will fail if not all specified number of bytes have been written.
6934 HRESULT SmallBlockChainStream_WriteAt(
6935 SmallBlockChainStream* This,
6936 ULARGE_INTEGER offset,
6937 ULONG size,
6938 const void* buffer,
6939 ULONG* bytesWritten)
6941 ULARGE_INTEGER offsetInBigBlockFile;
6942 ULONG blockNoInSequence =
6943 offset.u.LowPart / This->parentStorage->smallBlockSize;
6945 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6946 ULONG bytesToWriteInBuffer;
6947 ULONG blockIndex;
6948 ULONG bytesWrittenToBigBlockFile;
6949 const BYTE* bufferWalker;
6950 HRESULT res;
6953 * This should never happen on a small block file.
6955 assert(offset.u.HighPart==0);
6958 * Find the first block in the stream that contains part of the buffer.
6960 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6962 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6964 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6965 return STG_E_DOCFILECORRUPT;
6966 blockNoInSequence--;
6970 * Start writing the buffer.
6972 *bytesWritten = 0;
6973 bufferWalker = buffer;
6974 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6977 * Calculate how many bytes we can copy to this small block.
6979 bytesToWriteInBuffer =
6980 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6983 * Calculate the offset of the small block in the small block file.
6985 offsetInBigBlockFile.u.HighPart = 0;
6986 offsetInBigBlockFile.u.LowPart =
6987 blockIndex * This->parentStorage->smallBlockSize;
6989 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6992 * Write those bytes in the buffer to the small block file.
6994 res = BlockChainStream_WriteAt(
6995 This->parentStorage->smallBlockRootChain,
6996 offsetInBigBlockFile,
6997 bytesToWriteInBuffer,
6998 bufferWalker,
6999 &bytesWrittenToBigBlockFile);
7000 if (FAILED(res))
7001 return res;
7004 * Step to the next big block.
7006 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7007 &blockIndex)))
7008 return FALSE;
7009 bufferWalker += bytesWrittenToBigBlockFile;
7010 size -= bytesWrittenToBigBlockFile;
7011 *bytesWritten += bytesWrittenToBigBlockFile;
7012 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
7015 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
7018 /******************************************************************************
7019 * SmallBlockChainStream_Shrink
7021 * Shrinks this chain in the small block depot.
7023 static BOOL SmallBlockChainStream_Shrink(
7024 SmallBlockChainStream* This,
7025 ULARGE_INTEGER newSize)
7027 ULONG blockIndex, extraBlock;
7028 ULONG numBlocks;
7029 ULONG count = 0;
7031 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7033 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7034 numBlocks++;
7036 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7039 * Go to the new end of chain
7041 while (count < numBlocks)
7043 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7044 &blockIndex)))
7045 return FALSE;
7046 count++;
7050 * If the count is 0, we have a special case, the head of the chain was
7051 * just freed.
7053 if (count == 0)
7055 DirEntry chainEntry;
7057 StorageImpl_ReadDirEntry(This->parentStorage,
7058 This->ownerDirEntry,
7059 &chainEntry);
7061 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7063 StorageImpl_WriteDirEntry(This->parentStorage,
7064 This->ownerDirEntry,
7065 &chainEntry);
7068 * We start freeing the chain at the head block.
7070 extraBlock = blockIndex;
7072 else
7074 /* Get the next block before marking the new end */
7075 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7076 &extraBlock)))
7077 return FALSE;
7079 /* Mark the new end of chain */
7080 SmallBlockChainStream_SetNextBlockInChain(
7081 This,
7082 blockIndex,
7083 BLOCK_END_OF_CHAIN);
7087 * Mark the extra blocks as free
7089 while (extraBlock != BLOCK_END_OF_CHAIN)
7091 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7092 &blockIndex)))
7093 return FALSE;
7094 SmallBlockChainStream_FreeBlock(This, extraBlock);
7095 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7096 extraBlock = blockIndex;
7099 return TRUE;
7102 /******************************************************************************
7103 * SmallBlockChainStream_Enlarge
7105 * Grows this chain in the small block depot.
7107 static BOOL SmallBlockChainStream_Enlarge(
7108 SmallBlockChainStream* This,
7109 ULARGE_INTEGER newSize)
7111 ULONG blockIndex, currentBlock;
7112 ULONG newNumBlocks;
7113 ULONG oldNumBlocks = 0;
7115 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7118 * Empty chain. Create the head.
7120 if (blockIndex == BLOCK_END_OF_CHAIN)
7122 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7123 SmallBlockChainStream_SetNextBlockInChain(
7124 This,
7125 blockIndex,
7126 BLOCK_END_OF_CHAIN);
7128 if (This->headOfStreamPlaceHolder != NULL)
7130 *(This->headOfStreamPlaceHolder) = blockIndex;
7132 else
7134 DirEntry chainEntry;
7136 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7137 &chainEntry);
7139 chainEntry.startingBlock = blockIndex;
7141 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7142 &chainEntry);
7146 currentBlock = blockIndex;
7149 * Figure out how many blocks are needed to contain this stream
7151 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7153 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7154 newNumBlocks++;
7157 * Go to the current end of chain
7159 while (blockIndex != BLOCK_END_OF_CHAIN)
7161 oldNumBlocks++;
7162 currentBlock = blockIndex;
7163 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7164 return FALSE;
7168 * Add new blocks to the chain
7170 while (oldNumBlocks < newNumBlocks)
7172 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7173 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7175 SmallBlockChainStream_SetNextBlockInChain(
7176 This,
7177 blockIndex,
7178 BLOCK_END_OF_CHAIN);
7180 currentBlock = blockIndex;
7181 oldNumBlocks++;
7184 return TRUE;
7187 /******************************************************************************
7188 * SmallBlockChainStream_SetSize
7190 * Sets the size of this stream.
7191 * The file will grow if we grow the chain.
7193 * TODO: Free the actual blocks in the file when we shrink the chain.
7194 * Currently, the blocks are still in the file. So the file size
7195 * doesn't shrink even if we shrink streams.
7197 BOOL SmallBlockChainStream_SetSize(
7198 SmallBlockChainStream* This,
7199 ULARGE_INTEGER newSize)
7201 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7203 if (newSize.u.LowPart == size.u.LowPart)
7204 return TRUE;
7206 if (newSize.u.LowPart < size.u.LowPart)
7208 SmallBlockChainStream_Shrink(This, newSize);
7210 else
7212 SmallBlockChainStream_Enlarge(This, newSize);
7215 return TRUE;
7218 /******************************************************************************
7219 * SmallBlockChainStream_GetCount
7221 * Returns the number of small blocks that comprises this chain.
7222 * This is not the size of the stream as the last block may not be full!
7225 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7227 ULONG blockIndex;
7228 ULONG count = 0;
7230 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7232 while(blockIndex != BLOCK_END_OF_CHAIN)
7234 count++;
7236 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7237 blockIndex, &blockIndex)))
7238 return 0;
7241 return count;
7244 /******************************************************************************
7245 * SmallBlockChainStream_GetSize
7247 * Returns the size of this chain.
7249 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7251 DirEntry chainEntry;
7253 if(This->headOfStreamPlaceHolder != NULL)
7255 ULARGE_INTEGER result;
7256 result.u.HighPart = 0;
7258 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7259 This->parentStorage->smallBlockSize;
7261 return result;
7264 StorageImpl_ReadDirEntry(
7265 This->parentStorage,
7266 This->ownerDirEntry,
7267 &chainEntry);
7269 return chainEntry.size;
7272 static HRESULT create_storagefile(
7273 LPCOLESTR pwcsName,
7274 DWORD grfMode,
7275 DWORD grfAttrs,
7276 STGOPTIONS* pStgOptions,
7277 REFIID riid,
7278 void** ppstgOpen)
7280 StorageBaseImpl* newStorage = 0;
7281 HANDLE hFile = INVALID_HANDLE_VALUE;
7282 HRESULT hr = STG_E_INVALIDFLAG;
7283 DWORD shareMode;
7284 DWORD accessMode;
7285 DWORD creationMode;
7286 DWORD fileAttributes;
7287 WCHAR tempFileName[MAX_PATH];
7289 if (ppstgOpen == 0)
7290 return STG_E_INVALIDPOINTER;
7292 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7293 return STG_E_INVALIDPARAMETER;
7295 /* if no share mode given then DENY_NONE is the default */
7296 if (STGM_SHARE_MODE(grfMode) == 0)
7297 grfMode |= STGM_SHARE_DENY_NONE;
7299 if ( FAILED( validateSTGM(grfMode) ))
7300 goto end;
7302 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7303 switch(STGM_ACCESS_MODE(grfMode))
7305 case STGM_WRITE:
7306 case STGM_READWRITE:
7307 break;
7308 default:
7309 goto end;
7312 /* in direct mode, can only use SHARE_EXCLUSIVE */
7313 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7314 goto end;
7316 /* but in transacted mode, any share mode is valid */
7319 * Generate a unique name.
7321 if (pwcsName == 0)
7323 WCHAR tempPath[MAX_PATH];
7324 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7326 memset(tempPath, 0, sizeof(tempPath));
7327 memset(tempFileName, 0, sizeof(tempFileName));
7329 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7330 tempPath[0] = '.';
7332 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7333 pwcsName = tempFileName;
7334 else
7336 hr = STG_E_INSUFFICIENTMEMORY;
7337 goto end;
7340 creationMode = TRUNCATE_EXISTING;
7342 else
7344 creationMode = GetCreationModeFromSTGM(grfMode);
7348 * Interpret the STGM value grfMode
7350 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7351 accessMode = GetAccessModeFromSTGM(grfMode);
7353 if (grfMode & STGM_DELETEONRELEASE)
7354 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7355 else
7356 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7358 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7360 static int fixme;
7361 if (!fixme++)
7362 FIXME("Storage share mode not implemented.\n");
7365 *ppstgOpen = 0;
7367 hFile = CreateFileW(pwcsName,
7368 accessMode,
7369 shareMode,
7370 NULL,
7371 creationMode,
7372 fileAttributes,
7375 if (hFile == INVALID_HANDLE_VALUE)
7377 if(GetLastError() == ERROR_FILE_EXISTS)
7378 hr = STG_E_FILEALREADYEXISTS;
7379 else
7380 hr = E_FAIL;
7381 goto end;
7385 * Allocate and initialize the new IStorage32object.
7387 hr = Storage_Construct(
7388 hFile,
7389 pwcsName,
7390 NULL,
7391 grfMode,
7392 TRUE,
7393 TRUE,
7394 pStgOptions->ulSectorSize,
7395 &newStorage);
7397 if (FAILED(hr))
7399 goto end;
7402 hr = IStorage_QueryInterface(&newStorage->IStorage_iface, riid, ppstgOpen);
7403 IStorage_Release(&newStorage->IStorage_iface);
7405 end:
7406 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7408 return hr;
7411 /******************************************************************************
7412 * StgCreateDocfile [OLE32.@]
7413 * Creates a new compound file storage object
7415 * PARAMS
7416 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7417 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7418 * reserved [ ?] unused?, usually 0
7419 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7421 * RETURNS
7422 * S_OK if the file was successfully created
7423 * some STG_E_ value if error
7424 * NOTES
7425 * if pwcsName is NULL, create file with new unique name
7426 * the function can returns
7427 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7428 * (unrealized now)
7430 HRESULT WINAPI StgCreateDocfile(
7431 LPCOLESTR pwcsName,
7432 DWORD grfMode,
7433 DWORD reserved,
7434 IStorage **ppstgOpen)
7436 STGOPTIONS stgoptions = {1, 0, 512};
7438 TRACE("(%s, %x, %d, %p)\n",
7439 debugstr_w(pwcsName), grfMode,
7440 reserved, ppstgOpen);
7442 if (ppstgOpen == 0)
7443 return STG_E_INVALIDPOINTER;
7444 if (reserved != 0)
7445 return STG_E_INVALIDPARAMETER;
7447 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7450 /******************************************************************************
7451 * StgCreateStorageEx [OLE32.@]
7453 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7455 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7456 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7458 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7460 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7461 return STG_E_INVALIDPARAMETER;
7464 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7466 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7467 return STG_E_INVALIDPARAMETER;
7470 if (stgfmt == STGFMT_FILE)
7472 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7473 return STG_E_INVALIDPARAMETER;
7476 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7478 STGOPTIONS defaultOptions = {1, 0, 512};
7480 if (!pStgOptions) pStgOptions = &defaultOptions;
7481 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7485 ERR("Invalid stgfmt argument\n");
7486 return STG_E_INVALIDPARAMETER;
7489 /******************************************************************************
7490 * StgCreatePropSetStg [OLE32.@]
7492 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7493 IPropertySetStorage **propset)
7495 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, propset);
7496 if (reserved)
7497 return STG_E_INVALIDPARAMETER;
7499 return IStorage_QueryInterface(pstg, &IID_IPropertySetStorage, (void**)propset);
7502 /******************************************************************************
7503 * StgOpenStorageEx [OLE32.@]
7505 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7507 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7508 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7510 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7512 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7513 return STG_E_INVALIDPARAMETER;
7516 switch (stgfmt)
7518 case STGFMT_FILE:
7519 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7520 return STG_E_INVALIDPARAMETER;
7522 case STGFMT_STORAGE:
7523 break;
7525 case STGFMT_DOCFILE:
7526 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7528 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7529 return STG_E_INVALIDPARAMETER;
7531 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7532 break;
7534 case STGFMT_ANY:
7535 WARN("STGFMT_ANY assuming storage\n");
7536 break;
7538 default:
7539 return STG_E_INVALIDPARAMETER;
7542 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7546 /******************************************************************************
7547 * StgOpenStorage [OLE32.@]
7549 HRESULT WINAPI StgOpenStorage(
7550 const OLECHAR *pwcsName,
7551 IStorage *pstgPriority,
7552 DWORD grfMode,
7553 SNB snbExclude,
7554 DWORD reserved,
7555 IStorage **ppstgOpen)
7557 StorageBaseImpl* newStorage = 0;
7558 HRESULT hr = S_OK;
7559 HANDLE hFile = 0;
7560 DWORD shareMode;
7561 DWORD accessMode;
7563 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7564 debugstr_w(pwcsName), pstgPriority, grfMode,
7565 snbExclude, reserved, ppstgOpen);
7567 if (pwcsName == 0)
7569 hr = STG_E_INVALIDNAME;
7570 goto end;
7573 if (ppstgOpen == 0)
7575 hr = STG_E_INVALIDPOINTER;
7576 goto end;
7579 if (reserved)
7581 hr = STG_E_INVALIDPARAMETER;
7582 goto end;
7585 if (grfMode & STGM_PRIORITY)
7587 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7588 return STG_E_INVALIDFLAG;
7589 if (grfMode & STGM_DELETEONRELEASE)
7590 return STG_E_INVALIDFUNCTION;
7591 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7592 return STG_E_INVALIDFLAG;
7593 grfMode &= ~0xf0; /* remove the existing sharing mode */
7594 grfMode |= STGM_SHARE_DENY_NONE;
7596 /* STGM_PRIORITY stops other IStorage objects on the same file from
7597 * committing until the STGM_PRIORITY IStorage is closed. it also
7598 * stops non-transacted mode StgOpenStorage calls with write access from
7599 * succeeding. obviously, both of these cannot be achieved through just
7600 * file share flags */
7601 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7605 * Validate the sharing mode
7607 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7608 switch(STGM_SHARE_MODE(grfMode))
7610 case STGM_SHARE_EXCLUSIVE:
7611 case STGM_SHARE_DENY_WRITE:
7612 break;
7613 default:
7614 hr = STG_E_INVALIDFLAG;
7615 goto end;
7618 if ( FAILED( validateSTGM(grfMode) ) ||
7619 (grfMode&STGM_CREATE))
7621 hr = STG_E_INVALIDFLAG;
7622 goto end;
7625 /* shared reading requires transacted mode */
7626 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7627 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7628 !(grfMode&STGM_TRANSACTED) )
7630 hr = STG_E_INVALIDFLAG;
7631 goto end;
7635 * Interpret the STGM value grfMode
7637 shareMode = GetShareModeFromSTGM(grfMode);
7638 accessMode = GetAccessModeFromSTGM(grfMode);
7640 *ppstgOpen = 0;
7642 hFile = CreateFileW( pwcsName,
7643 accessMode,
7644 shareMode,
7645 NULL,
7646 OPEN_EXISTING,
7647 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7650 if (hFile==INVALID_HANDLE_VALUE)
7652 DWORD last_error = GetLastError();
7654 hr = E_FAIL;
7656 switch (last_error)
7658 case ERROR_FILE_NOT_FOUND:
7659 hr = STG_E_FILENOTFOUND;
7660 break;
7662 case ERROR_PATH_NOT_FOUND:
7663 hr = STG_E_PATHNOTFOUND;
7664 break;
7666 case ERROR_ACCESS_DENIED:
7667 case ERROR_WRITE_PROTECT:
7668 hr = STG_E_ACCESSDENIED;
7669 break;
7671 case ERROR_SHARING_VIOLATION:
7672 hr = STG_E_SHAREVIOLATION;
7673 break;
7675 default:
7676 hr = E_FAIL;
7679 goto end;
7683 * Refuse to open the file if it's too small to be a structured storage file
7684 * FIXME: verify the file when reading instead of here
7686 if (GetFileSize(hFile, NULL) < 0x100)
7688 CloseHandle(hFile);
7689 hr = STG_E_FILEALREADYEXISTS;
7690 goto end;
7694 * Allocate and initialize the new IStorage32object.
7696 hr = Storage_Construct(
7697 hFile,
7698 pwcsName,
7699 NULL,
7700 grfMode,
7701 TRUE,
7702 FALSE,
7703 512,
7704 &newStorage);
7706 if (FAILED(hr))
7709 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7711 if(hr == STG_E_INVALIDHEADER)
7712 hr = STG_E_FILEALREADYEXISTS;
7713 goto end;
7716 *ppstgOpen = &newStorage->IStorage_iface;
7718 end:
7719 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7720 return hr;
7723 /******************************************************************************
7724 * StgCreateDocfileOnILockBytes [OLE32.@]
7726 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7727 ILockBytes *plkbyt,
7728 DWORD grfMode,
7729 DWORD reserved,
7730 IStorage** ppstgOpen)
7732 StorageBaseImpl* newStorage = 0;
7733 HRESULT hr = S_OK;
7735 if ((ppstgOpen == 0) || (plkbyt == 0))
7736 return STG_E_INVALIDPOINTER;
7739 * Allocate and initialize the new IStorage object.
7741 hr = Storage_Construct(
7744 plkbyt,
7745 grfMode,
7746 FALSE,
7747 TRUE,
7748 512,
7749 &newStorage);
7751 if (FAILED(hr))
7753 return hr;
7756 *ppstgOpen = &newStorage->IStorage_iface;
7758 return hr;
7761 /******************************************************************************
7762 * StgOpenStorageOnILockBytes [OLE32.@]
7764 HRESULT WINAPI StgOpenStorageOnILockBytes(
7765 ILockBytes *plkbyt,
7766 IStorage *pstgPriority,
7767 DWORD grfMode,
7768 SNB snbExclude,
7769 DWORD reserved,
7770 IStorage **ppstgOpen)
7772 StorageBaseImpl* newStorage = 0;
7773 HRESULT hr = S_OK;
7775 if ((plkbyt == 0) || (ppstgOpen == 0))
7776 return STG_E_INVALIDPOINTER;
7778 if ( FAILED( validateSTGM(grfMode) ))
7779 return STG_E_INVALIDFLAG;
7781 *ppstgOpen = 0;
7784 * Allocate and initialize the new IStorage object.
7786 hr = Storage_Construct(
7789 plkbyt,
7790 grfMode,
7791 FALSE,
7792 FALSE,
7793 512,
7794 &newStorage);
7796 if (FAILED(hr))
7798 return hr;
7801 *ppstgOpen = &newStorage->IStorage_iface;
7803 return hr;
7806 /******************************************************************************
7807 * StgSetTimes [ole32.@]
7808 * StgSetTimes [OLE32.@]
7812 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7813 FILETIME const *patime, FILETIME const *pmtime)
7815 IStorage *stg = NULL;
7816 HRESULT r;
7818 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7820 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7821 0, 0, &stg);
7822 if( SUCCEEDED(r) )
7824 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7825 IStorage_Release(stg);
7828 return r;
7831 /******************************************************************************
7832 * StgIsStorageILockBytes [OLE32.@]
7834 * Determines if the ILockBytes contains a storage object.
7836 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7838 BYTE sig[sizeof(STORAGE_magic)];
7839 ULARGE_INTEGER offset;
7840 ULONG read = 0;
7842 offset.u.HighPart = 0;
7843 offset.u.LowPart = 0;
7845 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
7847 if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
7848 return S_OK;
7850 return S_FALSE;
7853 /******************************************************************************
7854 * WriteClassStg [OLE32.@]
7856 * This method will store the specified CLSID in the specified storage object
7858 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7860 if(!pStg)
7861 return E_INVALIDARG;
7863 if(!rclsid)
7864 return STG_E_INVALIDPOINTER;
7866 return IStorage_SetClass(pStg, rclsid);
7869 /***********************************************************************
7870 * ReadClassStg (OLE32.@)
7872 * This method reads the CLSID previously written to a storage object with
7873 * the WriteClassStg.
7875 * PARAMS
7876 * pstg [I] IStorage pointer
7877 * pclsid [O] Pointer to where the CLSID is written
7879 * RETURNS
7880 * Success: S_OK.
7881 * Failure: HRESULT code.
7883 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7885 STATSTG pstatstg;
7886 HRESULT hRes;
7888 TRACE("(%p, %p)\n", pstg, pclsid);
7890 if(!pstg || !pclsid)
7891 return E_INVALIDARG;
7894 * read a STATSTG structure (contains the clsid) from the storage
7896 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7898 if(SUCCEEDED(hRes))
7899 *pclsid=pstatstg.clsid;
7901 return hRes;
7904 /***********************************************************************
7905 * OleLoadFromStream (OLE32.@)
7907 * This function loads an object from stream
7909 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7911 CLSID clsid;
7912 HRESULT res;
7913 LPPERSISTSTREAM xstm;
7915 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7917 res=ReadClassStm(pStm,&clsid);
7918 if (FAILED(res))
7919 return res;
7920 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7921 if (FAILED(res))
7922 return res;
7923 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7924 if (FAILED(res)) {
7925 IUnknown_Release((IUnknown*)*ppvObj);
7926 return res;
7928 res=IPersistStream_Load(xstm,pStm);
7929 IPersistStream_Release(xstm);
7930 /* FIXME: all refcounts ok at this point? I think they should be:
7931 * pStm : unchanged
7932 * ppvObj : 1
7933 * xstm : 0 (released)
7935 return res;
7938 /***********************************************************************
7939 * OleSaveToStream (OLE32.@)
7941 * This function saves an object with the IPersistStream interface on it
7942 * to the specified stream.
7944 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7947 CLSID clsid;
7948 HRESULT res;
7950 TRACE("(%p,%p)\n",pPStm,pStm);
7952 res=IPersistStream_GetClassID(pPStm,&clsid);
7954 if (SUCCEEDED(res)){
7956 res=WriteClassStm(pStm,&clsid);
7958 if (SUCCEEDED(res))
7960 res=IPersistStream_Save(pPStm,pStm,TRUE);
7963 TRACE("Finished Save\n");
7964 return res;
7967 /****************************************************************************
7968 * This method validate a STGM parameter that can contain the values below
7970 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7971 * The stgm values contained in 0xffff0000 are bitmasks.
7973 * STGM_DIRECT 0x00000000
7974 * STGM_TRANSACTED 0x00010000
7975 * STGM_SIMPLE 0x08000000
7977 * STGM_READ 0x00000000
7978 * STGM_WRITE 0x00000001
7979 * STGM_READWRITE 0x00000002
7981 * STGM_SHARE_DENY_NONE 0x00000040
7982 * STGM_SHARE_DENY_READ 0x00000030
7983 * STGM_SHARE_DENY_WRITE 0x00000020
7984 * STGM_SHARE_EXCLUSIVE 0x00000010
7986 * STGM_PRIORITY 0x00040000
7987 * STGM_DELETEONRELEASE 0x04000000
7989 * STGM_CREATE 0x00001000
7990 * STGM_CONVERT 0x00020000
7991 * STGM_FAILIFTHERE 0x00000000
7993 * STGM_NOSCRATCH 0x00100000
7994 * STGM_NOSNAPSHOT 0x00200000
7996 static HRESULT validateSTGM(DWORD stgm)
7998 DWORD access = STGM_ACCESS_MODE(stgm);
7999 DWORD share = STGM_SHARE_MODE(stgm);
8000 DWORD create = STGM_CREATE_MODE(stgm);
8002 if (stgm&~STGM_KNOWN_FLAGS)
8004 ERR("unknown flags %08x\n", stgm);
8005 return E_FAIL;
8008 switch (access)
8010 case STGM_READ:
8011 case STGM_WRITE:
8012 case STGM_READWRITE:
8013 break;
8014 default:
8015 return E_FAIL;
8018 switch (share)
8020 case STGM_SHARE_DENY_NONE:
8021 case STGM_SHARE_DENY_READ:
8022 case STGM_SHARE_DENY_WRITE:
8023 case STGM_SHARE_EXCLUSIVE:
8024 break;
8025 default:
8026 return E_FAIL;
8029 switch (create)
8031 case STGM_CREATE:
8032 case STGM_FAILIFTHERE:
8033 break;
8034 default:
8035 return E_FAIL;
8039 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8041 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8042 return E_FAIL;
8045 * STGM_CREATE | STGM_CONVERT
8046 * if both are false, STGM_FAILIFTHERE is set to TRUE
8048 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8049 return E_FAIL;
8052 * STGM_NOSCRATCH requires STGM_TRANSACTED
8054 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8055 return E_FAIL;
8058 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8059 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8061 if ( (stgm & STGM_NOSNAPSHOT) &&
8062 (!(stgm & STGM_TRANSACTED) ||
8063 share == STGM_SHARE_EXCLUSIVE ||
8064 share == STGM_SHARE_DENY_WRITE) )
8065 return E_FAIL;
8067 return S_OK;
8070 /****************************************************************************
8071 * GetShareModeFromSTGM
8073 * This method will return a share mode flag from a STGM value.
8074 * The STGM value is assumed valid.
8076 static DWORD GetShareModeFromSTGM(DWORD stgm)
8078 switch (STGM_SHARE_MODE(stgm))
8080 case STGM_SHARE_DENY_NONE:
8081 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8082 case STGM_SHARE_DENY_READ:
8083 return FILE_SHARE_WRITE;
8084 case STGM_SHARE_DENY_WRITE:
8085 return FILE_SHARE_READ;
8086 case STGM_SHARE_EXCLUSIVE:
8087 return 0;
8089 ERR("Invalid share mode!\n");
8090 assert(0);
8091 return 0;
8094 /****************************************************************************
8095 * GetAccessModeFromSTGM
8097 * This method will return an access mode flag from a STGM value.
8098 * The STGM value is assumed valid.
8100 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8102 switch (STGM_ACCESS_MODE(stgm))
8104 case STGM_READ:
8105 return GENERIC_READ;
8106 case STGM_WRITE:
8107 case STGM_READWRITE:
8108 return GENERIC_READ | GENERIC_WRITE;
8110 ERR("Invalid access mode!\n");
8111 assert(0);
8112 return 0;
8115 /****************************************************************************
8116 * GetCreationModeFromSTGM
8118 * This method will return a creation mode flag from a STGM value.
8119 * The STGM value is assumed valid.
8121 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8123 switch(STGM_CREATE_MODE(stgm))
8125 case STGM_CREATE:
8126 return CREATE_ALWAYS;
8127 case STGM_CONVERT:
8128 FIXME("STGM_CONVERT not implemented!\n");
8129 return CREATE_NEW;
8130 case STGM_FAILIFTHERE:
8131 return CREATE_NEW;
8133 ERR("Invalid create mode!\n");
8134 assert(0);
8135 return 0;
8139 /*************************************************************************
8140 * OLECONVERT_LoadOLE10 [Internal]
8142 * Loads the OLE10 STREAM to memory
8144 * PARAMS
8145 * pOleStream [I] The OLESTREAM
8146 * pData [I] Data Structure for the OLESTREAM Data
8148 * RETURNS
8149 * Success: S_OK
8150 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8151 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8153 * NOTES
8154 * This function is used by OleConvertOLESTREAMToIStorage only.
8156 * Memory allocated for pData must be freed by the caller
8158 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8160 DWORD dwSize;
8161 HRESULT hRes = S_OK;
8162 int nTryCnt=0;
8163 int max_try = 6;
8165 pData->pData = NULL;
8166 pData->pstrOleObjFileName = NULL;
8168 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8170 /* Get the OleID */
8171 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8172 if(dwSize != sizeof(pData->dwOleID))
8174 hRes = CONVERT10_E_OLESTREAM_GET;
8176 else if(pData->dwOleID != OLESTREAM_ID)
8178 hRes = CONVERT10_E_OLESTREAM_FMT;
8180 else
8182 hRes = S_OK;
8183 break;
8187 if(hRes == S_OK)
8189 /* Get the TypeID... more info needed for this field */
8190 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8191 if(dwSize != sizeof(pData->dwTypeID))
8193 hRes = CONVERT10_E_OLESTREAM_GET;
8196 if(hRes == S_OK)
8198 if(pData->dwTypeID != 0)
8200 /* Get the length of the OleTypeName */
8201 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8202 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8204 hRes = CONVERT10_E_OLESTREAM_GET;
8207 if(hRes == S_OK)
8209 if(pData->dwOleTypeNameLength > 0)
8211 /* Get the OleTypeName */
8212 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8213 if(dwSize != pData->dwOleTypeNameLength)
8215 hRes = CONVERT10_E_OLESTREAM_GET;
8219 if(bStrem1)
8221 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8222 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8224 hRes = CONVERT10_E_OLESTREAM_GET;
8226 if(hRes == S_OK)
8228 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8229 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8230 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8231 if(pData->pstrOleObjFileName)
8233 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8234 if(dwSize != pData->dwOleObjFileNameLength)
8236 hRes = CONVERT10_E_OLESTREAM_GET;
8239 else
8240 hRes = CONVERT10_E_OLESTREAM_GET;
8243 else
8245 /* Get the Width of the Metafile */
8246 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8247 if(dwSize != sizeof(pData->dwMetaFileWidth))
8249 hRes = CONVERT10_E_OLESTREAM_GET;
8251 if(hRes == S_OK)
8253 /* Get the Height of the Metafile */
8254 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8255 if(dwSize != sizeof(pData->dwMetaFileHeight))
8257 hRes = CONVERT10_E_OLESTREAM_GET;
8261 if(hRes == S_OK)
8263 /* Get the Length of the Data */
8264 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8265 if(dwSize != sizeof(pData->dwDataLength))
8267 hRes = CONVERT10_E_OLESTREAM_GET;
8271 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8273 if(!bStrem1) /* if it is a second OLE stream data */
8275 pData->dwDataLength -= 8;
8276 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8277 if(dwSize != sizeof(pData->strUnknown))
8279 hRes = CONVERT10_E_OLESTREAM_GET;
8283 if(hRes == S_OK)
8285 if(pData->dwDataLength > 0)
8287 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8289 /* Get Data (ex. IStorage, Metafile, or BMP) */
8290 if(pData->pData)
8292 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8293 if(dwSize != pData->dwDataLength)
8295 hRes = CONVERT10_E_OLESTREAM_GET;
8298 else
8300 hRes = CONVERT10_E_OLESTREAM_GET;
8306 return hRes;
8309 /*************************************************************************
8310 * OLECONVERT_SaveOLE10 [Internal]
8312 * Saves the OLE10 STREAM From memory
8314 * PARAMS
8315 * pData [I] Data Structure for the OLESTREAM Data
8316 * pOleStream [I] The OLESTREAM to save
8318 * RETURNS
8319 * Success: S_OK
8320 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8322 * NOTES
8323 * This function is used by OleConvertIStorageToOLESTREAM only.
8326 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8328 DWORD dwSize;
8329 HRESULT hRes = S_OK;
8332 /* Set the OleID */
8333 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8334 if(dwSize != sizeof(pData->dwOleID))
8336 hRes = CONVERT10_E_OLESTREAM_PUT;
8339 if(hRes == S_OK)
8341 /* Set the TypeID */
8342 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8343 if(dwSize != sizeof(pData->dwTypeID))
8345 hRes = CONVERT10_E_OLESTREAM_PUT;
8349 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8351 /* Set the Length of the OleTypeName */
8352 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8353 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8355 hRes = CONVERT10_E_OLESTREAM_PUT;
8358 if(hRes == S_OK)
8360 if(pData->dwOleTypeNameLength > 0)
8362 /* Set the OleTypeName */
8363 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8364 if(dwSize != pData->dwOleTypeNameLength)
8366 hRes = CONVERT10_E_OLESTREAM_PUT;
8371 if(hRes == S_OK)
8373 /* Set the width of the Metafile */
8374 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8375 if(dwSize != sizeof(pData->dwMetaFileWidth))
8377 hRes = CONVERT10_E_OLESTREAM_PUT;
8381 if(hRes == S_OK)
8383 /* Set the height of the Metafile */
8384 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8385 if(dwSize != sizeof(pData->dwMetaFileHeight))
8387 hRes = CONVERT10_E_OLESTREAM_PUT;
8391 if(hRes == S_OK)
8393 /* Set the length of the Data */
8394 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8395 if(dwSize != sizeof(pData->dwDataLength))
8397 hRes = CONVERT10_E_OLESTREAM_PUT;
8401 if(hRes == S_OK)
8403 if(pData->dwDataLength > 0)
8405 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8406 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8407 if(dwSize != pData->dwDataLength)
8409 hRes = CONVERT10_E_OLESTREAM_PUT;
8414 return hRes;
8417 /*************************************************************************
8418 * OLECONVERT_GetOLE20FromOLE10[Internal]
8420 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8421 * opens it, and copies the content to the dest IStorage for
8422 * OleConvertOLESTREAMToIStorage
8425 * PARAMS
8426 * pDestStorage [I] The IStorage to copy the data to
8427 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8428 * nBufferLength [I] The size of the buffer
8430 * RETURNS
8431 * Nothing
8433 * NOTES
8437 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8439 HRESULT hRes;
8440 HANDLE hFile;
8441 IStorage *pTempStorage;
8442 DWORD dwNumOfBytesWritten;
8443 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8444 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8446 /* Create a temp File */
8447 GetTempPathW(MAX_PATH, wstrTempDir);
8448 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8449 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8451 if(hFile != INVALID_HANDLE_VALUE)
8453 /* Write IStorage Data to File */
8454 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8455 CloseHandle(hFile);
8457 /* Open and copy temp storage to the Dest Storage */
8458 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8459 if(hRes == S_OK)
8461 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8462 IStorage_Release(pTempStorage);
8464 DeleteFileW(wstrTempFile);
8469 /*************************************************************************
8470 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8472 * Saves the OLE10 STREAM From memory
8474 * PARAMS
8475 * pStorage [I] The Src IStorage to copy
8476 * pData [I] The Dest Memory to write to.
8478 * RETURNS
8479 * The size in bytes allocated for pData
8481 * NOTES
8482 * Memory allocated for pData must be freed by the caller
8484 * Used by OleConvertIStorageToOLESTREAM only.
8487 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8489 HANDLE hFile;
8490 HRESULT hRes;
8491 DWORD nDataLength = 0;
8492 IStorage *pTempStorage;
8493 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8494 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8496 *pData = NULL;
8498 /* Create temp Storage */
8499 GetTempPathW(MAX_PATH, wstrTempDir);
8500 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8501 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8503 if(hRes == S_OK)
8505 /* Copy Src Storage to the Temp Storage */
8506 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8507 IStorage_Release(pTempStorage);
8509 /* Open Temp Storage as a file and copy to memory */
8510 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8511 if(hFile != INVALID_HANDLE_VALUE)
8513 nDataLength = GetFileSize(hFile, NULL);
8514 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8515 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8516 CloseHandle(hFile);
8518 DeleteFileW(wstrTempFile);
8520 return nDataLength;
8523 /*************************************************************************
8524 * STORAGE_CreateOleStream [Internal]
8526 * Creates the "\001OLE" stream in the IStorage if necessary.
8528 * PARAMS
8529 * storage [I] Dest storage to create the stream in
8530 * flags [I] flags to be set for newly created stream
8532 * RETURNS
8533 * HRESULT return value
8535 * NOTES
8537 * This stream is still unknown, MS Word seems to have extra data
8538 * but since the data is stored in the OLESTREAM there should be
8539 * no need to recreate the stream. If the stream is manually
8540 * deleted it will create it with this default data.
8543 HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
8545 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
8546 static const DWORD version_magic = 0x02000001;
8547 IStream *stream;
8548 HRESULT hr;
8550 hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
8551 if (hr == S_OK)
8553 struct empty_1ole_stream {
8554 DWORD version_magic;
8555 DWORD flags;
8556 DWORD update_options;
8557 DWORD reserved;
8558 DWORD mon_stream_size;
8560 struct empty_1ole_stream stream_data;
8562 stream_data.version_magic = version_magic;
8563 stream_data.flags = flags;
8564 stream_data.update_options = 0;
8565 stream_data.reserved = 0;
8566 stream_data.mon_stream_size = 0;
8568 hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
8569 IStream_Release(stream);
8572 return hr;
8575 /* write a string to a stream, preceded by its length */
8576 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8578 HRESULT r;
8579 LPSTR str;
8580 DWORD len = 0;
8582 if( string )
8583 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8584 r = IStream_Write( stm, &len, sizeof(len), NULL);
8585 if( FAILED( r ) )
8586 return r;
8587 if(len == 0)
8588 return r;
8589 str = CoTaskMemAlloc( len );
8590 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8591 r = IStream_Write( stm, str, len, NULL);
8592 CoTaskMemFree( str );
8593 return r;
8596 /* read a string preceded by its length from a stream */
8597 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8599 HRESULT r;
8600 DWORD len, count = 0;
8601 LPSTR str;
8602 LPWSTR wstr;
8604 r = IStream_Read( stm, &len, sizeof(len), &count );
8605 if( FAILED( r ) )
8606 return r;
8607 if( count != sizeof(len) )
8608 return E_OUTOFMEMORY;
8610 TRACE("%d bytes\n",len);
8612 str = CoTaskMemAlloc( len );
8613 if( !str )
8614 return E_OUTOFMEMORY;
8615 count = 0;
8616 r = IStream_Read( stm, str, len, &count );
8617 if( FAILED( r ) )
8618 return r;
8619 if( count != len )
8621 CoTaskMemFree( str );
8622 return E_OUTOFMEMORY;
8625 TRACE("Read string %s\n",debugstr_an(str,len));
8627 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8628 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8629 if( wstr )
8630 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8631 CoTaskMemFree( str );
8633 *string = wstr;
8635 return r;
8639 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8640 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8642 IStream *pstm;
8643 HRESULT r = S_OK;
8644 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8646 static const BYTE unknown1[12] =
8647 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8648 0xFF, 0xFF, 0xFF, 0xFF};
8649 static const BYTE unknown2[16] =
8650 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8651 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8653 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8654 debugstr_w(lpszUserType), debugstr_w(szClipName),
8655 debugstr_w(szProgIDName));
8657 /* Create a CompObj stream */
8658 r = IStorage_CreateStream(pstg, szwStreamName,
8659 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8660 if( FAILED (r) )
8661 return r;
8663 /* Write CompObj Structure to stream */
8664 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8666 if( SUCCEEDED( r ) )
8667 r = WriteClassStm( pstm, clsid );
8669 if( SUCCEEDED( r ) )
8670 r = STREAM_WriteString( pstm, lpszUserType );
8671 if( SUCCEEDED( r ) )
8672 r = STREAM_WriteString( pstm, szClipName );
8673 if( SUCCEEDED( r ) )
8674 r = STREAM_WriteString( pstm, szProgIDName );
8675 if( SUCCEEDED( r ) )
8676 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8678 IStream_Release( pstm );
8680 return r;
8683 /***********************************************************************
8684 * WriteFmtUserTypeStg (OLE32.@)
8686 HRESULT WINAPI WriteFmtUserTypeStg(
8687 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8689 HRESULT r;
8690 WCHAR szwClipName[0x40];
8691 CLSID clsid = CLSID_NULL;
8692 LPWSTR wstrProgID = NULL;
8693 DWORD n;
8695 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8697 /* get the clipboard format name */
8698 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8699 szwClipName[n]=0;
8701 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8703 /* FIXME: There's room to save a CLSID and its ProgID, but
8704 the CLSID is not looked up in the registry and in all the
8705 tests I wrote it was CLSID_NULL. Where does it come from?
8708 /* get the real program ID. This may fail, but that's fine */
8709 ProgIDFromCLSID(&clsid, &wstrProgID);
8711 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8713 r = STORAGE_WriteCompObj( pstg, &clsid,
8714 lpszUserType, szwClipName, wstrProgID );
8716 CoTaskMemFree(wstrProgID);
8718 return r;
8722 /******************************************************************************
8723 * ReadFmtUserTypeStg [OLE32.@]
8725 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8727 HRESULT r;
8728 IStream *stm = 0;
8729 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8730 unsigned char unknown1[12];
8731 unsigned char unknown2[16];
8732 DWORD count;
8733 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8734 CLSID clsid;
8736 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8738 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8739 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8740 if( FAILED ( r ) )
8742 WARN("Failed to open stream r = %08x\n", r);
8743 return r;
8746 /* read the various parts of the structure */
8747 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8748 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8749 goto end;
8750 r = ReadClassStm( stm, &clsid );
8751 if( FAILED( r ) )
8752 goto end;
8754 r = STREAM_ReadString( stm, &szCLSIDName );
8755 if( FAILED( r ) )
8756 goto end;
8758 r = STREAM_ReadString( stm, &szOleTypeName );
8759 if( FAILED( r ) )
8760 goto end;
8762 r = STREAM_ReadString( stm, &szProgIDName );
8763 if( FAILED( r ) )
8764 goto end;
8766 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8767 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8768 goto end;
8770 /* ok, success... now we just need to store what we found */
8771 if( pcf )
8772 *pcf = RegisterClipboardFormatW( szOleTypeName );
8774 if( lplpszUserType )
8776 *lplpszUserType = szCLSIDName;
8777 szCLSIDName = NULL;
8780 end:
8781 CoTaskMemFree( szCLSIDName );
8782 CoTaskMemFree( szOleTypeName );
8783 CoTaskMemFree( szProgIDName );
8784 IStream_Release( stm );
8786 return r;
8790 /*************************************************************************
8791 * OLECONVERT_CreateCompObjStream [Internal]
8793 * Creates a "\001CompObj" is the destination IStorage if necessary.
8795 * PARAMS
8796 * pStorage [I] The dest IStorage to create the CompObj Stream
8797 * if necessary.
8798 * strOleTypeName [I] The ProgID
8800 * RETURNS
8801 * Success: S_OK
8802 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8804 * NOTES
8805 * This function is used by OleConvertOLESTREAMToIStorage only.
8807 * The stream data is stored in the OLESTREAM and there should be
8808 * no need to recreate the stream. If the stream is manually
8809 * deleted it will attempt to create it by querying the registry.
8813 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8815 IStream *pStream;
8816 HRESULT hStorageRes, hRes = S_OK;
8817 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8818 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8819 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8821 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8822 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8824 /* Initialize the CompObj structure */
8825 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8826 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8827 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8830 /* Create a CompObj stream if it doesn't exist */
8831 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8832 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8833 if(hStorageRes == S_OK)
8835 /* copy the OleTypeName to the compobj struct */
8836 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8837 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8839 /* copy the OleTypeName to the compobj struct */
8840 /* Note: in the test made, these were Identical */
8841 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8842 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8844 /* Get the CLSID */
8845 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8846 bufferW, OLESTREAM_MAX_STR_LEN );
8847 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8849 if(hRes == S_OK)
8851 HKEY hKey;
8852 LONG hErr;
8853 /* Get the CLSID Default Name from the Registry */
8854 hErr = open_classes_key(HKEY_CLASSES_ROOT, bufferW, MAXIMUM_ALLOWED, &hKey);
8855 if(hErr == ERROR_SUCCESS)
8857 char strTemp[OLESTREAM_MAX_STR_LEN];
8858 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8859 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8860 if(hErr == ERROR_SUCCESS)
8862 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8864 RegCloseKey(hKey);
8868 /* Write CompObj Structure to stream */
8869 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8871 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8873 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8874 if(IStorageCompObj.dwCLSIDNameLength > 0)
8876 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8878 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8879 if(IStorageCompObj.dwOleTypeNameLength > 0)
8881 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8883 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8884 if(IStorageCompObj.dwProgIDNameLength > 0)
8886 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8888 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8889 IStream_Release(pStream);
8891 return hRes;
8895 /*************************************************************************
8896 * OLECONVERT_CreateOlePresStream[Internal]
8898 * Creates the "\002OlePres000" Stream with the Metafile data
8900 * PARAMS
8901 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8902 * dwExtentX [I] Width of the Metafile
8903 * dwExtentY [I] Height of the Metafile
8904 * pData [I] Metafile data
8905 * dwDataLength [I] Size of the Metafile data
8907 * RETURNS
8908 * Success: S_OK
8909 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8911 * NOTES
8912 * This function is used by OleConvertOLESTREAMToIStorage only.
8915 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8917 HRESULT hRes;
8918 IStream *pStream;
8919 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8920 BYTE pOlePresStreamHeader [] =
8922 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8923 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8924 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8925 0x00, 0x00, 0x00, 0x00
8928 BYTE pOlePresStreamHeaderEmpty [] =
8930 0x00, 0x00, 0x00, 0x00,
8931 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8932 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8933 0x00, 0x00, 0x00, 0x00
8936 /* Create the OlePres000 Stream */
8937 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8938 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8940 if(hRes == S_OK)
8942 DWORD nHeaderSize;
8943 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8945 memset(&OlePres, 0, sizeof(OlePres));
8946 /* Do we have any metafile data to save */
8947 if(dwDataLength > 0)
8949 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8950 nHeaderSize = sizeof(pOlePresStreamHeader);
8952 else
8954 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8955 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8957 /* Set width and height of the metafile */
8958 OlePres.dwExtentX = dwExtentX;
8959 OlePres.dwExtentY = -dwExtentY;
8961 /* Set Data and Length */
8962 if(dwDataLength > sizeof(METAFILEPICT16))
8964 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8965 OlePres.pData = &(pData[8]);
8967 /* Save OlePres000 Data to Stream */
8968 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8969 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8970 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8971 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8972 if(OlePres.dwSize > 0)
8974 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8976 IStream_Release(pStream);
8980 /*************************************************************************
8981 * OLECONVERT_CreateOle10NativeStream [Internal]
8983 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8985 * PARAMS
8986 * pStorage [I] Dest storage to create the stream in
8987 * pData [I] Ole10 Native Data (ex. bmp)
8988 * dwDataLength [I] Size of the Ole10 Native Data
8990 * RETURNS
8991 * Nothing
8993 * NOTES
8994 * This function is used by OleConvertOLESTREAMToIStorage only.
8996 * Might need to verify the data and return appropriate error message
8999 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
9001 HRESULT hRes;
9002 IStream *pStream;
9003 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9005 /* Create the Ole10Native Stream */
9006 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
9007 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
9009 if(hRes == S_OK)
9011 /* Write info to stream */
9012 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
9013 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
9014 IStream_Release(pStream);
9019 /*************************************************************************
9020 * OLECONVERT_GetOLE10ProgID [Internal]
9022 * Finds the ProgID (or OleTypeID) from the IStorage
9024 * PARAMS
9025 * pStorage [I] The Src IStorage to get the ProgID
9026 * strProgID [I] the ProgID string to get
9027 * dwSize [I] the size of the string
9029 * RETURNS
9030 * Success: S_OK
9031 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
9033 * NOTES
9034 * This function is used by OleConvertIStorageToOLESTREAM only.
9038 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9040 HRESULT hRes;
9041 IStream *pStream;
9042 LARGE_INTEGER iSeekPos;
9043 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9044 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9046 /* Open the CompObj Stream */
9047 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9048 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9049 if(hRes == S_OK)
9052 /*Get the OleType from the CompObj Stream */
9053 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9054 iSeekPos.u.HighPart = 0;
9056 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9057 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9058 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9059 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9060 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9061 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9062 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9064 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9065 if(*dwSize > 0)
9067 IStream_Read(pStream, strProgID, *dwSize, NULL);
9069 IStream_Release(pStream);
9071 else
9073 STATSTG stat;
9074 LPOLESTR wstrProgID;
9076 /* Get the OleType from the registry */
9077 REFCLSID clsid = &(stat.clsid);
9078 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9079 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9080 if(hRes == S_OK)
9082 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9083 CoTaskMemFree(wstrProgID);
9087 return hRes;
9090 /*************************************************************************
9091 * OLECONVERT_GetOle10PresData [Internal]
9093 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9095 * PARAMS
9096 * pStorage [I] Src IStroage
9097 * pOleStream [I] Dest OleStream Mem Struct
9099 * RETURNS
9100 * Nothing
9102 * NOTES
9103 * This function is used by OleConvertIStorageToOLESTREAM only.
9105 * Memory allocated for pData must be freed by the caller
9109 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9112 HRESULT hRes;
9113 IStream *pStream;
9114 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9116 /* Initialize Default data for OLESTREAM */
9117 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9118 pOleStreamData[0].dwTypeID = 2;
9119 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9120 pOleStreamData[1].dwTypeID = 0;
9121 pOleStreamData[0].dwMetaFileWidth = 0;
9122 pOleStreamData[0].dwMetaFileHeight = 0;
9123 pOleStreamData[0].pData = NULL;
9124 pOleStreamData[1].pData = NULL;
9126 /* Open Ole10Native Stream */
9127 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9128 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9129 if(hRes == S_OK)
9132 /* Read Size and Data */
9133 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9134 if(pOleStreamData->dwDataLength > 0)
9136 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9137 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9139 IStream_Release(pStream);
9145 /*************************************************************************
9146 * OLECONVERT_GetOle20PresData[Internal]
9148 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9150 * PARAMS
9151 * pStorage [I] Src IStroage
9152 * pOleStreamData [I] Dest OleStream Mem Struct
9154 * RETURNS
9155 * Nothing
9157 * NOTES
9158 * This function is used by OleConvertIStorageToOLESTREAM only.
9160 * Memory allocated for pData must be freed by the caller
9162 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9164 HRESULT hRes;
9165 IStream *pStream;
9166 OLECONVERT_ISTORAGE_OLEPRES olePress;
9167 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9169 /* Initialize Default data for OLESTREAM */
9170 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9171 pOleStreamData[0].dwTypeID = 2;
9172 pOleStreamData[0].dwMetaFileWidth = 0;
9173 pOleStreamData[0].dwMetaFileHeight = 0;
9174 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9175 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9176 pOleStreamData[1].dwTypeID = 0;
9177 pOleStreamData[1].dwOleTypeNameLength = 0;
9178 pOleStreamData[1].strOleTypeName[0] = 0;
9179 pOleStreamData[1].dwMetaFileWidth = 0;
9180 pOleStreamData[1].dwMetaFileHeight = 0;
9181 pOleStreamData[1].pData = NULL;
9182 pOleStreamData[1].dwDataLength = 0;
9185 /* Open OlePress000 stream */
9186 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9187 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9188 if(hRes == S_OK)
9190 LARGE_INTEGER iSeekPos;
9191 METAFILEPICT16 MetaFilePict;
9192 static const char strMetafilePictName[] = "METAFILEPICT";
9194 /* Set the TypeID for a Metafile */
9195 pOleStreamData[1].dwTypeID = 5;
9197 /* Set the OleTypeName to Metafile */
9198 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9199 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9201 iSeekPos.u.HighPart = 0;
9202 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9204 /* Get Presentation Data */
9205 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9206 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9207 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9208 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9210 /*Set width and Height */
9211 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9212 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9213 if(olePress.dwSize > 0)
9215 /* Set Length */
9216 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9218 /* Set MetaFilePict struct */
9219 MetaFilePict.mm = 8;
9220 MetaFilePict.xExt = olePress.dwExtentX;
9221 MetaFilePict.yExt = olePress.dwExtentY;
9222 MetaFilePict.hMF = 0;
9224 /* Get Metafile Data */
9225 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9226 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9227 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9229 IStream_Release(pStream);
9233 /*************************************************************************
9234 * OleConvertOLESTREAMToIStorage [OLE32.@]
9236 * Read info on MSDN
9238 * TODO
9239 * DVTARGETDEVICE parameter is not handled
9240 * Still unsure of some mem fields for OLE 10 Stream
9241 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9242 * and "\001OLE" streams
9245 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9246 LPOLESTREAM pOleStream,
9247 LPSTORAGE pstg,
9248 const DVTARGETDEVICE* ptd)
9250 int i;
9251 HRESULT hRes=S_OK;
9252 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9254 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9256 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9258 if(ptd != NULL)
9260 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9263 if(pstg == NULL || pOleStream == NULL)
9265 hRes = E_INVALIDARG;
9268 if(hRes == S_OK)
9270 /* Load the OLESTREAM to Memory */
9271 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9274 if(hRes == S_OK)
9276 /* Load the OLESTREAM to Memory (part 2)*/
9277 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9280 if(hRes == S_OK)
9283 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9285 /* Do we have the IStorage Data in the OLESTREAM */
9286 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9288 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9289 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9291 else
9293 /* It must be an original OLE 1.0 source */
9294 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9297 else
9299 /* It must be an original OLE 1.0 source */
9300 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9303 /* Create CompObj Stream if necessary */
9304 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9305 if(hRes == S_OK)
9307 /*Create the Ole Stream if necessary */
9308 STORAGE_CreateOleStream(pstg, 0);
9313 /* Free allocated memory */
9314 for(i=0; i < 2; i++)
9316 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9317 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9318 pOleStreamData[i].pstrOleObjFileName = NULL;
9320 return hRes;
9323 /*************************************************************************
9324 * OleConvertIStorageToOLESTREAM [OLE32.@]
9326 * Read info on MSDN
9328 * Read info on MSDN
9330 * TODO
9331 * Still unsure of some mem fields for OLE 10 Stream
9332 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9333 * and "\001OLE" streams.
9336 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9337 LPSTORAGE pstg,
9338 LPOLESTREAM pOleStream)
9340 int i;
9341 HRESULT hRes = S_OK;
9342 IStream *pStream;
9343 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9344 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9346 TRACE("%p %p\n", pstg, pOleStream);
9348 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9350 if(pstg == NULL || pOleStream == NULL)
9352 hRes = E_INVALIDARG;
9354 if(hRes == S_OK)
9356 /* Get the ProgID */
9357 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9358 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9360 if(hRes == S_OK)
9362 /* Was it originally Ole10 */
9363 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9364 if(hRes == S_OK)
9366 IStream_Release(pStream);
9367 /* Get Presentation Data for Ole10Native */
9368 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9370 else
9372 /* Get Presentation Data (OLE20) */
9373 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9376 /* Save OLESTREAM */
9377 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9378 if(hRes == S_OK)
9380 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9385 /* Free allocated memory */
9386 for(i=0; i < 2; i++)
9388 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9391 return hRes;
9394 enum stream_1ole_flags {
9395 OleStream_LinkedObject = 0x00000001,
9396 OleStream_Convert = 0x00000004
9399 /***********************************************************************
9400 * GetConvertStg (OLE32.@)
9402 HRESULT WINAPI GetConvertStg(IStorage *stg)
9404 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9405 static const DWORD version_magic = 0x02000001;
9406 DWORD header[2];
9407 IStream *stream;
9408 HRESULT hr;
9410 TRACE("%p\n", stg);
9412 if (!stg) return E_INVALIDARG;
9414 hr = IStorage_OpenStream(stg, stream_1oleW, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream);
9415 if (FAILED(hr)) return hr;
9417 hr = IStream_Read(stream, header, sizeof(header), NULL);
9418 IStream_Release(stream);
9419 if (FAILED(hr)) return hr;
9421 if (header[0] != version_magic)
9423 ERR("got wrong version magic for 1Ole stream, 0x%08x\n", header[0]);
9424 return E_FAIL;
9427 return header[1] & OleStream_Convert ? S_OK : S_FALSE;
9430 /***********************************************************************
9431 * SetConvertStg (OLE32.@)
9433 HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
9435 DWORD flags = convert ? OleStream_Convert : 0;
9436 HRESULT hr;
9438 TRACE("(%p, %d)\n", storage, convert);
9440 hr = STORAGE_CreateOleStream(storage, flags);
9441 if (hr == STG_E_FILEALREADYEXISTS)
9443 static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
9444 IStream *stream;
9445 DWORD header[2];
9447 hr = IStorage_OpenStream(storage, stream_1oleW, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
9448 if (FAILED(hr)) return hr;
9450 hr = IStream_Read(stream, header, sizeof(header), NULL);
9451 if (FAILED(hr))
9453 IStream_Release(stream);
9454 return hr;
9457 /* update flag if differs */
9458 if ((header[1] ^ flags) & OleStream_Convert)
9460 LARGE_INTEGER pos;
9462 if (header[1] & OleStream_Convert)
9463 flags = header[1] & ~OleStream_Convert;
9464 else
9465 flags = header[1] | OleStream_Convert;
9467 pos.QuadPart = sizeof(DWORD);
9468 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
9469 if (FAILED(hr))
9471 IStream_Release(stream);
9472 return hr;
9475 hr = IStream_Write(stream, &flags, sizeof(flags), NULL);
9477 IStream_Release(stream);
9480 return hr;
9483 /******************************************************************************
9484 * StgIsStorageFile [OLE32.@]
9485 * Verify if the file contains a storage object
9487 * PARAMS
9488 * fn [ I] Filename
9490 * RETURNS
9491 * S_OK if file has magic bytes as a storage object
9492 * S_FALSE if file is not storage
9494 HRESULT WINAPI
9495 StgIsStorageFile(LPCOLESTR fn)
9497 HANDLE hf;
9498 BYTE magic[8];
9499 DWORD bytes_read;
9501 TRACE("%s\n", debugstr_w(fn));
9502 hf = CreateFileW(fn, GENERIC_READ,
9503 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9504 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9506 if (hf == INVALID_HANDLE_VALUE)
9507 return STG_E_FILENOTFOUND;
9509 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9511 WARN(" unable to read file\n");
9512 CloseHandle(hf);
9513 return S_FALSE;
9516 CloseHandle(hf);
9518 if (bytes_read != 8) {
9519 TRACE(" too short\n");
9520 return S_FALSE;
9523 if (!memcmp(magic,STORAGE_magic,8)) {
9524 TRACE(" -> YES\n");
9525 return S_OK;
9528 TRACE(" -> Invalid header.\n");
9529 return S_FALSE;
9532 /***********************************************************************
9533 * WriteClassStm (OLE32.@)
9535 * Writes a CLSID to a stream.
9537 * PARAMS
9538 * pStm [I] Stream to write to.
9539 * rclsid [I] CLSID to write.
9541 * RETURNS
9542 * Success: S_OK.
9543 * Failure: HRESULT code.
9545 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9547 TRACE("(%p,%p)\n",pStm,rclsid);
9549 if (!pStm || !rclsid)
9550 return E_INVALIDARG;
9552 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9555 /***********************************************************************
9556 * ReadClassStm (OLE32.@)
9558 * Reads a CLSID from a stream.
9560 * PARAMS
9561 * pStm [I] Stream to read from.
9562 * rclsid [O] CLSID to read.
9564 * RETURNS
9565 * Success: S_OK.
9566 * Failure: HRESULT code.
9568 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9570 ULONG nbByte;
9571 HRESULT res;
9573 TRACE("(%p,%p)\n",pStm,pclsid);
9575 if (!pStm || !pclsid)
9576 return E_INVALIDARG;
9578 /* clear the output args */
9579 *pclsid = CLSID_NULL;
9581 res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
9583 if (FAILED(res))
9584 return res;
9586 if (nbByte != sizeof(CLSID))
9587 return STG_E_READFAULT;
9588 else
9589 return S_OK;