comctl32: Shed unused parameter from TOOLTIPS_NCCreate.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob8cf3f0225493bcc3c393d6bde15304d328ac4bc0
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry;
86 StorageBaseImpl *parentStorage;
88 typedef struct StorageInternalImpl StorageInternalImpl;
90 /* Method definitions for the Storage32InternalImpl class. */
91 static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
92 DWORD openFlags, DirRef storageDirEntry);
93 static void StorageImpl_Destroy(StorageBaseImpl* iface);
94 static void StorageImpl_Invalidate(StorageBaseImpl* iface);
95 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
96 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
97 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
98 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
99 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
100 static void StorageImpl_SaveFileHeader(StorageImpl* This);
102 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
103 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
104 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
105 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
106 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
108 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
109 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
110 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
112 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
113 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
114 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
115 ULONG blockIndex, ULONG offset, DWORD value);
116 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
117 ULONG blockIndex, ULONG offset, DWORD* value);
119 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
120 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
122 typedef struct TransactedDirEntry
124 /* If applicable, a reference to the original DirEntry in the transacted
125 * parent. If this is a newly-created entry, DIRENTRY_NULL. */
126 DirRef transactedParentEntry;
128 /* True if this entry is being used. */
129 int inuse;
131 /* True if data is up to date. */
132 int read;
134 /* True if this entry has been modified. */
135 int dirty;
137 /* True if this entry's stream has been modified. */
138 int stream_dirty;
140 /* True if this entry has been deleted in the transacted storage, but the
141 * delete has not yet been committed. */
142 int deleted;
144 /* If this entry's stream has been modified, a reference to where the stream
145 * is stored in the snapshot file. */
146 DirRef stream_entry;
148 /* This directory entry's data, including any changes that have been made. */
149 DirEntry data;
151 /* A reference to the parent of this node. This is only valid while we are
152 * committing changes. */
153 DirRef parent;
155 /* A reference to a newly-created entry in the transacted parent. This is
156 * always equal to transactedParentEntry except when committing changes. */
157 DirRef newTransactedParentEntry;
158 } TransactedDirEntry;
160 /****************************************************************************
161 * Transacted storage object.
163 typedef struct TransactedSnapshotImpl
165 struct StorageBaseImpl base;
168 * Modified streams are temporarily saved to the scratch file.
170 StorageBaseImpl *scratch;
172 /* The directory structure is kept here, so that we can track how these
173 * entries relate to those in the parent storage. */
174 TransactedDirEntry *entries;
175 ULONG entries_size;
176 ULONG firstFreeEntry;
179 * Changes are committed to the transacted parent.
181 StorageBaseImpl *transactedParent;
182 } TransactedSnapshotImpl;
184 /* Generic function to create a transacted wrapper for a direct storage object. */
185 static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
187 /* OLESTREAM memory structure to use for Get and Put Routines */
188 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
189 typedef struct
191 DWORD dwOleID;
192 DWORD dwTypeID;
193 DWORD dwOleTypeNameLength;
194 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
195 CHAR *pstrOleObjFileName;
196 DWORD dwOleObjFileNameLength;
197 DWORD dwMetaFileWidth;
198 DWORD dwMetaFileHeight;
199 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
200 DWORD dwDataLength;
201 BYTE *pData;
202 }OLECONVERT_OLESTREAM_DATA;
204 /* CompObj Stream structure */
205 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
206 typedef struct
208 BYTE byUnknown1[12];
209 CLSID clsid;
210 DWORD dwCLSIDNameLength;
211 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
212 DWORD dwOleTypeNameLength;
213 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
214 DWORD dwProgIDNameLength;
215 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
216 BYTE byUnknown2[16];
217 }OLECONVERT_ISTORAGE_COMPOBJ;
220 /* Ole Presentation Stream structure */
221 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
222 typedef struct
224 BYTE byUnknown1[28];
225 DWORD dwExtentX;
226 DWORD dwExtentY;
227 DWORD dwSize;
228 BYTE *pData;
229 }OLECONVERT_ISTORAGE_OLEPRES;
233 /***********************************************************************
234 * Forward declaration of internal functions used by the method DestroyElement
236 static HRESULT deleteStorageContents(
237 StorageBaseImpl *parentStorage,
238 DirRef indexToDelete,
239 DirEntry entryDataToDelete);
241 static HRESULT deleteStreamContents(
242 StorageBaseImpl *parentStorage,
243 DirRef indexToDelete,
244 DirEntry entryDataToDelete);
246 static HRESULT removeFromTree(
247 StorageBaseImpl *This,
248 DirRef parentStorageIndex,
249 DirRef deletedIndex);
251 /***********************************************************************
252 * Declaration of the functions used to manipulate DirEntry
255 static HRESULT insertIntoTree(
256 StorageBaseImpl *This,
257 DirRef parentStorageIndex,
258 DirRef newEntryIndex);
260 static LONG entryNameCmp(
261 const OLECHAR *name1,
262 const OLECHAR *name2);
264 static DirRef findElement(
265 StorageBaseImpl *storage,
266 DirRef storageEntry,
267 const OLECHAR *name,
268 DirEntry *data);
270 static HRESULT findTreeParent(
271 StorageBaseImpl *storage,
272 DirRef storageEntry,
273 const OLECHAR *childName,
274 DirEntry *parentData,
275 DirRef *parentEntry,
276 ULONG *relation);
278 /***********************************************************************
279 * Declaration of miscellaneous functions...
281 static HRESULT validateSTGM(DWORD stgmValue);
283 static DWORD GetShareModeFromSTGM(DWORD stgm);
284 static DWORD GetAccessModeFromSTGM(DWORD stgm);
285 static DWORD GetCreationModeFromSTGM(DWORD stgm);
287 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
290 /****************************************************************************
291 * IEnumSTATSTGImpl definitions.
293 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
294 * This class allows iterating through the content of a storage and to find
295 * specific items inside it.
297 struct IEnumSTATSTGImpl
299 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
300 * since we want to cast this in an IEnumSTATSTG pointer */
302 LONG ref; /* Reference count */
303 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
304 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
306 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
310 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
311 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
313 /************************************************************************
314 ** Block Functions
317 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
319 return (index+1) * This->bigBlockSize;
322 /************************************************************************
323 ** Storage32BaseImpl implementation
325 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
326 ULARGE_INTEGER offset,
327 void* buffer,
328 ULONG size,
329 ULONG* bytesRead)
331 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
334 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
335 ULARGE_INTEGER offset,
336 const void* buffer,
337 const ULONG size,
338 ULONG* bytesWritten)
340 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
343 /************************************************************************
344 * Storage32BaseImpl_QueryInterface (IUnknown)
346 * This method implements the common QueryInterface for all IStorage32
347 * implementations contained in this file.
349 * See Windows documentation for more details on IUnknown methods.
351 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
352 IStorage* iface,
353 REFIID riid,
354 void** ppvObject)
356 StorageBaseImpl *This = (StorageBaseImpl *)iface;
358 if ( (This==0) || (ppvObject==0) )
359 return E_INVALIDARG;
361 *ppvObject = 0;
363 if (IsEqualGUID(&IID_IUnknown, riid) ||
364 IsEqualGUID(&IID_IStorage, riid))
366 *ppvObject = This;
368 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
370 *ppvObject = &This->pssVtbl;
373 if ((*ppvObject)==0)
374 return E_NOINTERFACE;
376 IStorage_AddRef(iface);
378 return S_OK;
381 /************************************************************************
382 * Storage32BaseImpl_AddRef (IUnknown)
384 * This method implements the common AddRef for all IStorage32
385 * implementations contained in this file.
387 * See Windows documentation for more details on IUnknown methods.
389 static ULONG WINAPI StorageBaseImpl_AddRef(
390 IStorage* iface)
392 StorageBaseImpl *This = (StorageBaseImpl *)iface;
393 ULONG ref = InterlockedIncrement(&This->ref);
395 TRACE("(%p) AddRef to %d\n", This, ref);
397 return ref;
400 /************************************************************************
401 * Storage32BaseImpl_Release (IUnknown)
403 * This method implements the common Release for all IStorage32
404 * implementations contained in this file.
406 * See Windows documentation for more details on IUnknown methods.
408 static ULONG WINAPI StorageBaseImpl_Release(
409 IStorage* iface)
411 StorageBaseImpl *This = (StorageBaseImpl *)iface;
413 ULONG ref = InterlockedDecrement(&This->ref);
415 TRACE("(%p) ReleaseRef to %d\n", This, ref);
417 if (ref == 0)
420 * Since we are using a system of base-classes, we want to call the
421 * destructor of the appropriate derived class. To do this, we are
422 * using virtual functions to implement the destructor.
424 StorageBaseImpl_Destroy(This);
427 return ref;
430 /************************************************************************
431 * Storage32BaseImpl_OpenStream (IStorage)
433 * This method will open the specified stream object from the current storage.
435 * See Windows documentation for more details on IStorage methods.
437 static HRESULT WINAPI StorageBaseImpl_OpenStream(
438 IStorage* iface,
439 const OLECHAR* pwcsName, /* [string][in] */
440 void* reserved1, /* [unique][in] */
441 DWORD grfMode, /* [in] */
442 DWORD reserved2, /* [in] */
443 IStream** ppstm) /* [out] */
445 StorageBaseImpl *This = (StorageBaseImpl *)iface;
446 StgStreamImpl* newStream;
447 DirEntry currentEntry;
448 DirRef streamEntryRef;
449 HRESULT res = STG_E_UNKNOWN;
451 TRACE("(%p, %s, %p, %x, %d, %p)\n",
452 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
454 if ( (pwcsName==NULL) || (ppstm==0) )
456 res = E_INVALIDARG;
457 goto end;
460 *ppstm = NULL;
462 if ( FAILED( validateSTGM(grfMode) ) ||
463 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
465 res = STG_E_INVALIDFLAG;
466 goto end;
470 * As documented.
472 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
474 res = STG_E_INVALIDFUNCTION;
475 goto end;
478 if (This->reverted)
480 res = STG_E_REVERTED;
481 goto end;
485 * Check that we're compatible with the parent's storage mode, but
486 * only if we are not in transacted mode
488 if(!(This->openFlags & STGM_TRANSACTED)) {
489 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
491 res = STG_E_INVALIDFLAG;
492 goto end;
497 * Search for the element with the given name
499 streamEntryRef = findElement(
500 This,
501 This->storageDirEntry,
502 pwcsName,
503 &currentEntry);
506 * If it was found, construct the stream object and return a pointer to it.
508 if ( (streamEntryRef!=DIRENTRY_NULL) &&
509 (currentEntry.stgType==STGTY_STREAM) )
511 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
513 /* A single stream cannot be opened a second time. */
514 res = STG_E_ACCESSDENIED;
515 goto end;
518 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
520 if (newStream!=0)
522 newStream->grfMode = grfMode;
523 *ppstm = (IStream*)newStream;
525 IStream_AddRef(*ppstm);
527 res = S_OK;
528 goto end;
531 res = E_OUTOFMEMORY;
532 goto end;
535 res = STG_E_FILENOTFOUND;
537 end:
538 if (res == S_OK)
539 TRACE("<-- IStream %p\n", *ppstm);
540 TRACE("<-- %08x\n", res);
541 return res;
544 /************************************************************************
545 * Storage32BaseImpl_OpenStorage (IStorage)
547 * This method will open a new storage object from the current storage.
549 * See Windows documentation for more details on IStorage methods.
551 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
552 IStorage* iface,
553 const OLECHAR* pwcsName, /* [string][unique][in] */
554 IStorage* pstgPriority, /* [unique][in] */
555 DWORD grfMode, /* [in] */
556 SNB snbExclude, /* [unique][in] */
557 DWORD reserved, /* [in] */
558 IStorage** ppstg) /* [out] */
560 StorageBaseImpl *This = (StorageBaseImpl *)iface;
561 StorageInternalImpl* newStorage;
562 StorageBaseImpl* newTransactedStorage;
563 DirEntry currentEntry;
564 DirRef storageEntryRef;
565 HRESULT res = STG_E_UNKNOWN;
567 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
568 iface, debugstr_w(pwcsName), pstgPriority,
569 grfMode, snbExclude, reserved, ppstg);
571 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
573 res = E_INVALIDARG;
574 goto end;
577 if (This->openFlags & STGM_SIMPLE)
579 res = STG_E_INVALIDFUNCTION;
580 goto end;
583 /* as documented */
584 if (snbExclude != NULL)
586 res = STG_E_INVALIDPARAMETER;
587 goto end;
590 if ( FAILED( validateSTGM(grfMode) ))
592 res = STG_E_INVALIDFLAG;
593 goto end;
597 * As documented.
599 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
600 (grfMode & STGM_DELETEONRELEASE) ||
601 (grfMode & STGM_PRIORITY) )
603 res = STG_E_INVALIDFUNCTION;
604 goto end;
607 if (This->reverted)
608 return STG_E_REVERTED;
611 * Check that we're compatible with the parent's storage mode,
612 * but only if we are not transacted
614 if(!(This->openFlags & STGM_TRANSACTED)) {
615 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
617 res = STG_E_ACCESSDENIED;
618 goto end;
622 *ppstg = NULL;
624 storageEntryRef = findElement(
625 This,
626 This->storageDirEntry,
627 pwcsName,
628 &currentEntry);
630 if ( (storageEntryRef!=DIRENTRY_NULL) &&
631 (currentEntry.stgType==STGTY_STORAGE) )
633 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
635 /* A single storage cannot be opened a second time. */
636 res = STG_E_ACCESSDENIED;
637 goto end;
640 newStorage = StorageInternalImpl_Construct(
641 This,
642 grfMode,
643 storageEntryRef);
645 if (newStorage != 0)
647 if (grfMode & STGM_TRANSACTED)
649 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
651 if (FAILED(res))
653 HeapFree(GetProcessHeap(), 0, newStorage);
654 goto end;
657 *ppstg = (IStorage*)newTransactedStorage;
659 else
661 *ppstg = (IStorage*)newStorage;
664 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
666 res = S_OK;
667 goto end;
670 res = STG_E_INSUFFICIENTMEMORY;
671 goto end;
674 res = STG_E_FILENOTFOUND;
676 end:
677 TRACE("<-- %08x\n", res);
678 return res;
681 /************************************************************************
682 * Storage32BaseImpl_EnumElements (IStorage)
684 * This method will create an enumerator object that can be used to
685 * retrieve information about all the elements in the storage object.
687 * See Windows documentation for more details on IStorage methods.
689 static HRESULT WINAPI StorageBaseImpl_EnumElements(
690 IStorage* iface,
691 DWORD reserved1, /* [in] */
692 void* reserved2, /* [size_is][unique][in] */
693 DWORD reserved3, /* [in] */
694 IEnumSTATSTG** ppenum) /* [out] */
696 StorageBaseImpl *This = (StorageBaseImpl *)iface;
697 IEnumSTATSTGImpl* newEnum;
699 TRACE("(%p, %d, %p, %d, %p)\n",
700 iface, reserved1, reserved2, reserved3, ppenum);
702 if ( (This==0) || (ppenum==0))
703 return E_INVALIDARG;
705 if (This->reverted)
706 return STG_E_REVERTED;
708 newEnum = IEnumSTATSTGImpl_Construct(
709 This,
710 This->storageDirEntry);
712 if (newEnum!=0)
714 *ppenum = (IEnumSTATSTG*)newEnum;
716 IEnumSTATSTG_AddRef(*ppenum);
718 return S_OK;
721 return E_OUTOFMEMORY;
724 /************************************************************************
725 * Storage32BaseImpl_Stat (IStorage)
727 * This method will retrieve information about this storage object.
729 * See Windows documentation for more details on IStorage methods.
731 static HRESULT WINAPI StorageBaseImpl_Stat(
732 IStorage* iface,
733 STATSTG* pstatstg, /* [out] */
734 DWORD grfStatFlag) /* [in] */
736 StorageBaseImpl *This = (StorageBaseImpl *)iface;
737 DirEntry currentEntry;
738 HRESULT res = STG_E_UNKNOWN;
740 TRACE("(%p, %p, %x)\n",
741 iface, pstatstg, grfStatFlag);
743 if ( (This==0) || (pstatstg==0))
745 res = E_INVALIDARG;
746 goto end;
749 if (This->reverted)
751 res = STG_E_REVERTED;
752 goto end;
755 res = StorageBaseImpl_ReadDirEntry(
756 This,
757 This->storageDirEntry,
758 &currentEntry);
760 if (SUCCEEDED(res))
762 StorageUtl_CopyDirEntryToSTATSTG(
763 This,
764 pstatstg,
765 &currentEntry,
766 grfStatFlag);
768 pstatstg->grfMode = This->openFlags;
769 pstatstg->grfStateBits = This->stateBits;
772 end:
773 if (res == S_OK)
775 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);
777 TRACE("<-- %08x\n", res);
778 return res;
781 /************************************************************************
782 * Storage32BaseImpl_RenameElement (IStorage)
784 * This method will rename the specified element.
786 * See Windows documentation for more details on IStorage methods.
788 static HRESULT WINAPI StorageBaseImpl_RenameElement(
789 IStorage* iface,
790 const OLECHAR* pwcsOldName, /* [in] */
791 const OLECHAR* pwcsNewName) /* [in] */
793 StorageBaseImpl *This = (StorageBaseImpl *)iface;
794 DirEntry currentEntry;
795 DirRef currentEntryRef;
797 TRACE("(%p, %s, %s)\n",
798 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
800 if (This->reverted)
801 return STG_E_REVERTED;
803 currentEntryRef = findElement(This,
804 This->storageDirEntry,
805 pwcsNewName,
806 &currentEntry);
808 if (currentEntryRef != DIRENTRY_NULL)
811 * There is already an element with the new name
813 return STG_E_FILEALREADYEXISTS;
817 * Search for the old element name
819 currentEntryRef = findElement(This,
820 This->storageDirEntry,
821 pwcsOldName,
822 &currentEntry);
824 if (currentEntryRef != DIRENTRY_NULL)
826 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
827 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
829 WARN("Element is already open; cannot rename.\n");
830 return STG_E_ACCESSDENIED;
833 /* Remove the element from its current position in the tree */
834 removeFromTree(This, This->storageDirEntry,
835 currentEntryRef);
837 /* Change the name of the element */
838 strcpyW(currentEntry.name, pwcsNewName);
840 /* Delete any sibling links */
841 currentEntry.leftChild = DIRENTRY_NULL;
842 currentEntry.rightChild = DIRENTRY_NULL;
844 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
845 &currentEntry);
847 /* Insert the element in a new position in the tree */
848 insertIntoTree(This, This->storageDirEntry,
849 currentEntryRef);
851 else
854 * There is no element with the old name
856 return STG_E_FILENOTFOUND;
859 return StorageBaseImpl_Flush(This);
862 /************************************************************************
863 * Storage32BaseImpl_CreateStream (IStorage)
865 * This method will create a stream object within this storage
867 * See Windows documentation for more details on IStorage methods.
869 static HRESULT WINAPI StorageBaseImpl_CreateStream(
870 IStorage* iface,
871 const OLECHAR* pwcsName, /* [string][in] */
872 DWORD grfMode, /* [in] */
873 DWORD reserved1, /* [in] */
874 DWORD reserved2, /* [in] */
875 IStream** ppstm) /* [out] */
877 StorageBaseImpl *This = (StorageBaseImpl *)iface;
878 StgStreamImpl* newStream;
879 DirEntry currentEntry, newStreamEntry;
880 DirRef currentEntryRef, newStreamEntryRef;
881 HRESULT hr;
883 TRACE("(%p, %s, %x, %d, %d, %p)\n",
884 iface, debugstr_w(pwcsName), grfMode,
885 reserved1, reserved2, ppstm);
887 if (ppstm == 0)
888 return STG_E_INVALIDPOINTER;
890 if (pwcsName == 0)
891 return STG_E_INVALIDNAME;
893 if (reserved1 || reserved2)
894 return STG_E_INVALIDPARAMETER;
896 if ( FAILED( validateSTGM(grfMode) ))
897 return STG_E_INVALIDFLAG;
899 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
900 return STG_E_INVALIDFLAG;
902 if (This->reverted)
903 return STG_E_REVERTED;
906 * As documented.
908 if ((grfMode & STGM_DELETEONRELEASE) ||
909 (grfMode & STGM_TRANSACTED))
910 return STG_E_INVALIDFUNCTION;
913 * Don't worry about permissions in transacted mode, as we can always write
914 * changes; we just can't always commit them.
916 if(!(This->openFlags & STGM_TRANSACTED)) {
917 /* Can't create a stream on read-only storage */
918 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
919 return STG_E_ACCESSDENIED;
921 /* Can't create a stream with greater access than the parent. */
922 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
923 return STG_E_ACCESSDENIED;
926 if(This->openFlags & STGM_SIMPLE)
927 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
929 *ppstm = 0;
931 currentEntryRef = findElement(This,
932 This->storageDirEntry,
933 pwcsName,
934 &currentEntry);
936 if (currentEntryRef != DIRENTRY_NULL)
939 * An element with this name already exists
941 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
943 IStorage_DestroyElement(iface, pwcsName);
945 else
946 return STG_E_FILEALREADYEXISTS;
950 * memset the empty entry
952 memset(&newStreamEntry, 0, sizeof(DirEntry));
954 newStreamEntry.sizeOfNameString =
955 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
957 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
958 return STG_E_INVALIDNAME;
960 strcpyW(newStreamEntry.name, pwcsName);
962 newStreamEntry.stgType = STGTY_STREAM;
963 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
964 newStreamEntry.size.u.LowPart = 0;
965 newStreamEntry.size.u.HighPart = 0;
967 newStreamEntry.leftChild = DIRENTRY_NULL;
968 newStreamEntry.rightChild = DIRENTRY_NULL;
969 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
971 /* call CoFileTime to get the current time
972 newStreamEntry.ctime
973 newStreamEntry.mtime
976 /* newStreamEntry.clsid */
979 * Create an entry with the new data
981 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
982 if (FAILED(hr))
983 return hr;
986 * Insert the new entry in the parent storage's tree.
988 hr = insertIntoTree(
989 This,
990 This->storageDirEntry,
991 newStreamEntryRef);
992 if (FAILED(hr))
994 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
995 return hr;
999 * Open the stream to return it.
1001 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1003 if (newStream != 0)
1005 *ppstm = (IStream*)newStream;
1007 IStream_AddRef(*ppstm);
1009 else
1011 return STG_E_INSUFFICIENTMEMORY;
1014 return StorageBaseImpl_Flush(This);
1017 /************************************************************************
1018 * Storage32BaseImpl_SetClass (IStorage)
1020 * This method will write the specified CLSID in the directory entry of this
1021 * storage.
1023 * See Windows documentation for more details on IStorage methods.
1025 static HRESULT WINAPI StorageBaseImpl_SetClass(
1026 IStorage* iface,
1027 REFCLSID clsid) /* [in] */
1029 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1030 HRESULT hRes;
1031 DirEntry currentEntry;
1033 TRACE("(%p, %p)\n", iface, clsid);
1035 if (This->reverted)
1036 return STG_E_REVERTED;
1038 hRes = StorageBaseImpl_ReadDirEntry(This,
1039 This->storageDirEntry,
1040 &currentEntry);
1041 if (SUCCEEDED(hRes))
1043 currentEntry.clsid = *clsid;
1045 hRes = StorageBaseImpl_WriteDirEntry(This,
1046 This->storageDirEntry,
1047 &currentEntry);
1050 if (SUCCEEDED(hRes))
1051 hRes = StorageBaseImpl_Flush(This);
1053 return hRes;
1056 /************************************************************************
1057 ** Storage32Impl implementation
1060 /************************************************************************
1061 * Storage32BaseImpl_CreateStorage (IStorage)
1063 * This method will create the storage object within the provided storage.
1065 * See Windows documentation for more details on IStorage methods.
1067 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1068 IStorage* iface,
1069 const OLECHAR *pwcsName, /* [string][in] */
1070 DWORD grfMode, /* [in] */
1071 DWORD reserved1, /* [in] */
1072 DWORD reserved2, /* [in] */
1073 IStorage **ppstg) /* [out] */
1075 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1077 DirEntry currentEntry;
1078 DirEntry newEntry;
1079 DirRef currentEntryRef;
1080 DirRef newEntryRef;
1081 HRESULT hr;
1083 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1084 iface, debugstr_w(pwcsName), grfMode,
1085 reserved1, reserved2, ppstg);
1087 if (ppstg == 0)
1088 return STG_E_INVALIDPOINTER;
1090 if (This->openFlags & STGM_SIMPLE)
1092 return STG_E_INVALIDFUNCTION;
1095 if (pwcsName == 0)
1096 return STG_E_INVALIDNAME;
1098 *ppstg = NULL;
1100 if ( FAILED( validateSTGM(grfMode) ) ||
1101 (grfMode & STGM_DELETEONRELEASE) )
1103 WARN("bad grfMode: 0x%x\n", grfMode);
1104 return STG_E_INVALIDFLAG;
1107 if (This->reverted)
1108 return STG_E_REVERTED;
1111 * Check that we're compatible with the parent's storage mode
1113 if ( !(This->openFlags & STGM_TRANSACTED) &&
1114 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1116 WARN("access denied\n");
1117 return STG_E_ACCESSDENIED;
1120 currentEntryRef = findElement(This,
1121 This->storageDirEntry,
1122 pwcsName,
1123 &currentEntry);
1125 if (currentEntryRef != DIRENTRY_NULL)
1128 * An element with this name already exists
1130 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1131 ((This->openFlags & STGM_TRANSACTED) ||
1132 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1134 hr = IStorage_DestroyElement(iface, pwcsName);
1135 if (FAILED(hr))
1136 return hr;
1138 else
1140 WARN("file already exists\n");
1141 return STG_E_FILEALREADYEXISTS;
1144 else if (!(This->openFlags & STGM_TRANSACTED) &&
1145 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1147 WARN("read-only storage\n");
1148 return STG_E_ACCESSDENIED;
1151 memset(&newEntry, 0, sizeof(DirEntry));
1153 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1155 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1157 FIXME("name too long\n");
1158 return STG_E_INVALIDNAME;
1161 strcpyW(newEntry.name, pwcsName);
1163 newEntry.stgType = STGTY_STORAGE;
1164 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1165 newEntry.size.u.LowPart = 0;
1166 newEntry.size.u.HighPart = 0;
1168 newEntry.leftChild = DIRENTRY_NULL;
1169 newEntry.rightChild = DIRENTRY_NULL;
1170 newEntry.dirRootEntry = DIRENTRY_NULL;
1172 /* call CoFileTime to get the current time
1173 newEntry.ctime
1174 newEntry.mtime
1177 /* newEntry.clsid */
1180 * Create a new directory entry for the storage
1182 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1183 if (FAILED(hr))
1184 return hr;
1187 * Insert the new directory entry into the parent storage's tree
1189 hr = insertIntoTree(
1190 This,
1191 This->storageDirEntry,
1192 newEntryRef);
1193 if (FAILED(hr))
1195 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1196 return hr;
1200 * Open it to get a pointer to return.
1202 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1204 if( (hr != S_OK) || (*ppstg == NULL))
1206 return hr;
1209 if (SUCCEEDED(hr))
1210 hr = StorageBaseImpl_Flush(This);
1212 return S_OK;
1216 /***************************************************************************
1218 * Internal Method
1220 * Reserve a directory entry in the file and initialize it.
1222 static HRESULT StorageImpl_CreateDirEntry(
1223 StorageBaseImpl *base,
1224 const DirEntry *newData,
1225 DirRef *index)
1227 StorageImpl *storage = (StorageImpl*)base;
1228 ULONG currentEntryIndex = 0;
1229 ULONG newEntryIndex = DIRENTRY_NULL;
1230 HRESULT hr = S_OK;
1231 BYTE currentData[RAW_DIRENTRY_SIZE];
1232 WORD sizeOfNameString;
1236 hr = StorageImpl_ReadRawDirEntry(storage,
1237 currentEntryIndex,
1238 currentData);
1240 if (SUCCEEDED(hr))
1242 StorageUtl_ReadWord(
1243 currentData,
1244 OFFSET_PS_NAMELENGTH,
1245 &sizeOfNameString);
1247 if (sizeOfNameString == 0)
1250 * The entry exists and is available, we found it.
1252 newEntryIndex = currentEntryIndex;
1255 else
1258 * We exhausted the directory entries, we will create more space below
1260 newEntryIndex = currentEntryIndex;
1262 currentEntryIndex++;
1264 } while (newEntryIndex == DIRENTRY_NULL);
1267 * grow the directory stream
1269 if (FAILED(hr))
1271 BYTE emptyData[RAW_DIRENTRY_SIZE];
1272 ULARGE_INTEGER newSize;
1273 ULONG entryIndex;
1274 ULONG lastEntry = 0;
1275 ULONG blockCount = 0;
1278 * obtain the new count of blocks in the directory stream
1280 blockCount = BlockChainStream_GetCount(
1281 storage->rootBlockChain)+1;
1284 * initialize the size used by the directory stream
1286 newSize.u.HighPart = 0;
1287 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1290 * add a block to the directory stream
1292 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1295 * memset the empty entry in order to initialize the unused newly
1296 * created entries
1298 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1301 * initialize them
1303 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1305 for(
1306 entryIndex = newEntryIndex + 1;
1307 entryIndex < lastEntry;
1308 entryIndex++)
1310 StorageImpl_WriteRawDirEntry(
1311 storage,
1312 entryIndex,
1313 emptyData);
1316 StorageImpl_SaveFileHeader(storage);
1319 UpdateRawDirEntry(currentData, newData);
1321 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1323 if (SUCCEEDED(hr))
1324 *index = newEntryIndex;
1326 return hr;
1329 /***************************************************************************
1331 * Internal Method
1333 * Mark a directory entry in the file as free.
1335 static HRESULT StorageImpl_DestroyDirEntry(
1336 StorageBaseImpl *base,
1337 DirRef index)
1339 HRESULT hr;
1340 BYTE emptyData[RAW_DIRENTRY_SIZE];
1341 StorageImpl *storage = (StorageImpl*)base;
1343 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1345 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1347 return hr;
1351 /****************************************************************************
1353 * Internal Method
1355 * Case insensitive comparison of DirEntry.name by first considering
1356 * their size.
1358 * Returns <0 when name1 < name2
1359 * >0 when name1 > name2
1360 * 0 when name1 == name2
1362 static LONG entryNameCmp(
1363 const OLECHAR *name1,
1364 const OLECHAR *name2)
1366 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1368 while (diff == 0 && *name1 != 0)
1371 * We compare the string themselves only when they are of the same length
1373 diff = toupperW(*name1++) - toupperW(*name2++);
1376 return diff;
1379 /****************************************************************************
1381 * Internal Method
1383 * Add a directory entry to a storage
1385 static HRESULT insertIntoTree(
1386 StorageBaseImpl *This,
1387 DirRef parentStorageIndex,
1388 DirRef newEntryIndex)
1390 DirEntry currentEntry;
1391 DirEntry newEntry;
1394 * Read the inserted entry
1396 StorageBaseImpl_ReadDirEntry(This,
1397 newEntryIndex,
1398 &newEntry);
1401 * Read the storage entry
1403 StorageBaseImpl_ReadDirEntry(This,
1404 parentStorageIndex,
1405 &currentEntry);
1407 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1410 * The root storage contains some element, therefore, start the research
1411 * for the appropriate location.
1413 BOOL found = 0;
1414 DirRef current, next, previous, currentEntryId;
1417 * Keep a reference to the root of the storage's element tree
1419 currentEntryId = currentEntry.dirRootEntry;
1422 * Read
1424 StorageBaseImpl_ReadDirEntry(This,
1425 currentEntry.dirRootEntry,
1426 &currentEntry);
1428 previous = currentEntry.leftChild;
1429 next = currentEntry.rightChild;
1430 current = currentEntryId;
1432 while (found == 0)
1434 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1436 if (diff < 0)
1438 if (previous != DIRENTRY_NULL)
1440 StorageBaseImpl_ReadDirEntry(This,
1441 previous,
1442 &currentEntry);
1443 current = previous;
1445 else
1447 currentEntry.leftChild = newEntryIndex;
1448 StorageBaseImpl_WriteDirEntry(This,
1449 current,
1450 &currentEntry);
1451 found = 1;
1454 else if (diff > 0)
1456 if (next != DIRENTRY_NULL)
1458 StorageBaseImpl_ReadDirEntry(This,
1459 next,
1460 &currentEntry);
1461 current = next;
1463 else
1465 currentEntry.rightChild = newEntryIndex;
1466 StorageBaseImpl_WriteDirEntry(This,
1467 current,
1468 &currentEntry);
1469 found = 1;
1472 else
1475 * Trying to insert an item with the same name in the
1476 * subtree structure.
1478 return STG_E_FILEALREADYEXISTS;
1481 previous = currentEntry.leftChild;
1482 next = currentEntry.rightChild;
1485 else
1488 * The storage is empty, make the new entry the root of its element tree
1490 currentEntry.dirRootEntry = newEntryIndex;
1491 StorageBaseImpl_WriteDirEntry(This,
1492 parentStorageIndex,
1493 &currentEntry);
1496 return S_OK;
1499 /****************************************************************************
1501 * Internal Method
1503 * Find and read the element of a storage with the given name.
1505 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1506 const OLECHAR *name, DirEntry *data)
1508 DirRef currentEntry;
1510 /* Read the storage entry to find the root of the tree. */
1511 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1513 currentEntry = data->dirRootEntry;
1515 while (currentEntry != DIRENTRY_NULL)
1517 LONG cmp;
1519 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1521 cmp = entryNameCmp(name, data->name);
1523 if (cmp == 0)
1524 /* found it */
1525 break;
1527 else if (cmp < 0)
1528 currentEntry = data->leftChild;
1530 else if (cmp > 0)
1531 currentEntry = data->rightChild;
1534 return currentEntry;
1537 /****************************************************************************
1539 * Internal Method
1541 * Find and read the binary tree parent of the element with the given name.
1543 * If there is no such element, find a place where it could be inserted and
1544 * return STG_E_FILENOTFOUND.
1546 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1547 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1548 ULONG *relation)
1550 DirRef childEntry;
1551 DirEntry childData;
1553 /* Read the storage entry to find the root of the tree. */
1554 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1556 *parentEntry = storageEntry;
1557 *relation = DIRENTRY_RELATION_DIR;
1559 childEntry = parentData->dirRootEntry;
1561 while (childEntry != DIRENTRY_NULL)
1563 LONG cmp;
1565 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1567 cmp = entryNameCmp(childName, childData.name);
1569 if (cmp == 0)
1570 /* found it */
1571 break;
1573 else if (cmp < 0)
1575 *parentData = childData;
1576 *parentEntry = childEntry;
1577 *relation = DIRENTRY_RELATION_PREVIOUS;
1579 childEntry = parentData->leftChild;
1582 else if (cmp > 0)
1584 *parentData = childData;
1585 *parentEntry = childEntry;
1586 *relation = DIRENTRY_RELATION_NEXT;
1588 childEntry = parentData->rightChild;
1592 if (childEntry == DIRENTRY_NULL)
1593 return STG_E_FILENOTFOUND;
1594 else
1595 return S_OK;
1599 /*************************************************************************
1600 * CopyTo (IStorage)
1602 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1603 IStorage* iface,
1604 DWORD ciidExclude, /* [in] */
1605 const IID* rgiidExclude, /* [size_is][unique][in] */
1606 SNB snbExclude, /* [unique][in] */
1607 IStorage* pstgDest) /* [unique][in] */
1609 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1611 IEnumSTATSTG *elements = 0;
1612 STATSTG curElement, strStat;
1613 HRESULT hr;
1614 IStorage *pstgTmp, *pstgChild;
1615 IStream *pstrTmp, *pstrChild;
1616 DirRef srcEntryRef;
1617 DirEntry srcEntry;
1618 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1619 int i;
1621 TRACE("(%p, %d, %p, %p, %p)\n",
1622 iface, ciidExclude, rgiidExclude,
1623 snbExclude, pstgDest);
1625 if ( pstgDest == 0 )
1626 return STG_E_INVALIDPOINTER;
1629 * Enumerate the elements
1631 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1633 if ( hr != S_OK )
1634 return hr;
1637 * set the class ID
1639 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1640 IStorage_SetClass( pstgDest, &curElement.clsid );
1642 for(i = 0; i < ciidExclude; ++i)
1644 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1645 skip_storage = TRUE;
1646 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1647 skip_stream = TRUE;
1648 else
1649 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1655 * Obtain the next element
1657 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1659 if ( hr == S_FALSE )
1661 hr = S_OK; /* done, every element has been copied */
1662 break;
1665 if ( snbExclude )
1667 WCHAR **snb = snbExclude;
1668 skip = FALSE;
1669 while ( *snb != NULL && !skip )
1671 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1672 skip = TRUE;
1673 ++snb;
1677 if ( skip )
1678 goto cleanup;
1680 if (curElement.type == STGTY_STORAGE)
1682 if(skip_storage)
1683 goto cleanup;
1686 * open child source storage
1688 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1689 STGM_READ|STGM_SHARE_EXCLUSIVE,
1690 NULL, 0, &pstgChild );
1692 if (hr != S_OK)
1693 goto cleanup;
1696 * create a new storage in destination storage
1698 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1699 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1700 0, 0,
1701 &pstgTmp );
1703 * if it already exist, don't create a new one use this one
1705 if (hr == STG_E_FILEALREADYEXISTS)
1707 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1708 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1709 NULL, 0, &pstgTmp );
1712 if (hr == S_OK)
1715 * do the copy recursively
1717 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1718 NULL, pstgTmp );
1720 IStorage_Release( pstgTmp );
1723 IStorage_Release( pstgChild );
1725 else if (curElement.type == STGTY_STREAM)
1727 if(skip_stream)
1728 goto cleanup;
1731 * create a new stream in destination storage. If the stream already
1732 * exist, it will be deleted and a new one will be created.
1734 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1735 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1736 0, 0, &pstrTmp );
1738 if (hr != S_OK)
1739 goto cleanup;
1742 * open child stream storage. This operation must succeed even if the
1743 * stream is already open, so we use internal functions to do it.
1745 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1746 &srcEntry);
1747 if (!srcEntryRef)
1749 ERR("source stream not found\n");
1750 hr = STG_E_DOCFILECORRUPT;
1753 if (hr == S_OK)
1755 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1756 if (pstrChild)
1757 IStream_AddRef(pstrChild);
1758 else
1759 hr = E_OUTOFMEMORY;
1762 if (hr == S_OK)
1765 * Get the size of the source stream
1767 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1770 * Set the size of the destination stream.
1772 IStream_SetSize(pstrTmp, strStat.cbSize);
1775 * do the copy
1777 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1778 NULL, NULL );
1780 IStream_Release( pstrChild );
1783 IStream_Release( pstrTmp );
1785 else
1787 WARN("unknown element type: %d\n", curElement.type);
1790 cleanup:
1791 CoTaskMemFree(curElement.pwcsName);
1792 } while (hr == S_OK);
1795 * Clean-up
1797 IEnumSTATSTG_Release(elements);
1799 return hr;
1802 /*************************************************************************
1803 * MoveElementTo (IStorage)
1805 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1806 IStorage* iface,
1807 const OLECHAR *pwcsName, /* [string][in] */
1808 IStorage *pstgDest, /* [unique][in] */
1809 const OLECHAR *pwcsNewName,/* [string][in] */
1810 DWORD grfFlags) /* [in] */
1812 FIXME("(%p %s %p %s %u): stub\n", iface,
1813 debugstr_w(pwcsName), pstgDest,
1814 debugstr_w(pwcsNewName), grfFlags);
1815 return E_NOTIMPL;
1818 /*************************************************************************
1819 * Commit (IStorage)
1821 * Ensures that any changes made to a storage object open in transacted mode
1822 * are reflected in the parent storage
1824 * In a non-transacted mode, this ensures all cached writes are completed.
1826 static HRESULT WINAPI StorageImpl_Commit(
1827 IStorage* iface,
1828 DWORD grfCommitFlags)/* [in] */
1830 StorageBaseImpl* const base=(StorageBaseImpl*)iface;
1831 TRACE("(%p %d)\n", iface, grfCommitFlags);
1832 return StorageBaseImpl_Flush(base);
1835 /*************************************************************************
1836 * Revert (IStorage)
1838 * Discard all changes that have been made since the last commit operation
1840 static HRESULT WINAPI StorageImpl_Revert(
1841 IStorage* iface)
1843 TRACE("(%p)\n", iface);
1844 return S_OK;
1847 /*************************************************************************
1848 * DestroyElement (IStorage)
1850 * Strategy: This implementation is built this way for simplicity not for speed.
1851 * I always delete the topmost element of the enumeration and adjust
1852 * the deleted element pointer all the time. This takes longer to
1853 * do but allow to reinvoke DestroyElement whenever we encounter a
1854 * storage object. The optimisation resides in the usage of another
1855 * enumeration strategy that would give all the leaves of a storage
1856 * first. (postfix order)
1858 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1859 IStorage* iface,
1860 const OLECHAR *pwcsName)/* [string][in] */
1862 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1864 HRESULT hr = S_OK;
1865 DirEntry entryToDelete;
1866 DirRef entryToDeleteRef;
1868 TRACE("(%p, %s)\n",
1869 iface, debugstr_w(pwcsName));
1871 if (pwcsName==NULL)
1872 return STG_E_INVALIDPOINTER;
1874 if (This->reverted)
1875 return STG_E_REVERTED;
1877 if ( !(This->openFlags & STGM_TRANSACTED) &&
1878 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1879 return STG_E_ACCESSDENIED;
1881 entryToDeleteRef = findElement(
1882 This,
1883 This->storageDirEntry,
1884 pwcsName,
1885 &entryToDelete);
1887 if ( entryToDeleteRef == DIRENTRY_NULL )
1889 return STG_E_FILENOTFOUND;
1892 if ( entryToDelete.stgType == STGTY_STORAGE )
1894 hr = deleteStorageContents(
1895 This,
1896 entryToDeleteRef,
1897 entryToDelete);
1899 else if ( entryToDelete.stgType == STGTY_STREAM )
1901 hr = deleteStreamContents(
1902 This,
1903 entryToDeleteRef,
1904 entryToDelete);
1907 if (hr!=S_OK)
1908 return hr;
1911 * Remove the entry from its parent storage
1913 hr = removeFromTree(
1914 This,
1915 This->storageDirEntry,
1916 entryToDeleteRef);
1919 * Invalidate the entry
1921 if (SUCCEEDED(hr))
1922 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1924 if (SUCCEEDED(hr))
1925 hr = StorageBaseImpl_Flush(This);
1927 return hr;
1931 /******************************************************************************
1932 * Internal stream list handlers
1935 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1937 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1938 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1941 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1943 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1944 list_remove(&(strm->StrmListEntry));
1947 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1949 StgStreamImpl *strm;
1951 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1953 if (strm->dirEntry == streamEntry)
1955 return TRUE;
1959 return FALSE;
1962 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1964 StorageInternalImpl *childstg;
1966 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1968 if (childstg->base.storageDirEntry == storageEntry)
1970 return TRUE;
1974 return FALSE;
1977 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1979 struct list *cur, *cur2;
1980 StgStreamImpl *strm=NULL;
1981 StorageInternalImpl *childstg=NULL;
1983 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1984 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1985 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1986 strm->parentStorage = NULL;
1987 list_remove(cur);
1990 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1991 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1992 StorageBaseImpl_Invalidate( &childstg->base );
1995 if (stg->transactedChild)
1997 StorageBaseImpl_Invalidate(stg->transactedChild);
1999 stg->transactedChild = NULL;
2004 /*********************************************************************
2006 * Internal Method
2008 * Delete the contents of a storage entry.
2011 static HRESULT deleteStorageContents(
2012 StorageBaseImpl *parentStorage,
2013 DirRef indexToDelete,
2014 DirEntry entryDataToDelete)
2016 IEnumSTATSTG *elements = 0;
2017 IStorage *childStorage = 0;
2018 STATSTG currentElement;
2019 HRESULT hr;
2020 HRESULT destroyHr = S_OK;
2021 StorageInternalImpl *stg, *stg2;
2023 /* Invalidate any open storage objects. */
2024 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2026 if (stg->base.storageDirEntry == indexToDelete)
2028 StorageBaseImpl_Invalidate(&stg->base);
2033 * Open the storage and enumerate it
2035 hr = StorageBaseImpl_OpenStorage(
2036 (IStorage*)parentStorage,
2037 entryDataToDelete.name,
2039 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2042 &childStorage);
2044 if (hr != S_OK)
2046 return hr;
2050 * Enumerate the elements
2052 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2057 * Obtain the next element
2059 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2060 if (hr==S_OK)
2062 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2064 CoTaskMemFree(currentElement.pwcsName);
2068 * We need to Reset the enumeration every time because we delete elements
2069 * and the enumeration could be invalid
2071 IEnumSTATSTG_Reset(elements);
2073 } while ((hr == S_OK) && (destroyHr == S_OK));
2075 IStorage_Release(childStorage);
2076 IEnumSTATSTG_Release(elements);
2078 return destroyHr;
2081 /*********************************************************************
2083 * Internal Method
2085 * Perform the deletion of a stream's data
2088 static HRESULT deleteStreamContents(
2089 StorageBaseImpl *parentStorage,
2090 DirRef indexToDelete,
2091 DirEntry entryDataToDelete)
2093 IStream *pis;
2094 HRESULT hr;
2095 ULARGE_INTEGER size;
2096 StgStreamImpl *strm, *strm2;
2098 /* Invalidate any open stream objects. */
2099 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2101 if (strm->dirEntry == indexToDelete)
2103 TRACE("Stream deleted %p\n", strm);
2104 strm->parentStorage = NULL;
2105 list_remove(&strm->StrmListEntry);
2109 size.u.HighPart = 0;
2110 size.u.LowPart = 0;
2112 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2113 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2115 if (hr!=S_OK)
2117 return(hr);
2121 * Zap the stream
2123 hr = IStream_SetSize(pis, size);
2125 if(hr != S_OK)
2127 return hr;
2131 * Release the stream object.
2133 IStream_Release(pis);
2135 return S_OK;
2138 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2140 switch (relation)
2142 case DIRENTRY_RELATION_PREVIOUS:
2143 entry->leftChild = new_target;
2144 break;
2145 case DIRENTRY_RELATION_NEXT:
2146 entry->rightChild = new_target;
2147 break;
2148 case DIRENTRY_RELATION_DIR:
2149 entry->dirRootEntry = new_target;
2150 break;
2151 default:
2152 assert(0);
2156 /*************************************************************************
2158 * Internal Method
2160 * This method removes a directory entry from its parent storage tree without
2161 * freeing any resources attached to it.
2163 static HRESULT removeFromTree(
2164 StorageBaseImpl *This,
2165 DirRef parentStorageIndex,
2166 DirRef deletedIndex)
2168 HRESULT hr = S_OK;
2169 DirEntry entryToDelete;
2170 DirEntry parentEntry;
2171 DirRef parentEntryRef;
2172 ULONG typeOfRelation;
2174 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2176 if (hr != S_OK)
2177 return hr;
2180 * Find the element that links to the one we want to delete.
2182 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2183 &parentEntry, &parentEntryRef, &typeOfRelation);
2185 if (hr != S_OK)
2186 return hr;
2188 if (entryToDelete.leftChild != DIRENTRY_NULL)
2191 * Replace the deleted entry with its left child
2193 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2195 hr = StorageBaseImpl_WriteDirEntry(
2196 This,
2197 parentEntryRef,
2198 &parentEntry);
2199 if(FAILED(hr))
2201 return hr;
2204 if (entryToDelete.rightChild != DIRENTRY_NULL)
2207 * We need to reinsert the right child somewhere. We already know it and
2208 * its children are greater than everything in the left tree, so we
2209 * insert it at the rightmost point in the left tree.
2211 DirRef newRightChildParent = entryToDelete.leftChild;
2212 DirEntry newRightChildParentEntry;
2216 hr = StorageBaseImpl_ReadDirEntry(
2217 This,
2218 newRightChildParent,
2219 &newRightChildParentEntry);
2220 if (FAILED(hr))
2222 return hr;
2225 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2226 newRightChildParent = newRightChildParentEntry.rightChild;
2227 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2229 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2231 hr = StorageBaseImpl_WriteDirEntry(
2232 This,
2233 newRightChildParent,
2234 &newRightChildParentEntry);
2235 if (FAILED(hr))
2237 return hr;
2241 else
2244 * Replace the deleted entry with its right child
2246 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2248 hr = StorageBaseImpl_WriteDirEntry(
2249 This,
2250 parentEntryRef,
2251 &parentEntry);
2252 if(FAILED(hr))
2254 return hr;
2258 return hr;
2262 /******************************************************************************
2263 * SetElementTimes (IStorage)
2265 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2266 IStorage* iface,
2267 const OLECHAR *pwcsName,/* [string][in] */
2268 const FILETIME *pctime, /* [in] */
2269 const FILETIME *patime, /* [in] */
2270 const FILETIME *pmtime) /* [in] */
2272 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2273 return S_OK;
2276 /******************************************************************************
2277 * SetStateBits (IStorage)
2279 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2280 IStorage* iface,
2281 DWORD grfStateBits,/* [in] */
2282 DWORD grfMask) /* [in] */
2284 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2286 if (This->reverted)
2287 return STG_E_REVERTED;
2289 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2290 return S_OK;
2293 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2294 DirRef index, const DirEntry *data)
2296 StorageImpl *This = (StorageImpl*)base;
2297 return StorageImpl_WriteDirEntry(This, index, data);
2300 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2301 DirRef index, DirEntry *data)
2303 StorageImpl *This = (StorageImpl*)base;
2304 return StorageImpl_ReadDirEntry(This, index, data);
2307 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2309 int i;
2311 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2313 if (!This->blockChainCache[i])
2315 return &This->blockChainCache[i];
2319 i = This->blockChainToEvict;
2321 BlockChainStream_Destroy(This->blockChainCache[i]);
2322 This->blockChainCache[i] = NULL;
2324 This->blockChainToEvict++;
2325 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2326 This->blockChainToEvict = 0;
2328 return &This->blockChainCache[i];
2331 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2332 DirRef index)
2334 int i, free_index=-1;
2336 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2338 if (!This->blockChainCache[i])
2340 if (free_index == -1) free_index = i;
2342 else if (This->blockChainCache[i]->ownerDirEntry == index)
2344 return &This->blockChainCache[i];
2348 if (free_index == -1)
2350 free_index = This->blockChainToEvict;
2352 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2353 This->blockChainCache[free_index] = NULL;
2355 This->blockChainToEvict++;
2356 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2357 This->blockChainToEvict = 0;
2360 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2361 return &This->blockChainCache[free_index];
2364 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2366 int i;
2368 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2370 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2372 BlockChainStream_Destroy(This->blockChainCache[i]);
2373 This->blockChainCache[i] = NULL;
2374 return;
2379 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2380 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2382 StorageImpl *This = (StorageImpl*)base;
2383 DirEntry data;
2384 HRESULT hr;
2385 ULONG bytesToRead;
2387 hr = StorageImpl_ReadDirEntry(This, index, &data);
2388 if (FAILED(hr)) return hr;
2390 if (data.size.QuadPart == 0)
2392 *bytesRead = 0;
2393 return S_OK;
2396 if (offset.QuadPart + size > data.size.QuadPart)
2398 bytesToRead = data.size.QuadPart - offset.QuadPart;
2400 else
2402 bytesToRead = size;
2405 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2407 SmallBlockChainStream *stream;
2409 stream = SmallBlockChainStream_Construct(This, NULL, index);
2410 if (!stream) return E_OUTOFMEMORY;
2412 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2414 SmallBlockChainStream_Destroy(stream);
2416 return hr;
2418 else
2420 BlockChainStream *stream = NULL;
2422 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2423 if (!stream) return E_OUTOFMEMORY;
2425 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2427 return hr;
2431 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2432 ULARGE_INTEGER newsize)
2434 StorageImpl *This = (StorageImpl*)base;
2435 DirEntry data;
2436 HRESULT hr;
2437 SmallBlockChainStream *smallblock=NULL;
2438 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2440 hr = StorageImpl_ReadDirEntry(This, index, &data);
2441 if (FAILED(hr)) return hr;
2443 /* In simple mode keep the stream size above the small block limit */
2444 if (This->base.openFlags & STGM_SIMPLE)
2445 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2447 if (data.size.QuadPart == newsize.QuadPart)
2448 return S_OK;
2450 /* Create a block chain object of the appropriate type */
2451 if (data.size.QuadPart == 0)
2453 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2455 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2456 if (!smallblock) return E_OUTOFMEMORY;
2458 else
2460 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2461 bigblock = *pbigblock;
2462 if (!bigblock) return E_OUTOFMEMORY;
2465 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2467 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2468 if (!smallblock) return E_OUTOFMEMORY;
2470 else
2472 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2473 bigblock = *pbigblock;
2474 if (!bigblock) return E_OUTOFMEMORY;
2477 /* Change the block chain type if necessary. */
2478 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2480 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2481 if (!bigblock)
2483 SmallBlockChainStream_Destroy(smallblock);
2484 return E_FAIL;
2487 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2488 *pbigblock = bigblock;
2490 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2492 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2493 if (!smallblock)
2494 return E_FAIL;
2497 /* Set the size of the block chain. */
2498 if (smallblock)
2500 SmallBlockChainStream_SetSize(smallblock, newsize);
2501 SmallBlockChainStream_Destroy(smallblock);
2503 else
2505 BlockChainStream_SetSize(bigblock, newsize);
2508 /* Set the size in the directory entry. */
2509 hr = StorageImpl_ReadDirEntry(This, index, &data);
2510 if (SUCCEEDED(hr))
2512 data.size = newsize;
2514 hr = StorageImpl_WriteDirEntry(This, index, &data);
2516 return hr;
2519 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2520 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2522 StorageImpl *This = (StorageImpl*)base;
2523 DirEntry data;
2524 HRESULT hr;
2525 ULARGE_INTEGER newSize;
2527 hr = StorageImpl_ReadDirEntry(This, index, &data);
2528 if (FAILED(hr)) return hr;
2530 /* Grow the stream if necessary */
2531 newSize.QuadPart = 0;
2532 newSize.QuadPart = offset.QuadPart + size;
2534 if (newSize.QuadPart > data.size.QuadPart)
2536 hr = StorageImpl_StreamSetSize(base, index, newSize);
2537 if (FAILED(hr))
2538 return hr;
2540 hr = StorageImpl_ReadDirEntry(This, index, &data);
2541 if (FAILED(hr)) return hr;
2544 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2546 SmallBlockChainStream *stream;
2548 stream = SmallBlockChainStream_Construct(This, NULL, index);
2549 if (!stream) return E_OUTOFMEMORY;
2551 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2553 SmallBlockChainStream_Destroy(stream);
2555 return hr;
2557 else
2559 BlockChainStream *stream;
2561 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2562 if (!stream) return E_OUTOFMEMORY;
2564 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2566 return hr;
2570 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2571 DirRef src)
2573 StorageImpl *This = (StorageImpl*)base;
2574 DirEntry dst_data, src_data;
2575 HRESULT hr;
2577 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2579 if (SUCCEEDED(hr))
2580 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2582 if (SUCCEEDED(hr))
2584 StorageImpl_DeleteCachedBlockChainStream(This, src);
2585 dst_data.startingBlock = src_data.startingBlock;
2586 dst_data.size = src_data.size;
2588 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2591 return hr;
2594 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2596 StorageImpl *This = (StorageImpl*) iface;
2597 STATSTG statstg;
2598 HRESULT hr;
2600 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2602 *result = statstg.pwcsName;
2604 return hr;
2608 * Virtual function table for the IStorage32Impl class.
2610 static const IStorageVtbl Storage32Impl_Vtbl =
2612 StorageBaseImpl_QueryInterface,
2613 StorageBaseImpl_AddRef,
2614 StorageBaseImpl_Release,
2615 StorageBaseImpl_CreateStream,
2616 StorageBaseImpl_OpenStream,
2617 StorageBaseImpl_CreateStorage,
2618 StorageBaseImpl_OpenStorage,
2619 StorageBaseImpl_CopyTo,
2620 StorageBaseImpl_MoveElementTo,
2621 StorageImpl_Commit,
2622 StorageImpl_Revert,
2623 StorageBaseImpl_EnumElements,
2624 StorageBaseImpl_DestroyElement,
2625 StorageBaseImpl_RenameElement,
2626 StorageBaseImpl_SetElementTimes,
2627 StorageBaseImpl_SetClass,
2628 StorageBaseImpl_SetStateBits,
2629 StorageBaseImpl_Stat
2632 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2634 StorageImpl_Destroy,
2635 StorageImpl_Invalidate,
2636 StorageImpl_Flush,
2637 StorageImpl_GetFilename,
2638 StorageImpl_CreateDirEntry,
2639 StorageImpl_BaseWriteDirEntry,
2640 StorageImpl_BaseReadDirEntry,
2641 StorageImpl_DestroyDirEntry,
2642 StorageImpl_StreamReadAt,
2643 StorageImpl_StreamWriteAt,
2644 StorageImpl_StreamSetSize,
2645 StorageImpl_StreamLink
2648 static HRESULT StorageImpl_Construct(
2649 HANDLE hFile,
2650 LPCOLESTR pwcsName,
2651 ILockBytes* pLkbyt,
2652 DWORD openFlags,
2653 BOOL fileBased,
2654 BOOL create,
2655 ULONG sector_size,
2656 StorageImpl** result)
2658 StorageImpl* This;
2659 HRESULT hr = S_OK;
2660 DirEntry currentEntry;
2661 DirRef currentEntryRef;
2663 if ( FAILED( validateSTGM(openFlags) ))
2664 return STG_E_INVALIDFLAG;
2666 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2667 if (!This)
2668 return E_OUTOFMEMORY;
2670 memset(This, 0, sizeof(StorageImpl));
2672 list_init(&This->base.strmHead);
2674 list_init(&This->base.storageHead);
2676 This->base.lpVtbl = &Storage32Impl_Vtbl;
2677 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2678 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2679 This->base.openFlags = (openFlags & ~STGM_CREATE);
2680 This->base.ref = 1;
2681 This->base.create = create;
2683 This->base.reverted = 0;
2686 * Initialize the big block cache.
2688 This->bigBlockSize = sector_size;
2689 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2690 if (hFile)
2691 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2692 else
2694 This->lockBytes = pLkbyt;
2695 ILockBytes_AddRef(pLkbyt);
2698 if (FAILED(hr))
2699 goto end;
2701 if (create)
2703 ULARGE_INTEGER size;
2704 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2707 * Initialize all header variables:
2708 * - The big block depot consists of one block and it is at block 0
2709 * - The directory table starts at block 1
2710 * - There is no small block depot
2712 memset( This->bigBlockDepotStart,
2713 BLOCK_UNUSED,
2714 sizeof(This->bigBlockDepotStart));
2716 This->bigBlockDepotCount = 1;
2717 This->bigBlockDepotStart[0] = 0;
2718 This->rootStartBlock = 1;
2719 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2720 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2721 if (sector_size == 4096)
2722 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2723 else
2724 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2725 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2726 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2727 This->extBigBlockDepotCount = 0;
2729 StorageImpl_SaveFileHeader(This);
2732 * Add one block for the big block depot and one block for the directory table
2734 size.u.HighPart = 0;
2735 size.u.LowPart = This->bigBlockSize * 3;
2736 ILockBytes_SetSize(This->lockBytes, size);
2739 * Initialize the big block depot
2741 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2742 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2743 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2744 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2746 else
2749 * Load the header for the file.
2751 hr = StorageImpl_LoadFileHeader(This);
2753 if (FAILED(hr))
2755 goto end;
2760 * There is no block depot cached yet.
2762 This->indexBlockDepotCached = 0xFFFFFFFF;
2763 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2766 * Start searching for free blocks with block 0.
2768 This->prevFreeBlock = 0;
2770 This->firstFreeSmallBlock = 0;
2772 /* Read the extended big block depot locations. */
2773 if (This->extBigBlockDepotCount != 0)
2775 ULONG current_block = This->extBigBlockDepotStart;
2776 ULONG cache_size = This->extBigBlockDepotCount * 2;
2777 int i;
2779 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2780 if (!This->extBigBlockDepotLocations)
2782 hr = E_OUTOFMEMORY;
2783 goto end;
2786 This->extBigBlockDepotLocationsSize = cache_size;
2788 for (i=0; i<This->extBigBlockDepotCount; i++)
2790 if (current_block == BLOCK_END_OF_CHAIN)
2792 WARN("File has too few extended big block depot blocks.\n");
2793 hr = STG_E_DOCFILECORRUPT;
2794 goto end;
2796 This->extBigBlockDepotLocations[i] = current_block;
2797 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2800 else
2802 This->extBigBlockDepotLocations = NULL;
2803 This->extBigBlockDepotLocationsSize = 0;
2807 * Create the block chain abstractions.
2809 if(!(This->rootBlockChain =
2810 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2812 hr = STG_E_READFAULT;
2813 goto end;
2816 if(!(This->smallBlockDepotChain =
2817 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2818 DIRENTRY_NULL)))
2820 hr = STG_E_READFAULT;
2821 goto end;
2825 * Write the root storage entry (memory only)
2827 if (create)
2829 DirEntry rootEntry;
2831 * Initialize the directory table
2833 memset(&rootEntry, 0, sizeof(rootEntry));
2834 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2835 sizeof(rootEntry.name)/sizeof(WCHAR) );
2836 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2837 rootEntry.stgType = STGTY_ROOT;
2838 rootEntry.leftChild = DIRENTRY_NULL;
2839 rootEntry.rightChild = DIRENTRY_NULL;
2840 rootEntry.dirRootEntry = DIRENTRY_NULL;
2841 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2842 rootEntry.size.u.HighPart = 0;
2843 rootEntry.size.u.LowPart = 0;
2845 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2849 * Find the ID of the root storage.
2851 currentEntryRef = 0;
2855 hr = StorageImpl_ReadDirEntry(
2856 This,
2857 currentEntryRef,
2858 &currentEntry);
2860 if (SUCCEEDED(hr))
2862 if ( (currentEntry.sizeOfNameString != 0 ) &&
2863 (currentEntry.stgType == STGTY_ROOT) )
2865 This->base.storageDirEntry = currentEntryRef;
2869 currentEntryRef++;
2871 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2873 if (FAILED(hr))
2875 hr = STG_E_READFAULT;
2876 goto end;
2880 * Create the block chain abstraction for the small block root chain.
2882 if(!(This->smallBlockRootChain =
2883 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2885 hr = STG_E_READFAULT;
2888 end:
2889 if (FAILED(hr))
2891 IStorage_Release((IStorage*)This);
2892 *result = NULL;
2894 else
2896 StorageImpl_Flush((StorageBaseImpl*)This);
2897 *result = This;
2900 return hr;
2903 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2905 StorageImpl *This = (StorageImpl*) iface;
2907 StorageBaseImpl_DeleteAll(&This->base);
2909 This->base.reverted = 1;
2912 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2914 StorageImpl *This = (StorageImpl*) iface;
2915 int i;
2916 TRACE("(%p)\n", This);
2918 StorageImpl_Flush(iface);
2920 StorageImpl_Invalidate(iface);
2922 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
2924 BlockChainStream_Destroy(This->smallBlockRootChain);
2925 BlockChainStream_Destroy(This->rootBlockChain);
2926 BlockChainStream_Destroy(This->smallBlockDepotChain);
2928 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2929 BlockChainStream_Destroy(This->blockChainCache[i]);
2931 if (This->lockBytes)
2932 ILockBytes_Release(This->lockBytes);
2933 HeapFree(GetProcessHeap(), 0, This);
2936 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2938 StorageImpl *This = (StorageImpl*) iface;
2939 int i;
2940 HRESULT hr;
2941 TRACE("(%p)\n", This);
2943 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2945 if (SUCCEEDED(hr))
2946 hr = BlockChainStream_Flush(This->rootBlockChain);
2948 if (SUCCEEDED(hr))
2949 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2951 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
2952 if (This->blockChainCache[i])
2953 hr = BlockChainStream_Flush(This->blockChainCache[i]);
2955 if (SUCCEEDED(hr))
2956 hr = ILockBytes_Flush(This->lockBytes);
2958 return hr;
2961 /******************************************************************************
2962 * Storage32Impl_GetNextFreeBigBlock
2964 * Returns the index of the next free big block.
2965 * If the big block depot is filled, this method will enlarge it.
2968 static ULONG StorageImpl_GetNextFreeBigBlock(
2969 StorageImpl* This)
2971 ULONG depotBlockIndexPos;
2972 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2973 BOOL success;
2974 ULONG depotBlockOffset;
2975 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2976 ULONG nextBlockIndex = BLOCK_SPECIAL;
2977 int depotIndex = 0;
2978 ULONG freeBlock = BLOCK_UNUSED;
2979 ULARGE_INTEGER neededSize;
2980 STATSTG statstg;
2982 depotIndex = This->prevFreeBlock / blocksPerDepot;
2983 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2986 * Scan the entire big block depot until we find a block marked free
2988 while (nextBlockIndex != BLOCK_UNUSED)
2990 if (depotIndex < COUNT_BBDEPOTINHEADER)
2992 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2995 * Grow the primary depot.
2997 if (depotBlockIndexPos == BLOCK_UNUSED)
2999 depotBlockIndexPos = depotIndex*blocksPerDepot;
3002 * Add a block depot.
3004 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3005 This->bigBlockDepotCount++;
3006 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3009 * Flag it as a block depot.
3011 StorageImpl_SetNextBlockInChain(This,
3012 depotBlockIndexPos,
3013 BLOCK_SPECIAL);
3015 /* Save new header information.
3017 StorageImpl_SaveFileHeader(This);
3020 else
3022 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3024 if (depotBlockIndexPos == BLOCK_UNUSED)
3027 * Grow the extended depot.
3029 ULONG extIndex = BLOCK_UNUSED;
3030 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3031 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3033 if (extBlockOffset == 0)
3035 /* We need an extended block.
3037 extIndex = Storage32Impl_AddExtBlockDepot(This);
3038 This->extBigBlockDepotCount++;
3039 depotBlockIndexPos = extIndex + 1;
3041 else
3042 depotBlockIndexPos = depotIndex * blocksPerDepot;
3045 * Add a block depot and mark it in the extended block.
3047 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3048 This->bigBlockDepotCount++;
3049 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3051 /* Flag the block depot.
3053 StorageImpl_SetNextBlockInChain(This,
3054 depotBlockIndexPos,
3055 BLOCK_SPECIAL);
3057 /* If necessary, flag the extended depot block.
3059 if (extIndex != BLOCK_UNUSED)
3060 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3062 /* Save header information.
3064 StorageImpl_SaveFileHeader(This);
3068 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3070 if (success)
3072 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3073 ( nextBlockIndex != BLOCK_UNUSED))
3075 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3077 if (nextBlockIndex == BLOCK_UNUSED)
3079 freeBlock = (depotIndex * blocksPerDepot) +
3080 (depotBlockOffset/sizeof(ULONG));
3083 depotBlockOffset += sizeof(ULONG);
3087 depotIndex++;
3088 depotBlockOffset = 0;
3092 * make sure that the block physically exists before using it
3094 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3096 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3098 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3099 ILockBytes_SetSize(This->lockBytes, neededSize);
3101 This->prevFreeBlock = freeBlock;
3103 return freeBlock;
3106 /******************************************************************************
3107 * Storage32Impl_AddBlockDepot
3109 * This will create a depot block, essentially it is a block initialized
3110 * to BLOCK_UNUSEDs.
3112 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3114 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3117 * Initialize blocks as free
3119 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3120 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3123 /******************************************************************************
3124 * Storage32Impl_GetExtDepotBlock
3126 * Returns the index of the block that corresponds to the specified depot
3127 * index. This method is only for depot indexes equal or greater than
3128 * COUNT_BBDEPOTINHEADER.
3130 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3132 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3133 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3134 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3135 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3136 ULONG blockIndex = BLOCK_UNUSED;
3137 ULONG extBlockIndex;
3138 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3139 int index, num_blocks;
3141 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3143 if (extBlockCount >= This->extBigBlockDepotCount)
3144 return BLOCK_UNUSED;
3146 if (This->indexExtBlockDepotCached != extBlockCount)
3148 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3150 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);
3152 num_blocks = This->bigBlockSize / 4;
3154 for (index = 0; index < num_blocks; index++)
3156 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3157 This->extBlockDepotCached[index] = blockIndex;
3160 This->indexExtBlockDepotCached = extBlockCount;
3163 blockIndex = This->extBlockDepotCached[extBlockOffset];
3165 return blockIndex;
3168 /******************************************************************************
3169 * Storage32Impl_SetExtDepotBlock
3171 * Associates the specified block index to the specified depot index.
3172 * This method is only for depot indexes equal or greater than
3173 * COUNT_BBDEPOTINHEADER.
3175 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3177 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3178 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3179 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3180 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3181 ULONG extBlockIndex;
3183 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3185 assert(extBlockCount < This->extBigBlockDepotCount);
3187 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3189 if (extBlockIndex != BLOCK_UNUSED)
3191 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3192 extBlockOffset * sizeof(ULONG),
3193 blockIndex);
3196 if (This->indexExtBlockDepotCached == extBlockCount)
3198 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3202 /******************************************************************************
3203 * Storage32Impl_AddExtBlockDepot
3205 * Creates an extended depot block.
3207 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3209 ULONG numExtBlocks = This->extBigBlockDepotCount;
3210 ULONG nextExtBlock = This->extBigBlockDepotStart;
3211 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3212 ULONG index = BLOCK_UNUSED;
3213 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3214 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3215 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3217 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3218 blocksPerDepotBlock;
3220 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3223 * The first extended block.
3225 This->extBigBlockDepotStart = index;
3227 else
3230 * Find the last existing extended block.
3232 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3235 * Add the new extended block to the chain.
3237 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3238 index);
3242 * Initialize this block.
3244 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3245 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3247 /* Add the block to our cache. */
3248 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3250 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3251 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3253 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3254 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3256 This->extBigBlockDepotLocations = new_cache;
3257 This->extBigBlockDepotLocationsSize = new_cache_size;
3259 This->extBigBlockDepotLocations[numExtBlocks] = index;
3261 return index;
3264 /******************************************************************************
3265 * Storage32Impl_FreeBigBlock
3267 * This method will flag the specified block as free in the big block depot.
3269 static void StorageImpl_FreeBigBlock(
3270 StorageImpl* This,
3271 ULONG blockIndex)
3273 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3275 if (blockIndex < This->prevFreeBlock)
3276 This->prevFreeBlock = blockIndex;
3279 /************************************************************************
3280 * Storage32Impl_GetNextBlockInChain
3282 * This method will retrieve the block index of the next big block in
3283 * in the chain.
3285 * Params: This - Pointer to the Storage object.
3286 * blockIndex - Index of the block to retrieve the chain
3287 * for.
3288 * nextBlockIndex - receives the return value.
3290 * Returns: This method returns the index of the next block in the chain.
3291 * It will return the constants:
3292 * BLOCK_SPECIAL - If the block given was not part of a
3293 * chain.
3294 * BLOCK_END_OF_CHAIN - If the block given was the last in
3295 * a chain.
3296 * BLOCK_UNUSED - If the block given was not past of a chain
3297 * and is available.
3298 * BLOCK_EXTBBDEPOT - This block is part of the extended
3299 * big block depot.
3301 * See Windows documentation for more details on IStorage methods.
3303 static HRESULT StorageImpl_GetNextBlockInChain(
3304 StorageImpl* This,
3305 ULONG blockIndex,
3306 ULONG* nextBlockIndex)
3308 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3309 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3310 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3311 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3312 BOOL success;
3313 ULONG depotBlockIndexPos;
3314 int index, num_blocks;
3316 *nextBlockIndex = BLOCK_SPECIAL;
3318 if(depotBlockCount >= This->bigBlockDepotCount)
3320 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3321 This->bigBlockDepotCount);
3322 return STG_E_READFAULT;
3326 * Cache the currently accessed depot block.
3328 if (depotBlockCount != This->indexBlockDepotCached)
3330 This->indexBlockDepotCached = depotBlockCount;
3332 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3334 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3336 else
3339 * We have to look in the extended depot.
3341 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3344 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3346 if (!success)
3347 return STG_E_READFAULT;
3349 num_blocks = This->bigBlockSize / 4;
3351 for (index = 0; index < num_blocks; index++)
3353 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3354 This->blockDepotCached[index] = *nextBlockIndex;
3358 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3360 return S_OK;
3363 /******************************************************************************
3364 * Storage32Impl_GetNextExtendedBlock
3366 * Given an extended block this method will return the next extended block.
3368 * NOTES:
3369 * The last ULONG of an extended block is the block index of the next
3370 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3371 * depot.
3373 * Return values:
3374 * - The index of the next extended block
3375 * - BLOCK_UNUSED: there is no next extended block.
3376 * - Any other return values denotes failure.
3378 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3380 ULONG nextBlockIndex = BLOCK_SPECIAL;
3381 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3383 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3384 &nextBlockIndex);
3386 return nextBlockIndex;
3389 /******************************************************************************
3390 * Storage32Impl_SetNextBlockInChain
3392 * This method will write the index of the specified block's next block
3393 * in the big block depot.
3395 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3396 * do the following
3398 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3399 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3400 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3403 static void StorageImpl_SetNextBlockInChain(
3404 StorageImpl* This,
3405 ULONG blockIndex,
3406 ULONG nextBlock)
3408 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3409 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3410 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3411 ULONG depotBlockIndexPos;
3413 assert(depotBlockCount < This->bigBlockDepotCount);
3414 assert(blockIndex != nextBlock);
3416 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3418 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3420 else
3423 * We have to look in the extended depot.
3425 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3428 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3429 nextBlock);
3431 * Update the cached block depot, if necessary.
3433 if (depotBlockCount == This->indexBlockDepotCached)
3435 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3439 /******************************************************************************
3440 * Storage32Impl_LoadFileHeader
3442 * This method will read in the file header
3444 static HRESULT StorageImpl_LoadFileHeader(
3445 StorageImpl* This)
3447 HRESULT hr;
3448 BYTE headerBigBlock[HEADER_SIZE];
3449 int index;
3450 ULARGE_INTEGER offset;
3451 DWORD bytes_read;
3453 TRACE("\n");
3455 * Get a pointer to the big block of data containing the header.
3457 offset.u.HighPart = 0;
3458 offset.u.LowPart = 0;
3459 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3460 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3461 hr = STG_E_FILENOTFOUND;
3464 * Extract the information from the header.
3466 if (SUCCEEDED(hr))
3469 * Check for the "magic number" signature and return an error if it is not
3470 * found.
3472 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3474 return STG_E_OLDFORMAT;
3477 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3479 return STG_E_INVALIDHEADER;
3482 StorageUtl_ReadWord(
3483 headerBigBlock,
3484 OFFSET_BIGBLOCKSIZEBITS,
3485 &This->bigBlockSizeBits);
3487 StorageUtl_ReadWord(
3488 headerBigBlock,
3489 OFFSET_SMALLBLOCKSIZEBITS,
3490 &This->smallBlockSizeBits);
3492 StorageUtl_ReadDWord(
3493 headerBigBlock,
3494 OFFSET_BBDEPOTCOUNT,
3495 &This->bigBlockDepotCount);
3497 StorageUtl_ReadDWord(
3498 headerBigBlock,
3499 OFFSET_ROOTSTARTBLOCK,
3500 &This->rootStartBlock);
3502 StorageUtl_ReadDWord(
3503 headerBigBlock,
3504 OFFSET_SMALLBLOCKLIMIT,
3505 &This->smallBlockLimit);
3507 StorageUtl_ReadDWord(
3508 headerBigBlock,
3509 OFFSET_SBDEPOTSTART,
3510 &This->smallBlockDepotStart);
3512 StorageUtl_ReadDWord(
3513 headerBigBlock,
3514 OFFSET_EXTBBDEPOTSTART,
3515 &This->extBigBlockDepotStart);
3517 StorageUtl_ReadDWord(
3518 headerBigBlock,
3519 OFFSET_EXTBBDEPOTCOUNT,
3520 &This->extBigBlockDepotCount);
3522 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3524 StorageUtl_ReadDWord(
3525 headerBigBlock,
3526 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3527 &(This->bigBlockDepotStart[index]));
3531 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3533 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3534 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3537 * Right now, the code is making some assumptions about the size of the
3538 * blocks, just make sure they are what we're expecting.
3540 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3541 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3542 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3544 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3545 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3546 hr = STG_E_INVALIDHEADER;
3548 else
3549 hr = S_OK;
3552 return hr;
3555 /******************************************************************************
3556 * Storage32Impl_SaveFileHeader
3558 * This method will save to the file the header
3560 static void StorageImpl_SaveFileHeader(
3561 StorageImpl* This)
3563 BYTE headerBigBlock[HEADER_SIZE];
3564 int index;
3565 HRESULT hr;
3566 ULARGE_INTEGER offset;
3567 DWORD bytes_read, bytes_written;
3568 DWORD major_version, dirsectorcount;
3571 * Get a pointer to the big block of data containing the header.
3573 offset.u.HighPart = 0;
3574 offset.u.LowPart = 0;
3575 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3576 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3577 hr = STG_E_FILENOTFOUND;
3579 if (This->bigBlockSizeBits == 0x9)
3580 major_version = 3;
3581 else if (This->bigBlockSizeBits == 0xc)
3582 major_version = 4;
3583 else
3585 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3586 major_version = 4;
3590 * If the block read failed, the file is probably new.
3592 if (FAILED(hr))
3595 * Initialize for all unknown fields.
3597 memset(headerBigBlock, 0, HEADER_SIZE);
3600 * Initialize the magic number.
3602 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3606 * Write the information to the header.
3608 StorageUtl_WriteWord(
3609 headerBigBlock,
3610 OFFSET_MINORVERSION,
3611 0x3e);
3613 StorageUtl_WriteWord(
3614 headerBigBlock,
3615 OFFSET_MAJORVERSION,
3616 major_version);
3618 StorageUtl_WriteWord(
3619 headerBigBlock,
3620 OFFSET_BYTEORDERMARKER,
3621 (WORD)-2);
3623 StorageUtl_WriteWord(
3624 headerBigBlock,
3625 OFFSET_BIGBLOCKSIZEBITS,
3626 This->bigBlockSizeBits);
3628 StorageUtl_WriteWord(
3629 headerBigBlock,
3630 OFFSET_SMALLBLOCKSIZEBITS,
3631 This->smallBlockSizeBits);
3633 if (major_version >= 4)
3635 if (This->rootBlockChain)
3636 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3637 else
3638 /* This file is being created, and it will start out with one block. */
3639 dirsectorcount = 1;
3641 else
3642 /* This field must be 0 in versions older than 4 */
3643 dirsectorcount = 0;
3645 StorageUtl_WriteDWord(
3646 headerBigBlock,
3647 OFFSET_DIRSECTORCOUNT,
3648 dirsectorcount);
3650 StorageUtl_WriteDWord(
3651 headerBigBlock,
3652 OFFSET_BBDEPOTCOUNT,
3653 This->bigBlockDepotCount);
3655 StorageUtl_WriteDWord(
3656 headerBigBlock,
3657 OFFSET_ROOTSTARTBLOCK,
3658 This->rootStartBlock);
3660 StorageUtl_WriteDWord(
3661 headerBigBlock,
3662 OFFSET_SMALLBLOCKLIMIT,
3663 This->smallBlockLimit);
3665 StorageUtl_WriteDWord(
3666 headerBigBlock,
3667 OFFSET_SBDEPOTSTART,
3668 This->smallBlockDepotStart);
3670 StorageUtl_WriteDWord(
3671 headerBigBlock,
3672 OFFSET_SBDEPOTCOUNT,
3673 This->smallBlockDepotChain ?
3674 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3676 StorageUtl_WriteDWord(
3677 headerBigBlock,
3678 OFFSET_EXTBBDEPOTSTART,
3679 This->extBigBlockDepotStart);
3681 StorageUtl_WriteDWord(
3682 headerBigBlock,
3683 OFFSET_EXTBBDEPOTCOUNT,
3684 This->extBigBlockDepotCount);
3686 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3688 StorageUtl_WriteDWord(
3689 headerBigBlock,
3690 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3691 (This->bigBlockDepotStart[index]));
3695 * Write the big block back to the file.
3697 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3700 /******************************************************************************
3701 * StorageImpl_ReadRawDirEntry
3703 * This method will read the raw data from a directory entry in the file.
3705 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3707 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3709 ULARGE_INTEGER offset;
3710 HRESULT hr;
3711 ULONG bytesRead;
3713 offset.u.HighPart = 0;
3714 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3716 hr = BlockChainStream_ReadAt(
3717 This->rootBlockChain,
3718 offset,
3719 RAW_DIRENTRY_SIZE,
3720 buffer,
3721 &bytesRead);
3723 if (bytesRead != RAW_DIRENTRY_SIZE)
3724 return STG_E_READFAULT;
3726 return hr;
3729 /******************************************************************************
3730 * StorageImpl_WriteRawDirEntry
3732 * This method will write the raw data from a directory entry in the file.
3734 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3736 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3738 ULARGE_INTEGER offset;
3739 HRESULT hr;
3740 ULONG bytesRead;
3742 offset.u.HighPart = 0;
3743 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3745 hr = BlockChainStream_WriteAt(
3746 This->rootBlockChain,
3747 offset,
3748 RAW_DIRENTRY_SIZE,
3749 buffer,
3750 &bytesRead);
3752 return hr;
3755 /******************************************************************************
3756 * UpdateRawDirEntry
3758 * Update raw directory entry data from the fields in newData.
3760 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3762 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3764 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3766 memcpy(
3767 buffer + OFFSET_PS_NAME,
3768 newData->name,
3769 DIRENTRY_NAME_BUFFER_LEN );
3771 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3773 StorageUtl_WriteWord(
3774 buffer,
3775 OFFSET_PS_NAMELENGTH,
3776 newData->sizeOfNameString);
3778 StorageUtl_WriteDWord(
3779 buffer,
3780 OFFSET_PS_LEFTCHILD,
3781 newData->leftChild);
3783 StorageUtl_WriteDWord(
3784 buffer,
3785 OFFSET_PS_RIGHTCHILD,
3786 newData->rightChild);
3788 StorageUtl_WriteDWord(
3789 buffer,
3790 OFFSET_PS_DIRROOT,
3791 newData->dirRootEntry);
3793 StorageUtl_WriteGUID(
3794 buffer,
3795 OFFSET_PS_GUID,
3796 &newData->clsid);
3798 StorageUtl_WriteDWord(
3799 buffer,
3800 OFFSET_PS_CTIMELOW,
3801 newData->ctime.dwLowDateTime);
3803 StorageUtl_WriteDWord(
3804 buffer,
3805 OFFSET_PS_CTIMEHIGH,
3806 newData->ctime.dwHighDateTime);
3808 StorageUtl_WriteDWord(
3809 buffer,
3810 OFFSET_PS_MTIMELOW,
3811 newData->mtime.dwLowDateTime);
3813 StorageUtl_WriteDWord(
3814 buffer,
3815 OFFSET_PS_MTIMEHIGH,
3816 newData->ctime.dwHighDateTime);
3818 StorageUtl_WriteDWord(
3819 buffer,
3820 OFFSET_PS_STARTBLOCK,
3821 newData->startingBlock);
3823 StorageUtl_WriteDWord(
3824 buffer,
3825 OFFSET_PS_SIZE,
3826 newData->size.u.LowPart);
3829 /******************************************************************************
3830 * Storage32Impl_ReadDirEntry
3832 * This method will read the specified directory entry.
3834 HRESULT StorageImpl_ReadDirEntry(
3835 StorageImpl* This,
3836 DirRef index,
3837 DirEntry* buffer)
3839 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3840 HRESULT readRes;
3842 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3844 if (SUCCEEDED(readRes))
3846 memset(buffer->name, 0, sizeof(buffer->name));
3847 memcpy(
3848 buffer->name,
3849 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3850 DIRENTRY_NAME_BUFFER_LEN );
3851 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3853 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3855 StorageUtl_ReadWord(
3856 currentEntry,
3857 OFFSET_PS_NAMELENGTH,
3858 &buffer->sizeOfNameString);
3860 StorageUtl_ReadDWord(
3861 currentEntry,
3862 OFFSET_PS_LEFTCHILD,
3863 &buffer->leftChild);
3865 StorageUtl_ReadDWord(
3866 currentEntry,
3867 OFFSET_PS_RIGHTCHILD,
3868 &buffer->rightChild);
3870 StorageUtl_ReadDWord(
3871 currentEntry,
3872 OFFSET_PS_DIRROOT,
3873 &buffer->dirRootEntry);
3875 StorageUtl_ReadGUID(
3876 currentEntry,
3877 OFFSET_PS_GUID,
3878 &buffer->clsid);
3880 StorageUtl_ReadDWord(
3881 currentEntry,
3882 OFFSET_PS_CTIMELOW,
3883 &buffer->ctime.dwLowDateTime);
3885 StorageUtl_ReadDWord(
3886 currentEntry,
3887 OFFSET_PS_CTIMEHIGH,
3888 &buffer->ctime.dwHighDateTime);
3890 StorageUtl_ReadDWord(
3891 currentEntry,
3892 OFFSET_PS_MTIMELOW,
3893 &buffer->mtime.dwLowDateTime);
3895 StorageUtl_ReadDWord(
3896 currentEntry,
3897 OFFSET_PS_MTIMEHIGH,
3898 &buffer->mtime.dwHighDateTime);
3900 StorageUtl_ReadDWord(
3901 currentEntry,
3902 OFFSET_PS_STARTBLOCK,
3903 &buffer->startingBlock);
3905 StorageUtl_ReadDWord(
3906 currentEntry,
3907 OFFSET_PS_SIZE,
3908 &buffer->size.u.LowPart);
3910 buffer->size.u.HighPart = 0;
3913 return readRes;
3916 /*********************************************************************
3917 * Write the specified directory entry to the file
3919 HRESULT StorageImpl_WriteDirEntry(
3920 StorageImpl* This,
3921 DirRef index,
3922 const DirEntry* buffer)
3924 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3925 HRESULT writeRes;
3927 UpdateRawDirEntry(currentEntry, buffer);
3929 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3930 return writeRes;
3933 static BOOL StorageImpl_ReadBigBlock(
3934 StorageImpl* This,
3935 ULONG blockIndex,
3936 void* buffer)
3938 ULARGE_INTEGER ulOffset;
3939 DWORD read=0;
3941 ulOffset.u.HighPart = 0;
3942 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3944 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3946 if (read && read < This->bigBlockSize)
3948 /* File ends during this block; fill the rest with 0's. */
3949 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3952 return (read != 0);
3955 static BOOL StorageImpl_ReadDWordFromBigBlock(
3956 StorageImpl* This,
3957 ULONG blockIndex,
3958 ULONG offset,
3959 DWORD* value)
3961 ULARGE_INTEGER ulOffset;
3962 DWORD read;
3963 DWORD tmp;
3965 ulOffset.u.HighPart = 0;
3966 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3967 ulOffset.u.LowPart += offset;
3969 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3970 *value = lendian32toh(tmp);
3971 return (read == sizeof(DWORD));
3974 static BOOL StorageImpl_WriteBigBlock(
3975 StorageImpl* This,
3976 ULONG blockIndex,
3977 const void* buffer)
3979 ULARGE_INTEGER ulOffset;
3980 DWORD wrote;
3982 ulOffset.u.HighPart = 0;
3983 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3985 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3986 return (wrote == This->bigBlockSize);
3989 static BOOL StorageImpl_WriteDWordToBigBlock(
3990 StorageImpl* This,
3991 ULONG blockIndex,
3992 ULONG offset,
3993 DWORD value)
3995 ULARGE_INTEGER ulOffset;
3996 DWORD wrote;
3998 ulOffset.u.HighPart = 0;
3999 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4000 ulOffset.u.LowPart += offset;
4002 value = htole32(value);
4003 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4004 return (wrote == sizeof(DWORD));
4007 /******************************************************************************
4008 * Storage32Impl_SmallBlocksToBigBlocks
4010 * This method will convert a small block chain to a big block chain.
4011 * The small block chain will be destroyed.
4013 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4014 StorageImpl* This,
4015 SmallBlockChainStream** ppsbChain)
4017 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4018 ULARGE_INTEGER size, offset;
4019 ULONG cbRead, cbWritten;
4020 ULARGE_INTEGER cbTotalRead;
4021 DirRef streamEntryRef;
4022 HRESULT resWrite = S_OK;
4023 HRESULT resRead;
4024 DirEntry streamEntry;
4025 BYTE *buffer;
4026 BlockChainStream *bbTempChain = NULL;
4027 BlockChainStream *bigBlockChain = NULL;
4030 * Create a temporary big block chain that doesn't have
4031 * an associated directory entry. This temporary chain will be
4032 * used to copy data from small blocks to big blocks.
4034 bbTempChain = BlockChainStream_Construct(This,
4035 &bbHeadOfChain,
4036 DIRENTRY_NULL);
4037 if(!bbTempChain) return NULL;
4039 * Grow the big block chain.
4041 size = SmallBlockChainStream_GetSize(*ppsbChain);
4042 BlockChainStream_SetSize(bbTempChain, size);
4045 * Copy the contents of the small block chain to the big block chain
4046 * by small block size increments.
4048 offset.u.LowPart = 0;
4049 offset.u.HighPart = 0;
4050 cbTotalRead.QuadPart = 0;
4052 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4055 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4056 offset,
4057 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4058 buffer,
4059 &cbRead);
4060 if (FAILED(resRead))
4061 break;
4063 if (cbRead > 0)
4065 cbTotalRead.QuadPart += cbRead;
4067 resWrite = BlockChainStream_WriteAt(bbTempChain,
4068 offset,
4069 cbRead,
4070 buffer,
4071 &cbWritten);
4073 if (FAILED(resWrite))
4074 break;
4076 offset.u.LowPart += cbRead;
4078 else
4080 resRead = STG_E_READFAULT;
4081 break;
4083 } while (cbTotalRead.QuadPart < size.QuadPart);
4084 HeapFree(GetProcessHeap(),0,buffer);
4086 size.u.HighPart = 0;
4087 size.u.LowPart = 0;
4089 if (FAILED(resRead) || FAILED(resWrite))
4091 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4092 BlockChainStream_SetSize(bbTempChain, size);
4093 BlockChainStream_Destroy(bbTempChain);
4094 return NULL;
4098 * Destroy the small block chain.
4100 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4101 SmallBlockChainStream_SetSize(*ppsbChain, size);
4102 SmallBlockChainStream_Destroy(*ppsbChain);
4103 *ppsbChain = 0;
4106 * Change the directory entry. This chain is now a big block chain
4107 * and it doesn't reside in the small blocks chain anymore.
4109 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4111 streamEntry.startingBlock = bbHeadOfChain;
4113 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4116 * Destroy the temporary entryless big block chain.
4117 * Create a new big block chain associated with this entry.
4119 BlockChainStream_Destroy(bbTempChain);
4120 bigBlockChain = BlockChainStream_Construct(This,
4121 NULL,
4122 streamEntryRef);
4124 return bigBlockChain;
4127 /******************************************************************************
4128 * Storage32Impl_BigBlocksToSmallBlocks
4130 * This method will convert a big block chain to a small block chain.
4131 * The big block chain will be destroyed on success.
4133 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4134 StorageImpl* This,
4135 BlockChainStream** ppbbChain,
4136 ULARGE_INTEGER newSize)
4138 ULARGE_INTEGER size, offset, cbTotalRead;
4139 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4140 DirRef streamEntryRef;
4141 HRESULT resWrite = S_OK, resRead = S_OK;
4142 DirEntry streamEntry;
4143 BYTE* buffer;
4144 SmallBlockChainStream* sbTempChain;
4146 TRACE("%p %p\n", This, ppbbChain);
4148 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4149 DIRENTRY_NULL);
4151 if(!sbTempChain)
4152 return NULL;
4154 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4155 size = BlockChainStream_GetSize(*ppbbChain);
4156 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4158 offset.u.HighPart = 0;
4159 offset.u.LowPart = 0;
4160 cbTotalRead.QuadPart = 0;
4161 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4162 while(cbTotalRead.QuadPart < size.QuadPart)
4164 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4165 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4166 buffer, &cbRead);
4168 if(FAILED(resRead))
4169 break;
4171 if(cbRead > 0)
4173 cbTotalRead.QuadPart += cbRead;
4175 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4176 cbRead, buffer, &cbWritten);
4178 if(FAILED(resWrite))
4179 break;
4181 offset.u.LowPart += cbRead;
4183 else
4185 resRead = STG_E_READFAULT;
4186 break;
4189 HeapFree(GetProcessHeap(), 0, buffer);
4191 size.u.HighPart = 0;
4192 size.u.LowPart = 0;
4194 if(FAILED(resRead) || FAILED(resWrite))
4196 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4197 SmallBlockChainStream_SetSize(sbTempChain, size);
4198 SmallBlockChainStream_Destroy(sbTempChain);
4199 return NULL;
4202 /* destroy the original big block chain */
4203 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4204 BlockChainStream_SetSize(*ppbbChain, size);
4205 BlockChainStream_Destroy(*ppbbChain);
4206 *ppbbChain = NULL;
4208 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4209 streamEntry.startingBlock = sbHeadOfChain;
4210 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4212 SmallBlockChainStream_Destroy(sbTempChain);
4213 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4216 static HRESULT StorageBaseImpl_CopyStream(
4217 StorageBaseImpl *dst, DirRef dst_entry,
4218 StorageBaseImpl *src, DirRef src_entry)
4220 HRESULT hr;
4221 BYTE data[4096];
4222 DirEntry srcdata;
4223 ULARGE_INTEGER bytes_copied;
4224 ULONG bytestocopy, bytesread, byteswritten;
4226 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4228 if (SUCCEEDED(hr))
4230 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4232 bytes_copied.QuadPart = 0;
4233 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4235 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4237 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4238 data, &bytesread);
4239 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4241 if (SUCCEEDED(hr))
4242 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4243 data, &byteswritten);
4244 if (SUCCEEDED(hr))
4246 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4247 bytes_copied.QuadPart += byteswritten;
4252 return hr;
4255 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4257 DirRef result=This->firstFreeEntry;
4259 while (result < This->entries_size && This->entries[result].inuse)
4260 result++;
4262 if (result == This->entries_size)
4264 ULONG new_size = This->entries_size * 2;
4265 TransactedDirEntry *new_entries;
4267 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4268 if (!new_entries) return DIRENTRY_NULL;
4270 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4271 HeapFree(GetProcessHeap(), 0, This->entries);
4273 This->entries = new_entries;
4274 This->entries_size = new_size;
4277 This->entries[result].inuse = 1;
4279 This->firstFreeEntry = result+1;
4281 return result;
4284 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4285 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4287 DirRef stubEntryRef;
4288 TransactedDirEntry *entry;
4290 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4292 if (stubEntryRef != DIRENTRY_NULL)
4294 entry = &This->entries[stubEntryRef];
4296 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4298 entry->read = 0;
4301 return stubEntryRef;
4304 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4305 TransactedSnapshotImpl *This, DirRef entry)
4307 HRESULT hr=S_OK;
4308 DirEntry data;
4310 if (!This->entries[entry].read)
4312 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4313 This->entries[entry].transactedParentEntry,
4314 &data);
4316 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4318 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4320 if (data.leftChild == DIRENTRY_NULL)
4321 hr = E_OUTOFMEMORY;
4324 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4326 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4328 if (data.rightChild == DIRENTRY_NULL)
4329 hr = E_OUTOFMEMORY;
4332 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4334 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4336 if (data.dirRootEntry == DIRENTRY_NULL)
4337 hr = E_OUTOFMEMORY;
4340 if (SUCCEEDED(hr))
4342 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4343 This->entries[entry].read = 1;
4347 return hr;
4350 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4351 TransactedSnapshotImpl *This, DirRef entry)
4353 HRESULT hr = S_OK;
4355 if (!This->entries[entry].stream_dirty)
4357 DirEntry new_entrydata;
4359 memset(&new_entrydata, 0, sizeof(DirEntry));
4360 new_entrydata.name[0] = 'S';
4361 new_entrydata.sizeOfNameString = 1;
4362 new_entrydata.stgType = STGTY_STREAM;
4363 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4364 new_entrydata.leftChild = DIRENTRY_NULL;
4365 new_entrydata.rightChild = DIRENTRY_NULL;
4366 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4368 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4369 &This->entries[entry].stream_entry);
4371 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4373 hr = StorageBaseImpl_CopyStream(
4374 This->scratch, This->entries[entry].stream_entry,
4375 This->transactedParent, This->entries[entry].transactedParentEntry);
4377 if (FAILED(hr))
4378 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4381 if (SUCCEEDED(hr))
4382 This->entries[entry].stream_dirty = 1;
4384 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4386 /* Since this entry is modified, and we aren't using its stream data, we
4387 * no longer care about the original entry. */
4388 DirRef delete_ref;
4389 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4391 if (delete_ref != DIRENTRY_NULL)
4392 This->entries[delete_ref].deleted = 1;
4394 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4398 return hr;
4401 /* Find the first entry in a depth-first traversal. */
4402 static DirRef TransactedSnapshotImpl_FindFirstChild(
4403 TransactedSnapshotImpl* This, DirRef parent)
4405 DirRef cursor, prev;
4406 TransactedDirEntry *entry;
4408 cursor = parent;
4409 entry = &This->entries[cursor];
4410 while (entry->read)
4412 if (entry->data.leftChild != DIRENTRY_NULL)
4414 prev = cursor;
4415 cursor = entry->data.leftChild;
4416 entry = &This->entries[cursor];
4417 entry->parent = prev;
4419 else if (entry->data.rightChild != DIRENTRY_NULL)
4421 prev = cursor;
4422 cursor = entry->data.rightChild;
4423 entry = &This->entries[cursor];
4424 entry->parent = prev;
4426 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4428 prev = cursor;
4429 cursor = entry->data.dirRootEntry;
4430 entry = &This->entries[cursor];
4431 entry->parent = prev;
4433 else
4434 break;
4437 return cursor;
4440 /* Find the next entry in a depth-first traversal. */
4441 static DirRef TransactedSnapshotImpl_FindNextChild(
4442 TransactedSnapshotImpl* This, DirRef current)
4444 DirRef parent;
4445 TransactedDirEntry *parent_entry;
4447 parent = This->entries[current].parent;
4448 parent_entry = &This->entries[parent];
4450 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4452 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4454 This->entries[parent_entry->data.rightChild].parent = parent;
4455 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4458 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4460 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4461 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4465 return parent;
4468 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4469 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4470 TransactedSnapshotImpl* This, DirRef entry)
4472 return entry != DIRENTRY_NULL &&
4473 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4476 /* Destroy the entries created by CopyTree. */
4477 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4478 TransactedSnapshotImpl* This, DirRef stop)
4480 DirRef cursor;
4481 TransactedDirEntry *entry;
4482 ULARGE_INTEGER zero;
4484 zero.QuadPart = 0;
4486 if (!This->entries[This->base.storageDirEntry].read)
4487 return;
4489 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4491 if (cursor == DIRENTRY_NULL)
4492 return;
4494 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4496 while (cursor != DIRENTRY_NULL && cursor != stop)
4498 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4500 entry = &This->entries[cursor];
4502 if (entry->stream_dirty)
4503 StorageBaseImpl_StreamSetSize(This->transactedParent,
4504 entry->newTransactedParentEntry, zero);
4506 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4507 entry->newTransactedParentEntry);
4509 entry->newTransactedParentEntry = entry->transactedParentEntry;
4512 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4516 /* Make a copy of our edited tree that we can use in the parent. */
4517 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4519 DirRef cursor;
4520 TransactedDirEntry *entry;
4521 HRESULT hr = S_OK;
4523 cursor = This->base.storageDirEntry;
4524 entry = &This->entries[cursor];
4525 entry->parent = DIRENTRY_NULL;
4526 entry->newTransactedParentEntry = entry->transactedParentEntry;
4528 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4529 return S_OK;
4531 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4533 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4534 entry = &This->entries[cursor];
4536 while (cursor != DIRENTRY_NULL)
4538 /* Make a copy of this entry in the transacted parent. */
4539 if (!entry->read ||
4540 (!entry->dirty && !entry->stream_dirty &&
4541 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4542 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4543 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4544 entry->newTransactedParentEntry = entry->transactedParentEntry;
4545 else
4547 DirEntry newData;
4549 memcpy(&newData, &entry->data, sizeof(DirEntry));
4551 newData.size.QuadPart = 0;
4552 newData.startingBlock = BLOCK_END_OF_CHAIN;
4554 if (newData.leftChild != DIRENTRY_NULL)
4555 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4557 if (newData.rightChild != DIRENTRY_NULL)
4558 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4560 if (newData.dirRootEntry != DIRENTRY_NULL)
4561 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4563 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4564 &entry->newTransactedParentEntry);
4565 if (FAILED(hr))
4567 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4568 return hr;
4571 if (entry->stream_dirty)
4573 hr = StorageBaseImpl_CopyStream(
4574 This->transactedParent, entry->newTransactedParentEntry,
4575 This->scratch, entry->stream_entry);
4577 else if (entry->data.size.QuadPart)
4579 hr = StorageBaseImpl_StreamLink(
4580 This->transactedParent, entry->newTransactedParentEntry,
4581 entry->transactedParentEntry);
4584 if (FAILED(hr))
4586 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4587 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4588 return hr;
4592 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4593 entry = &This->entries[cursor];
4596 return hr;
4599 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4600 IStorage* iface,
4601 DWORD grfCommitFlags) /* [in] */
4603 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4604 TransactedDirEntry *root_entry;
4605 DirRef i, dir_root_ref;
4606 DirEntry data;
4607 ULARGE_INTEGER zero;
4608 HRESULT hr;
4610 zero.QuadPart = 0;
4612 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4614 /* Cannot commit a read-only transacted storage */
4615 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4616 return STG_E_ACCESSDENIED;
4618 /* To prevent data loss, we create the new structure in the file before we
4619 * delete the old one, so that in case of errors the old data is intact. We
4620 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4621 * needed in the rare situation where we have just enough free disk space to
4622 * overwrite the existing data. */
4624 root_entry = &This->entries[This->base.storageDirEntry];
4626 if (!root_entry->read)
4627 return S_OK;
4629 hr = TransactedSnapshotImpl_CopyTree(This);
4630 if (FAILED(hr)) return hr;
4632 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4633 dir_root_ref = DIRENTRY_NULL;
4634 else
4635 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4637 hr = StorageBaseImpl_Flush(This->transactedParent);
4639 /* Update the storage to use the new data in one step. */
4640 if (SUCCEEDED(hr))
4641 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4642 root_entry->transactedParentEntry, &data);
4644 if (SUCCEEDED(hr))
4646 data.dirRootEntry = dir_root_ref;
4647 data.clsid = root_entry->data.clsid;
4648 data.ctime = root_entry->data.ctime;
4649 data.mtime = root_entry->data.mtime;
4651 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4652 root_entry->transactedParentEntry, &data);
4655 /* Try to flush after updating the root storage, but if the flush fails, keep
4656 * going, on the theory that it'll either succeed later or the subsequent
4657 * writes will fail. */
4658 StorageBaseImpl_Flush(This->transactedParent);
4660 if (SUCCEEDED(hr))
4662 /* Destroy the old now-orphaned data. */
4663 for (i=0; i<This->entries_size; i++)
4665 TransactedDirEntry *entry = &This->entries[i];
4666 if (entry->inuse)
4668 if (entry->deleted)
4670 StorageBaseImpl_StreamSetSize(This->transactedParent,
4671 entry->transactedParentEntry, zero);
4672 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4673 entry->transactedParentEntry);
4674 memset(entry, 0, sizeof(TransactedDirEntry));
4675 This->firstFreeEntry = min(i, This->firstFreeEntry);
4677 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4679 if (entry->transactedParentEntry != DIRENTRY_NULL)
4680 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4681 entry->transactedParentEntry);
4682 if (entry->stream_dirty)
4684 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4685 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4686 entry->stream_dirty = 0;
4688 entry->dirty = 0;
4689 entry->transactedParentEntry = entry->newTransactedParentEntry;
4694 else
4696 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4699 if (SUCCEEDED(hr))
4700 hr = StorageBaseImpl_Flush(This->transactedParent);
4702 return hr;
4705 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4706 IStorage* iface)
4708 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4709 ULARGE_INTEGER zero;
4710 ULONG i;
4712 TRACE("(%p)\n", iface);
4714 /* Destroy the open objects. */
4715 StorageBaseImpl_DeleteAll(&This->base);
4717 /* Clear out the scratch file. */
4718 zero.QuadPart = 0;
4719 for (i=0; i<This->entries_size; i++)
4721 if (This->entries[i].stream_dirty)
4723 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4724 zero);
4726 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4730 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4732 This->firstFreeEntry = 0;
4733 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4735 return S_OK;
4738 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4740 if (!This->reverted)
4742 TRACE("Storage invalidated (stg=%p)\n", This);
4744 This->reverted = 1;
4746 StorageBaseImpl_DeleteAll(This);
4750 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4752 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4754 TransactedSnapshotImpl_Revert((IStorage*)iface);
4756 IStorage_Release((IStorage*)This->transactedParent);
4758 IStorage_Release((IStorage*)This->scratch);
4760 HeapFree(GetProcessHeap(), 0, This->entries);
4762 HeapFree(GetProcessHeap(), 0, This);
4765 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4767 /* We only need to flush when committing. */
4768 return S_OK;
4771 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4773 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4775 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4778 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4779 const DirEntry *newData, DirRef *index)
4781 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4782 DirRef new_ref;
4783 TransactedDirEntry *new_entry;
4785 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4786 if (new_ref == DIRENTRY_NULL)
4787 return E_OUTOFMEMORY;
4789 new_entry = &This->entries[new_ref];
4791 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4792 new_entry->read = 1;
4793 new_entry->dirty = 1;
4794 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4796 *index = new_ref;
4798 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4800 return S_OK;
4803 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4804 DirRef index, const DirEntry *data)
4806 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4807 HRESULT hr;
4809 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4811 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4812 if (FAILED(hr)) return hr;
4814 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4816 if (index != This->base.storageDirEntry)
4818 This->entries[index].dirty = 1;
4820 if (data->size.QuadPart == 0 &&
4821 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4823 /* Since this entry is modified, and we aren't using its stream data, we
4824 * no longer care about the original entry. */
4825 DirRef delete_ref;
4826 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4828 if (delete_ref != DIRENTRY_NULL)
4829 This->entries[delete_ref].deleted = 1;
4831 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4835 return S_OK;
4838 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4839 DirRef index, DirEntry *data)
4841 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4842 HRESULT hr;
4844 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4845 if (FAILED(hr)) return hr;
4847 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4849 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4851 return S_OK;
4854 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4855 DirRef index)
4857 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4859 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4860 This->entries[index].data.size.QuadPart != 0)
4862 /* If we deleted this entry while it has stream data. We must have left the
4863 * data because some other entry is using it, and we need to leave the
4864 * original entry alone. */
4865 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4866 This->firstFreeEntry = min(index, This->firstFreeEntry);
4868 else
4870 This->entries[index].deleted = 1;
4873 return S_OK;
4876 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4877 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4879 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4881 if (This->entries[index].stream_dirty)
4883 return StorageBaseImpl_StreamReadAt(This->scratch,
4884 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4886 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4888 /* This stream doesn't live in the parent, and we haven't allocated storage
4889 * for it yet */
4890 *bytesRead = 0;
4891 return S_OK;
4893 else
4895 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4896 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4900 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4901 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4903 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4904 HRESULT hr;
4906 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4907 if (FAILED(hr)) return hr;
4909 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4910 if (FAILED(hr)) return hr;
4912 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4913 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4915 if (SUCCEEDED(hr) && size != 0)
4916 This->entries[index].data.size.QuadPart = max(
4917 This->entries[index].data.size.QuadPart,
4918 offset.QuadPart + size);
4920 return hr;
4923 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4924 DirRef index, ULARGE_INTEGER newsize)
4926 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4927 HRESULT hr;
4929 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4930 if (FAILED(hr)) return hr;
4932 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4933 return S_OK;
4935 if (newsize.QuadPart == 0)
4937 /* Destroy any parent references or entries in the scratch file. */
4938 if (This->entries[index].stream_dirty)
4940 ULARGE_INTEGER zero;
4941 zero.QuadPart = 0;
4942 StorageBaseImpl_StreamSetSize(This->scratch,
4943 This->entries[index].stream_entry, zero);
4944 StorageBaseImpl_DestroyDirEntry(This->scratch,
4945 This->entries[index].stream_entry);
4946 This->entries[index].stream_dirty = 0;
4948 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4950 DirRef delete_ref;
4951 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4953 if (delete_ref != DIRENTRY_NULL)
4954 This->entries[delete_ref].deleted = 1;
4956 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4959 else
4961 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4962 if (FAILED(hr)) return hr;
4964 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4965 This->entries[index].stream_entry, newsize);
4968 if (SUCCEEDED(hr))
4969 This->entries[index].data.size = newsize;
4971 return hr;
4974 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
4975 DirRef dst, DirRef src)
4977 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4978 HRESULT hr;
4979 TransactedDirEntry *dst_entry, *src_entry;
4981 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
4982 if (FAILED(hr)) return hr;
4984 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
4985 if (FAILED(hr)) return hr;
4987 dst_entry = &This->entries[dst];
4988 src_entry = &This->entries[src];
4990 dst_entry->stream_dirty = src_entry->stream_dirty;
4991 dst_entry->stream_entry = src_entry->stream_entry;
4992 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
4993 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
4994 dst_entry->data.size = src_entry->data.size;
4996 return S_OK;
4999 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5001 StorageBaseImpl_QueryInterface,
5002 StorageBaseImpl_AddRef,
5003 StorageBaseImpl_Release,
5004 StorageBaseImpl_CreateStream,
5005 StorageBaseImpl_OpenStream,
5006 StorageBaseImpl_CreateStorage,
5007 StorageBaseImpl_OpenStorage,
5008 StorageBaseImpl_CopyTo,
5009 StorageBaseImpl_MoveElementTo,
5010 TransactedSnapshotImpl_Commit,
5011 TransactedSnapshotImpl_Revert,
5012 StorageBaseImpl_EnumElements,
5013 StorageBaseImpl_DestroyElement,
5014 StorageBaseImpl_RenameElement,
5015 StorageBaseImpl_SetElementTimes,
5016 StorageBaseImpl_SetClass,
5017 StorageBaseImpl_SetStateBits,
5018 StorageBaseImpl_Stat
5021 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5023 TransactedSnapshotImpl_Destroy,
5024 TransactedSnapshotImpl_Invalidate,
5025 TransactedSnapshotImpl_Flush,
5026 TransactedSnapshotImpl_GetFilename,
5027 TransactedSnapshotImpl_CreateDirEntry,
5028 TransactedSnapshotImpl_WriteDirEntry,
5029 TransactedSnapshotImpl_ReadDirEntry,
5030 TransactedSnapshotImpl_DestroyDirEntry,
5031 TransactedSnapshotImpl_StreamReadAt,
5032 TransactedSnapshotImpl_StreamWriteAt,
5033 TransactedSnapshotImpl_StreamSetSize,
5034 TransactedSnapshotImpl_StreamLink
5037 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5038 TransactedSnapshotImpl** result)
5040 HRESULT hr;
5042 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5043 if (*result)
5045 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5047 /* This is OK because the property set storage functions use the IStorage functions. */
5048 (*result)->base.pssVtbl = parentStorage->pssVtbl;
5050 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5052 list_init(&(*result)->base.strmHead);
5054 list_init(&(*result)->base.storageHead);
5056 (*result)->base.ref = 1;
5058 (*result)->base.openFlags = parentStorage->openFlags;
5060 /* Create a new temporary storage to act as the scratch file. */
5061 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
5062 0, (IStorage**)&(*result)->scratch);
5064 if (SUCCEEDED(hr))
5066 ULONG num_entries = 20;
5068 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5070 (*result)->entries_size = num_entries;
5072 (*result)->firstFreeEntry = 0;
5074 if ((*result)->entries)
5076 /* parentStorage already has 1 reference, which we take over here. */
5077 (*result)->transactedParent = parentStorage;
5079 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5081 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5083 else
5085 IStorage_Release((IStorage*)(*result)->scratch);
5087 hr = E_OUTOFMEMORY;
5091 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5093 return hr;
5095 else
5096 return E_OUTOFMEMORY;
5099 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5100 StorageBaseImpl** result)
5102 static int fixme=0;
5104 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5106 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5109 return TransactedSnapshotImpl_Construct(parentStorage,
5110 (TransactedSnapshotImpl**)result);
5113 static HRESULT Storage_Construct(
5114 HANDLE hFile,
5115 LPCOLESTR pwcsName,
5116 ILockBytes* pLkbyt,
5117 DWORD openFlags,
5118 BOOL fileBased,
5119 BOOL create,
5120 ULONG sector_size,
5121 StorageBaseImpl** result)
5123 StorageImpl *newStorage;
5124 StorageBaseImpl *newTransactedStorage;
5125 HRESULT hr;
5127 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5128 if (FAILED(hr)) goto end;
5130 if (openFlags & STGM_TRANSACTED)
5132 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5133 if (FAILED(hr))
5134 IStorage_Release((IStorage*)newStorage);
5135 else
5136 *result = newTransactedStorage;
5138 else
5139 *result = &newStorage->base;
5141 end:
5142 return hr;
5145 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5147 StorageInternalImpl* This = (StorageInternalImpl*) base;
5149 if (!This->base.reverted)
5151 TRACE("Storage invalidated (stg=%p)\n", This);
5153 This->base.reverted = 1;
5155 This->parentStorage = NULL;
5157 StorageBaseImpl_DeleteAll(&This->base);
5159 list_remove(&This->ParentListEntry);
5163 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5165 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5167 StorageInternalImpl_Invalidate(&This->base);
5169 HeapFree(GetProcessHeap(), 0, This);
5172 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5174 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5176 return StorageBaseImpl_Flush(This->parentStorage);
5179 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5181 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5183 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5186 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5187 const DirEntry *newData, DirRef *index)
5189 StorageInternalImpl* This = (StorageInternalImpl*) base;
5191 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5192 newData, index);
5195 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5196 DirRef index, const DirEntry *data)
5198 StorageInternalImpl* This = (StorageInternalImpl*) base;
5200 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5201 index, data);
5204 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5205 DirRef index, DirEntry *data)
5207 StorageInternalImpl* This = (StorageInternalImpl*) base;
5209 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5210 index, data);
5213 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5214 DirRef index)
5216 StorageInternalImpl* This = (StorageInternalImpl*) base;
5218 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5219 index);
5222 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5223 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5225 StorageInternalImpl* This = (StorageInternalImpl*) base;
5227 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5228 index, offset, size, buffer, bytesRead);
5231 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5232 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5234 StorageInternalImpl* This = (StorageInternalImpl*) base;
5236 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5237 index, offset, size, buffer, bytesWritten);
5240 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5241 DirRef index, ULARGE_INTEGER newsize)
5243 StorageInternalImpl* This = (StorageInternalImpl*) base;
5245 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5246 index, newsize);
5249 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5250 DirRef dst, DirRef src)
5252 StorageInternalImpl* This = (StorageInternalImpl*) base;
5254 return StorageBaseImpl_StreamLink(This->parentStorage,
5255 dst, src);
5258 /******************************************************************************
5260 ** Storage32InternalImpl_Commit
5263 static HRESULT WINAPI StorageInternalImpl_Commit(
5264 IStorage* iface,
5265 DWORD grfCommitFlags) /* [in] */
5267 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5268 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5269 return StorageBaseImpl_Flush(base);
5272 /******************************************************************************
5274 ** Storage32InternalImpl_Revert
5277 static HRESULT WINAPI StorageInternalImpl_Revert(
5278 IStorage* iface)
5280 FIXME("(%p): stub\n", iface);
5281 return S_OK;
5284 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5286 IStorage_Release((IStorage*)This->parentStorage);
5287 HeapFree(GetProcessHeap(), 0, This);
5290 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5291 IEnumSTATSTG* iface,
5292 REFIID riid,
5293 void** ppvObject)
5295 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5297 if (ppvObject==0)
5298 return E_INVALIDARG;
5300 *ppvObject = 0;
5302 if (IsEqualGUID(&IID_IUnknown, riid) ||
5303 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5305 *ppvObject = This;
5306 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
5307 return S_OK;
5310 return E_NOINTERFACE;
5313 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5314 IEnumSTATSTG* iface)
5316 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5317 return InterlockedIncrement(&This->ref);
5320 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5321 IEnumSTATSTG* iface)
5323 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5325 ULONG newRef;
5327 newRef = InterlockedDecrement(&This->ref);
5329 if (newRef==0)
5331 IEnumSTATSTGImpl_Destroy(This);
5334 return newRef;
5337 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5338 IEnumSTATSTGImpl* This,
5339 DirRef *ref)
5341 DirRef result = DIRENTRY_NULL;
5342 DirRef searchNode;
5343 DirEntry entry;
5344 HRESULT hr;
5345 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5347 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5348 This->parentStorage->storageDirEntry, &entry);
5349 searchNode = entry.dirRootEntry;
5351 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5353 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5355 if (SUCCEEDED(hr))
5357 LONG diff = entryNameCmp( entry.name, This->name);
5359 if (diff <= 0)
5361 searchNode = entry.rightChild;
5363 else
5365 result = searchNode;
5366 memcpy(result_name, entry.name, sizeof(result_name));
5367 searchNode = entry.leftChild;
5372 if (SUCCEEDED(hr))
5374 *ref = result;
5375 if (result != DIRENTRY_NULL)
5376 memcpy(This->name, result_name, sizeof(result_name));
5379 return hr;
5382 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5383 IEnumSTATSTG* iface,
5384 ULONG celt,
5385 STATSTG* rgelt,
5386 ULONG* pceltFetched)
5388 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5390 DirEntry currentEntry;
5391 STATSTG* currentReturnStruct = rgelt;
5392 ULONG objectFetched = 0;
5393 DirRef currentSearchNode;
5394 HRESULT hr=S_OK;
5396 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5397 return E_INVALIDARG;
5399 if (This->parentStorage->reverted)
5400 return STG_E_REVERTED;
5403 * To avoid the special case, get another pointer to a ULONG value if
5404 * the caller didn't supply one.
5406 if (pceltFetched==0)
5407 pceltFetched = &objectFetched;
5410 * Start the iteration, we will iterate until we hit the end of the
5411 * linked list or until we hit the number of items to iterate through
5413 *pceltFetched = 0;
5415 while ( *pceltFetched < celt )
5417 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5419 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5420 break;
5423 * Read the entry from the storage.
5425 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5426 currentSearchNode,
5427 &currentEntry);
5430 * Copy the information to the return buffer.
5432 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5433 currentReturnStruct,
5434 &currentEntry,
5435 STATFLAG_DEFAULT);
5438 * Step to the next item in the iteration
5440 (*pceltFetched)++;
5441 currentReturnStruct++;
5444 if (SUCCEEDED(hr) && *pceltFetched != celt)
5445 hr = S_FALSE;
5447 return hr;
5451 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5452 IEnumSTATSTG* iface,
5453 ULONG celt)
5455 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5457 ULONG objectFetched = 0;
5458 DirRef currentSearchNode;
5459 HRESULT hr=S_OK;
5461 if (This->parentStorage->reverted)
5462 return STG_E_REVERTED;
5464 while ( (objectFetched < celt) )
5466 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5468 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5469 break;
5471 objectFetched++;
5474 if (SUCCEEDED(hr) && objectFetched != celt)
5475 return S_FALSE;
5477 return hr;
5480 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5481 IEnumSTATSTG* iface)
5483 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5485 if (This->parentStorage->reverted)
5486 return STG_E_REVERTED;
5488 This->name[0] = 0;
5490 return S_OK;
5493 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5494 IEnumSTATSTG* iface,
5495 IEnumSTATSTG** ppenum)
5497 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
5499 IEnumSTATSTGImpl* newClone;
5501 if (This->parentStorage->reverted)
5502 return STG_E_REVERTED;
5505 * Perform a sanity check on the parameters.
5507 if (ppenum==0)
5508 return E_INVALIDARG;
5510 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5511 This->storageDirEntry);
5515 * The new clone enumeration must point to the same current node as
5516 * the ole one.
5518 memcpy(newClone->name, This->name, sizeof(newClone->name));
5520 *ppenum = (IEnumSTATSTG*)newClone;
5523 * Don't forget to nail down a reference to the clone before
5524 * returning it.
5526 IEnumSTATSTGImpl_AddRef(*ppenum);
5528 return S_OK;
5532 * Virtual function table for the IEnumSTATSTGImpl class.
5534 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5536 IEnumSTATSTGImpl_QueryInterface,
5537 IEnumSTATSTGImpl_AddRef,
5538 IEnumSTATSTGImpl_Release,
5539 IEnumSTATSTGImpl_Next,
5540 IEnumSTATSTGImpl_Skip,
5541 IEnumSTATSTGImpl_Reset,
5542 IEnumSTATSTGImpl_Clone
5545 /******************************************************************************
5546 ** IEnumSTATSTGImpl implementation
5549 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5550 StorageBaseImpl* parentStorage,
5551 DirRef storageDirEntry)
5553 IEnumSTATSTGImpl* newEnumeration;
5555 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5557 if (newEnumeration!=0)
5560 * Set-up the virtual function table and reference count.
5562 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5563 newEnumeration->ref = 0;
5566 * We want to nail-down the reference to the storage in case the
5567 * enumeration out-lives the storage in the client application.
5569 newEnumeration->parentStorage = parentStorage;
5570 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5572 newEnumeration->storageDirEntry = storageDirEntry;
5575 * Make sure the current node of the iterator is the first one.
5577 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
5580 return newEnumeration;
5584 * Virtual function table for the Storage32InternalImpl class.
5586 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5588 StorageBaseImpl_QueryInterface,
5589 StorageBaseImpl_AddRef,
5590 StorageBaseImpl_Release,
5591 StorageBaseImpl_CreateStream,
5592 StorageBaseImpl_OpenStream,
5593 StorageBaseImpl_CreateStorage,
5594 StorageBaseImpl_OpenStorage,
5595 StorageBaseImpl_CopyTo,
5596 StorageBaseImpl_MoveElementTo,
5597 StorageInternalImpl_Commit,
5598 StorageInternalImpl_Revert,
5599 StorageBaseImpl_EnumElements,
5600 StorageBaseImpl_DestroyElement,
5601 StorageBaseImpl_RenameElement,
5602 StorageBaseImpl_SetElementTimes,
5603 StorageBaseImpl_SetClass,
5604 StorageBaseImpl_SetStateBits,
5605 StorageBaseImpl_Stat
5608 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5610 StorageInternalImpl_Destroy,
5611 StorageInternalImpl_Invalidate,
5612 StorageInternalImpl_Flush,
5613 StorageInternalImpl_GetFilename,
5614 StorageInternalImpl_CreateDirEntry,
5615 StorageInternalImpl_WriteDirEntry,
5616 StorageInternalImpl_ReadDirEntry,
5617 StorageInternalImpl_DestroyDirEntry,
5618 StorageInternalImpl_StreamReadAt,
5619 StorageInternalImpl_StreamWriteAt,
5620 StorageInternalImpl_StreamSetSize,
5621 StorageInternalImpl_StreamLink
5624 /******************************************************************************
5625 ** Storage32InternalImpl implementation
5628 static StorageInternalImpl* StorageInternalImpl_Construct(
5629 StorageBaseImpl* parentStorage,
5630 DWORD openFlags,
5631 DirRef storageDirEntry)
5633 StorageInternalImpl* newStorage;
5635 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5637 if (newStorage!=0)
5639 list_init(&newStorage->base.strmHead);
5641 list_init(&newStorage->base.storageHead);
5644 * Initialize the virtual function table.
5646 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5647 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5648 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5649 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5651 newStorage->base.reverted = 0;
5653 newStorage->base.ref = 1;
5655 newStorage->parentStorage = parentStorage;
5658 * Keep a reference to the directory entry of this storage
5660 newStorage->base.storageDirEntry = storageDirEntry;
5662 newStorage->base.create = 0;
5664 return newStorage;
5667 return 0;
5670 /******************************************************************************
5671 ** StorageUtl implementation
5674 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5676 WORD tmp;
5678 memcpy(&tmp, buffer+offset, sizeof(WORD));
5679 *value = lendian16toh(tmp);
5682 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5684 value = htole16(value);
5685 memcpy(buffer+offset, &value, sizeof(WORD));
5688 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5690 DWORD tmp;
5692 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5693 *value = lendian32toh(tmp);
5696 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5698 value = htole32(value);
5699 memcpy(buffer+offset, &value, sizeof(DWORD));
5702 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5703 ULARGE_INTEGER* value)
5705 #ifdef WORDS_BIGENDIAN
5706 ULARGE_INTEGER tmp;
5708 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5709 value->u.LowPart = htole32(tmp.u.HighPart);
5710 value->u.HighPart = htole32(tmp.u.LowPart);
5711 #else
5712 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5713 #endif
5716 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5717 const ULARGE_INTEGER *value)
5719 #ifdef WORDS_BIGENDIAN
5720 ULARGE_INTEGER tmp;
5722 tmp.u.LowPart = htole32(value->u.HighPart);
5723 tmp.u.HighPart = htole32(value->u.LowPart);
5724 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5725 #else
5726 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5727 #endif
5730 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5732 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5733 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5734 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5736 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5739 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5741 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5742 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5743 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5745 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5748 void StorageUtl_CopyDirEntryToSTATSTG(
5749 StorageBaseImpl* storage,
5750 STATSTG* destination,
5751 const DirEntry* source,
5752 int statFlags)
5755 * The copy of the string occurs only when the flag is not set
5757 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5759 /* Use the filename for the root storage. */
5760 destination->pwcsName = 0;
5761 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5763 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5764 (source->name[0] == 0) )
5766 destination->pwcsName = 0;
5768 else
5770 destination->pwcsName =
5771 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5773 strcpyW(destination->pwcsName, source->name);
5776 switch (source->stgType)
5778 case STGTY_STORAGE:
5779 case STGTY_ROOT:
5780 destination->type = STGTY_STORAGE;
5781 break;
5782 case STGTY_STREAM:
5783 destination->type = STGTY_STREAM;
5784 break;
5785 default:
5786 destination->type = STGTY_STREAM;
5787 break;
5790 destination->cbSize = source->size;
5792 currentReturnStruct->mtime = {0}; TODO
5793 currentReturnStruct->ctime = {0};
5794 currentReturnStruct->atime = {0};
5796 destination->grfMode = 0;
5797 destination->grfLocksSupported = 0;
5798 destination->clsid = source->clsid;
5799 destination->grfStateBits = 0;
5800 destination->reserved = 0;
5803 /******************************************************************************
5804 ** BlockChainStream implementation
5807 /* Read and save the index of all blocks in this stream. */
5808 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5810 ULONG next_sector, next_offset;
5811 HRESULT hr;
5812 struct BlockChainRun *last_run;
5814 if (This->indexCacheLen == 0)
5816 last_run = NULL;
5817 next_offset = 0;
5818 next_sector = BlockChainStream_GetHeadOfChain(This);
5820 else
5822 last_run = &This->indexCache[This->indexCacheLen-1];
5823 next_offset = last_run->lastOffset+1;
5824 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5825 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5826 &next_sector);
5827 if (FAILED(hr)) return hr;
5830 while (next_sector != BLOCK_END_OF_CHAIN)
5832 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5834 /* Add the current block to the cache. */
5835 if (This->indexCacheSize == 0)
5837 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5838 if (!This->indexCache) return E_OUTOFMEMORY;
5839 This->indexCacheSize = 16;
5841 else if (This->indexCacheSize == This->indexCacheLen)
5843 struct BlockChainRun *new_cache;
5844 ULONG new_size;
5846 new_size = This->indexCacheSize * 2;
5847 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5848 if (!new_cache) return E_OUTOFMEMORY;
5849 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5851 HeapFree(GetProcessHeap(), 0, This->indexCache);
5852 This->indexCache = new_cache;
5853 This->indexCacheSize = new_size;
5856 This->indexCacheLen++;
5857 last_run = &This->indexCache[This->indexCacheLen-1];
5858 last_run->firstSector = next_sector;
5859 last_run->firstOffset = next_offset;
5862 last_run->lastOffset = next_offset;
5864 /* Find the next block. */
5865 next_offset++;
5866 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5867 if (FAILED(hr)) return hr;
5870 if (This->indexCacheLen)
5872 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5873 This->numBlocks = last_run->lastOffset+1;
5875 else
5877 This->tailIndex = BLOCK_END_OF_CHAIN;
5878 This->numBlocks = 0;
5881 return S_OK;
5884 /* Locate the nth block in this stream. */
5885 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5887 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5888 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5890 if (offset >= This->numBlocks)
5891 return BLOCK_END_OF_CHAIN;
5893 while (min_run < max_run)
5895 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5896 if (offset < This->indexCache[run_to_check].firstOffset)
5898 max_offset = This->indexCache[run_to_check].firstOffset-1;
5899 max_run = run_to_check-1;
5901 else if (offset > This->indexCache[run_to_check].lastOffset)
5903 min_offset = This->indexCache[run_to_check].lastOffset+1;
5904 min_run = run_to_check+1;
5906 else
5907 /* Block is in this run. */
5908 min_run = max_run = run_to_check;
5911 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5914 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5915 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5917 BlockChainBlock *result=NULL;
5918 int i;
5920 for (i=0; i<2; i++)
5921 if (This->cachedBlocks[i].index == index)
5923 *sector = This->cachedBlocks[i].sector;
5924 *block = &This->cachedBlocks[i];
5925 return S_OK;
5928 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5929 if (*sector == BLOCK_END_OF_CHAIN)
5930 return STG_E_DOCFILECORRUPT;
5932 if (create)
5934 if (This->cachedBlocks[0].index == 0xffffffff)
5935 result = &This->cachedBlocks[0];
5936 else if (This->cachedBlocks[1].index == 0xffffffff)
5937 result = &This->cachedBlocks[1];
5938 else
5940 result = &This->cachedBlocks[This->blockToEvict++];
5941 if (This->blockToEvict == 2)
5942 This->blockToEvict = 0;
5945 if (result->dirty)
5947 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5948 return STG_E_WRITEFAULT;
5949 result->dirty = 0;
5952 result->read = 0;
5953 result->index = index;
5954 result->sector = *sector;
5957 *block = result;
5958 return S_OK;
5961 BlockChainStream* BlockChainStream_Construct(
5962 StorageImpl* parentStorage,
5963 ULONG* headOfStreamPlaceHolder,
5964 DirRef dirEntry)
5966 BlockChainStream* newStream;
5968 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5970 newStream->parentStorage = parentStorage;
5971 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5972 newStream->ownerDirEntry = dirEntry;
5973 newStream->indexCache = NULL;
5974 newStream->indexCacheLen = 0;
5975 newStream->indexCacheSize = 0;
5976 newStream->cachedBlocks[0].index = 0xffffffff;
5977 newStream->cachedBlocks[0].dirty = 0;
5978 newStream->cachedBlocks[1].index = 0xffffffff;
5979 newStream->cachedBlocks[1].dirty = 0;
5980 newStream->blockToEvict = 0;
5982 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5984 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5985 HeapFree(GetProcessHeap(), 0, newStream);
5986 return NULL;
5989 return newStream;
5992 HRESULT BlockChainStream_Flush(BlockChainStream* This)
5994 int i;
5995 if (!This) return S_OK;
5996 for (i=0; i<2; i++)
5998 if (This->cachedBlocks[i].dirty)
6000 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6001 This->cachedBlocks[i].dirty = 0;
6002 else
6003 return STG_E_WRITEFAULT;
6006 return S_OK;
6009 void BlockChainStream_Destroy(BlockChainStream* This)
6011 if (This)
6013 BlockChainStream_Flush(This);
6014 HeapFree(GetProcessHeap(), 0, This->indexCache);
6016 HeapFree(GetProcessHeap(), 0, This);
6019 /******************************************************************************
6020 * BlockChainStream_GetHeadOfChain
6022 * Returns the head of this stream chain.
6023 * Some special chains don't have directory entries, their heads are kept in
6024 * This->headOfStreamPlaceHolder.
6027 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6029 DirEntry chainEntry;
6030 HRESULT hr;
6032 if (This->headOfStreamPlaceHolder != 0)
6033 return *(This->headOfStreamPlaceHolder);
6035 if (This->ownerDirEntry != DIRENTRY_NULL)
6037 hr = StorageImpl_ReadDirEntry(
6038 This->parentStorage,
6039 This->ownerDirEntry,
6040 &chainEntry);
6042 if (SUCCEEDED(hr))
6044 return chainEntry.startingBlock;
6048 return BLOCK_END_OF_CHAIN;
6051 /******************************************************************************
6052 * BlockChainStream_GetCount
6054 * Returns the number of blocks that comprises this chain.
6055 * This is not the size of the stream as the last block may not be full!
6057 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6059 return This->numBlocks;
6062 /******************************************************************************
6063 * BlockChainStream_ReadAt
6065 * Reads a specified number of bytes from this chain at the specified offset.
6066 * bytesRead may be NULL.
6067 * Failure will be returned if the specified number of bytes has not been read.
6069 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6070 ULARGE_INTEGER offset,
6071 ULONG size,
6072 void* buffer,
6073 ULONG* bytesRead)
6075 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6076 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6077 ULONG bytesToReadInBuffer;
6078 ULONG blockIndex;
6079 BYTE* bufferWalker;
6080 ULARGE_INTEGER stream_size;
6081 HRESULT hr;
6082 BlockChainBlock *cachedBlock;
6084 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6087 * Find the first block in the stream that contains part of the buffer.
6089 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6091 *bytesRead = 0;
6093 stream_size = BlockChainStream_GetSize(This);
6094 if (stream_size.QuadPart > offset.QuadPart)
6095 size = min(stream_size.QuadPart - offset.QuadPart, size);
6096 else
6097 return S_OK;
6100 * Start reading the buffer.
6102 bufferWalker = buffer;
6104 while (size > 0)
6106 ULARGE_INTEGER ulOffset;
6107 DWORD bytesReadAt;
6110 * Calculate how many bytes we can copy from this big block.
6112 bytesToReadInBuffer =
6113 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6115 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6117 if (FAILED(hr))
6118 return hr;
6120 if (!cachedBlock)
6122 /* Not in cache, and we're going to read past the end of the block. */
6123 ulOffset.u.HighPart = 0;
6124 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6125 offsetInBlock;
6127 StorageImpl_ReadAt(This->parentStorage,
6128 ulOffset,
6129 bufferWalker,
6130 bytesToReadInBuffer,
6131 &bytesReadAt);
6133 else
6135 if (!cachedBlock->read)
6137 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6138 return STG_E_READFAULT;
6140 cachedBlock->read = 1;
6143 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6144 bytesReadAt = bytesToReadInBuffer;
6147 blockNoInSequence++;
6148 bufferWalker += bytesReadAt;
6149 size -= bytesReadAt;
6150 *bytesRead += bytesReadAt;
6151 offsetInBlock = 0; /* There is no offset on the next block */
6153 if (bytesToReadInBuffer != bytesReadAt)
6154 break;
6157 return S_OK;
6160 /******************************************************************************
6161 * BlockChainStream_WriteAt
6163 * Writes the specified number of bytes to this chain at the specified offset.
6164 * Will fail if not all specified number of bytes have been written.
6166 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6167 ULARGE_INTEGER offset,
6168 ULONG size,
6169 const void* buffer,
6170 ULONG* bytesWritten)
6172 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6173 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6174 ULONG bytesToWrite;
6175 ULONG blockIndex;
6176 const BYTE* bufferWalker;
6177 HRESULT hr;
6178 BlockChainBlock *cachedBlock;
6180 *bytesWritten = 0;
6181 bufferWalker = buffer;
6183 while (size > 0)
6185 ULARGE_INTEGER ulOffset;
6186 DWORD bytesWrittenAt;
6189 * Calculate how many bytes we can copy to this big block.
6191 bytesToWrite =
6192 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6194 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6196 /* BlockChainStream_SetSize should have already been called to ensure we have
6197 * enough blocks in the chain to write into */
6198 if (FAILED(hr))
6200 ERR("not enough blocks in chain to write data\n");
6201 return hr;
6204 if (!cachedBlock)
6206 /* Not in cache, and we're going to write past the end of the block. */
6207 ulOffset.u.HighPart = 0;
6208 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6209 offsetInBlock;
6211 StorageImpl_WriteAt(This->parentStorage,
6212 ulOffset,
6213 bufferWalker,
6214 bytesToWrite,
6215 &bytesWrittenAt);
6217 else
6219 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6221 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6222 return STG_E_READFAULT;
6225 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6226 bytesWrittenAt = bytesToWrite;
6227 cachedBlock->read = 1;
6228 cachedBlock->dirty = 1;
6231 blockNoInSequence++;
6232 bufferWalker += bytesWrittenAt;
6233 size -= bytesWrittenAt;
6234 *bytesWritten += bytesWrittenAt;
6235 offsetInBlock = 0; /* There is no offset on the next block */
6237 if (bytesWrittenAt != bytesToWrite)
6238 break;
6241 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6244 /******************************************************************************
6245 * BlockChainStream_Shrink
6247 * Shrinks this chain in the big block depot.
6249 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6250 ULARGE_INTEGER newSize)
6252 ULONG blockIndex;
6253 ULONG numBlocks;
6254 int i;
6257 * Figure out how many blocks are needed to contain the new size
6259 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6261 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6262 numBlocks++;
6264 if (numBlocks)
6267 * Go to the new end of chain
6269 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6271 /* Mark the new end of chain */
6272 StorageImpl_SetNextBlockInChain(
6273 This->parentStorage,
6274 blockIndex,
6275 BLOCK_END_OF_CHAIN);
6277 This->tailIndex = blockIndex;
6279 else
6281 if (This->headOfStreamPlaceHolder != 0)
6283 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6285 else
6287 DirEntry chainEntry;
6288 assert(This->ownerDirEntry != DIRENTRY_NULL);
6290 StorageImpl_ReadDirEntry(
6291 This->parentStorage,
6292 This->ownerDirEntry,
6293 &chainEntry);
6295 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6297 StorageImpl_WriteDirEntry(
6298 This->parentStorage,
6299 This->ownerDirEntry,
6300 &chainEntry);
6303 This->tailIndex = BLOCK_END_OF_CHAIN;
6306 This->numBlocks = numBlocks;
6309 * Mark the extra blocks as free
6311 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6313 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6314 StorageImpl_FreeBigBlock(This->parentStorage,
6315 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6316 if (last_run->lastOffset == last_run->firstOffset)
6317 This->indexCacheLen--;
6318 else
6319 last_run->lastOffset--;
6323 * Reset the last accessed block cache.
6325 for (i=0; i<2; i++)
6327 if (This->cachedBlocks[i].index >= numBlocks)
6329 This->cachedBlocks[i].index = 0xffffffff;
6330 This->cachedBlocks[i].dirty = 0;
6334 return TRUE;
6337 /******************************************************************************
6338 * BlockChainStream_Enlarge
6340 * Grows this chain in the big block depot.
6342 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6343 ULARGE_INTEGER newSize)
6345 ULONG blockIndex, currentBlock;
6346 ULONG newNumBlocks;
6347 ULONG oldNumBlocks = 0;
6349 blockIndex = BlockChainStream_GetHeadOfChain(This);
6352 * Empty chain. Create the head.
6354 if (blockIndex == BLOCK_END_OF_CHAIN)
6356 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6357 StorageImpl_SetNextBlockInChain(This->parentStorage,
6358 blockIndex,
6359 BLOCK_END_OF_CHAIN);
6361 if (This->headOfStreamPlaceHolder != 0)
6363 *(This->headOfStreamPlaceHolder) = blockIndex;
6365 else
6367 DirEntry chainEntry;
6368 assert(This->ownerDirEntry != DIRENTRY_NULL);
6370 StorageImpl_ReadDirEntry(
6371 This->parentStorage,
6372 This->ownerDirEntry,
6373 &chainEntry);
6375 chainEntry.startingBlock = blockIndex;
6377 StorageImpl_WriteDirEntry(
6378 This->parentStorage,
6379 This->ownerDirEntry,
6380 &chainEntry);
6383 This->tailIndex = blockIndex;
6384 This->numBlocks = 1;
6388 * Figure out how many blocks are needed to contain this stream
6390 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6392 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6393 newNumBlocks++;
6396 * Go to the current end of chain
6398 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6400 currentBlock = blockIndex;
6402 while (blockIndex != BLOCK_END_OF_CHAIN)
6404 This->numBlocks++;
6405 currentBlock = blockIndex;
6407 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6408 &blockIndex)))
6409 return FALSE;
6412 This->tailIndex = currentBlock;
6415 currentBlock = This->tailIndex;
6416 oldNumBlocks = This->numBlocks;
6419 * Add new blocks to the chain
6421 if (oldNumBlocks < newNumBlocks)
6423 while (oldNumBlocks < newNumBlocks)
6425 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6427 StorageImpl_SetNextBlockInChain(
6428 This->parentStorage,
6429 currentBlock,
6430 blockIndex);
6432 StorageImpl_SetNextBlockInChain(
6433 This->parentStorage,
6434 blockIndex,
6435 BLOCK_END_OF_CHAIN);
6437 currentBlock = blockIndex;
6438 oldNumBlocks++;
6441 This->tailIndex = blockIndex;
6442 This->numBlocks = newNumBlocks;
6445 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6446 return FALSE;
6448 return TRUE;
6451 /******************************************************************************
6452 * BlockChainStream_SetSize
6454 * Sets the size of this stream. The big block depot will be updated.
6455 * The file will grow if we grow the chain.
6457 * TODO: Free the actual blocks in the file when we shrink the chain.
6458 * Currently, the blocks are still in the file. So the file size
6459 * doesn't shrink even if we shrink streams.
6461 BOOL BlockChainStream_SetSize(
6462 BlockChainStream* This,
6463 ULARGE_INTEGER newSize)
6465 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6467 if (newSize.u.LowPart == size.u.LowPart)
6468 return TRUE;
6470 if (newSize.u.LowPart < size.u.LowPart)
6472 BlockChainStream_Shrink(This, newSize);
6474 else
6476 BlockChainStream_Enlarge(This, newSize);
6479 return TRUE;
6482 /******************************************************************************
6483 * BlockChainStream_GetSize
6485 * Returns the size of this chain.
6486 * Will return the block count if this chain doesn't have a directory entry.
6488 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6490 DirEntry chainEntry;
6492 if(This->headOfStreamPlaceHolder == NULL)
6495 * This chain has a directory entry so use the size value from there.
6497 StorageImpl_ReadDirEntry(
6498 This->parentStorage,
6499 This->ownerDirEntry,
6500 &chainEntry);
6502 return chainEntry.size;
6504 else
6507 * this chain is a chain that does not have a directory entry, figure out the
6508 * size by making the product number of used blocks times the
6509 * size of them
6511 ULARGE_INTEGER result;
6512 result.u.HighPart = 0;
6514 result.u.LowPart =
6515 BlockChainStream_GetCount(This) *
6516 This->parentStorage->bigBlockSize;
6518 return result;
6522 /******************************************************************************
6523 ** SmallBlockChainStream implementation
6526 SmallBlockChainStream* SmallBlockChainStream_Construct(
6527 StorageImpl* parentStorage,
6528 ULONG* headOfStreamPlaceHolder,
6529 DirRef dirEntry)
6531 SmallBlockChainStream* newStream;
6533 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6535 newStream->parentStorage = parentStorage;
6536 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6537 newStream->ownerDirEntry = dirEntry;
6539 return newStream;
6542 void SmallBlockChainStream_Destroy(
6543 SmallBlockChainStream* This)
6545 HeapFree(GetProcessHeap(), 0, This);
6548 /******************************************************************************
6549 * SmallBlockChainStream_GetHeadOfChain
6551 * Returns the head of this chain of small blocks.
6553 static ULONG SmallBlockChainStream_GetHeadOfChain(
6554 SmallBlockChainStream* This)
6556 DirEntry chainEntry;
6557 HRESULT hr;
6559 if (This->headOfStreamPlaceHolder != NULL)
6560 return *(This->headOfStreamPlaceHolder);
6562 if (This->ownerDirEntry)
6564 hr = StorageImpl_ReadDirEntry(
6565 This->parentStorage,
6566 This->ownerDirEntry,
6567 &chainEntry);
6569 if (SUCCEEDED(hr))
6571 return chainEntry.startingBlock;
6576 return BLOCK_END_OF_CHAIN;
6579 /******************************************************************************
6580 * SmallBlockChainStream_GetNextBlockInChain
6582 * Returns the index of the next small block in this chain.
6584 * Return Values:
6585 * - BLOCK_END_OF_CHAIN: end of this chain
6586 * - BLOCK_UNUSED: small block 'blockIndex' is free
6588 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6589 SmallBlockChainStream* This,
6590 ULONG blockIndex,
6591 ULONG* nextBlockInChain)
6593 ULARGE_INTEGER offsetOfBlockInDepot;
6594 DWORD buffer;
6595 ULONG bytesRead;
6596 HRESULT res;
6598 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6600 offsetOfBlockInDepot.u.HighPart = 0;
6601 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6604 * Read those bytes in the buffer from the small block file.
6606 res = BlockChainStream_ReadAt(
6607 This->parentStorage->smallBlockDepotChain,
6608 offsetOfBlockInDepot,
6609 sizeof(DWORD),
6610 &buffer,
6611 &bytesRead);
6613 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6614 res = STG_E_READFAULT;
6616 if (SUCCEEDED(res))
6618 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6619 return S_OK;
6622 return res;
6625 /******************************************************************************
6626 * SmallBlockChainStream_SetNextBlockInChain
6628 * Writes the index of the next block of the specified block in the small
6629 * block depot.
6630 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6631 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6633 static void SmallBlockChainStream_SetNextBlockInChain(
6634 SmallBlockChainStream* This,
6635 ULONG blockIndex,
6636 ULONG nextBlock)
6638 ULARGE_INTEGER offsetOfBlockInDepot;
6639 DWORD buffer;
6640 ULONG bytesWritten;
6642 offsetOfBlockInDepot.u.HighPart = 0;
6643 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6645 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6648 * Read those bytes in the buffer from the small block file.
6650 BlockChainStream_WriteAt(
6651 This->parentStorage->smallBlockDepotChain,
6652 offsetOfBlockInDepot,
6653 sizeof(DWORD),
6654 &buffer,
6655 &bytesWritten);
6658 /******************************************************************************
6659 * SmallBlockChainStream_FreeBlock
6661 * Flag small block 'blockIndex' as free in the small block depot.
6663 static void SmallBlockChainStream_FreeBlock(
6664 SmallBlockChainStream* This,
6665 ULONG blockIndex)
6667 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6670 /******************************************************************************
6671 * SmallBlockChainStream_GetNextFreeBlock
6673 * Returns the index of a free small block. The small block depot will be
6674 * enlarged if necessary. The small block chain will also be enlarged if
6675 * necessary.
6677 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6678 SmallBlockChainStream* This)
6680 ULARGE_INTEGER offsetOfBlockInDepot;
6681 DWORD buffer;
6682 ULONG bytesRead;
6683 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6684 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6685 HRESULT res = S_OK;
6686 ULONG smallBlocksPerBigBlock;
6687 DirEntry rootEntry;
6688 ULONG blocksRequired;
6689 ULARGE_INTEGER old_size, size_required;
6691 offsetOfBlockInDepot.u.HighPart = 0;
6694 * Scan the small block depot for a free block
6696 while (nextBlockIndex != BLOCK_UNUSED)
6698 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6700 res = BlockChainStream_ReadAt(
6701 This->parentStorage->smallBlockDepotChain,
6702 offsetOfBlockInDepot,
6703 sizeof(DWORD),
6704 &buffer,
6705 &bytesRead);
6708 * If we run out of space for the small block depot, enlarge it
6710 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6712 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6714 if (nextBlockIndex != BLOCK_UNUSED)
6715 blockIndex++;
6717 else
6719 ULONG count =
6720 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6722 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6723 ULARGE_INTEGER newSize, offset;
6724 ULONG bytesWritten;
6726 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6727 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6730 * Initialize all the small blocks to free
6732 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6733 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6734 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6735 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6737 StorageImpl_SaveFileHeader(This->parentStorage);
6741 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6743 smallBlocksPerBigBlock =
6744 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6747 * Verify if we have to allocate big blocks to contain small blocks
6749 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6751 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6753 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6755 if (size_required.QuadPart > old_size.QuadPart)
6757 BlockChainStream_SetSize(
6758 This->parentStorage->smallBlockRootChain,
6759 size_required);
6761 StorageImpl_ReadDirEntry(
6762 This->parentStorage,
6763 This->parentStorage->base.storageDirEntry,
6764 &rootEntry);
6766 rootEntry.size = size_required;
6768 StorageImpl_WriteDirEntry(
6769 This->parentStorage,
6770 This->parentStorage->base.storageDirEntry,
6771 &rootEntry);
6774 return blockIndex;
6777 /******************************************************************************
6778 * SmallBlockChainStream_ReadAt
6780 * Reads a specified number of bytes from this chain at the specified offset.
6781 * bytesRead may be NULL.
6782 * Failure will be returned if the specified number of bytes has not been read.
6784 HRESULT SmallBlockChainStream_ReadAt(
6785 SmallBlockChainStream* This,
6786 ULARGE_INTEGER offset,
6787 ULONG size,
6788 void* buffer,
6789 ULONG* bytesRead)
6791 HRESULT rc = S_OK;
6792 ULARGE_INTEGER offsetInBigBlockFile;
6793 ULONG blockNoInSequence =
6794 offset.u.LowPart / This->parentStorage->smallBlockSize;
6796 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6797 ULONG bytesToReadInBuffer;
6798 ULONG blockIndex;
6799 ULONG bytesReadFromBigBlockFile;
6800 BYTE* bufferWalker;
6801 ULARGE_INTEGER stream_size;
6804 * This should never happen on a small block file.
6806 assert(offset.u.HighPart==0);
6808 *bytesRead = 0;
6810 stream_size = SmallBlockChainStream_GetSize(This);
6811 if (stream_size.QuadPart > offset.QuadPart)
6812 size = min(stream_size.QuadPart - offset.QuadPart, size);
6813 else
6814 return S_OK;
6817 * Find the first block in the stream that contains part of the buffer.
6819 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6821 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6823 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6824 if(FAILED(rc))
6825 return rc;
6826 blockNoInSequence--;
6830 * Start reading the buffer.
6832 bufferWalker = buffer;
6834 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6837 * Calculate how many bytes we can copy from this small block.
6839 bytesToReadInBuffer =
6840 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6843 * Calculate the offset of the small block in the small block file.
6845 offsetInBigBlockFile.u.HighPart = 0;
6846 offsetInBigBlockFile.u.LowPart =
6847 blockIndex * This->parentStorage->smallBlockSize;
6849 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6852 * Read those bytes in the buffer from the small block file.
6853 * The small block has already been identified so it shouldn't fail
6854 * unless the file is corrupt.
6856 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6857 offsetInBigBlockFile,
6858 bytesToReadInBuffer,
6859 bufferWalker,
6860 &bytesReadFromBigBlockFile);
6862 if (FAILED(rc))
6863 return rc;
6865 if (!bytesReadFromBigBlockFile)
6866 return STG_E_DOCFILECORRUPT;
6869 * Step to the next big block.
6871 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6872 if(FAILED(rc))
6873 return STG_E_DOCFILECORRUPT;
6875 bufferWalker += bytesReadFromBigBlockFile;
6876 size -= bytesReadFromBigBlockFile;
6877 *bytesRead += bytesReadFromBigBlockFile;
6878 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6881 return S_OK;
6884 /******************************************************************************
6885 * SmallBlockChainStream_WriteAt
6887 * Writes the specified number of bytes to this chain at the specified offset.
6888 * Will fail if not all specified number of bytes have been written.
6890 HRESULT SmallBlockChainStream_WriteAt(
6891 SmallBlockChainStream* This,
6892 ULARGE_INTEGER offset,
6893 ULONG size,
6894 const void* buffer,
6895 ULONG* bytesWritten)
6897 ULARGE_INTEGER offsetInBigBlockFile;
6898 ULONG blockNoInSequence =
6899 offset.u.LowPart / This->parentStorage->smallBlockSize;
6901 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6902 ULONG bytesToWriteInBuffer;
6903 ULONG blockIndex;
6904 ULONG bytesWrittenToBigBlockFile;
6905 const BYTE* bufferWalker;
6906 HRESULT res;
6909 * This should never happen on a small block file.
6911 assert(offset.u.HighPart==0);
6914 * Find the first block in the stream that contains part of the buffer.
6916 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6918 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6920 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6921 return STG_E_DOCFILECORRUPT;
6922 blockNoInSequence--;
6926 * Start writing the buffer.
6928 *bytesWritten = 0;
6929 bufferWalker = buffer;
6930 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6933 * Calculate how many bytes we can copy to this small block.
6935 bytesToWriteInBuffer =
6936 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6939 * Calculate the offset of the small block in the small block file.
6941 offsetInBigBlockFile.u.HighPart = 0;
6942 offsetInBigBlockFile.u.LowPart =
6943 blockIndex * This->parentStorage->smallBlockSize;
6945 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6948 * Write those bytes in the buffer to the small block file.
6950 res = BlockChainStream_WriteAt(
6951 This->parentStorage->smallBlockRootChain,
6952 offsetInBigBlockFile,
6953 bytesToWriteInBuffer,
6954 bufferWalker,
6955 &bytesWrittenToBigBlockFile);
6956 if (FAILED(res))
6957 return res;
6960 * Step to the next big block.
6962 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6963 &blockIndex)))
6964 return FALSE;
6965 bufferWalker += bytesWrittenToBigBlockFile;
6966 size -= bytesWrittenToBigBlockFile;
6967 *bytesWritten += bytesWrittenToBigBlockFile;
6968 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6971 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6974 /******************************************************************************
6975 * SmallBlockChainStream_Shrink
6977 * Shrinks this chain in the small block depot.
6979 static BOOL SmallBlockChainStream_Shrink(
6980 SmallBlockChainStream* This,
6981 ULARGE_INTEGER newSize)
6983 ULONG blockIndex, extraBlock;
6984 ULONG numBlocks;
6985 ULONG count = 0;
6987 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6989 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6990 numBlocks++;
6992 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6995 * Go to the new end of chain
6997 while (count < numBlocks)
6999 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7000 &blockIndex)))
7001 return FALSE;
7002 count++;
7006 * If the count is 0, we have a special case, the head of the chain was
7007 * just freed.
7009 if (count == 0)
7011 DirEntry chainEntry;
7013 StorageImpl_ReadDirEntry(This->parentStorage,
7014 This->ownerDirEntry,
7015 &chainEntry);
7017 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7019 StorageImpl_WriteDirEntry(This->parentStorage,
7020 This->ownerDirEntry,
7021 &chainEntry);
7024 * We start freeing the chain at the head block.
7026 extraBlock = blockIndex;
7028 else
7030 /* Get the next block before marking the new end */
7031 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7032 &extraBlock)))
7033 return FALSE;
7035 /* Mark the new end of chain */
7036 SmallBlockChainStream_SetNextBlockInChain(
7037 This,
7038 blockIndex,
7039 BLOCK_END_OF_CHAIN);
7043 * Mark the extra blocks as free
7045 while (extraBlock != BLOCK_END_OF_CHAIN)
7047 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7048 &blockIndex)))
7049 return FALSE;
7050 SmallBlockChainStream_FreeBlock(This, extraBlock);
7051 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7052 extraBlock = blockIndex;
7055 return TRUE;
7058 /******************************************************************************
7059 * SmallBlockChainStream_Enlarge
7061 * Grows this chain in the small block depot.
7063 static BOOL SmallBlockChainStream_Enlarge(
7064 SmallBlockChainStream* This,
7065 ULARGE_INTEGER newSize)
7067 ULONG blockIndex, currentBlock;
7068 ULONG newNumBlocks;
7069 ULONG oldNumBlocks = 0;
7071 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7074 * Empty chain. Create the head.
7076 if (blockIndex == BLOCK_END_OF_CHAIN)
7078 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7079 SmallBlockChainStream_SetNextBlockInChain(
7080 This,
7081 blockIndex,
7082 BLOCK_END_OF_CHAIN);
7084 if (This->headOfStreamPlaceHolder != NULL)
7086 *(This->headOfStreamPlaceHolder) = blockIndex;
7088 else
7090 DirEntry chainEntry;
7092 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7093 &chainEntry);
7095 chainEntry.startingBlock = blockIndex;
7097 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7098 &chainEntry);
7102 currentBlock = blockIndex;
7105 * Figure out how many blocks are needed to contain this stream
7107 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7109 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7110 newNumBlocks++;
7113 * Go to the current end of chain
7115 while (blockIndex != BLOCK_END_OF_CHAIN)
7117 oldNumBlocks++;
7118 currentBlock = blockIndex;
7119 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7120 return FALSE;
7124 * Add new blocks to the chain
7126 while (oldNumBlocks < newNumBlocks)
7128 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7129 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7131 SmallBlockChainStream_SetNextBlockInChain(
7132 This,
7133 blockIndex,
7134 BLOCK_END_OF_CHAIN);
7136 currentBlock = blockIndex;
7137 oldNumBlocks++;
7140 return TRUE;
7143 /******************************************************************************
7144 * SmallBlockChainStream_SetSize
7146 * Sets the size of this stream.
7147 * The file will grow if we grow the chain.
7149 * TODO: Free the actual blocks in the file when we shrink the chain.
7150 * Currently, the blocks are still in the file. So the file size
7151 * doesn't shrink even if we shrink streams.
7153 BOOL SmallBlockChainStream_SetSize(
7154 SmallBlockChainStream* This,
7155 ULARGE_INTEGER newSize)
7157 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7159 if (newSize.u.LowPart == size.u.LowPart)
7160 return TRUE;
7162 if (newSize.u.LowPart < size.u.LowPart)
7164 SmallBlockChainStream_Shrink(This, newSize);
7166 else
7168 SmallBlockChainStream_Enlarge(This, newSize);
7171 return TRUE;
7174 /******************************************************************************
7175 * SmallBlockChainStream_GetCount
7177 * Returns the number of small blocks that comprises this chain.
7178 * This is not the size of the stream as the last block may not be full!
7181 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7183 ULONG blockIndex;
7184 ULONG count = 0;
7186 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7188 while(blockIndex != BLOCK_END_OF_CHAIN)
7190 count++;
7192 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7193 blockIndex, &blockIndex)))
7194 return 0;
7197 return count;
7200 /******************************************************************************
7201 * SmallBlockChainStream_GetSize
7203 * Returns the size of this chain.
7205 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7207 DirEntry chainEntry;
7209 if(This->headOfStreamPlaceHolder != NULL)
7211 ULARGE_INTEGER result;
7212 result.u.HighPart = 0;
7214 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7215 This->parentStorage->smallBlockSize;
7217 return result;
7220 StorageImpl_ReadDirEntry(
7221 This->parentStorage,
7222 This->ownerDirEntry,
7223 &chainEntry);
7225 return chainEntry.size;
7228 static HRESULT create_storagefile(
7229 LPCOLESTR pwcsName,
7230 DWORD grfMode,
7231 DWORD grfAttrs,
7232 STGOPTIONS* pStgOptions,
7233 REFIID riid,
7234 void** ppstgOpen)
7236 StorageBaseImpl* newStorage = 0;
7237 HANDLE hFile = INVALID_HANDLE_VALUE;
7238 HRESULT hr = STG_E_INVALIDFLAG;
7239 DWORD shareMode;
7240 DWORD accessMode;
7241 DWORD creationMode;
7242 DWORD fileAttributes;
7243 WCHAR tempFileName[MAX_PATH];
7245 if (ppstgOpen == 0)
7246 return STG_E_INVALIDPOINTER;
7248 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7249 return STG_E_INVALIDPARAMETER;
7251 /* if no share mode given then DENY_NONE is the default */
7252 if (STGM_SHARE_MODE(grfMode) == 0)
7253 grfMode |= STGM_SHARE_DENY_NONE;
7255 if ( FAILED( validateSTGM(grfMode) ))
7256 goto end;
7258 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7259 switch(STGM_ACCESS_MODE(grfMode))
7261 case STGM_WRITE:
7262 case STGM_READWRITE:
7263 break;
7264 default:
7265 goto end;
7268 /* in direct mode, can only use SHARE_EXCLUSIVE */
7269 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7270 goto end;
7272 /* but in transacted mode, any share mode is valid */
7275 * Generate a unique name.
7277 if (pwcsName == 0)
7279 WCHAR tempPath[MAX_PATH];
7280 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7282 memset(tempPath, 0, sizeof(tempPath));
7283 memset(tempFileName, 0, sizeof(tempFileName));
7285 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7286 tempPath[0] = '.';
7288 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7289 pwcsName = tempFileName;
7290 else
7292 hr = STG_E_INSUFFICIENTMEMORY;
7293 goto end;
7296 creationMode = TRUNCATE_EXISTING;
7298 else
7300 creationMode = GetCreationModeFromSTGM(grfMode);
7304 * Interpret the STGM value grfMode
7306 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7307 accessMode = GetAccessModeFromSTGM(grfMode);
7309 if (grfMode & STGM_DELETEONRELEASE)
7310 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7311 else
7312 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7314 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7316 static int fixme;
7317 if (!fixme++)
7318 FIXME("Storage share mode not implemented.\n");
7321 *ppstgOpen = 0;
7323 hFile = CreateFileW(pwcsName,
7324 accessMode,
7325 shareMode,
7326 NULL,
7327 creationMode,
7328 fileAttributes,
7331 if (hFile == INVALID_HANDLE_VALUE)
7333 if(GetLastError() == ERROR_FILE_EXISTS)
7334 hr = STG_E_FILEALREADYEXISTS;
7335 else
7336 hr = E_FAIL;
7337 goto end;
7341 * Allocate and initialize the new IStorage32object.
7343 hr = Storage_Construct(
7344 hFile,
7345 pwcsName,
7346 NULL,
7347 grfMode,
7348 TRUE,
7349 TRUE,
7350 pStgOptions->ulSectorSize,
7351 &newStorage);
7353 if (FAILED(hr))
7355 goto end;
7358 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7360 IStorage_Release((IStorage*)newStorage);
7362 end:
7363 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7365 return hr;
7368 /******************************************************************************
7369 * StgCreateDocfile [OLE32.@]
7370 * Creates a new compound file storage object
7372 * PARAMS
7373 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7374 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7375 * reserved [ ?] unused?, usually 0
7376 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7378 * RETURNS
7379 * S_OK if the file was successfully created
7380 * some STG_E_ value if error
7381 * NOTES
7382 * if pwcsName is NULL, create file with new unique name
7383 * the function can returns
7384 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7385 * (unrealized now)
7387 HRESULT WINAPI StgCreateDocfile(
7388 LPCOLESTR pwcsName,
7389 DWORD grfMode,
7390 DWORD reserved,
7391 IStorage **ppstgOpen)
7393 STGOPTIONS stgoptions = {1, 0, 512};
7395 TRACE("(%s, %x, %d, %p)\n",
7396 debugstr_w(pwcsName), grfMode,
7397 reserved, ppstgOpen);
7399 if (ppstgOpen == 0)
7400 return STG_E_INVALIDPOINTER;
7401 if (reserved != 0)
7402 return STG_E_INVALIDPARAMETER;
7404 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7407 /******************************************************************************
7408 * StgCreateStorageEx [OLE32.@]
7410 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7412 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7413 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7415 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7417 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7418 return STG_E_INVALIDPARAMETER;
7421 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7423 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7424 return STG_E_INVALIDPARAMETER;
7427 if (stgfmt == STGFMT_FILE)
7429 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7430 return STG_E_INVALIDPARAMETER;
7433 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7435 STGOPTIONS defaultOptions = {1, 0, 512};
7437 if (!pStgOptions) pStgOptions = &defaultOptions;
7438 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7442 ERR("Invalid stgfmt argument\n");
7443 return STG_E_INVALIDPARAMETER;
7446 /******************************************************************************
7447 * StgCreatePropSetStg [OLE32.@]
7449 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7450 IPropertySetStorage **ppPropSetStg)
7452 HRESULT hr;
7454 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7455 if (reserved)
7456 hr = STG_E_INVALIDPARAMETER;
7457 else
7458 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7459 (void**)ppPropSetStg);
7460 return hr;
7463 /******************************************************************************
7464 * StgOpenStorageEx [OLE32.@]
7466 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7468 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7469 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7471 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7473 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7474 return STG_E_INVALIDPARAMETER;
7477 switch (stgfmt)
7479 case STGFMT_FILE:
7480 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7481 return STG_E_INVALIDPARAMETER;
7483 case STGFMT_STORAGE:
7484 break;
7486 case STGFMT_DOCFILE:
7487 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7489 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7490 return STG_E_INVALIDPARAMETER;
7492 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7493 break;
7495 case STGFMT_ANY:
7496 WARN("STGFMT_ANY assuming storage\n");
7497 break;
7499 default:
7500 return STG_E_INVALIDPARAMETER;
7503 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7507 /******************************************************************************
7508 * StgOpenStorage [OLE32.@]
7510 HRESULT WINAPI StgOpenStorage(
7511 const OLECHAR *pwcsName,
7512 IStorage *pstgPriority,
7513 DWORD grfMode,
7514 SNB snbExclude,
7515 DWORD reserved,
7516 IStorage **ppstgOpen)
7518 StorageBaseImpl* newStorage = 0;
7519 HRESULT hr = S_OK;
7520 HANDLE hFile = 0;
7521 DWORD shareMode;
7522 DWORD accessMode;
7524 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7525 debugstr_w(pwcsName), pstgPriority, grfMode,
7526 snbExclude, reserved, ppstgOpen);
7528 if (pwcsName == 0)
7530 hr = STG_E_INVALIDNAME;
7531 goto end;
7534 if (ppstgOpen == 0)
7536 hr = STG_E_INVALIDPOINTER;
7537 goto end;
7540 if (reserved)
7542 hr = STG_E_INVALIDPARAMETER;
7543 goto end;
7546 if (grfMode & STGM_PRIORITY)
7548 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7549 return STG_E_INVALIDFLAG;
7550 if (grfMode & STGM_DELETEONRELEASE)
7551 return STG_E_INVALIDFUNCTION;
7552 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7553 return STG_E_INVALIDFLAG;
7554 grfMode &= ~0xf0; /* remove the existing sharing mode */
7555 grfMode |= STGM_SHARE_DENY_NONE;
7557 /* STGM_PRIORITY stops other IStorage objects on the same file from
7558 * committing until the STGM_PRIORITY IStorage is closed. it also
7559 * stops non-transacted mode StgOpenStorage calls with write access from
7560 * succeeding. obviously, both of these cannot be achieved through just
7561 * file share flags */
7562 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7566 * Validate the sharing mode
7568 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7569 switch(STGM_SHARE_MODE(grfMode))
7571 case STGM_SHARE_EXCLUSIVE:
7572 case STGM_SHARE_DENY_WRITE:
7573 break;
7574 default:
7575 hr = STG_E_INVALIDFLAG;
7576 goto end;
7579 if ( FAILED( validateSTGM(grfMode) ) ||
7580 (grfMode&STGM_CREATE))
7582 hr = STG_E_INVALIDFLAG;
7583 goto end;
7586 /* shared reading requires transacted mode */
7587 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7588 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7589 !(grfMode&STGM_TRANSACTED) )
7591 hr = STG_E_INVALIDFLAG;
7592 goto end;
7596 * Interpret the STGM value grfMode
7598 shareMode = GetShareModeFromSTGM(grfMode);
7599 accessMode = GetAccessModeFromSTGM(grfMode);
7601 *ppstgOpen = 0;
7603 hFile = CreateFileW( pwcsName,
7604 accessMode,
7605 shareMode,
7606 NULL,
7607 OPEN_EXISTING,
7608 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7611 if (hFile==INVALID_HANDLE_VALUE)
7613 DWORD last_error = GetLastError();
7615 hr = E_FAIL;
7617 switch (last_error)
7619 case ERROR_FILE_NOT_FOUND:
7620 hr = STG_E_FILENOTFOUND;
7621 break;
7623 case ERROR_PATH_NOT_FOUND:
7624 hr = STG_E_PATHNOTFOUND;
7625 break;
7627 case ERROR_ACCESS_DENIED:
7628 case ERROR_WRITE_PROTECT:
7629 hr = STG_E_ACCESSDENIED;
7630 break;
7632 case ERROR_SHARING_VIOLATION:
7633 hr = STG_E_SHAREVIOLATION;
7634 break;
7636 default:
7637 hr = E_FAIL;
7640 goto end;
7644 * Refuse to open the file if it's too small to be a structured storage file
7645 * FIXME: verify the file when reading instead of here
7647 if (GetFileSize(hFile, NULL) < 0x100)
7649 CloseHandle(hFile);
7650 hr = STG_E_FILEALREADYEXISTS;
7651 goto end;
7655 * Allocate and initialize the new IStorage32object.
7657 hr = Storage_Construct(
7658 hFile,
7659 pwcsName,
7660 NULL,
7661 grfMode,
7662 TRUE,
7663 FALSE,
7664 512,
7665 &newStorage);
7667 if (FAILED(hr))
7670 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7672 if(hr == STG_E_INVALIDHEADER)
7673 hr = STG_E_FILEALREADYEXISTS;
7674 goto end;
7678 * Get an "out" pointer for the caller.
7680 *ppstgOpen = (IStorage*)newStorage;
7682 end:
7683 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7684 return hr;
7687 /******************************************************************************
7688 * StgCreateDocfileOnILockBytes [OLE32.@]
7690 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7691 ILockBytes *plkbyt,
7692 DWORD grfMode,
7693 DWORD reserved,
7694 IStorage** ppstgOpen)
7696 StorageBaseImpl* newStorage = 0;
7697 HRESULT hr = S_OK;
7699 if ((ppstgOpen == 0) || (plkbyt == 0))
7700 return STG_E_INVALIDPOINTER;
7703 * Allocate and initialize the new IStorage object.
7705 hr = Storage_Construct(
7708 plkbyt,
7709 grfMode,
7710 FALSE,
7711 TRUE,
7712 512,
7713 &newStorage);
7715 if (FAILED(hr))
7717 return hr;
7721 * Get an "out" pointer for the caller.
7723 *ppstgOpen = (IStorage*)newStorage;
7725 return hr;
7728 /******************************************************************************
7729 * StgOpenStorageOnILockBytes [OLE32.@]
7731 HRESULT WINAPI StgOpenStorageOnILockBytes(
7732 ILockBytes *plkbyt,
7733 IStorage *pstgPriority,
7734 DWORD grfMode,
7735 SNB snbExclude,
7736 DWORD reserved,
7737 IStorage **ppstgOpen)
7739 StorageBaseImpl* newStorage = 0;
7740 HRESULT hr = S_OK;
7742 if ((plkbyt == 0) || (ppstgOpen == 0))
7743 return STG_E_INVALIDPOINTER;
7745 if ( FAILED( validateSTGM(grfMode) ))
7746 return STG_E_INVALIDFLAG;
7748 *ppstgOpen = 0;
7751 * Allocate and initialize the new IStorage object.
7753 hr = Storage_Construct(
7756 plkbyt,
7757 grfMode,
7758 FALSE,
7759 FALSE,
7760 512,
7761 &newStorage);
7763 if (FAILED(hr))
7765 return hr;
7769 * Get an "out" pointer for the caller.
7771 *ppstgOpen = (IStorage*)newStorage;
7773 return hr;
7776 /******************************************************************************
7777 * StgSetTimes [ole32.@]
7778 * StgSetTimes [OLE32.@]
7782 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7783 FILETIME const *patime, FILETIME const *pmtime)
7785 IStorage *stg = NULL;
7786 HRESULT r;
7788 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7790 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7791 0, 0, &stg);
7792 if( SUCCEEDED(r) )
7794 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7795 IStorage_Release(stg);
7798 return r;
7801 /******************************************************************************
7802 * StgIsStorageILockBytes [OLE32.@]
7804 * Determines if the ILockBytes contains a storage object.
7806 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7808 BYTE sig[8];
7809 ULARGE_INTEGER offset;
7811 offset.u.HighPart = 0;
7812 offset.u.LowPart = 0;
7814 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7816 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7817 return S_OK;
7819 return S_FALSE;
7822 /******************************************************************************
7823 * WriteClassStg [OLE32.@]
7825 * This method will store the specified CLSID in the specified storage object
7827 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7829 HRESULT hRes;
7831 if(!pStg)
7832 return E_INVALIDARG;
7834 if(!rclsid)
7835 return STG_E_INVALIDPOINTER;
7837 hRes = IStorage_SetClass(pStg, rclsid);
7839 return hRes;
7842 /***********************************************************************
7843 * ReadClassStg (OLE32.@)
7845 * This method reads the CLSID previously written to a storage object with
7846 * the WriteClassStg.
7848 * PARAMS
7849 * pstg [I] IStorage pointer
7850 * pclsid [O] Pointer to where the CLSID is written
7852 * RETURNS
7853 * Success: S_OK.
7854 * Failure: HRESULT code.
7856 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7858 STATSTG pstatstg;
7859 HRESULT hRes;
7861 TRACE("(%p, %p)\n", pstg, pclsid);
7863 if(!pstg || !pclsid)
7864 return E_INVALIDARG;
7867 * read a STATSTG structure (contains the clsid) from the storage
7869 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7871 if(SUCCEEDED(hRes))
7872 *pclsid=pstatstg.clsid;
7874 return hRes;
7877 /***********************************************************************
7878 * OleLoadFromStream (OLE32.@)
7880 * This function loads an object from stream
7882 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7884 CLSID clsid;
7885 HRESULT res;
7886 LPPERSISTSTREAM xstm;
7888 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7890 res=ReadClassStm(pStm,&clsid);
7891 if (FAILED(res))
7892 return res;
7893 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7894 if (FAILED(res))
7895 return res;
7896 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7897 if (FAILED(res)) {
7898 IUnknown_Release((IUnknown*)*ppvObj);
7899 return res;
7901 res=IPersistStream_Load(xstm,pStm);
7902 IPersistStream_Release(xstm);
7903 /* FIXME: all refcounts ok at this point? I think they should be:
7904 * pStm : unchanged
7905 * ppvObj : 1
7906 * xstm : 0 (released)
7908 return res;
7911 /***********************************************************************
7912 * OleSaveToStream (OLE32.@)
7914 * This function saves an object with the IPersistStream interface on it
7915 * to the specified stream.
7917 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7920 CLSID clsid;
7921 HRESULT res;
7923 TRACE("(%p,%p)\n",pPStm,pStm);
7925 res=IPersistStream_GetClassID(pPStm,&clsid);
7927 if (SUCCEEDED(res)){
7929 res=WriteClassStm(pStm,&clsid);
7931 if (SUCCEEDED(res))
7933 res=IPersistStream_Save(pPStm,pStm,TRUE);
7936 TRACE("Finished Save\n");
7937 return res;
7940 /****************************************************************************
7941 * This method validate a STGM parameter that can contain the values below
7943 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7944 * The stgm values contained in 0xffff0000 are bitmasks.
7946 * STGM_DIRECT 0x00000000
7947 * STGM_TRANSACTED 0x00010000
7948 * STGM_SIMPLE 0x08000000
7950 * STGM_READ 0x00000000
7951 * STGM_WRITE 0x00000001
7952 * STGM_READWRITE 0x00000002
7954 * STGM_SHARE_DENY_NONE 0x00000040
7955 * STGM_SHARE_DENY_READ 0x00000030
7956 * STGM_SHARE_DENY_WRITE 0x00000020
7957 * STGM_SHARE_EXCLUSIVE 0x00000010
7959 * STGM_PRIORITY 0x00040000
7960 * STGM_DELETEONRELEASE 0x04000000
7962 * STGM_CREATE 0x00001000
7963 * STGM_CONVERT 0x00020000
7964 * STGM_FAILIFTHERE 0x00000000
7966 * STGM_NOSCRATCH 0x00100000
7967 * STGM_NOSNAPSHOT 0x00200000
7969 static HRESULT validateSTGM(DWORD stgm)
7971 DWORD access = STGM_ACCESS_MODE(stgm);
7972 DWORD share = STGM_SHARE_MODE(stgm);
7973 DWORD create = STGM_CREATE_MODE(stgm);
7975 if (stgm&~STGM_KNOWN_FLAGS)
7977 ERR("unknown flags %08x\n", stgm);
7978 return E_FAIL;
7981 switch (access)
7983 case STGM_READ:
7984 case STGM_WRITE:
7985 case STGM_READWRITE:
7986 break;
7987 default:
7988 return E_FAIL;
7991 switch (share)
7993 case STGM_SHARE_DENY_NONE:
7994 case STGM_SHARE_DENY_READ:
7995 case STGM_SHARE_DENY_WRITE:
7996 case STGM_SHARE_EXCLUSIVE:
7997 break;
7998 default:
7999 return E_FAIL;
8002 switch (create)
8004 case STGM_CREATE:
8005 case STGM_FAILIFTHERE:
8006 break;
8007 default:
8008 return E_FAIL;
8012 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8014 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8015 return E_FAIL;
8018 * STGM_CREATE | STGM_CONVERT
8019 * if both are false, STGM_FAILIFTHERE is set to TRUE
8021 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8022 return E_FAIL;
8025 * STGM_NOSCRATCH requires STGM_TRANSACTED
8027 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8028 return E_FAIL;
8031 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8032 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8034 if ( (stgm & STGM_NOSNAPSHOT) &&
8035 (!(stgm & STGM_TRANSACTED) ||
8036 share == STGM_SHARE_EXCLUSIVE ||
8037 share == STGM_SHARE_DENY_WRITE) )
8038 return E_FAIL;
8040 return S_OK;
8043 /****************************************************************************
8044 * GetShareModeFromSTGM
8046 * This method will return a share mode flag from a STGM value.
8047 * The STGM value is assumed valid.
8049 static DWORD GetShareModeFromSTGM(DWORD stgm)
8051 switch (STGM_SHARE_MODE(stgm))
8053 case STGM_SHARE_DENY_NONE:
8054 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8055 case STGM_SHARE_DENY_READ:
8056 return FILE_SHARE_WRITE;
8057 case STGM_SHARE_DENY_WRITE:
8058 return FILE_SHARE_READ;
8059 case STGM_SHARE_EXCLUSIVE:
8060 return 0;
8062 ERR("Invalid share mode!\n");
8063 assert(0);
8064 return 0;
8067 /****************************************************************************
8068 * GetAccessModeFromSTGM
8070 * This method will return an access mode flag from a STGM value.
8071 * The STGM value is assumed valid.
8073 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8075 switch (STGM_ACCESS_MODE(stgm))
8077 case STGM_READ:
8078 return GENERIC_READ;
8079 case STGM_WRITE:
8080 case STGM_READWRITE:
8081 return GENERIC_READ | GENERIC_WRITE;
8083 ERR("Invalid access mode!\n");
8084 assert(0);
8085 return 0;
8088 /****************************************************************************
8089 * GetCreationModeFromSTGM
8091 * This method will return a creation mode flag from a STGM value.
8092 * The STGM value is assumed valid.
8094 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8096 switch(STGM_CREATE_MODE(stgm))
8098 case STGM_CREATE:
8099 return CREATE_ALWAYS;
8100 case STGM_CONVERT:
8101 FIXME("STGM_CONVERT not implemented!\n");
8102 return CREATE_NEW;
8103 case STGM_FAILIFTHERE:
8104 return CREATE_NEW;
8106 ERR("Invalid create mode!\n");
8107 assert(0);
8108 return 0;
8112 /*************************************************************************
8113 * OLECONVERT_LoadOLE10 [Internal]
8115 * Loads the OLE10 STREAM to memory
8117 * PARAMS
8118 * pOleStream [I] The OLESTREAM
8119 * pData [I] Data Structure for the OLESTREAM Data
8121 * RETURNS
8122 * Success: S_OK
8123 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8124 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8126 * NOTES
8127 * This function is used by OleConvertOLESTREAMToIStorage only.
8129 * Memory allocated for pData must be freed by the caller
8131 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8133 DWORD dwSize;
8134 HRESULT hRes = S_OK;
8135 int nTryCnt=0;
8136 int max_try = 6;
8138 pData->pData = NULL;
8139 pData->pstrOleObjFileName = NULL;
8141 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8143 /* Get the OleID */
8144 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8145 if(dwSize != sizeof(pData->dwOleID))
8147 hRes = CONVERT10_E_OLESTREAM_GET;
8149 else if(pData->dwOleID != OLESTREAM_ID)
8151 hRes = CONVERT10_E_OLESTREAM_FMT;
8153 else
8155 hRes = S_OK;
8156 break;
8160 if(hRes == S_OK)
8162 /* Get the TypeID... more info needed for this field */
8163 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8164 if(dwSize != sizeof(pData->dwTypeID))
8166 hRes = CONVERT10_E_OLESTREAM_GET;
8169 if(hRes == S_OK)
8171 if(pData->dwTypeID != 0)
8173 /* Get the length of the OleTypeName */
8174 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8175 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8177 hRes = CONVERT10_E_OLESTREAM_GET;
8180 if(hRes == S_OK)
8182 if(pData->dwOleTypeNameLength > 0)
8184 /* Get the OleTypeName */
8185 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8186 if(dwSize != pData->dwOleTypeNameLength)
8188 hRes = CONVERT10_E_OLESTREAM_GET;
8192 if(bStrem1)
8194 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8195 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8197 hRes = CONVERT10_E_OLESTREAM_GET;
8199 if(hRes == S_OK)
8201 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8202 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8203 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8204 if(pData->pstrOleObjFileName)
8206 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8207 if(dwSize != pData->dwOleObjFileNameLength)
8209 hRes = CONVERT10_E_OLESTREAM_GET;
8212 else
8213 hRes = CONVERT10_E_OLESTREAM_GET;
8216 else
8218 /* Get the Width of the Metafile */
8219 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8220 if(dwSize != sizeof(pData->dwMetaFileWidth))
8222 hRes = CONVERT10_E_OLESTREAM_GET;
8224 if(hRes == S_OK)
8226 /* Get the Height of the Metafile */
8227 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8228 if(dwSize != sizeof(pData->dwMetaFileHeight))
8230 hRes = CONVERT10_E_OLESTREAM_GET;
8234 if(hRes == S_OK)
8236 /* Get the Length of the Data */
8237 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8238 if(dwSize != sizeof(pData->dwDataLength))
8240 hRes = CONVERT10_E_OLESTREAM_GET;
8244 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8246 if(!bStrem1) /* if it is a second OLE stream data */
8248 pData->dwDataLength -= 8;
8249 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8250 if(dwSize != sizeof(pData->strUnknown))
8252 hRes = CONVERT10_E_OLESTREAM_GET;
8256 if(hRes == S_OK)
8258 if(pData->dwDataLength > 0)
8260 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8262 /* Get Data (ex. IStorage, Metafile, or BMP) */
8263 if(pData->pData)
8265 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8266 if(dwSize != pData->dwDataLength)
8268 hRes = CONVERT10_E_OLESTREAM_GET;
8271 else
8273 hRes = CONVERT10_E_OLESTREAM_GET;
8279 return hRes;
8282 /*************************************************************************
8283 * OLECONVERT_SaveOLE10 [Internal]
8285 * Saves the OLE10 STREAM From memory
8287 * PARAMS
8288 * pData [I] Data Structure for the OLESTREAM Data
8289 * pOleStream [I] The OLESTREAM to save
8291 * RETURNS
8292 * Success: S_OK
8293 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8295 * NOTES
8296 * This function is used by OleConvertIStorageToOLESTREAM only.
8299 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8301 DWORD dwSize;
8302 HRESULT hRes = S_OK;
8305 /* Set the OleID */
8306 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8307 if(dwSize != sizeof(pData->dwOleID))
8309 hRes = CONVERT10_E_OLESTREAM_PUT;
8312 if(hRes == S_OK)
8314 /* Set the TypeID */
8315 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8316 if(dwSize != sizeof(pData->dwTypeID))
8318 hRes = CONVERT10_E_OLESTREAM_PUT;
8322 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8324 /* Set the Length of the OleTypeName */
8325 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8326 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8328 hRes = CONVERT10_E_OLESTREAM_PUT;
8331 if(hRes == S_OK)
8333 if(pData->dwOleTypeNameLength > 0)
8335 /* Set the OleTypeName */
8336 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8337 if(dwSize != pData->dwOleTypeNameLength)
8339 hRes = CONVERT10_E_OLESTREAM_PUT;
8344 if(hRes == S_OK)
8346 /* Set the width of the Metafile */
8347 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8348 if(dwSize != sizeof(pData->dwMetaFileWidth))
8350 hRes = CONVERT10_E_OLESTREAM_PUT;
8354 if(hRes == S_OK)
8356 /* Set the height of the Metafile */
8357 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8358 if(dwSize != sizeof(pData->dwMetaFileHeight))
8360 hRes = CONVERT10_E_OLESTREAM_PUT;
8364 if(hRes == S_OK)
8366 /* Set the length of the Data */
8367 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8368 if(dwSize != sizeof(pData->dwDataLength))
8370 hRes = CONVERT10_E_OLESTREAM_PUT;
8374 if(hRes == S_OK)
8376 if(pData->dwDataLength > 0)
8378 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8379 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8380 if(dwSize != pData->dwDataLength)
8382 hRes = CONVERT10_E_OLESTREAM_PUT;
8387 return hRes;
8390 /*************************************************************************
8391 * OLECONVERT_GetOLE20FromOLE10[Internal]
8393 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8394 * opens it, and copies the content to the dest IStorage for
8395 * OleConvertOLESTREAMToIStorage
8398 * PARAMS
8399 * pDestStorage [I] The IStorage to copy the data to
8400 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8401 * nBufferLength [I] The size of the buffer
8403 * RETURNS
8404 * Nothing
8406 * NOTES
8410 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8412 HRESULT hRes;
8413 HANDLE hFile;
8414 IStorage *pTempStorage;
8415 DWORD dwNumOfBytesWritten;
8416 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8417 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8419 /* Create a temp File */
8420 GetTempPathW(MAX_PATH, wstrTempDir);
8421 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8422 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8424 if(hFile != INVALID_HANDLE_VALUE)
8426 /* Write IStorage Data to File */
8427 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8428 CloseHandle(hFile);
8430 /* Open and copy temp storage to the Dest Storage */
8431 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8432 if(hRes == S_OK)
8434 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8435 IStorage_Release(pTempStorage);
8437 DeleteFileW(wstrTempFile);
8442 /*************************************************************************
8443 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8445 * Saves the OLE10 STREAM From memory
8447 * PARAMS
8448 * pStorage [I] The Src IStorage to copy
8449 * pData [I] The Dest Memory to write to.
8451 * RETURNS
8452 * The size in bytes allocated for pData
8454 * NOTES
8455 * Memory allocated for pData must be freed by the caller
8457 * Used by OleConvertIStorageToOLESTREAM only.
8460 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8462 HANDLE hFile;
8463 HRESULT hRes;
8464 DWORD nDataLength = 0;
8465 IStorage *pTempStorage;
8466 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8467 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8469 *pData = NULL;
8471 /* Create temp Storage */
8472 GetTempPathW(MAX_PATH, wstrTempDir);
8473 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8474 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8476 if(hRes == S_OK)
8478 /* Copy Src Storage to the Temp Storage */
8479 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8480 IStorage_Release(pTempStorage);
8482 /* Open Temp Storage as a file and copy to memory */
8483 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8484 if(hFile != INVALID_HANDLE_VALUE)
8486 nDataLength = GetFileSize(hFile, NULL);
8487 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8488 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8489 CloseHandle(hFile);
8491 DeleteFileW(wstrTempFile);
8493 return nDataLength;
8496 /*************************************************************************
8497 * OLECONVERT_CreateOleStream [Internal]
8499 * Creates the "\001OLE" stream in the IStorage if necessary.
8501 * PARAMS
8502 * pStorage [I] Dest storage to create the stream in
8504 * RETURNS
8505 * Nothing
8507 * NOTES
8508 * This function is used by OleConvertOLESTREAMToIStorage only.
8510 * This stream is still unknown, MS Word seems to have extra data
8511 * but since the data is stored in the OLESTREAM there should be
8512 * no need to recreate the stream. If the stream is manually
8513 * deleted it will create it with this default data.
8516 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8518 HRESULT hRes;
8519 IStream *pStream;
8520 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8521 BYTE pOleStreamHeader [] =
8523 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8524 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8525 0x00, 0x00, 0x00, 0x00
8528 /* Create stream if not present */
8529 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8530 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8532 if(hRes == S_OK)
8534 /* Write default Data */
8535 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8536 IStream_Release(pStream);
8540 /* write a string to a stream, preceded by its length */
8541 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8543 HRESULT r;
8544 LPSTR str;
8545 DWORD len = 0;
8547 if( string )
8548 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8549 r = IStream_Write( stm, &len, sizeof(len), NULL);
8550 if( FAILED( r ) )
8551 return r;
8552 if(len == 0)
8553 return r;
8554 str = CoTaskMemAlloc( len );
8555 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8556 r = IStream_Write( stm, str, len, NULL);
8557 CoTaskMemFree( str );
8558 return r;
8561 /* read a string preceded by its length from a stream */
8562 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8564 HRESULT r;
8565 DWORD len, count = 0;
8566 LPSTR str;
8567 LPWSTR wstr;
8569 r = IStream_Read( stm, &len, sizeof(len), &count );
8570 if( FAILED( r ) )
8571 return r;
8572 if( count != sizeof(len) )
8573 return E_OUTOFMEMORY;
8575 TRACE("%d bytes\n",len);
8577 str = CoTaskMemAlloc( len );
8578 if( !str )
8579 return E_OUTOFMEMORY;
8580 count = 0;
8581 r = IStream_Read( stm, str, len, &count );
8582 if( FAILED( r ) )
8583 return r;
8584 if( count != len )
8586 CoTaskMemFree( str );
8587 return E_OUTOFMEMORY;
8590 TRACE("Read string %s\n",debugstr_an(str,len));
8592 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8593 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8594 if( wstr )
8595 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8596 CoTaskMemFree( str );
8598 *string = wstr;
8600 return r;
8604 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8605 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8607 IStream *pstm;
8608 HRESULT r = S_OK;
8609 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8611 static const BYTE unknown1[12] =
8612 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8613 0xFF, 0xFF, 0xFF, 0xFF};
8614 static const BYTE unknown2[16] =
8615 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8616 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8618 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8619 debugstr_w(lpszUserType), debugstr_w(szClipName),
8620 debugstr_w(szProgIDName));
8622 /* Create a CompObj stream */
8623 r = IStorage_CreateStream(pstg, szwStreamName,
8624 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8625 if( FAILED (r) )
8626 return r;
8628 /* Write CompObj Structure to stream */
8629 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8631 if( SUCCEEDED( r ) )
8632 r = WriteClassStm( pstm, clsid );
8634 if( SUCCEEDED( r ) )
8635 r = STREAM_WriteString( pstm, lpszUserType );
8636 if( SUCCEEDED( r ) )
8637 r = STREAM_WriteString( pstm, szClipName );
8638 if( SUCCEEDED( r ) )
8639 r = STREAM_WriteString( pstm, szProgIDName );
8640 if( SUCCEEDED( r ) )
8641 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8643 IStream_Release( pstm );
8645 return r;
8648 /***********************************************************************
8649 * WriteFmtUserTypeStg (OLE32.@)
8651 HRESULT WINAPI WriteFmtUserTypeStg(
8652 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8654 HRESULT r;
8655 WCHAR szwClipName[0x40];
8656 CLSID clsid = CLSID_NULL;
8657 LPWSTR wstrProgID = NULL;
8658 DWORD n;
8660 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8662 /* get the clipboard format name */
8663 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8664 szwClipName[n]=0;
8666 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8668 /* FIXME: There's room to save a CLSID and its ProgID, but
8669 the CLSID is not looked up in the registry and in all the
8670 tests I wrote it was CLSID_NULL. Where does it come from?
8673 /* get the real program ID. This may fail, but that's fine */
8674 ProgIDFromCLSID(&clsid, &wstrProgID);
8676 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8678 r = STORAGE_WriteCompObj( pstg, &clsid,
8679 lpszUserType, szwClipName, wstrProgID );
8681 CoTaskMemFree(wstrProgID);
8683 return r;
8687 /******************************************************************************
8688 * ReadFmtUserTypeStg [OLE32.@]
8690 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8692 HRESULT r;
8693 IStream *stm = 0;
8694 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8695 unsigned char unknown1[12];
8696 unsigned char unknown2[16];
8697 DWORD count;
8698 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8699 CLSID clsid;
8701 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8703 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8704 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8705 if( FAILED ( r ) )
8707 WARN("Failed to open stream r = %08x\n", r);
8708 return r;
8711 /* read the various parts of the structure */
8712 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8713 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8714 goto end;
8715 r = ReadClassStm( stm, &clsid );
8716 if( FAILED( r ) )
8717 goto end;
8719 r = STREAM_ReadString( stm, &szCLSIDName );
8720 if( FAILED( r ) )
8721 goto end;
8723 r = STREAM_ReadString( stm, &szOleTypeName );
8724 if( FAILED( r ) )
8725 goto end;
8727 r = STREAM_ReadString( stm, &szProgIDName );
8728 if( FAILED( r ) )
8729 goto end;
8731 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8732 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8733 goto end;
8735 /* ok, success... now we just need to store what we found */
8736 if( pcf )
8737 *pcf = RegisterClipboardFormatW( szOleTypeName );
8738 CoTaskMemFree( szOleTypeName );
8740 if( lplpszUserType )
8741 *lplpszUserType = szCLSIDName;
8742 CoTaskMemFree( szProgIDName );
8744 end:
8745 IStream_Release( stm );
8747 return r;
8751 /*************************************************************************
8752 * OLECONVERT_CreateCompObjStream [Internal]
8754 * Creates a "\001CompObj" is the destination IStorage if necessary.
8756 * PARAMS
8757 * pStorage [I] The dest IStorage to create the CompObj Stream
8758 * if necessary.
8759 * strOleTypeName [I] The ProgID
8761 * RETURNS
8762 * Success: S_OK
8763 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8765 * NOTES
8766 * This function is used by OleConvertOLESTREAMToIStorage only.
8768 * The stream data is stored in the OLESTREAM and there should be
8769 * no need to recreate the stream. If the stream is manually
8770 * deleted it will attempt to create it by querying the registry.
8774 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8776 IStream *pStream;
8777 HRESULT hStorageRes, hRes = S_OK;
8778 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8779 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8780 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8782 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8783 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8785 /* Initialize the CompObj structure */
8786 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8787 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8788 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8791 /* Create a CompObj stream if it doesn't exist */
8792 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8793 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8794 if(hStorageRes == S_OK)
8796 /* copy the OleTypeName to the compobj struct */
8797 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8798 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8800 /* copy the OleTypeName to the compobj struct */
8801 /* Note: in the test made, these were Identical */
8802 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8803 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8805 /* Get the CLSID */
8806 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8807 bufferW, OLESTREAM_MAX_STR_LEN );
8808 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8810 if(hRes == S_OK)
8812 HKEY hKey;
8813 LONG hErr;
8814 /* Get the CLSID Default Name from the Registry */
8815 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8816 if(hErr == ERROR_SUCCESS)
8818 char strTemp[OLESTREAM_MAX_STR_LEN];
8819 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8820 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8821 if(hErr == ERROR_SUCCESS)
8823 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8825 RegCloseKey(hKey);
8829 /* Write CompObj Structure to stream */
8830 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8832 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8834 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8835 if(IStorageCompObj.dwCLSIDNameLength > 0)
8837 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8839 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8840 if(IStorageCompObj.dwOleTypeNameLength > 0)
8842 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8844 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8845 if(IStorageCompObj.dwProgIDNameLength > 0)
8847 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8849 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8850 IStream_Release(pStream);
8852 return hRes;
8856 /*************************************************************************
8857 * OLECONVERT_CreateOlePresStream[Internal]
8859 * Creates the "\002OlePres000" Stream with the Metafile data
8861 * PARAMS
8862 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8863 * dwExtentX [I] Width of the Metafile
8864 * dwExtentY [I] Height of the Metafile
8865 * pData [I] Metafile data
8866 * dwDataLength [I] Size of the Metafile data
8868 * RETURNS
8869 * Success: S_OK
8870 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8872 * NOTES
8873 * This function is used by OleConvertOLESTREAMToIStorage only.
8876 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8878 HRESULT hRes;
8879 IStream *pStream;
8880 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8881 BYTE pOlePresStreamHeader [] =
8883 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8884 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8885 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8886 0x00, 0x00, 0x00, 0x00
8889 BYTE pOlePresStreamHeaderEmpty [] =
8891 0x00, 0x00, 0x00, 0x00,
8892 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8893 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8894 0x00, 0x00, 0x00, 0x00
8897 /* Create the OlePres000 Stream */
8898 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8899 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8901 if(hRes == S_OK)
8903 DWORD nHeaderSize;
8904 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8906 memset(&OlePres, 0, sizeof(OlePres));
8907 /* Do we have any metafile data to save */
8908 if(dwDataLength > 0)
8910 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8911 nHeaderSize = sizeof(pOlePresStreamHeader);
8913 else
8915 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8916 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8918 /* Set width and height of the metafile */
8919 OlePres.dwExtentX = dwExtentX;
8920 OlePres.dwExtentY = -dwExtentY;
8922 /* Set Data and Length */
8923 if(dwDataLength > sizeof(METAFILEPICT16))
8925 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8926 OlePres.pData = &(pData[8]);
8928 /* Save OlePres000 Data to Stream */
8929 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8930 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8931 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8932 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8933 if(OlePres.dwSize > 0)
8935 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8937 IStream_Release(pStream);
8941 /*************************************************************************
8942 * OLECONVERT_CreateOle10NativeStream [Internal]
8944 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8946 * PARAMS
8947 * pStorage [I] Dest storage to create the stream in
8948 * pData [I] Ole10 Native Data (ex. bmp)
8949 * dwDataLength [I] Size of the Ole10 Native Data
8951 * RETURNS
8952 * Nothing
8954 * NOTES
8955 * This function is used by OleConvertOLESTREAMToIStorage only.
8957 * Might need to verify the data and return appropriate error message
8960 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8962 HRESULT hRes;
8963 IStream *pStream;
8964 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8966 /* Create the Ole10Native Stream */
8967 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8968 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8970 if(hRes == S_OK)
8972 /* Write info to stream */
8973 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8974 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8975 IStream_Release(pStream);
8980 /*************************************************************************
8981 * OLECONVERT_GetOLE10ProgID [Internal]
8983 * Finds the ProgID (or OleTypeID) from the IStorage
8985 * PARAMS
8986 * pStorage [I] The Src IStorage to get the ProgID
8987 * strProgID [I] the ProgID string to get
8988 * dwSize [I] the size of the string
8990 * RETURNS
8991 * Success: S_OK
8992 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8994 * NOTES
8995 * This function is used by OleConvertIStorageToOLESTREAM only.
8999 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9001 HRESULT hRes;
9002 IStream *pStream;
9003 LARGE_INTEGER iSeekPos;
9004 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9005 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9007 /* Open the CompObj Stream */
9008 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9009 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9010 if(hRes == S_OK)
9013 /*Get the OleType from the CompObj Stream */
9014 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9015 iSeekPos.u.HighPart = 0;
9017 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9018 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9019 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9020 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9021 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9022 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9023 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9025 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9026 if(*dwSize > 0)
9028 IStream_Read(pStream, strProgID, *dwSize, NULL);
9030 IStream_Release(pStream);
9032 else
9034 STATSTG stat;
9035 LPOLESTR wstrProgID;
9037 /* Get the OleType from the registry */
9038 REFCLSID clsid = &(stat.clsid);
9039 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9040 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9041 if(hRes == S_OK)
9043 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9047 return hRes;
9050 /*************************************************************************
9051 * OLECONVERT_GetOle10PresData [Internal]
9053 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9055 * PARAMS
9056 * pStorage [I] Src IStroage
9057 * pOleStream [I] Dest OleStream Mem Struct
9059 * RETURNS
9060 * Nothing
9062 * NOTES
9063 * This function is used by OleConvertIStorageToOLESTREAM only.
9065 * Memory allocated for pData must be freed by the caller
9069 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9072 HRESULT hRes;
9073 IStream *pStream;
9074 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9076 /* Initialize Default data for OLESTREAM */
9077 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9078 pOleStreamData[0].dwTypeID = 2;
9079 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9080 pOleStreamData[1].dwTypeID = 0;
9081 pOleStreamData[0].dwMetaFileWidth = 0;
9082 pOleStreamData[0].dwMetaFileHeight = 0;
9083 pOleStreamData[0].pData = NULL;
9084 pOleStreamData[1].pData = NULL;
9086 /* Open Ole10Native Stream */
9087 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9088 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9089 if(hRes == S_OK)
9092 /* Read Size and Data */
9093 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9094 if(pOleStreamData->dwDataLength > 0)
9096 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9097 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9099 IStream_Release(pStream);
9105 /*************************************************************************
9106 * OLECONVERT_GetOle20PresData[Internal]
9108 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9110 * PARAMS
9111 * pStorage [I] Src IStroage
9112 * pOleStreamData [I] Dest OleStream Mem Struct
9114 * RETURNS
9115 * Nothing
9117 * NOTES
9118 * This function is used by OleConvertIStorageToOLESTREAM only.
9120 * Memory allocated for pData must be freed by the caller
9122 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9124 HRESULT hRes;
9125 IStream *pStream;
9126 OLECONVERT_ISTORAGE_OLEPRES olePress;
9127 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9129 /* Initialize Default data for OLESTREAM */
9130 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9131 pOleStreamData[0].dwTypeID = 2;
9132 pOleStreamData[0].dwMetaFileWidth = 0;
9133 pOleStreamData[0].dwMetaFileHeight = 0;
9134 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9135 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9136 pOleStreamData[1].dwTypeID = 0;
9137 pOleStreamData[1].dwOleTypeNameLength = 0;
9138 pOleStreamData[1].strOleTypeName[0] = 0;
9139 pOleStreamData[1].dwMetaFileWidth = 0;
9140 pOleStreamData[1].dwMetaFileHeight = 0;
9141 pOleStreamData[1].pData = NULL;
9142 pOleStreamData[1].dwDataLength = 0;
9145 /* Open OlePress000 stream */
9146 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9147 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9148 if(hRes == S_OK)
9150 LARGE_INTEGER iSeekPos;
9151 METAFILEPICT16 MetaFilePict;
9152 static const char strMetafilePictName[] = "METAFILEPICT";
9154 /* Set the TypeID for a Metafile */
9155 pOleStreamData[1].dwTypeID = 5;
9157 /* Set the OleTypeName to Metafile */
9158 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9159 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9161 iSeekPos.u.HighPart = 0;
9162 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9164 /* Get Presentation Data */
9165 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9166 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9167 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9168 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9170 /*Set width and Height */
9171 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9172 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9173 if(olePress.dwSize > 0)
9175 /* Set Length */
9176 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9178 /* Set MetaFilePict struct */
9179 MetaFilePict.mm = 8;
9180 MetaFilePict.xExt = olePress.dwExtentX;
9181 MetaFilePict.yExt = olePress.dwExtentY;
9182 MetaFilePict.hMF = 0;
9184 /* Get Metafile Data */
9185 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9186 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9187 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9189 IStream_Release(pStream);
9193 /*************************************************************************
9194 * OleConvertOLESTREAMToIStorage [OLE32.@]
9196 * Read info on MSDN
9198 * TODO
9199 * DVTARGETDEVICE parameter is not handled
9200 * Still unsure of some mem fields for OLE 10 Stream
9201 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9202 * and "\001OLE" streams
9205 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9206 LPOLESTREAM pOleStream,
9207 LPSTORAGE pstg,
9208 const DVTARGETDEVICE* ptd)
9210 int i;
9211 HRESULT hRes=S_OK;
9212 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9214 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9216 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9218 if(ptd != NULL)
9220 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9223 if(pstg == NULL || pOleStream == NULL)
9225 hRes = E_INVALIDARG;
9228 if(hRes == S_OK)
9230 /* Load the OLESTREAM to Memory */
9231 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9234 if(hRes == S_OK)
9236 /* Load the OLESTREAM to Memory (part 2)*/
9237 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9240 if(hRes == S_OK)
9243 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9245 /* Do we have the IStorage Data in the OLESTREAM */
9246 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9248 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9249 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9251 else
9253 /* It must be an original OLE 1.0 source */
9254 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9257 else
9259 /* It must be an original OLE 1.0 source */
9260 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9263 /* Create CompObj Stream if necessary */
9264 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9265 if(hRes == S_OK)
9267 /*Create the Ole Stream if necessary */
9268 OLECONVERT_CreateOleStream(pstg);
9273 /* Free allocated memory */
9274 for(i=0; i < 2; i++)
9276 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9277 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9278 pOleStreamData[i].pstrOleObjFileName = NULL;
9280 return hRes;
9283 /*************************************************************************
9284 * OleConvertIStorageToOLESTREAM [OLE32.@]
9286 * Read info on MSDN
9288 * Read info on MSDN
9290 * TODO
9291 * Still unsure of some mem fields for OLE 10 Stream
9292 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9293 * and "\001OLE" streams.
9296 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9297 LPSTORAGE pstg,
9298 LPOLESTREAM pOleStream)
9300 int i;
9301 HRESULT hRes = S_OK;
9302 IStream *pStream;
9303 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9304 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9306 TRACE("%p %p\n", pstg, pOleStream);
9308 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9310 if(pstg == NULL || pOleStream == NULL)
9312 hRes = E_INVALIDARG;
9314 if(hRes == S_OK)
9316 /* Get the ProgID */
9317 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9318 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9320 if(hRes == S_OK)
9322 /* Was it originally Ole10 */
9323 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9324 if(hRes == S_OK)
9326 IStream_Release(pStream);
9327 /* Get Presentation Data for Ole10Native */
9328 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9330 else
9332 /* Get Presentation Data (OLE20) */
9333 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9336 /* Save OLESTREAM */
9337 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9338 if(hRes == S_OK)
9340 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9345 /* Free allocated memory */
9346 for(i=0; i < 2; i++)
9348 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9351 return hRes;
9354 /***********************************************************************
9355 * GetConvertStg (OLE32.@)
9357 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9358 FIXME("unimplemented stub!\n");
9359 return E_FAIL;
9362 /******************************************************************************
9363 * StgIsStorageFile [OLE32.@]
9364 * Verify if the file contains a storage object
9366 * PARAMS
9367 * fn [ I] Filename
9369 * RETURNS
9370 * S_OK if file has magic bytes as a storage object
9371 * S_FALSE if file is not storage
9373 HRESULT WINAPI
9374 StgIsStorageFile(LPCOLESTR fn)
9376 HANDLE hf;
9377 BYTE magic[8];
9378 DWORD bytes_read;
9380 TRACE("%s\n", debugstr_w(fn));
9381 hf = CreateFileW(fn, GENERIC_READ,
9382 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9383 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9385 if (hf == INVALID_HANDLE_VALUE)
9386 return STG_E_FILENOTFOUND;
9388 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9390 WARN(" unable to read file\n");
9391 CloseHandle(hf);
9392 return S_FALSE;
9395 CloseHandle(hf);
9397 if (bytes_read != 8) {
9398 TRACE(" too short\n");
9399 return S_FALSE;
9402 if (!memcmp(magic,STORAGE_magic,8)) {
9403 TRACE(" -> YES\n");
9404 return S_OK;
9407 TRACE(" -> Invalid header.\n");
9408 return S_FALSE;
9411 /***********************************************************************
9412 * WriteClassStm (OLE32.@)
9414 * Writes a CLSID to a stream.
9416 * PARAMS
9417 * pStm [I] Stream to write to.
9418 * rclsid [I] CLSID to write.
9420 * RETURNS
9421 * Success: S_OK.
9422 * Failure: HRESULT code.
9424 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9426 TRACE("(%p,%p)\n",pStm,rclsid);
9428 if (!pStm || !rclsid)
9429 return E_INVALIDARG;
9431 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9434 /***********************************************************************
9435 * ReadClassStm (OLE32.@)
9437 * Reads a CLSID from a stream.
9439 * PARAMS
9440 * pStm [I] Stream to read from.
9441 * rclsid [O] CLSID to read.
9443 * RETURNS
9444 * Success: S_OK.
9445 * Failure: HRESULT code.
9447 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9449 ULONG nbByte;
9450 HRESULT res;
9452 TRACE("(%p,%p)\n",pStm,pclsid);
9454 if (!pStm || !pclsid)
9455 return E_INVALIDARG;
9457 /* clear the output args */
9458 *pclsid = CLSID_NULL;
9460 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9462 if (FAILED(res))
9463 return res;
9465 if (nbByte != sizeof(CLSID))
9466 return STG_E_READFAULT;
9467 else
9468 return S_OK;