gdi32: Add some additional tests for negative axes extents.
[wine/wine-gecko.git] / dlls / ole32 / storage32.c
blobfa9c06f9ddb3b8ab6a3df56e7b93ef0fff6b7d4e
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 IEnumSTATSTG IEnumSTATSTG_iface;
301 LONG ref; /* Reference count */
302 StorageBaseImpl* parentStorage; /* Reference to the parent storage */
303 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
305 WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
308 static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
310 return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
314 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
315 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
317 /************************************************************************
318 ** Block Functions
321 static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
323 return (index+1) * This->bigBlockSize;
326 /************************************************************************
327 ** Storage32BaseImpl implementation
329 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
330 ULARGE_INTEGER offset,
331 void* buffer,
332 ULONG size,
333 ULONG* bytesRead)
335 return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
338 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
339 ULARGE_INTEGER offset,
340 const void* buffer,
341 const ULONG size,
342 ULONG* bytesWritten)
344 return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
347 /************************************************************************
348 * Storage32BaseImpl_QueryInterface (IUnknown)
350 * This method implements the common QueryInterface for all IStorage32
351 * implementations contained in this file.
353 * See Windows documentation for more details on IUnknown methods.
355 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
356 IStorage* iface,
357 REFIID riid,
358 void** ppvObject)
360 StorageBaseImpl *This = (StorageBaseImpl *)iface;
362 if ( (This==0) || (ppvObject==0) )
363 return E_INVALIDARG;
365 *ppvObject = 0;
367 if (IsEqualGUID(&IID_IUnknown, riid) ||
368 IsEqualGUID(&IID_IStorage, riid))
370 *ppvObject = This;
372 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
374 *ppvObject = &This->pssVtbl;
377 if ((*ppvObject)==0)
378 return E_NOINTERFACE;
380 IStorage_AddRef(iface);
382 return S_OK;
385 /************************************************************************
386 * Storage32BaseImpl_AddRef (IUnknown)
388 * This method implements the common AddRef for all IStorage32
389 * implementations contained in this file.
391 * See Windows documentation for more details on IUnknown methods.
393 static ULONG WINAPI StorageBaseImpl_AddRef(
394 IStorage* iface)
396 StorageBaseImpl *This = (StorageBaseImpl *)iface;
397 ULONG ref = InterlockedIncrement(&This->ref);
399 TRACE("(%p) AddRef to %d\n", This, ref);
401 return ref;
404 /************************************************************************
405 * Storage32BaseImpl_Release (IUnknown)
407 * This method implements the common Release for all IStorage32
408 * implementations contained in this file.
410 * See Windows documentation for more details on IUnknown methods.
412 static ULONG WINAPI StorageBaseImpl_Release(
413 IStorage* iface)
415 StorageBaseImpl *This = (StorageBaseImpl *)iface;
417 ULONG ref = InterlockedDecrement(&This->ref);
419 TRACE("(%p) ReleaseRef to %d\n", This, ref);
421 if (ref == 0)
424 * Since we are using a system of base-classes, we want to call the
425 * destructor of the appropriate derived class. To do this, we are
426 * using virtual functions to implement the destructor.
428 StorageBaseImpl_Destroy(This);
431 return ref;
434 /************************************************************************
435 * Storage32BaseImpl_OpenStream (IStorage)
437 * This method will open the specified stream object from the current storage.
439 * See Windows documentation for more details on IStorage methods.
441 static HRESULT WINAPI StorageBaseImpl_OpenStream(
442 IStorage* iface,
443 const OLECHAR* pwcsName, /* [string][in] */
444 void* reserved1, /* [unique][in] */
445 DWORD grfMode, /* [in] */
446 DWORD reserved2, /* [in] */
447 IStream** ppstm) /* [out] */
449 StorageBaseImpl *This = (StorageBaseImpl *)iface;
450 StgStreamImpl* newStream;
451 DirEntry currentEntry;
452 DirRef streamEntryRef;
453 HRESULT res = STG_E_UNKNOWN;
455 TRACE("(%p, %s, %p, %x, %d, %p)\n",
456 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
458 if ( (pwcsName==NULL) || (ppstm==0) )
460 res = E_INVALIDARG;
461 goto end;
464 *ppstm = NULL;
466 if ( FAILED( validateSTGM(grfMode) ) ||
467 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
469 res = STG_E_INVALIDFLAG;
470 goto end;
474 * As documented.
476 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
478 res = STG_E_INVALIDFUNCTION;
479 goto end;
482 if (This->reverted)
484 res = STG_E_REVERTED;
485 goto end;
489 * Check that we're compatible with the parent's storage mode, but
490 * only if we are not in transacted mode
492 if(!(This->openFlags & STGM_TRANSACTED)) {
493 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
495 res = STG_E_INVALIDFLAG;
496 goto end;
501 * Search for the element with the given name
503 streamEntryRef = findElement(
504 This,
505 This->storageDirEntry,
506 pwcsName,
507 &currentEntry);
510 * If it was found, construct the stream object and return a pointer to it.
512 if ( (streamEntryRef!=DIRENTRY_NULL) &&
513 (currentEntry.stgType==STGTY_STREAM) )
515 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
517 /* A single stream cannot be opened a second time. */
518 res = STG_E_ACCESSDENIED;
519 goto end;
522 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
524 if (newStream!=0)
526 newStream->grfMode = grfMode;
527 *ppstm = (IStream*)newStream;
529 IStream_AddRef(*ppstm);
531 res = S_OK;
532 goto end;
535 res = E_OUTOFMEMORY;
536 goto end;
539 res = STG_E_FILENOTFOUND;
541 end:
542 if (res == S_OK)
543 TRACE("<-- IStream %p\n", *ppstm);
544 TRACE("<-- %08x\n", res);
545 return res;
548 /************************************************************************
549 * Storage32BaseImpl_OpenStorage (IStorage)
551 * This method will open a new storage object from the current storage.
553 * See Windows documentation for more details on IStorage methods.
555 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
556 IStorage* iface,
557 const OLECHAR* pwcsName, /* [string][unique][in] */
558 IStorage* pstgPriority, /* [unique][in] */
559 DWORD grfMode, /* [in] */
560 SNB snbExclude, /* [unique][in] */
561 DWORD reserved, /* [in] */
562 IStorage** ppstg) /* [out] */
564 StorageBaseImpl *This = (StorageBaseImpl *)iface;
565 StorageInternalImpl* newStorage;
566 StorageBaseImpl* newTransactedStorage;
567 DirEntry currentEntry;
568 DirRef storageEntryRef;
569 HRESULT res = STG_E_UNKNOWN;
571 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
572 iface, debugstr_w(pwcsName), pstgPriority,
573 grfMode, snbExclude, reserved, ppstg);
575 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
577 res = E_INVALIDARG;
578 goto end;
581 if (This->openFlags & STGM_SIMPLE)
583 res = STG_E_INVALIDFUNCTION;
584 goto end;
587 /* as documented */
588 if (snbExclude != NULL)
590 res = STG_E_INVALIDPARAMETER;
591 goto end;
594 if ( FAILED( validateSTGM(grfMode) ))
596 res = STG_E_INVALIDFLAG;
597 goto end;
601 * As documented.
603 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
604 (grfMode & STGM_DELETEONRELEASE) ||
605 (grfMode & STGM_PRIORITY) )
607 res = STG_E_INVALIDFUNCTION;
608 goto end;
611 if (This->reverted)
612 return STG_E_REVERTED;
615 * Check that we're compatible with the parent's storage mode,
616 * but only if we are not transacted
618 if(!(This->openFlags & STGM_TRANSACTED)) {
619 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
621 res = STG_E_ACCESSDENIED;
622 goto end;
626 *ppstg = NULL;
628 storageEntryRef = findElement(
629 This,
630 This->storageDirEntry,
631 pwcsName,
632 &currentEntry);
634 if ( (storageEntryRef!=DIRENTRY_NULL) &&
635 (currentEntry.stgType==STGTY_STORAGE) )
637 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
639 /* A single storage cannot be opened a second time. */
640 res = STG_E_ACCESSDENIED;
641 goto end;
644 newStorage = StorageInternalImpl_Construct(
645 This,
646 grfMode,
647 storageEntryRef);
649 if (newStorage != 0)
651 if (grfMode & STGM_TRANSACTED)
653 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
655 if (FAILED(res))
657 HeapFree(GetProcessHeap(), 0, newStorage);
658 goto end;
661 *ppstg = (IStorage*)newTransactedStorage;
663 else
665 *ppstg = (IStorage*)newStorage;
668 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
670 res = S_OK;
671 goto end;
674 res = STG_E_INSUFFICIENTMEMORY;
675 goto end;
678 res = STG_E_FILENOTFOUND;
680 end:
681 TRACE("<-- %08x\n", res);
682 return res;
685 /************************************************************************
686 * Storage32BaseImpl_EnumElements (IStorage)
688 * This method will create an enumerator object that can be used to
689 * retrieve information about all the elements in the storage object.
691 * See Windows documentation for more details on IStorage methods.
693 static HRESULT WINAPI StorageBaseImpl_EnumElements(
694 IStorage* iface,
695 DWORD reserved1, /* [in] */
696 void* reserved2, /* [size_is][unique][in] */
697 DWORD reserved3, /* [in] */
698 IEnumSTATSTG** ppenum) /* [out] */
700 StorageBaseImpl *This = (StorageBaseImpl *)iface;
701 IEnumSTATSTGImpl* newEnum;
703 TRACE("(%p, %d, %p, %d, %p)\n",
704 iface, reserved1, reserved2, reserved3, ppenum);
706 if ( (This==0) || (ppenum==0))
707 return E_INVALIDARG;
709 if (This->reverted)
710 return STG_E_REVERTED;
712 newEnum = IEnumSTATSTGImpl_Construct(
713 This,
714 This->storageDirEntry);
716 if (newEnum!=0)
718 *ppenum = &newEnum->IEnumSTATSTG_iface;
720 IEnumSTATSTG_AddRef(*ppenum);
722 return S_OK;
725 return E_OUTOFMEMORY;
728 /************************************************************************
729 * Storage32BaseImpl_Stat (IStorage)
731 * This method will retrieve information about this storage object.
733 * See Windows documentation for more details on IStorage methods.
735 static HRESULT WINAPI StorageBaseImpl_Stat(
736 IStorage* iface,
737 STATSTG* pstatstg, /* [out] */
738 DWORD grfStatFlag) /* [in] */
740 StorageBaseImpl *This = (StorageBaseImpl *)iface;
741 DirEntry currentEntry;
742 HRESULT res = STG_E_UNKNOWN;
744 TRACE("(%p, %p, %x)\n",
745 iface, pstatstg, grfStatFlag);
747 if ( (This==0) || (pstatstg==0))
749 res = E_INVALIDARG;
750 goto end;
753 if (This->reverted)
755 res = STG_E_REVERTED;
756 goto end;
759 res = StorageBaseImpl_ReadDirEntry(
760 This,
761 This->storageDirEntry,
762 &currentEntry);
764 if (SUCCEEDED(res))
766 StorageUtl_CopyDirEntryToSTATSTG(
767 This,
768 pstatstg,
769 &currentEntry,
770 grfStatFlag);
772 pstatstg->grfMode = This->openFlags;
773 pstatstg->grfStateBits = This->stateBits;
776 end:
777 if (res == S_OK)
779 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);
781 TRACE("<-- %08x\n", res);
782 return res;
785 /************************************************************************
786 * Storage32BaseImpl_RenameElement (IStorage)
788 * This method will rename the specified element.
790 * See Windows documentation for more details on IStorage methods.
792 static HRESULT WINAPI StorageBaseImpl_RenameElement(
793 IStorage* iface,
794 const OLECHAR* pwcsOldName, /* [in] */
795 const OLECHAR* pwcsNewName) /* [in] */
797 StorageBaseImpl *This = (StorageBaseImpl *)iface;
798 DirEntry currentEntry;
799 DirRef currentEntryRef;
801 TRACE("(%p, %s, %s)\n",
802 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
804 if (This->reverted)
805 return STG_E_REVERTED;
807 currentEntryRef = findElement(This,
808 This->storageDirEntry,
809 pwcsNewName,
810 &currentEntry);
812 if (currentEntryRef != DIRENTRY_NULL)
815 * There is already an element with the new name
817 return STG_E_FILEALREADYEXISTS;
821 * Search for the old element name
823 currentEntryRef = findElement(This,
824 This->storageDirEntry,
825 pwcsOldName,
826 &currentEntry);
828 if (currentEntryRef != DIRENTRY_NULL)
830 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
831 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
833 WARN("Element is already open; cannot rename.\n");
834 return STG_E_ACCESSDENIED;
837 /* Remove the element from its current position in the tree */
838 removeFromTree(This, This->storageDirEntry,
839 currentEntryRef);
841 /* Change the name of the element */
842 strcpyW(currentEntry.name, pwcsNewName);
844 /* Delete any sibling links */
845 currentEntry.leftChild = DIRENTRY_NULL;
846 currentEntry.rightChild = DIRENTRY_NULL;
848 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
849 &currentEntry);
851 /* Insert the element in a new position in the tree */
852 insertIntoTree(This, This->storageDirEntry,
853 currentEntryRef);
855 else
858 * There is no element with the old name
860 return STG_E_FILENOTFOUND;
863 return StorageBaseImpl_Flush(This);
866 /************************************************************************
867 * Storage32BaseImpl_CreateStream (IStorage)
869 * This method will create a stream object within this storage
871 * See Windows documentation for more details on IStorage methods.
873 static HRESULT WINAPI StorageBaseImpl_CreateStream(
874 IStorage* iface,
875 const OLECHAR* pwcsName, /* [string][in] */
876 DWORD grfMode, /* [in] */
877 DWORD reserved1, /* [in] */
878 DWORD reserved2, /* [in] */
879 IStream** ppstm) /* [out] */
881 StorageBaseImpl *This = (StorageBaseImpl *)iface;
882 StgStreamImpl* newStream;
883 DirEntry currentEntry, newStreamEntry;
884 DirRef currentEntryRef, newStreamEntryRef;
885 HRESULT hr;
887 TRACE("(%p, %s, %x, %d, %d, %p)\n",
888 iface, debugstr_w(pwcsName), grfMode,
889 reserved1, reserved2, ppstm);
891 if (ppstm == 0)
892 return STG_E_INVALIDPOINTER;
894 if (pwcsName == 0)
895 return STG_E_INVALIDNAME;
897 if (reserved1 || reserved2)
898 return STG_E_INVALIDPARAMETER;
900 if ( FAILED( validateSTGM(grfMode) ))
901 return STG_E_INVALIDFLAG;
903 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
904 return STG_E_INVALIDFLAG;
906 if (This->reverted)
907 return STG_E_REVERTED;
910 * As documented.
912 if ((grfMode & STGM_DELETEONRELEASE) ||
913 (grfMode & STGM_TRANSACTED))
914 return STG_E_INVALIDFUNCTION;
917 * Don't worry about permissions in transacted mode, as we can always write
918 * changes; we just can't always commit them.
920 if(!(This->openFlags & STGM_TRANSACTED)) {
921 /* Can't create a stream on read-only storage */
922 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
923 return STG_E_ACCESSDENIED;
925 /* Can't create a stream with greater access than the parent. */
926 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
927 return STG_E_ACCESSDENIED;
930 if(This->openFlags & STGM_SIMPLE)
931 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
933 *ppstm = 0;
935 currentEntryRef = findElement(This,
936 This->storageDirEntry,
937 pwcsName,
938 &currentEntry);
940 if (currentEntryRef != DIRENTRY_NULL)
943 * An element with this name already exists
945 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
947 IStorage_DestroyElement(iface, pwcsName);
949 else
950 return STG_E_FILEALREADYEXISTS;
954 * memset the empty entry
956 memset(&newStreamEntry, 0, sizeof(DirEntry));
958 newStreamEntry.sizeOfNameString =
959 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
961 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
962 return STG_E_INVALIDNAME;
964 strcpyW(newStreamEntry.name, pwcsName);
966 newStreamEntry.stgType = STGTY_STREAM;
967 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
968 newStreamEntry.size.u.LowPart = 0;
969 newStreamEntry.size.u.HighPart = 0;
971 newStreamEntry.leftChild = DIRENTRY_NULL;
972 newStreamEntry.rightChild = DIRENTRY_NULL;
973 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
975 /* call CoFileTime to get the current time
976 newStreamEntry.ctime
977 newStreamEntry.mtime
980 /* newStreamEntry.clsid */
983 * Create an entry with the new data
985 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
986 if (FAILED(hr))
987 return hr;
990 * Insert the new entry in the parent storage's tree.
992 hr = insertIntoTree(
993 This,
994 This->storageDirEntry,
995 newStreamEntryRef);
996 if (FAILED(hr))
998 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
999 return hr;
1003 * Open the stream to return it.
1005 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
1007 if (newStream != 0)
1009 *ppstm = (IStream*)newStream;
1011 IStream_AddRef(*ppstm);
1013 else
1015 return STG_E_INSUFFICIENTMEMORY;
1018 return StorageBaseImpl_Flush(This);
1021 /************************************************************************
1022 * Storage32BaseImpl_SetClass (IStorage)
1024 * This method will write the specified CLSID in the directory entry of this
1025 * storage.
1027 * See Windows documentation for more details on IStorage methods.
1029 static HRESULT WINAPI StorageBaseImpl_SetClass(
1030 IStorage* iface,
1031 REFCLSID clsid) /* [in] */
1033 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1034 HRESULT hRes;
1035 DirEntry currentEntry;
1037 TRACE("(%p, %p)\n", iface, clsid);
1039 if (This->reverted)
1040 return STG_E_REVERTED;
1042 hRes = StorageBaseImpl_ReadDirEntry(This,
1043 This->storageDirEntry,
1044 &currentEntry);
1045 if (SUCCEEDED(hRes))
1047 currentEntry.clsid = *clsid;
1049 hRes = StorageBaseImpl_WriteDirEntry(This,
1050 This->storageDirEntry,
1051 &currentEntry);
1054 if (SUCCEEDED(hRes))
1055 hRes = StorageBaseImpl_Flush(This);
1057 return hRes;
1060 /************************************************************************
1061 ** Storage32Impl implementation
1064 /************************************************************************
1065 * Storage32BaseImpl_CreateStorage (IStorage)
1067 * This method will create the storage object within the provided storage.
1069 * See Windows documentation for more details on IStorage methods.
1071 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1072 IStorage* iface,
1073 const OLECHAR *pwcsName, /* [string][in] */
1074 DWORD grfMode, /* [in] */
1075 DWORD reserved1, /* [in] */
1076 DWORD reserved2, /* [in] */
1077 IStorage **ppstg) /* [out] */
1079 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1081 DirEntry currentEntry;
1082 DirEntry newEntry;
1083 DirRef currentEntryRef;
1084 DirRef newEntryRef;
1085 HRESULT hr;
1087 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1088 iface, debugstr_w(pwcsName), grfMode,
1089 reserved1, reserved2, ppstg);
1091 if (ppstg == 0)
1092 return STG_E_INVALIDPOINTER;
1094 if (This->openFlags & STGM_SIMPLE)
1096 return STG_E_INVALIDFUNCTION;
1099 if (pwcsName == 0)
1100 return STG_E_INVALIDNAME;
1102 *ppstg = NULL;
1104 if ( FAILED( validateSTGM(grfMode) ) ||
1105 (grfMode & STGM_DELETEONRELEASE) )
1107 WARN("bad grfMode: 0x%x\n", grfMode);
1108 return STG_E_INVALIDFLAG;
1111 if (This->reverted)
1112 return STG_E_REVERTED;
1115 * Check that we're compatible with the parent's storage mode
1117 if ( !(This->openFlags & STGM_TRANSACTED) &&
1118 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1120 WARN("access denied\n");
1121 return STG_E_ACCESSDENIED;
1124 currentEntryRef = findElement(This,
1125 This->storageDirEntry,
1126 pwcsName,
1127 &currentEntry);
1129 if (currentEntryRef != DIRENTRY_NULL)
1132 * An element with this name already exists
1134 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1135 ((This->openFlags & STGM_TRANSACTED) ||
1136 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1138 hr = IStorage_DestroyElement(iface, pwcsName);
1139 if (FAILED(hr))
1140 return hr;
1142 else
1144 WARN("file already exists\n");
1145 return STG_E_FILEALREADYEXISTS;
1148 else if (!(This->openFlags & STGM_TRANSACTED) &&
1149 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1151 WARN("read-only storage\n");
1152 return STG_E_ACCESSDENIED;
1155 memset(&newEntry, 0, sizeof(DirEntry));
1157 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1159 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1161 FIXME("name too long\n");
1162 return STG_E_INVALIDNAME;
1165 strcpyW(newEntry.name, pwcsName);
1167 newEntry.stgType = STGTY_STORAGE;
1168 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1169 newEntry.size.u.LowPart = 0;
1170 newEntry.size.u.HighPart = 0;
1172 newEntry.leftChild = DIRENTRY_NULL;
1173 newEntry.rightChild = DIRENTRY_NULL;
1174 newEntry.dirRootEntry = DIRENTRY_NULL;
1176 /* call CoFileTime to get the current time
1177 newEntry.ctime
1178 newEntry.mtime
1181 /* newEntry.clsid */
1184 * Create a new directory entry for the storage
1186 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1187 if (FAILED(hr))
1188 return hr;
1191 * Insert the new directory entry into the parent storage's tree
1193 hr = insertIntoTree(
1194 This,
1195 This->storageDirEntry,
1196 newEntryRef);
1197 if (FAILED(hr))
1199 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1200 return hr;
1204 * Open it to get a pointer to return.
1206 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1208 if( (hr != S_OK) || (*ppstg == NULL))
1210 return hr;
1213 if (SUCCEEDED(hr))
1214 hr = StorageBaseImpl_Flush(This);
1216 return S_OK;
1220 /***************************************************************************
1222 * Internal Method
1224 * Reserve a directory entry in the file and initialize it.
1226 static HRESULT StorageImpl_CreateDirEntry(
1227 StorageBaseImpl *base,
1228 const DirEntry *newData,
1229 DirRef *index)
1231 StorageImpl *storage = (StorageImpl*)base;
1232 ULONG currentEntryIndex = 0;
1233 ULONG newEntryIndex = DIRENTRY_NULL;
1234 HRESULT hr = S_OK;
1235 BYTE currentData[RAW_DIRENTRY_SIZE];
1236 WORD sizeOfNameString;
1240 hr = StorageImpl_ReadRawDirEntry(storage,
1241 currentEntryIndex,
1242 currentData);
1244 if (SUCCEEDED(hr))
1246 StorageUtl_ReadWord(
1247 currentData,
1248 OFFSET_PS_NAMELENGTH,
1249 &sizeOfNameString);
1251 if (sizeOfNameString == 0)
1254 * The entry exists and is available, we found it.
1256 newEntryIndex = currentEntryIndex;
1259 else
1262 * We exhausted the directory entries, we will create more space below
1264 newEntryIndex = currentEntryIndex;
1266 currentEntryIndex++;
1268 } while (newEntryIndex == DIRENTRY_NULL);
1271 * grow the directory stream
1273 if (FAILED(hr))
1275 BYTE emptyData[RAW_DIRENTRY_SIZE];
1276 ULARGE_INTEGER newSize;
1277 ULONG entryIndex;
1278 ULONG lastEntry = 0;
1279 ULONG blockCount = 0;
1282 * obtain the new count of blocks in the directory stream
1284 blockCount = BlockChainStream_GetCount(
1285 storage->rootBlockChain)+1;
1288 * initialize the size used by the directory stream
1290 newSize.u.HighPart = 0;
1291 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1294 * add a block to the directory stream
1296 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1299 * memset the empty entry in order to initialize the unused newly
1300 * created entries
1302 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1305 * initialize them
1307 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1309 for(
1310 entryIndex = newEntryIndex + 1;
1311 entryIndex < lastEntry;
1312 entryIndex++)
1314 StorageImpl_WriteRawDirEntry(
1315 storage,
1316 entryIndex,
1317 emptyData);
1320 StorageImpl_SaveFileHeader(storage);
1323 UpdateRawDirEntry(currentData, newData);
1325 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1327 if (SUCCEEDED(hr))
1328 *index = newEntryIndex;
1330 return hr;
1333 /***************************************************************************
1335 * Internal Method
1337 * Mark a directory entry in the file as free.
1339 static HRESULT StorageImpl_DestroyDirEntry(
1340 StorageBaseImpl *base,
1341 DirRef index)
1343 HRESULT hr;
1344 BYTE emptyData[RAW_DIRENTRY_SIZE];
1345 StorageImpl *storage = (StorageImpl*)base;
1347 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1349 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1351 return hr;
1355 /****************************************************************************
1357 * Internal Method
1359 * Case insensitive comparison of DirEntry.name by first considering
1360 * their size.
1362 * Returns <0 when name1 < name2
1363 * >0 when name1 > name2
1364 * 0 when name1 == name2
1366 static LONG entryNameCmp(
1367 const OLECHAR *name1,
1368 const OLECHAR *name2)
1370 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1372 while (diff == 0 && *name1 != 0)
1375 * We compare the string themselves only when they are of the same length
1377 diff = toupperW(*name1++) - toupperW(*name2++);
1380 return diff;
1383 /****************************************************************************
1385 * Internal Method
1387 * Add a directory entry to a storage
1389 static HRESULT insertIntoTree(
1390 StorageBaseImpl *This,
1391 DirRef parentStorageIndex,
1392 DirRef newEntryIndex)
1394 DirEntry currentEntry;
1395 DirEntry newEntry;
1398 * Read the inserted entry
1400 StorageBaseImpl_ReadDirEntry(This,
1401 newEntryIndex,
1402 &newEntry);
1405 * Read the storage entry
1407 StorageBaseImpl_ReadDirEntry(This,
1408 parentStorageIndex,
1409 &currentEntry);
1411 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1414 * The root storage contains some element, therefore, start the research
1415 * for the appropriate location.
1417 BOOL found = 0;
1418 DirRef current, next, previous, currentEntryId;
1421 * Keep a reference to the root of the storage's element tree
1423 currentEntryId = currentEntry.dirRootEntry;
1426 * Read
1428 StorageBaseImpl_ReadDirEntry(This,
1429 currentEntry.dirRootEntry,
1430 &currentEntry);
1432 previous = currentEntry.leftChild;
1433 next = currentEntry.rightChild;
1434 current = currentEntryId;
1436 while (found == 0)
1438 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1440 if (diff < 0)
1442 if (previous != DIRENTRY_NULL)
1444 StorageBaseImpl_ReadDirEntry(This,
1445 previous,
1446 &currentEntry);
1447 current = previous;
1449 else
1451 currentEntry.leftChild = newEntryIndex;
1452 StorageBaseImpl_WriteDirEntry(This,
1453 current,
1454 &currentEntry);
1455 found = 1;
1458 else if (diff > 0)
1460 if (next != DIRENTRY_NULL)
1462 StorageBaseImpl_ReadDirEntry(This,
1463 next,
1464 &currentEntry);
1465 current = next;
1467 else
1469 currentEntry.rightChild = newEntryIndex;
1470 StorageBaseImpl_WriteDirEntry(This,
1471 current,
1472 &currentEntry);
1473 found = 1;
1476 else
1479 * Trying to insert an item with the same name in the
1480 * subtree structure.
1482 return STG_E_FILEALREADYEXISTS;
1485 previous = currentEntry.leftChild;
1486 next = currentEntry.rightChild;
1489 else
1492 * The storage is empty, make the new entry the root of its element tree
1494 currentEntry.dirRootEntry = newEntryIndex;
1495 StorageBaseImpl_WriteDirEntry(This,
1496 parentStorageIndex,
1497 &currentEntry);
1500 return S_OK;
1503 /****************************************************************************
1505 * Internal Method
1507 * Find and read the element of a storage with the given name.
1509 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1510 const OLECHAR *name, DirEntry *data)
1512 DirRef currentEntry;
1514 /* Read the storage entry to find the root of the tree. */
1515 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1517 currentEntry = data->dirRootEntry;
1519 while (currentEntry != DIRENTRY_NULL)
1521 LONG cmp;
1523 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1525 cmp = entryNameCmp(name, data->name);
1527 if (cmp == 0)
1528 /* found it */
1529 break;
1531 else if (cmp < 0)
1532 currentEntry = data->leftChild;
1534 else if (cmp > 0)
1535 currentEntry = data->rightChild;
1538 return currentEntry;
1541 /****************************************************************************
1543 * Internal Method
1545 * Find and read the binary tree parent of the element with the given name.
1547 * If there is no such element, find a place where it could be inserted and
1548 * return STG_E_FILENOTFOUND.
1550 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1551 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1552 ULONG *relation)
1554 DirRef childEntry;
1555 DirEntry childData;
1557 /* Read the storage entry to find the root of the tree. */
1558 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1560 *parentEntry = storageEntry;
1561 *relation = DIRENTRY_RELATION_DIR;
1563 childEntry = parentData->dirRootEntry;
1565 while (childEntry != DIRENTRY_NULL)
1567 LONG cmp;
1569 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1571 cmp = entryNameCmp(childName, childData.name);
1573 if (cmp == 0)
1574 /* found it */
1575 break;
1577 else if (cmp < 0)
1579 *parentData = childData;
1580 *parentEntry = childEntry;
1581 *relation = DIRENTRY_RELATION_PREVIOUS;
1583 childEntry = parentData->leftChild;
1586 else if (cmp > 0)
1588 *parentData = childData;
1589 *parentEntry = childEntry;
1590 *relation = DIRENTRY_RELATION_NEXT;
1592 childEntry = parentData->rightChild;
1596 if (childEntry == DIRENTRY_NULL)
1597 return STG_E_FILENOTFOUND;
1598 else
1599 return S_OK;
1603 /*************************************************************************
1604 * CopyTo (IStorage)
1606 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1607 IStorage* iface,
1608 DWORD ciidExclude, /* [in] */
1609 const IID* rgiidExclude, /* [size_is][unique][in] */
1610 SNB snbExclude, /* [unique][in] */
1611 IStorage* pstgDest) /* [unique][in] */
1613 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1615 IEnumSTATSTG *elements = 0;
1616 STATSTG curElement, strStat;
1617 HRESULT hr;
1618 IStorage *pstgTmp, *pstgChild;
1619 IStream *pstrTmp, *pstrChild;
1620 DirRef srcEntryRef;
1621 DirEntry srcEntry;
1622 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1623 int i;
1625 TRACE("(%p, %d, %p, %p, %p)\n",
1626 iface, ciidExclude, rgiidExclude,
1627 snbExclude, pstgDest);
1629 if ( pstgDest == 0 )
1630 return STG_E_INVALIDPOINTER;
1633 * Enumerate the elements
1635 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1637 if ( hr != S_OK )
1638 return hr;
1641 * set the class ID
1643 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1644 IStorage_SetClass( pstgDest, &curElement.clsid );
1646 for(i = 0; i < ciidExclude; ++i)
1648 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1649 skip_storage = TRUE;
1650 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1651 skip_stream = TRUE;
1652 else
1653 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1659 * Obtain the next element
1661 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1663 if ( hr == S_FALSE )
1665 hr = S_OK; /* done, every element has been copied */
1666 break;
1669 if ( snbExclude )
1671 WCHAR **snb = snbExclude;
1672 skip = FALSE;
1673 while ( *snb != NULL && !skip )
1675 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1676 skip = TRUE;
1677 ++snb;
1681 if ( skip )
1682 goto cleanup;
1684 if (curElement.type == STGTY_STORAGE)
1686 if(skip_storage)
1687 goto cleanup;
1690 * open child source storage
1692 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1693 STGM_READ|STGM_SHARE_EXCLUSIVE,
1694 NULL, 0, &pstgChild );
1696 if (hr != S_OK)
1697 goto cleanup;
1700 * create a new storage in destination storage
1702 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1703 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1704 0, 0,
1705 &pstgTmp );
1707 * if it already exist, don't create a new one use this one
1709 if (hr == STG_E_FILEALREADYEXISTS)
1711 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1712 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1713 NULL, 0, &pstgTmp );
1716 if (hr == S_OK)
1719 * do the copy recursively
1721 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1722 NULL, pstgTmp );
1724 IStorage_Release( pstgTmp );
1727 IStorage_Release( pstgChild );
1729 else if (curElement.type == STGTY_STREAM)
1731 if(skip_stream)
1732 goto cleanup;
1735 * create a new stream in destination storage. If the stream already
1736 * exist, it will be deleted and a new one will be created.
1738 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1739 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1740 0, 0, &pstrTmp );
1742 if (hr != S_OK)
1743 goto cleanup;
1746 * open child stream storage. This operation must succeed even if the
1747 * stream is already open, so we use internal functions to do it.
1749 srcEntryRef = findElement( This, This->storageDirEntry, curElement.pwcsName,
1750 &srcEntry);
1751 if (!srcEntryRef)
1753 ERR("source stream not found\n");
1754 hr = STG_E_DOCFILECORRUPT;
1757 if (hr == S_OK)
1759 pstrChild = (IStream*)StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntryRef);
1760 if (pstrChild)
1761 IStream_AddRef(pstrChild);
1762 else
1763 hr = E_OUTOFMEMORY;
1766 if (hr == S_OK)
1769 * Get the size of the source stream
1771 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1774 * Set the size of the destination stream.
1776 IStream_SetSize(pstrTmp, strStat.cbSize);
1779 * do the copy
1781 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1782 NULL, NULL );
1784 IStream_Release( pstrChild );
1787 IStream_Release( pstrTmp );
1789 else
1791 WARN("unknown element type: %d\n", curElement.type);
1794 cleanup:
1795 CoTaskMemFree(curElement.pwcsName);
1796 } while (hr == S_OK);
1799 * Clean-up
1801 IEnumSTATSTG_Release(elements);
1803 return hr;
1806 /*************************************************************************
1807 * MoveElementTo (IStorage)
1809 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1810 IStorage* iface,
1811 const OLECHAR *pwcsName, /* [string][in] */
1812 IStorage *pstgDest, /* [unique][in] */
1813 const OLECHAR *pwcsNewName,/* [string][in] */
1814 DWORD grfFlags) /* [in] */
1816 FIXME("(%p %s %p %s %u): stub\n", iface,
1817 debugstr_w(pwcsName), pstgDest,
1818 debugstr_w(pwcsNewName), grfFlags);
1819 return E_NOTIMPL;
1822 /*************************************************************************
1823 * Commit (IStorage)
1825 * Ensures that any changes made to a storage object open in transacted mode
1826 * are reflected in the parent storage
1828 * In a non-transacted mode, this ensures all cached writes are completed.
1830 static HRESULT WINAPI StorageImpl_Commit(
1831 IStorage* iface,
1832 DWORD grfCommitFlags)/* [in] */
1834 StorageBaseImpl* const base=(StorageBaseImpl*)iface;
1835 TRACE("(%p %d)\n", iface, grfCommitFlags);
1836 return StorageBaseImpl_Flush(base);
1839 /*************************************************************************
1840 * Revert (IStorage)
1842 * Discard all changes that have been made since the last commit operation
1844 static HRESULT WINAPI StorageImpl_Revert(
1845 IStorage* iface)
1847 TRACE("(%p)\n", iface);
1848 return S_OK;
1851 /*************************************************************************
1852 * DestroyElement (IStorage)
1854 * Strategy: This implementation is built this way for simplicity not for speed.
1855 * I always delete the topmost element of the enumeration and adjust
1856 * the deleted element pointer all the time. This takes longer to
1857 * do but allow to reinvoke DestroyElement whenever we encounter a
1858 * storage object. The optimisation resides in the usage of another
1859 * enumeration strategy that would give all the leaves of a storage
1860 * first. (postfix order)
1862 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1863 IStorage* iface,
1864 const OLECHAR *pwcsName)/* [string][in] */
1866 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1868 HRESULT hr = S_OK;
1869 DirEntry entryToDelete;
1870 DirRef entryToDeleteRef;
1872 TRACE("(%p, %s)\n",
1873 iface, debugstr_w(pwcsName));
1875 if (pwcsName==NULL)
1876 return STG_E_INVALIDPOINTER;
1878 if (This->reverted)
1879 return STG_E_REVERTED;
1881 if ( !(This->openFlags & STGM_TRANSACTED) &&
1882 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1883 return STG_E_ACCESSDENIED;
1885 entryToDeleteRef = findElement(
1886 This,
1887 This->storageDirEntry,
1888 pwcsName,
1889 &entryToDelete);
1891 if ( entryToDeleteRef == DIRENTRY_NULL )
1893 return STG_E_FILENOTFOUND;
1896 if ( entryToDelete.stgType == STGTY_STORAGE )
1898 hr = deleteStorageContents(
1899 This,
1900 entryToDeleteRef,
1901 entryToDelete);
1903 else if ( entryToDelete.stgType == STGTY_STREAM )
1905 hr = deleteStreamContents(
1906 This,
1907 entryToDeleteRef,
1908 entryToDelete);
1911 if (hr!=S_OK)
1912 return hr;
1915 * Remove the entry from its parent storage
1917 hr = removeFromTree(
1918 This,
1919 This->storageDirEntry,
1920 entryToDeleteRef);
1923 * Invalidate the entry
1925 if (SUCCEEDED(hr))
1926 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1928 if (SUCCEEDED(hr))
1929 hr = StorageBaseImpl_Flush(This);
1931 return hr;
1935 /******************************************************************************
1936 * Internal stream list handlers
1939 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1941 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1942 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1945 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1947 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1948 list_remove(&(strm->StrmListEntry));
1951 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1953 StgStreamImpl *strm;
1955 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1957 if (strm->dirEntry == streamEntry)
1959 return TRUE;
1963 return FALSE;
1966 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1968 StorageInternalImpl *childstg;
1970 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1972 if (childstg->base.storageDirEntry == storageEntry)
1974 return TRUE;
1978 return FALSE;
1981 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1983 struct list *cur, *cur2;
1984 StgStreamImpl *strm=NULL;
1985 StorageInternalImpl *childstg=NULL;
1987 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1988 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1989 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1990 strm->parentStorage = NULL;
1991 list_remove(cur);
1994 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1995 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1996 StorageBaseImpl_Invalidate( &childstg->base );
1999 if (stg->transactedChild)
2001 StorageBaseImpl_Invalidate(stg->transactedChild);
2003 stg->transactedChild = NULL;
2008 /*********************************************************************
2010 * Internal Method
2012 * Delete the contents of a storage entry.
2015 static HRESULT deleteStorageContents(
2016 StorageBaseImpl *parentStorage,
2017 DirRef indexToDelete,
2018 DirEntry entryDataToDelete)
2020 IEnumSTATSTG *elements = 0;
2021 IStorage *childStorage = 0;
2022 STATSTG currentElement;
2023 HRESULT hr;
2024 HRESULT destroyHr = S_OK;
2025 StorageInternalImpl *stg, *stg2;
2027 /* Invalidate any open storage objects. */
2028 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2030 if (stg->base.storageDirEntry == indexToDelete)
2032 StorageBaseImpl_Invalidate(&stg->base);
2037 * Open the storage and enumerate it
2039 hr = StorageBaseImpl_OpenStorage(
2040 (IStorage*)parentStorage,
2041 entryDataToDelete.name,
2043 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2046 &childStorage);
2048 if (hr != S_OK)
2050 return hr;
2054 * Enumerate the elements
2056 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2061 * Obtain the next element
2063 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2064 if (hr==S_OK)
2066 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2068 CoTaskMemFree(currentElement.pwcsName);
2072 * We need to Reset the enumeration every time because we delete elements
2073 * and the enumeration could be invalid
2075 IEnumSTATSTG_Reset(elements);
2077 } while ((hr == S_OK) && (destroyHr == S_OK));
2079 IStorage_Release(childStorage);
2080 IEnumSTATSTG_Release(elements);
2082 return destroyHr;
2085 /*********************************************************************
2087 * Internal Method
2089 * Perform the deletion of a stream's data
2092 static HRESULT deleteStreamContents(
2093 StorageBaseImpl *parentStorage,
2094 DirRef indexToDelete,
2095 DirEntry entryDataToDelete)
2097 IStream *pis;
2098 HRESULT hr;
2099 ULARGE_INTEGER size;
2100 StgStreamImpl *strm, *strm2;
2102 /* Invalidate any open stream objects. */
2103 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2105 if (strm->dirEntry == indexToDelete)
2107 TRACE("Stream deleted %p\n", strm);
2108 strm->parentStorage = NULL;
2109 list_remove(&strm->StrmListEntry);
2113 size.u.HighPart = 0;
2114 size.u.LowPart = 0;
2116 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2117 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2119 if (hr!=S_OK)
2121 return(hr);
2125 * Zap the stream
2127 hr = IStream_SetSize(pis, size);
2129 if(hr != S_OK)
2131 return hr;
2135 * Release the stream object.
2137 IStream_Release(pis);
2139 return S_OK;
2142 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2144 switch (relation)
2146 case DIRENTRY_RELATION_PREVIOUS:
2147 entry->leftChild = new_target;
2148 break;
2149 case DIRENTRY_RELATION_NEXT:
2150 entry->rightChild = new_target;
2151 break;
2152 case DIRENTRY_RELATION_DIR:
2153 entry->dirRootEntry = new_target;
2154 break;
2155 default:
2156 assert(0);
2160 /*************************************************************************
2162 * Internal Method
2164 * This method removes a directory entry from its parent storage tree without
2165 * freeing any resources attached to it.
2167 static HRESULT removeFromTree(
2168 StorageBaseImpl *This,
2169 DirRef parentStorageIndex,
2170 DirRef deletedIndex)
2172 HRESULT hr = S_OK;
2173 DirEntry entryToDelete;
2174 DirEntry parentEntry;
2175 DirRef parentEntryRef;
2176 ULONG typeOfRelation;
2178 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2180 if (hr != S_OK)
2181 return hr;
2184 * Find the element that links to the one we want to delete.
2186 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2187 &parentEntry, &parentEntryRef, &typeOfRelation);
2189 if (hr != S_OK)
2190 return hr;
2192 if (entryToDelete.leftChild != DIRENTRY_NULL)
2195 * Replace the deleted entry with its left child
2197 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2199 hr = StorageBaseImpl_WriteDirEntry(
2200 This,
2201 parentEntryRef,
2202 &parentEntry);
2203 if(FAILED(hr))
2205 return hr;
2208 if (entryToDelete.rightChild != DIRENTRY_NULL)
2211 * We need to reinsert the right child somewhere. We already know it and
2212 * its children are greater than everything in the left tree, so we
2213 * insert it at the rightmost point in the left tree.
2215 DirRef newRightChildParent = entryToDelete.leftChild;
2216 DirEntry newRightChildParentEntry;
2220 hr = StorageBaseImpl_ReadDirEntry(
2221 This,
2222 newRightChildParent,
2223 &newRightChildParentEntry);
2224 if (FAILED(hr))
2226 return hr;
2229 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2230 newRightChildParent = newRightChildParentEntry.rightChild;
2231 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2233 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2235 hr = StorageBaseImpl_WriteDirEntry(
2236 This,
2237 newRightChildParent,
2238 &newRightChildParentEntry);
2239 if (FAILED(hr))
2241 return hr;
2245 else
2248 * Replace the deleted entry with its right child
2250 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2252 hr = StorageBaseImpl_WriteDirEntry(
2253 This,
2254 parentEntryRef,
2255 &parentEntry);
2256 if(FAILED(hr))
2258 return hr;
2262 return hr;
2266 /******************************************************************************
2267 * SetElementTimes (IStorage)
2269 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2270 IStorage* iface,
2271 const OLECHAR *pwcsName,/* [string][in] */
2272 const FILETIME *pctime, /* [in] */
2273 const FILETIME *patime, /* [in] */
2274 const FILETIME *pmtime) /* [in] */
2276 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2277 return S_OK;
2280 /******************************************************************************
2281 * SetStateBits (IStorage)
2283 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2284 IStorage* iface,
2285 DWORD grfStateBits,/* [in] */
2286 DWORD grfMask) /* [in] */
2288 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2290 if (This->reverted)
2291 return STG_E_REVERTED;
2293 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2294 return S_OK;
2297 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2298 DirRef index, const DirEntry *data)
2300 StorageImpl *This = (StorageImpl*)base;
2301 return StorageImpl_WriteDirEntry(This, index, data);
2304 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2305 DirRef index, DirEntry *data)
2307 StorageImpl *This = (StorageImpl*)base;
2308 return StorageImpl_ReadDirEntry(This, index, data);
2311 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2313 int i;
2315 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2317 if (!This->blockChainCache[i])
2319 return &This->blockChainCache[i];
2323 i = This->blockChainToEvict;
2325 BlockChainStream_Destroy(This->blockChainCache[i]);
2326 This->blockChainCache[i] = NULL;
2328 This->blockChainToEvict++;
2329 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2330 This->blockChainToEvict = 0;
2332 return &This->blockChainCache[i];
2335 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2336 DirRef index)
2338 int i, free_index=-1;
2340 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2342 if (!This->blockChainCache[i])
2344 if (free_index == -1) free_index = i;
2346 else if (This->blockChainCache[i]->ownerDirEntry == index)
2348 return &This->blockChainCache[i];
2352 if (free_index == -1)
2354 free_index = This->blockChainToEvict;
2356 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2357 This->blockChainCache[free_index] = NULL;
2359 This->blockChainToEvict++;
2360 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2361 This->blockChainToEvict = 0;
2364 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2365 return &This->blockChainCache[free_index];
2368 static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
2370 int i;
2372 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2374 if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
2376 BlockChainStream_Destroy(This->blockChainCache[i]);
2377 This->blockChainCache[i] = NULL;
2378 return;
2383 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2384 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2386 StorageImpl *This = (StorageImpl*)base;
2387 DirEntry data;
2388 HRESULT hr;
2389 ULONG bytesToRead;
2391 hr = StorageImpl_ReadDirEntry(This, index, &data);
2392 if (FAILED(hr)) return hr;
2394 if (data.size.QuadPart == 0)
2396 *bytesRead = 0;
2397 return S_OK;
2400 if (offset.QuadPart + size > data.size.QuadPart)
2402 bytesToRead = data.size.QuadPart - offset.QuadPart;
2404 else
2406 bytesToRead = size;
2409 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2411 SmallBlockChainStream *stream;
2413 stream = SmallBlockChainStream_Construct(This, NULL, index);
2414 if (!stream) return E_OUTOFMEMORY;
2416 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2418 SmallBlockChainStream_Destroy(stream);
2420 return hr;
2422 else
2424 BlockChainStream *stream = NULL;
2426 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2427 if (!stream) return E_OUTOFMEMORY;
2429 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2431 return hr;
2435 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2436 ULARGE_INTEGER newsize)
2438 StorageImpl *This = (StorageImpl*)base;
2439 DirEntry data;
2440 HRESULT hr;
2441 SmallBlockChainStream *smallblock=NULL;
2442 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2444 hr = StorageImpl_ReadDirEntry(This, index, &data);
2445 if (FAILED(hr)) return hr;
2447 /* In simple mode keep the stream size above the small block limit */
2448 if (This->base.openFlags & STGM_SIMPLE)
2449 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2451 if (data.size.QuadPart == newsize.QuadPart)
2452 return S_OK;
2454 /* Create a block chain object of the appropriate type */
2455 if (data.size.QuadPart == 0)
2457 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2459 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2460 if (!smallblock) return E_OUTOFMEMORY;
2462 else
2464 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2465 bigblock = *pbigblock;
2466 if (!bigblock) return E_OUTOFMEMORY;
2469 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2471 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2472 if (!smallblock) return E_OUTOFMEMORY;
2474 else
2476 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2477 bigblock = *pbigblock;
2478 if (!bigblock) return E_OUTOFMEMORY;
2481 /* Change the block chain type if necessary. */
2482 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2484 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2485 if (!bigblock)
2487 SmallBlockChainStream_Destroy(smallblock);
2488 return E_FAIL;
2491 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2492 *pbigblock = bigblock;
2494 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2496 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
2497 if (!smallblock)
2498 return E_FAIL;
2501 /* Set the size of the block chain. */
2502 if (smallblock)
2504 SmallBlockChainStream_SetSize(smallblock, newsize);
2505 SmallBlockChainStream_Destroy(smallblock);
2507 else
2509 BlockChainStream_SetSize(bigblock, newsize);
2512 /* Set the size in the directory entry. */
2513 hr = StorageImpl_ReadDirEntry(This, index, &data);
2514 if (SUCCEEDED(hr))
2516 data.size = newsize;
2518 hr = StorageImpl_WriteDirEntry(This, index, &data);
2520 return hr;
2523 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2524 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2526 StorageImpl *This = (StorageImpl*)base;
2527 DirEntry data;
2528 HRESULT hr;
2529 ULARGE_INTEGER newSize;
2531 hr = StorageImpl_ReadDirEntry(This, index, &data);
2532 if (FAILED(hr)) return hr;
2534 /* Grow the stream if necessary */
2535 newSize.QuadPart = 0;
2536 newSize.QuadPart = offset.QuadPart + size;
2538 if (newSize.QuadPart > data.size.QuadPart)
2540 hr = StorageImpl_StreamSetSize(base, index, newSize);
2541 if (FAILED(hr))
2542 return hr;
2544 hr = StorageImpl_ReadDirEntry(This, index, &data);
2545 if (FAILED(hr)) return hr;
2548 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2550 SmallBlockChainStream *stream;
2552 stream = SmallBlockChainStream_Construct(This, NULL, index);
2553 if (!stream) return E_OUTOFMEMORY;
2555 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2557 SmallBlockChainStream_Destroy(stream);
2559 return hr;
2561 else
2563 BlockChainStream *stream;
2565 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2566 if (!stream) return E_OUTOFMEMORY;
2568 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2570 return hr;
2574 static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
2575 DirRef src)
2577 StorageImpl *This = (StorageImpl*)base;
2578 DirEntry dst_data, src_data;
2579 HRESULT hr;
2581 hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
2583 if (SUCCEEDED(hr))
2584 hr = StorageImpl_ReadDirEntry(This, src, &src_data);
2586 if (SUCCEEDED(hr))
2588 StorageImpl_DeleteCachedBlockChainStream(This, src);
2589 dst_data.startingBlock = src_data.startingBlock;
2590 dst_data.size = src_data.size;
2592 hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
2595 return hr;
2598 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
2600 StorageImpl *This = (StorageImpl*) iface;
2601 STATSTG statstg;
2602 HRESULT hr;
2604 hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
2606 *result = statstg.pwcsName;
2608 return hr;
2612 * Virtual function table for the IStorage32Impl class.
2614 static const IStorageVtbl Storage32Impl_Vtbl =
2616 StorageBaseImpl_QueryInterface,
2617 StorageBaseImpl_AddRef,
2618 StorageBaseImpl_Release,
2619 StorageBaseImpl_CreateStream,
2620 StorageBaseImpl_OpenStream,
2621 StorageBaseImpl_CreateStorage,
2622 StorageBaseImpl_OpenStorage,
2623 StorageBaseImpl_CopyTo,
2624 StorageBaseImpl_MoveElementTo,
2625 StorageImpl_Commit,
2626 StorageImpl_Revert,
2627 StorageBaseImpl_EnumElements,
2628 StorageBaseImpl_DestroyElement,
2629 StorageBaseImpl_RenameElement,
2630 StorageBaseImpl_SetElementTimes,
2631 StorageBaseImpl_SetClass,
2632 StorageBaseImpl_SetStateBits,
2633 StorageBaseImpl_Stat
2636 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2638 StorageImpl_Destroy,
2639 StorageImpl_Invalidate,
2640 StorageImpl_Flush,
2641 StorageImpl_GetFilename,
2642 StorageImpl_CreateDirEntry,
2643 StorageImpl_BaseWriteDirEntry,
2644 StorageImpl_BaseReadDirEntry,
2645 StorageImpl_DestroyDirEntry,
2646 StorageImpl_StreamReadAt,
2647 StorageImpl_StreamWriteAt,
2648 StorageImpl_StreamSetSize,
2649 StorageImpl_StreamLink
2652 static HRESULT StorageImpl_Construct(
2653 HANDLE hFile,
2654 LPCOLESTR pwcsName,
2655 ILockBytes* pLkbyt,
2656 DWORD openFlags,
2657 BOOL fileBased,
2658 BOOL create,
2659 ULONG sector_size,
2660 StorageImpl** result)
2662 StorageImpl* This;
2663 HRESULT hr = S_OK;
2664 DirEntry currentEntry;
2665 DirRef currentEntryRef;
2667 if ( FAILED( validateSTGM(openFlags) ))
2668 return STG_E_INVALIDFLAG;
2670 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2671 if (!This)
2672 return E_OUTOFMEMORY;
2674 memset(This, 0, sizeof(StorageImpl));
2676 list_init(&This->base.strmHead);
2678 list_init(&This->base.storageHead);
2680 This->base.lpVtbl = &Storage32Impl_Vtbl;
2681 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2682 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2683 This->base.openFlags = (openFlags & ~STGM_CREATE);
2684 This->base.ref = 1;
2685 This->base.create = create;
2687 This->base.reverted = 0;
2690 * Initialize the big block cache.
2692 This->bigBlockSize = sector_size;
2693 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2694 if (hFile)
2695 hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
2696 else
2698 This->lockBytes = pLkbyt;
2699 ILockBytes_AddRef(pLkbyt);
2702 if (FAILED(hr))
2703 goto end;
2705 if (create)
2707 ULARGE_INTEGER size;
2708 BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
2711 * Initialize all header variables:
2712 * - The big block depot consists of one block and it is at block 0
2713 * - The directory table starts at block 1
2714 * - There is no small block depot
2716 memset( This->bigBlockDepotStart,
2717 BLOCK_UNUSED,
2718 sizeof(This->bigBlockDepotStart));
2720 This->bigBlockDepotCount = 1;
2721 This->bigBlockDepotStart[0] = 0;
2722 This->rootStartBlock = 1;
2723 This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
2724 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2725 if (sector_size == 4096)
2726 This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
2727 else
2728 This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
2729 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2730 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2731 This->extBigBlockDepotCount = 0;
2733 StorageImpl_SaveFileHeader(This);
2736 * Add one block for the big block depot and one block for the directory table
2738 size.u.HighPart = 0;
2739 size.u.LowPart = This->bigBlockSize * 3;
2740 ILockBytes_SetSize(This->lockBytes, size);
2743 * Initialize the big block depot
2745 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2746 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2747 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2748 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2750 else
2753 * Load the header for the file.
2755 hr = StorageImpl_LoadFileHeader(This);
2757 if (FAILED(hr))
2759 goto end;
2764 * There is no block depot cached yet.
2766 This->indexBlockDepotCached = 0xFFFFFFFF;
2767 This->indexExtBlockDepotCached = 0xFFFFFFFF;
2770 * Start searching for free blocks with block 0.
2772 This->prevFreeBlock = 0;
2774 This->firstFreeSmallBlock = 0;
2776 /* Read the extended big block depot locations. */
2777 if (This->extBigBlockDepotCount != 0)
2779 ULONG current_block = This->extBigBlockDepotStart;
2780 ULONG cache_size = This->extBigBlockDepotCount * 2;
2781 int i;
2783 This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
2784 if (!This->extBigBlockDepotLocations)
2786 hr = E_OUTOFMEMORY;
2787 goto end;
2790 This->extBigBlockDepotLocationsSize = cache_size;
2792 for (i=0; i<This->extBigBlockDepotCount; i++)
2794 if (current_block == BLOCK_END_OF_CHAIN)
2796 WARN("File has too few extended big block depot blocks.\n");
2797 hr = STG_E_DOCFILECORRUPT;
2798 goto end;
2800 This->extBigBlockDepotLocations[i] = current_block;
2801 current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
2804 else
2806 This->extBigBlockDepotLocations = NULL;
2807 This->extBigBlockDepotLocationsSize = 0;
2811 * Create the block chain abstractions.
2813 if(!(This->rootBlockChain =
2814 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2816 hr = STG_E_READFAULT;
2817 goto end;
2820 if(!(This->smallBlockDepotChain =
2821 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2822 DIRENTRY_NULL)))
2824 hr = STG_E_READFAULT;
2825 goto end;
2829 * Write the root storage entry (memory only)
2831 if (create)
2833 DirEntry rootEntry;
2835 * Initialize the directory table
2837 memset(&rootEntry, 0, sizeof(rootEntry));
2838 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2839 sizeof(rootEntry.name)/sizeof(WCHAR) );
2840 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2841 rootEntry.stgType = STGTY_ROOT;
2842 rootEntry.leftChild = DIRENTRY_NULL;
2843 rootEntry.rightChild = DIRENTRY_NULL;
2844 rootEntry.dirRootEntry = DIRENTRY_NULL;
2845 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2846 rootEntry.size.u.HighPart = 0;
2847 rootEntry.size.u.LowPart = 0;
2849 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2853 * Find the ID of the root storage.
2855 currentEntryRef = 0;
2859 hr = StorageImpl_ReadDirEntry(
2860 This,
2861 currentEntryRef,
2862 &currentEntry);
2864 if (SUCCEEDED(hr))
2866 if ( (currentEntry.sizeOfNameString != 0 ) &&
2867 (currentEntry.stgType == STGTY_ROOT) )
2869 This->base.storageDirEntry = currentEntryRef;
2873 currentEntryRef++;
2875 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2877 if (FAILED(hr))
2879 hr = STG_E_READFAULT;
2880 goto end;
2884 * Create the block chain abstraction for the small block root chain.
2886 if(!(This->smallBlockRootChain =
2887 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2889 hr = STG_E_READFAULT;
2892 end:
2893 if (FAILED(hr))
2895 IStorage_Release((IStorage*)This);
2896 *result = NULL;
2898 else
2900 StorageImpl_Flush((StorageBaseImpl*)This);
2901 *result = This;
2904 return hr;
2907 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2909 StorageImpl *This = (StorageImpl*) iface;
2911 StorageBaseImpl_DeleteAll(&This->base);
2913 This->base.reverted = 1;
2916 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2918 StorageImpl *This = (StorageImpl*) iface;
2919 int i;
2920 TRACE("(%p)\n", This);
2922 StorageImpl_Flush(iface);
2924 StorageImpl_Invalidate(iface);
2926 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
2928 BlockChainStream_Destroy(This->smallBlockRootChain);
2929 BlockChainStream_Destroy(This->rootBlockChain);
2930 BlockChainStream_Destroy(This->smallBlockDepotChain);
2932 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2933 BlockChainStream_Destroy(This->blockChainCache[i]);
2935 if (This->lockBytes)
2936 ILockBytes_Release(This->lockBytes);
2937 HeapFree(GetProcessHeap(), 0, This);
2940 static HRESULT StorageImpl_Flush(StorageBaseImpl* iface)
2942 StorageImpl *This = (StorageImpl*) iface;
2943 int i;
2944 HRESULT hr;
2945 TRACE("(%p)\n", This);
2947 hr = BlockChainStream_Flush(This->smallBlockRootChain);
2949 if (SUCCEEDED(hr))
2950 hr = BlockChainStream_Flush(This->rootBlockChain);
2952 if (SUCCEEDED(hr))
2953 hr = BlockChainStream_Flush(This->smallBlockDepotChain);
2955 for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
2956 if (This->blockChainCache[i])
2957 hr = BlockChainStream_Flush(This->blockChainCache[i]);
2959 if (SUCCEEDED(hr))
2960 hr = ILockBytes_Flush(This->lockBytes);
2962 return hr;
2965 /******************************************************************************
2966 * Storage32Impl_GetNextFreeBigBlock
2968 * Returns the index of the next free big block.
2969 * If the big block depot is filled, this method will enlarge it.
2972 static ULONG StorageImpl_GetNextFreeBigBlock(
2973 StorageImpl* This)
2975 ULONG depotBlockIndexPos;
2976 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
2977 BOOL success;
2978 ULONG depotBlockOffset;
2979 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2980 ULONG nextBlockIndex = BLOCK_SPECIAL;
2981 int depotIndex = 0;
2982 ULONG freeBlock = BLOCK_UNUSED;
2983 ULARGE_INTEGER neededSize;
2984 STATSTG statstg;
2986 depotIndex = This->prevFreeBlock / blocksPerDepot;
2987 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2990 * Scan the entire big block depot until we find a block marked free
2992 while (nextBlockIndex != BLOCK_UNUSED)
2994 if (depotIndex < COUNT_BBDEPOTINHEADER)
2996 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2999 * Grow the primary depot.
3001 if (depotBlockIndexPos == BLOCK_UNUSED)
3003 depotBlockIndexPos = depotIndex*blocksPerDepot;
3006 * Add a block depot.
3008 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3009 This->bigBlockDepotCount++;
3010 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
3013 * Flag it as a block depot.
3015 StorageImpl_SetNextBlockInChain(This,
3016 depotBlockIndexPos,
3017 BLOCK_SPECIAL);
3019 /* Save new header information.
3021 StorageImpl_SaveFileHeader(This);
3024 else
3026 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
3028 if (depotBlockIndexPos == BLOCK_UNUSED)
3031 * Grow the extended depot.
3033 ULONG extIndex = BLOCK_UNUSED;
3034 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3035 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
3037 if (extBlockOffset == 0)
3039 /* We need an extended block.
3041 extIndex = Storage32Impl_AddExtBlockDepot(This);
3042 This->extBigBlockDepotCount++;
3043 depotBlockIndexPos = extIndex + 1;
3045 else
3046 depotBlockIndexPos = depotIndex * blocksPerDepot;
3049 * Add a block depot and mark it in the extended block.
3051 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
3052 This->bigBlockDepotCount++;
3053 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
3055 /* Flag the block depot.
3057 StorageImpl_SetNextBlockInChain(This,
3058 depotBlockIndexPos,
3059 BLOCK_SPECIAL);
3061 /* If necessary, flag the extended depot block.
3063 if (extIndex != BLOCK_UNUSED)
3064 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
3066 /* Save header information.
3068 StorageImpl_SaveFileHeader(This);
3072 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3074 if (success)
3076 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
3077 ( nextBlockIndex != BLOCK_UNUSED))
3079 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3081 if (nextBlockIndex == BLOCK_UNUSED)
3083 freeBlock = (depotIndex * blocksPerDepot) +
3084 (depotBlockOffset/sizeof(ULONG));
3087 depotBlockOffset += sizeof(ULONG);
3091 depotIndex++;
3092 depotBlockOffset = 0;
3096 * make sure that the block physically exists before using it
3098 neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
3100 ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
3102 if (neededSize.QuadPart > statstg.cbSize.QuadPart)
3103 ILockBytes_SetSize(This->lockBytes, neededSize);
3105 This->prevFreeBlock = freeBlock;
3107 return freeBlock;
3110 /******************************************************************************
3111 * Storage32Impl_AddBlockDepot
3113 * This will create a depot block, essentially it is a block initialized
3114 * to BLOCK_UNUSEDs.
3116 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
3118 BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3121 * Initialize blocks as free
3123 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3124 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3127 /******************************************************************************
3128 * Storage32Impl_GetExtDepotBlock
3130 * Returns the index of the block that corresponds to the specified depot
3131 * index. This method is only for depot indexes equal or greater than
3132 * COUNT_BBDEPOTINHEADER.
3134 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
3136 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3137 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3138 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3139 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3140 ULONG blockIndex = BLOCK_UNUSED;
3141 ULONG extBlockIndex;
3142 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3143 int index, num_blocks;
3145 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3147 if (extBlockCount >= This->extBigBlockDepotCount)
3148 return BLOCK_UNUSED;
3150 if (This->indexExtBlockDepotCached != extBlockCount)
3152 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3154 StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer);
3156 num_blocks = This->bigBlockSize / 4;
3158 for (index = 0; index < num_blocks; index++)
3160 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3161 This->extBlockDepotCached[index] = blockIndex;
3164 This->indexExtBlockDepotCached = extBlockCount;
3167 blockIndex = This->extBlockDepotCached[extBlockOffset];
3169 return blockIndex;
3172 /******************************************************************************
3173 * Storage32Impl_SetExtDepotBlock
3175 * Associates the specified block index to the specified depot index.
3176 * This method is only for depot indexes equal or greater than
3177 * COUNT_BBDEPOTINHEADER.
3179 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3181 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3182 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3183 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3184 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3185 ULONG extBlockIndex;
3187 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3189 assert(extBlockCount < This->extBigBlockDepotCount);
3191 extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3193 if (extBlockIndex != BLOCK_UNUSED)
3195 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3196 extBlockOffset * sizeof(ULONG),
3197 blockIndex);
3200 if (This->indexExtBlockDepotCached == extBlockCount)
3202 This->extBlockDepotCached[extBlockOffset] = blockIndex;
3206 /******************************************************************************
3207 * Storage32Impl_AddExtBlockDepot
3209 * Creates an extended depot block.
3211 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3213 ULONG numExtBlocks = This->extBigBlockDepotCount;
3214 ULONG nextExtBlock = This->extBigBlockDepotStart;
3215 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3216 ULONG index = BLOCK_UNUSED;
3217 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3218 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3219 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3221 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3222 blocksPerDepotBlock;
3224 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3227 * The first extended block.
3229 This->extBigBlockDepotStart = index;
3231 else
3234 * Find the last existing extended block.
3236 nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3239 * Add the new extended block to the chain.
3241 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3242 index);
3246 * Initialize this block.
3248 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3249 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3251 /* Add the block to our cache. */
3252 if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3254 ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3255 ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3257 memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3258 HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3260 This->extBigBlockDepotLocations = new_cache;
3261 This->extBigBlockDepotLocationsSize = new_cache_size;
3263 This->extBigBlockDepotLocations[numExtBlocks] = index;
3265 return index;
3268 /******************************************************************************
3269 * Storage32Impl_FreeBigBlock
3271 * This method will flag the specified block as free in the big block depot.
3273 static void StorageImpl_FreeBigBlock(
3274 StorageImpl* This,
3275 ULONG blockIndex)
3277 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3279 if (blockIndex < This->prevFreeBlock)
3280 This->prevFreeBlock = blockIndex;
3283 /************************************************************************
3284 * Storage32Impl_GetNextBlockInChain
3286 * This method will retrieve the block index of the next big block in
3287 * in the chain.
3289 * Params: This - Pointer to the Storage object.
3290 * blockIndex - Index of the block to retrieve the chain
3291 * for.
3292 * nextBlockIndex - receives the return value.
3294 * Returns: This method returns the index of the next block in the chain.
3295 * It will return the constants:
3296 * BLOCK_SPECIAL - If the block given was not part of a
3297 * chain.
3298 * BLOCK_END_OF_CHAIN - If the block given was the last in
3299 * a chain.
3300 * BLOCK_UNUSED - If the block given was not past of a chain
3301 * and is available.
3302 * BLOCK_EXTBBDEPOT - This block is part of the extended
3303 * big block depot.
3305 * See Windows documentation for more details on IStorage methods.
3307 static HRESULT StorageImpl_GetNextBlockInChain(
3308 StorageImpl* This,
3309 ULONG blockIndex,
3310 ULONG* nextBlockIndex)
3312 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3313 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3314 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3315 BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3316 BOOL success;
3317 ULONG depotBlockIndexPos;
3318 int index, num_blocks;
3320 *nextBlockIndex = BLOCK_SPECIAL;
3322 if(depotBlockCount >= This->bigBlockDepotCount)
3324 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3325 This->bigBlockDepotCount);
3326 return STG_E_READFAULT;
3330 * Cache the currently accessed depot block.
3332 if (depotBlockCount != This->indexBlockDepotCached)
3334 This->indexBlockDepotCached = depotBlockCount;
3336 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3338 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3340 else
3343 * We have to look in the extended depot.
3345 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3348 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3350 if (!success)
3351 return STG_E_READFAULT;
3353 num_blocks = This->bigBlockSize / 4;
3355 for (index = 0; index < num_blocks; index++)
3357 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3358 This->blockDepotCached[index] = *nextBlockIndex;
3362 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3364 return S_OK;
3367 /******************************************************************************
3368 * Storage32Impl_GetNextExtendedBlock
3370 * Given an extended block this method will return the next extended block.
3372 * NOTES:
3373 * The last ULONG of an extended block is the block index of the next
3374 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3375 * depot.
3377 * Return values:
3378 * - The index of the next extended block
3379 * - BLOCK_UNUSED: there is no next extended block.
3380 * - Any other return values denotes failure.
3382 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3384 ULONG nextBlockIndex = BLOCK_SPECIAL;
3385 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3387 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3388 &nextBlockIndex);
3390 return nextBlockIndex;
3393 /******************************************************************************
3394 * Storage32Impl_SetNextBlockInChain
3396 * This method will write the index of the specified block's next block
3397 * in the big block depot.
3399 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3400 * do the following
3402 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3403 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3404 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3407 static void StorageImpl_SetNextBlockInChain(
3408 StorageImpl* This,
3409 ULONG blockIndex,
3410 ULONG nextBlock)
3412 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3413 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3414 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3415 ULONG depotBlockIndexPos;
3417 assert(depotBlockCount < This->bigBlockDepotCount);
3418 assert(blockIndex != nextBlock);
3420 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3422 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3424 else
3427 * We have to look in the extended depot.
3429 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3432 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3433 nextBlock);
3435 * Update the cached block depot, if necessary.
3437 if (depotBlockCount == This->indexBlockDepotCached)
3439 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3443 /******************************************************************************
3444 * Storage32Impl_LoadFileHeader
3446 * This method will read in the file header
3448 static HRESULT StorageImpl_LoadFileHeader(
3449 StorageImpl* This)
3451 HRESULT hr;
3452 BYTE headerBigBlock[HEADER_SIZE];
3453 int index;
3454 ULARGE_INTEGER offset;
3455 DWORD bytes_read;
3457 TRACE("\n");
3459 * Get a pointer to the big block of data containing the header.
3461 offset.u.HighPart = 0;
3462 offset.u.LowPart = 0;
3463 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3464 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3465 hr = STG_E_FILENOTFOUND;
3468 * Extract the information from the header.
3470 if (SUCCEEDED(hr))
3473 * Check for the "magic number" signature and return an error if it is not
3474 * found.
3476 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3478 return STG_E_OLDFORMAT;
3481 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3483 return STG_E_INVALIDHEADER;
3486 StorageUtl_ReadWord(
3487 headerBigBlock,
3488 OFFSET_BIGBLOCKSIZEBITS,
3489 &This->bigBlockSizeBits);
3491 StorageUtl_ReadWord(
3492 headerBigBlock,
3493 OFFSET_SMALLBLOCKSIZEBITS,
3494 &This->smallBlockSizeBits);
3496 StorageUtl_ReadDWord(
3497 headerBigBlock,
3498 OFFSET_BBDEPOTCOUNT,
3499 &This->bigBlockDepotCount);
3501 StorageUtl_ReadDWord(
3502 headerBigBlock,
3503 OFFSET_ROOTSTARTBLOCK,
3504 &This->rootStartBlock);
3506 StorageUtl_ReadDWord(
3507 headerBigBlock,
3508 OFFSET_SMALLBLOCKLIMIT,
3509 &This->smallBlockLimit);
3511 StorageUtl_ReadDWord(
3512 headerBigBlock,
3513 OFFSET_SBDEPOTSTART,
3514 &This->smallBlockDepotStart);
3516 StorageUtl_ReadDWord(
3517 headerBigBlock,
3518 OFFSET_EXTBBDEPOTSTART,
3519 &This->extBigBlockDepotStart);
3521 StorageUtl_ReadDWord(
3522 headerBigBlock,
3523 OFFSET_EXTBBDEPOTCOUNT,
3524 &This->extBigBlockDepotCount);
3526 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3528 StorageUtl_ReadDWord(
3529 headerBigBlock,
3530 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3531 &(This->bigBlockDepotStart[index]));
3535 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3537 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3538 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3541 * Right now, the code is making some assumptions about the size of the
3542 * blocks, just make sure they are what we're expecting.
3544 if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
3545 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
3546 This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
3548 FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
3549 This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3550 hr = STG_E_INVALIDHEADER;
3552 else
3553 hr = S_OK;
3556 return hr;
3559 /******************************************************************************
3560 * Storage32Impl_SaveFileHeader
3562 * This method will save to the file the header
3564 static void StorageImpl_SaveFileHeader(
3565 StorageImpl* This)
3567 BYTE headerBigBlock[HEADER_SIZE];
3568 int index;
3569 HRESULT hr;
3570 ULARGE_INTEGER offset;
3571 DWORD bytes_read, bytes_written;
3572 DWORD major_version, dirsectorcount;
3575 * Get a pointer to the big block of data containing the header.
3577 offset.u.HighPart = 0;
3578 offset.u.LowPart = 0;
3579 hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3580 if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3581 hr = STG_E_FILENOTFOUND;
3583 if (This->bigBlockSizeBits == 0x9)
3584 major_version = 3;
3585 else if (This->bigBlockSizeBits == 0xc)
3586 major_version = 4;
3587 else
3589 ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3590 major_version = 4;
3594 * If the block read failed, the file is probably new.
3596 if (FAILED(hr))
3599 * Initialize for all unknown fields.
3601 memset(headerBigBlock, 0, HEADER_SIZE);
3604 * Initialize the magic number.
3606 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3610 * Write the information to the header.
3612 StorageUtl_WriteWord(
3613 headerBigBlock,
3614 OFFSET_MINORVERSION,
3615 0x3e);
3617 StorageUtl_WriteWord(
3618 headerBigBlock,
3619 OFFSET_MAJORVERSION,
3620 major_version);
3622 StorageUtl_WriteWord(
3623 headerBigBlock,
3624 OFFSET_BYTEORDERMARKER,
3625 (WORD)-2);
3627 StorageUtl_WriteWord(
3628 headerBigBlock,
3629 OFFSET_BIGBLOCKSIZEBITS,
3630 This->bigBlockSizeBits);
3632 StorageUtl_WriteWord(
3633 headerBigBlock,
3634 OFFSET_SMALLBLOCKSIZEBITS,
3635 This->smallBlockSizeBits);
3637 if (major_version >= 4)
3639 if (This->rootBlockChain)
3640 dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3641 else
3642 /* This file is being created, and it will start out with one block. */
3643 dirsectorcount = 1;
3645 else
3646 /* This field must be 0 in versions older than 4 */
3647 dirsectorcount = 0;
3649 StorageUtl_WriteDWord(
3650 headerBigBlock,
3651 OFFSET_DIRSECTORCOUNT,
3652 dirsectorcount);
3654 StorageUtl_WriteDWord(
3655 headerBigBlock,
3656 OFFSET_BBDEPOTCOUNT,
3657 This->bigBlockDepotCount);
3659 StorageUtl_WriteDWord(
3660 headerBigBlock,
3661 OFFSET_ROOTSTARTBLOCK,
3662 This->rootStartBlock);
3664 StorageUtl_WriteDWord(
3665 headerBigBlock,
3666 OFFSET_SMALLBLOCKLIMIT,
3667 This->smallBlockLimit);
3669 StorageUtl_WriteDWord(
3670 headerBigBlock,
3671 OFFSET_SBDEPOTSTART,
3672 This->smallBlockDepotStart);
3674 StorageUtl_WriteDWord(
3675 headerBigBlock,
3676 OFFSET_SBDEPOTCOUNT,
3677 This->smallBlockDepotChain ?
3678 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3680 StorageUtl_WriteDWord(
3681 headerBigBlock,
3682 OFFSET_EXTBBDEPOTSTART,
3683 This->extBigBlockDepotStart);
3685 StorageUtl_WriteDWord(
3686 headerBigBlock,
3687 OFFSET_EXTBBDEPOTCOUNT,
3688 This->extBigBlockDepotCount);
3690 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3692 StorageUtl_WriteDWord(
3693 headerBigBlock,
3694 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3695 (This->bigBlockDepotStart[index]));
3699 * Write the big block back to the file.
3701 StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3704 /******************************************************************************
3705 * StorageImpl_ReadRawDirEntry
3707 * This method will read the raw data from a directory entry in the file.
3709 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3711 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3713 ULARGE_INTEGER offset;
3714 HRESULT hr;
3715 ULONG bytesRead;
3717 offset.u.HighPart = 0;
3718 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3720 hr = BlockChainStream_ReadAt(
3721 This->rootBlockChain,
3722 offset,
3723 RAW_DIRENTRY_SIZE,
3724 buffer,
3725 &bytesRead);
3727 if (bytesRead != RAW_DIRENTRY_SIZE)
3728 return STG_E_READFAULT;
3730 return hr;
3733 /******************************************************************************
3734 * StorageImpl_WriteRawDirEntry
3736 * This method will write the raw data from a directory entry in the file.
3738 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3740 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3742 ULARGE_INTEGER offset;
3743 HRESULT hr;
3744 ULONG bytesRead;
3746 offset.u.HighPart = 0;
3747 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3749 hr = BlockChainStream_WriteAt(
3750 This->rootBlockChain,
3751 offset,
3752 RAW_DIRENTRY_SIZE,
3753 buffer,
3754 &bytesRead);
3756 return hr;
3759 /******************************************************************************
3760 * UpdateRawDirEntry
3762 * Update raw directory entry data from the fields in newData.
3764 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3766 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3768 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3770 memcpy(
3771 buffer + OFFSET_PS_NAME,
3772 newData->name,
3773 DIRENTRY_NAME_BUFFER_LEN );
3775 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3777 StorageUtl_WriteWord(
3778 buffer,
3779 OFFSET_PS_NAMELENGTH,
3780 newData->sizeOfNameString);
3782 StorageUtl_WriteDWord(
3783 buffer,
3784 OFFSET_PS_LEFTCHILD,
3785 newData->leftChild);
3787 StorageUtl_WriteDWord(
3788 buffer,
3789 OFFSET_PS_RIGHTCHILD,
3790 newData->rightChild);
3792 StorageUtl_WriteDWord(
3793 buffer,
3794 OFFSET_PS_DIRROOT,
3795 newData->dirRootEntry);
3797 StorageUtl_WriteGUID(
3798 buffer,
3799 OFFSET_PS_GUID,
3800 &newData->clsid);
3802 StorageUtl_WriteDWord(
3803 buffer,
3804 OFFSET_PS_CTIMELOW,
3805 newData->ctime.dwLowDateTime);
3807 StorageUtl_WriteDWord(
3808 buffer,
3809 OFFSET_PS_CTIMEHIGH,
3810 newData->ctime.dwHighDateTime);
3812 StorageUtl_WriteDWord(
3813 buffer,
3814 OFFSET_PS_MTIMELOW,
3815 newData->mtime.dwLowDateTime);
3817 StorageUtl_WriteDWord(
3818 buffer,
3819 OFFSET_PS_MTIMEHIGH,
3820 newData->ctime.dwHighDateTime);
3822 StorageUtl_WriteDWord(
3823 buffer,
3824 OFFSET_PS_STARTBLOCK,
3825 newData->startingBlock);
3827 StorageUtl_WriteDWord(
3828 buffer,
3829 OFFSET_PS_SIZE,
3830 newData->size.u.LowPart);
3833 /******************************************************************************
3834 * Storage32Impl_ReadDirEntry
3836 * This method will read the specified directory entry.
3838 HRESULT StorageImpl_ReadDirEntry(
3839 StorageImpl* This,
3840 DirRef index,
3841 DirEntry* buffer)
3843 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3844 HRESULT readRes;
3846 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3848 if (SUCCEEDED(readRes))
3850 memset(buffer->name, 0, sizeof(buffer->name));
3851 memcpy(
3852 buffer->name,
3853 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3854 DIRENTRY_NAME_BUFFER_LEN );
3855 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3857 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3859 StorageUtl_ReadWord(
3860 currentEntry,
3861 OFFSET_PS_NAMELENGTH,
3862 &buffer->sizeOfNameString);
3864 StorageUtl_ReadDWord(
3865 currentEntry,
3866 OFFSET_PS_LEFTCHILD,
3867 &buffer->leftChild);
3869 StorageUtl_ReadDWord(
3870 currentEntry,
3871 OFFSET_PS_RIGHTCHILD,
3872 &buffer->rightChild);
3874 StorageUtl_ReadDWord(
3875 currentEntry,
3876 OFFSET_PS_DIRROOT,
3877 &buffer->dirRootEntry);
3879 StorageUtl_ReadGUID(
3880 currentEntry,
3881 OFFSET_PS_GUID,
3882 &buffer->clsid);
3884 StorageUtl_ReadDWord(
3885 currentEntry,
3886 OFFSET_PS_CTIMELOW,
3887 &buffer->ctime.dwLowDateTime);
3889 StorageUtl_ReadDWord(
3890 currentEntry,
3891 OFFSET_PS_CTIMEHIGH,
3892 &buffer->ctime.dwHighDateTime);
3894 StorageUtl_ReadDWord(
3895 currentEntry,
3896 OFFSET_PS_MTIMELOW,
3897 &buffer->mtime.dwLowDateTime);
3899 StorageUtl_ReadDWord(
3900 currentEntry,
3901 OFFSET_PS_MTIMEHIGH,
3902 &buffer->mtime.dwHighDateTime);
3904 StorageUtl_ReadDWord(
3905 currentEntry,
3906 OFFSET_PS_STARTBLOCK,
3907 &buffer->startingBlock);
3909 StorageUtl_ReadDWord(
3910 currentEntry,
3911 OFFSET_PS_SIZE,
3912 &buffer->size.u.LowPart);
3914 buffer->size.u.HighPart = 0;
3917 return readRes;
3920 /*********************************************************************
3921 * Write the specified directory entry to the file
3923 HRESULT StorageImpl_WriteDirEntry(
3924 StorageImpl* This,
3925 DirRef index,
3926 const DirEntry* buffer)
3928 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3929 HRESULT writeRes;
3931 UpdateRawDirEntry(currentEntry, buffer);
3933 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3934 return writeRes;
3937 static BOOL StorageImpl_ReadBigBlock(
3938 StorageImpl* This,
3939 ULONG blockIndex,
3940 void* buffer)
3942 ULARGE_INTEGER ulOffset;
3943 DWORD read=0;
3945 ulOffset.u.HighPart = 0;
3946 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3948 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3950 if (read && read < This->bigBlockSize)
3952 /* File ends during this block; fill the rest with 0's. */
3953 memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3956 return (read != 0);
3959 static BOOL StorageImpl_ReadDWordFromBigBlock(
3960 StorageImpl* This,
3961 ULONG blockIndex,
3962 ULONG offset,
3963 DWORD* value)
3965 ULARGE_INTEGER ulOffset;
3966 DWORD read;
3967 DWORD tmp;
3969 ulOffset.u.HighPart = 0;
3970 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3971 ulOffset.u.LowPart += offset;
3973 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3974 *value = lendian32toh(tmp);
3975 return (read == sizeof(DWORD));
3978 static BOOL StorageImpl_WriteBigBlock(
3979 StorageImpl* This,
3980 ULONG blockIndex,
3981 const void* buffer)
3983 ULARGE_INTEGER ulOffset;
3984 DWORD wrote;
3986 ulOffset.u.HighPart = 0;
3987 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3989 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3990 return (wrote == This->bigBlockSize);
3993 static BOOL StorageImpl_WriteDWordToBigBlock(
3994 StorageImpl* This,
3995 ULONG blockIndex,
3996 ULONG offset,
3997 DWORD value)
3999 ULARGE_INTEGER ulOffset;
4000 DWORD wrote;
4002 ulOffset.u.HighPart = 0;
4003 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
4004 ulOffset.u.LowPart += offset;
4006 value = htole32(value);
4007 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
4008 return (wrote == sizeof(DWORD));
4011 /******************************************************************************
4012 * Storage32Impl_SmallBlocksToBigBlocks
4014 * This method will convert a small block chain to a big block chain.
4015 * The small block chain will be destroyed.
4017 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
4018 StorageImpl* This,
4019 SmallBlockChainStream** ppsbChain)
4021 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
4022 ULARGE_INTEGER size, offset;
4023 ULONG cbRead, cbWritten;
4024 ULARGE_INTEGER cbTotalRead;
4025 DirRef streamEntryRef;
4026 HRESULT resWrite = S_OK;
4027 HRESULT resRead;
4028 DirEntry streamEntry;
4029 BYTE *buffer;
4030 BlockChainStream *bbTempChain = NULL;
4031 BlockChainStream *bigBlockChain = NULL;
4034 * Create a temporary big block chain that doesn't have
4035 * an associated directory entry. This temporary chain will be
4036 * used to copy data from small blocks to big blocks.
4038 bbTempChain = BlockChainStream_Construct(This,
4039 &bbHeadOfChain,
4040 DIRENTRY_NULL);
4041 if(!bbTempChain) return NULL;
4043 * Grow the big block chain.
4045 size = SmallBlockChainStream_GetSize(*ppsbChain);
4046 BlockChainStream_SetSize(bbTempChain, size);
4049 * Copy the contents of the small block chain to the big block chain
4050 * by small block size increments.
4052 offset.u.LowPart = 0;
4053 offset.u.HighPart = 0;
4054 cbTotalRead.QuadPart = 0;
4056 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
4059 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
4060 offset,
4061 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
4062 buffer,
4063 &cbRead);
4064 if (FAILED(resRead))
4065 break;
4067 if (cbRead > 0)
4069 cbTotalRead.QuadPart += cbRead;
4071 resWrite = BlockChainStream_WriteAt(bbTempChain,
4072 offset,
4073 cbRead,
4074 buffer,
4075 &cbWritten);
4077 if (FAILED(resWrite))
4078 break;
4080 offset.u.LowPart += cbRead;
4082 else
4084 resRead = STG_E_READFAULT;
4085 break;
4087 } while (cbTotalRead.QuadPart < size.QuadPart);
4088 HeapFree(GetProcessHeap(),0,buffer);
4090 size.u.HighPart = 0;
4091 size.u.LowPart = 0;
4093 if (FAILED(resRead) || FAILED(resWrite))
4095 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4096 BlockChainStream_SetSize(bbTempChain, size);
4097 BlockChainStream_Destroy(bbTempChain);
4098 return NULL;
4102 * Destroy the small block chain.
4104 streamEntryRef = (*ppsbChain)->ownerDirEntry;
4105 SmallBlockChainStream_SetSize(*ppsbChain, size);
4106 SmallBlockChainStream_Destroy(*ppsbChain);
4107 *ppsbChain = 0;
4110 * Change the directory entry. This chain is now a big block chain
4111 * and it doesn't reside in the small blocks chain anymore.
4113 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4115 streamEntry.startingBlock = bbHeadOfChain;
4117 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4120 * Destroy the temporary entryless big block chain.
4121 * Create a new big block chain associated with this entry.
4123 BlockChainStream_Destroy(bbTempChain);
4124 bigBlockChain = BlockChainStream_Construct(This,
4125 NULL,
4126 streamEntryRef);
4128 return bigBlockChain;
4131 /******************************************************************************
4132 * Storage32Impl_BigBlocksToSmallBlocks
4134 * This method will convert a big block chain to a small block chain.
4135 * The big block chain will be destroyed on success.
4137 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
4138 StorageImpl* This,
4139 BlockChainStream** ppbbChain,
4140 ULARGE_INTEGER newSize)
4142 ULARGE_INTEGER size, offset, cbTotalRead;
4143 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
4144 DirRef streamEntryRef;
4145 HRESULT resWrite = S_OK, resRead = S_OK;
4146 DirEntry streamEntry;
4147 BYTE* buffer;
4148 SmallBlockChainStream* sbTempChain;
4150 TRACE("%p %p\n", This, ppbbChain);
4152 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
4153 DIRENTRY_NULL);
4155 if(!sbTempChain)
4156 return NULL;
4158 SmallBlockChainStream_SetSize(sbTempChain, newSize);
4159 size = BlockChainStream_GetSize(*ppbbChain);
4160 size.QuadPart = min(size.QuadPart, newSize.QuadPart);
4162 offset.u.HighPart = 0;
4163 offset.u.LowPart = 0;
4164 cbTotalRead.QuadPart = 0;
4165 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
4166 while(cbTotalRead.QuadPart < size.QuadPart)
4168 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
4169 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
4170 buffer, &cbRead);
4172 if(FAILED(resRead))
4173 break;
4175 if(cbRead > 0)
4177 cbTotalRead.QuadPart += cbRead;
4179 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
4180 cbRead, buffer, &cbWritten);
4182 if(FAILED(resWrite))
4183 break;
4185 offset.u.LowPart += cbRead;
4187 else
4189 resRead = STG_E_READFAULT;
4190 break;
4193 HeapFree(GetProcessHeap(), 0, buffer);
4195 size.u.HighPart = 0;
4196 size.u.LowPart = 0;
4198 if(FAILED(resRead) || FAILED(resWrite))
4200 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
4201 SmallBlockChainStream_SetSize(sbTempChain, size);
4202 SmallBlockChainStream_Destroy(sbTempChain);
4203 return NULL;
4206 /* destroy the original big block chain */
4207 streamEntryRef = (*ppbbChain)->ownerDirEntry;
4208 BlockChainStream_SetSize(*ppbbChain, size);
4209 BlockChainStream_Destroy(*ppbbChain);
4210 *ppbbChain = NULL;
4212 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
4213 streamEntry.startingBlock = sbHeadOfChain;
4214 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
4216 SmallBlockChainStream_Destroy(sbTempChain);
4217 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
4220 static HRESULT StorageBaseImpl_CopyStream(
4221 StorageBaseImpl *dst, DirRef dst_entry,
4222 StorageBaseImpl *src, DirRef src_entry)
4224 HRESULT hr;
4225 BYTE data[4096];
4226 DirEntry srcdata;
4227 ULARGE_INTEGER bytes_copied;
4228 ULONG bytestocopy, bytesread, byteswritten;
4230 hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
4232 if (SUCCEEDED(hr))
4234 hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
4236 bytes_copied.QuadPart = 0;
4237 while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
4239 bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
4241 hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
4242 data, &bytesread);
4243 if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
4245 if (SUCCEEDED(hr))
4246 hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
4247 data, &byteswritten);
4248 if (SUCCEEDED(hr))
4250 if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
4251 bytes_copied.QuadPart += byteswritten;
4256 return hr;
4259 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
4261 DirRef result=This->firstFreeEntry;
4263 while (result < This->entries_size && This->entries[result].inuse)
4264 result++;
4266 if (result == This->entries_size)
4268 ULONG new_size = This->entries_size * 2;
4269 TransactedDirEntry *new_entries;
4271 new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
4272 if (!new_entries) return DIRENTRY_NULL;
4274 memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
4275 HeapFree(GetProcessHeap(), 0, This->entries);
4277 This->entries = new_entries;
4278 This->entries_size = new_size;
4281 This->entries[result].inuse = 1;
4283 This->firstFreeEntry = result+1;
4285 return result;
4288 static DirRef TransactedSnapshotImpl_CreateStubEntry(
4289 TransactedSnapshotImpl *This, DirRef parentEntryRef)
4291 DirRef stubEntryRef;
4292 TransactedDirEntry *entry;
4294 stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
4296 if (stubEntryRef != DIRENTRY_NULL)
4298 entry = &This->entries[stubEntryRef];
4300 entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
4302 entry->read = 0;
4305 return stubEntryRef;
4308 static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
4309 TransactedSnapshotImpl *This, DirRef entry)
4311 HRESULT hr=S_OK;
4312 DirEntry data;
4314 if (!This->entries[entry].read)
4316 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4317 This->entries[entry].transactedParentEntry,
4318 &data);
4320 if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
4322 data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
4324 if (data.leftChild == DIRENTRY_NULL)
4325 hr = E_OUTOFMEMORY;
4328 if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
4330 data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
4332 if (data.rightChild == DIRENTRY_NULL)
4333 hr = E_OUTOFMEMORY;
4336 if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
4338 data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
4340 if (data.dirRootEntry == DIRENTRY_NULL)
4341 hr = E_OUTOFMEMORY;
4344 if (SUCCEEDED(hr))
4346 memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
4347 This->entries[entry].read = 1;
4351 return hr;
4354 static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
4355 TransactedSnapshotImpl *This, DirRef entry)
4357 HRESULT hr = S_OK;
4359 if (!This->entries[entry].stream_dirty)
4361 DirEntry new_entrydata;
4363 memset(&new_entrydata, 0, sizeof(DirEntry));
4364 new_entrydata.name[0] = 'S';
4365 new_entrydata.sizeOfNameString = 1;
4366 new_entrydata.stgType = STGTY_STREAM;
4367 new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
4368 new_entrydata.leftChild = DIRENTRY_NULL;
4369 new_entrydata.rightChild = DIRENTRY_NULL;
4370 new_entrydata.dirRootEntry = DIRENTRY_NULL;
4372 hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
4373 &This->entries[entry].stream_entry);
4375 if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4377 hr = StorageBaseImpl_CopyStream(
4378 This->scratch, This->entries[entry].stream_entry,
4379 This->transactedParent, This->entries[entry].transactedParentEntry);
4381 if (FAILED(hr))
4382 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
4385 if (SUCCEEDED(hr))
4386 This->entries[entry].stream_dirty = 1;
4388 if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
4390 /* Since this entry is modified, and we aren't using its stream data, we
4391 * no longer care about the original entry. */
4392 DirRef delete_ref;
4393 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
4395 if (delete_ref != DIRENTRY_NULL)
4396 This->entries[delete_ref].deleted = 1;
4398 This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
4402 return hr;
4405 /* Find the first entry in a depth-first traversal. */
4406 static DirRef TransactedSnapshotImpl_FindFirstChild(
4407 TransactedSnapshotImpl* This, DirRef parent)
4409 DirRef cursor, prev;
4410 TransactedDirEntry *entry;
4412 cursor = parent;
4413 entry = &This->entries[cursor];
4414 while (entry->read)
4416 if (entry->data.leftChild != DIRENTRY_NULL)
4418 prev = cursor;
4419 cursor = entry->data.leftChild;
4420 entry = &This->entries[cursor];
4421 entry->parent = prev;
4423 else if (entry->data.rightChild != DIRENTRY_NULL)
4425 prev = cursor;
4426 cursor = entry->data.rightChild;
4427 entry = &This->entries[cursor];
4428 entry->parent = prev;
4430 else if (entry->data.dirRootEntry != DIRENTRY_NULL)
4432 prev = cursor;
4433 cursor = entry->data.dirRootEntry;
4434 entry = &This->entries[cursor];
4435 entry->parent = prev;
4437 else
4438 break;
4441 return cursor;
4444 /* Find the next entry in a depth-first traversal. */
4445 static DirRef TransactedSnapshotImpl_FindNextChild(
4446 TransactedSnapshotImpl* This, DirRef current)
4448 DirRef parent;
4449 TransactedDirEntry *parent_entry;
4451 parent = This->entries[current].parent;
4452 parent_entry = &This->entries[parent];
4454 if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
4456 if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
4458 This->entries[parent_entry->data.rightChild].parent = parent;
4459 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
4462 if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
4464 This->entries[parent_entry->data.dirRootEntry].parent = parent;
4465 return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
4469 return parent;
4472 /* Return TRUE if we've made a copy of this entry for committing to the parent. */
4473 static inline BOOL TransactedSnapshotImpl_MadeCopy(
4474 TransactedSnapshotImpl* This, DirRef entry)
4476 return entry != DIRENTRY_NULL &&
4477 This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
4480 /* Destroy the entries created by CopyTree. */
4481 static void TransactedSnapshotImpl_DestroyTemporaryCopy(
4482 TransactedSnapshotImpl* This, DirRef stop)
4484 DirRef cursor;
4485 TransactedDirEntry *entry;
4486 ULARGE_INTEGER zero;
4488 zero.QuadPart = 0;
4490 if (!This->entries[This->base.storageDirEntry].read)
4491 return;
4493 cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
4495 if (cursor == DIRENTRY_NULL)
4496 return;
4498 cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
4500 while (cursor != DIRENTRY_NULL && cursor != stop)
4502 if (TransactedSnapshotImpl_MadeCopy(This, cursor))
4504 entry = &This->entries[cursor];
4506 if (entry->stream_dirty)
4507 StorageBaseImpl_StreamSetSize(This->transactedParent,
4508 entry->newTransactedParentEntry, zero);
4510 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4511 entry->newTransactedParentEntry);
4513 entry->newTransactedParentEntry = entry->transactedParentEntry;
4516 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4520 /* Make a copy of our edited tree that we can use in the parent. */
4521 static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
4523 DirRef cursor;
4524 TransactedDirEntry *entry;
4525 HRESULT hr = S_OK;
4527 cursor = This->base.storageDirEntry;
4528 entry = &This->entries[cursor];
4529 entry->parent = DIRENTRY_NULL;
4530 entry->newTransactedParentEntry = entry->transactedParentEntry;
4532 if (entry->data.dirRootEntry == DIRENTRY_NULL)
4533 return S_OK;
4535 This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
4537 cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
4538 entry = &This->entries[cursor];
4540 while (cursor != DIRENTRY_NULL)
4542 /* Make a copy of this entry in the transacted parent. */
4543 if (!entry->read ||
4544 (!entry->dirty && !entry->stream_dirty &&
4545 !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
4546 !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
4547 !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
4548 entry->newTransactedParentEntry = entry->transactedParentEntry;
4549 else
4551 DirEntry newData;
4553 memcpy(&newData, &entry->data, sizeof(DirEntry));
4555 newData.size.QuadPart = 0;
4556 newData.startingBlock = BLOCK_END_OF_CHAIN;
4558 if (newData.leftChild != DIRENTRY_NULL)
4559 newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
4561 if (newData.rightChild != DIRENTRY_NULL)
4562 newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
4564 if (newData.dirRootEntry != DIRENTRY_NULL)
4565 newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
4567 hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
4568 &entry->newTransactedParentEntry);
4569 if (FAILED(hr))
4571 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4572 return hr;
4575 if (entry->stream_dirty)
4577 hr = StorageBaseImpl_CopyStream(
4578 This->transactedParent, entry->newTransactedParentEntry,
4579 This->scratch, entry->stream_entry);
4581 else if (entry->data.size.QuadPart)
4583 hr = StorageBaseImpl_StreamLink(
4584 This->transactedParent, entry->newTransactedParentEntry,
4585 entry->transactedParentEntry);
4588 if (FAILED(hr))
4590 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4591 TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
4592 return hr;
4596 cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
4597 entry = &This->entries[cursor];
4600 return hr;
4603 static HRESULT WINAPI TransactedSnapshotImpl_Commit(
4604 IStorage* iface,
4605 DWORD grfCommitFlags) /* [in] */
4607 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4608 TransactedDirEntry *root_entry;
4609 DirRef i, dir_root_ref;
4610 DirEntry data;
4611 ULARGE_INTEGER zero;
4612 HRESULT hr;
4614 zero.QuadPart = 0;
4616 TRACE("(%p,%x)\n", iface, grfCommitFlags);
4618 /* Cannot commit a read-only transacted storage */
4619 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
4620 return STG_E_ACCESSDENIED;
4622 /* To prevent data loss, we create the new structure in the file before we
4623 * delete the old one, so that in case of errors the old data is intact. We
4624 * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
4625 * needed in the rare situation where we have just enough free disk space to
4626 * overwrite the existing data. */
4628 root_entry = &This->entries[This->base.storageDirEntry];
4630 if (!root_entry->read)
4631 return S_OK;
4633 hr = TransactedSnapshotImpl_CopyTree(This);
4634 if (FAILED(hr)) return hr;
4636 if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
4637 dir_root_ref = DIRENTRY_NULL;
4638 else
4639 dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
4641 hr = StorageBaseImpl_Flush(This->transactedParent);
4643 /* Update the storage to use the new data in one step. */
4644 if (SUCCEEDED(hr))
4645 hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
4646 root_entry->transactedParentEntry, &data);
4648 if (SUCCEEDED(hr))
4650 data.dirRootEntry = dir_root_ref;
4651 data.clsid = root_entry->data.clsid;
4652 data.ctime = root_entry->data.ctime;
4653 data.mtime = root_entry->data.mtime;
4655 hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
4656 root_entry->transactedParentEntry, &data);
4659 /* Try to flush after updating the root storage, but if the flush fails, keep
4660 * going, on the theory that it'll either succeed later or the subsequent
4661 * writes will fail. */
4662 StorageBaseImpl_Flush(This->transactedParent);
4664 if (SUCCEEDED(hr))
4666 /* Destroy the old now-orphaned data. */
4667 for (i=0; i<This->entries_size; i++)
4669 TransactedDirEntry *entry = &This->entries[i];
4670 if (entry->inuse)
4672 if (entry->deleted)
4674 StorageBaseImpl_StreamSetSize(This->transactedParent,
4675 entry->transactedParentEntry, zero);
4676 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4677 entry->transactedParentEntry);
4678 memset(entry, 0, sizeof(TransactedDirEntry));
4679 This->firstFreeEntry = min(i, This->firstFreeEntry);
4681 else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
4683 if (entry->transactedParentEntry != DIRENTRY_NULL)
4684 StorageBaseImpl_DestroyDirEntry(This->transactedParent,
4685 entry->transactedParentEntry);
4686 if (entry->stream_dirty)
4688 StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
4689 StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
4690 entry->stream_dirty = 0;
4692 entry->dirty = 0;
4693 entry->transactedParentEntry = entry->newTransactedParentEntry;
4698 else
4700 TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
4703 if (SUCCEEDED(hr))
4704 hr = StorageBaseImpl_Flush(This->transactedParent);
4706 return hr;
4709 static HRESULT WINAPI TransactedSnapshotImpl_Revert(
4710 IStorage* iface)
4712 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4713 ULARGE_INTEGER zero;
4714 ULONG i;
4716 TRACE("(%p)\n", iface);
4718 /* Destroy the open objects. */
4719 StorageBaseImpl_DeleteAll(&This->base);
4721 /* Clear out the scratch file. */
4722 zero.QuadPart = 0;
4723 for (i=0; i<This->entries_size; i++)
4725 if (This->entries[i].stream_dirty)
4727 StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
4728 zero);
4730 StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
4734 memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
4736 This->firstFreeEntry = 0;
4737 This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
4739 return S_OK;
4742 static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
4744 if (!This->reverted)
4746 TRACE("Storage invalidated (stg=%p)\n", This);
4748 This->reverted = 1;
4750 StorageBaseImpl_DeleteAll(This);
4754 static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
4756 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4758 TransactedSnapshotImpl_Revert((IStorage*)iface);
4760 IStorage_Release((IStorage*)This->transactedParent);
4762 IStorage_Release((IStorage*)This->scratch);
4764 HeapFree(GetProcessHeap(), 0, This->entries);
4766 HeapFree(GetProcessHeap(), 0, This);
4769 static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
4771 /* We only need to flush when committing. */
4772 return S_OK;
4775 static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
4777 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
4779 return StorageBaseImpl_GetFilename(This->transactedParent, result);
4782 static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
4783 const DirEntry *newData, DirRef *index)
4785 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4786 DirRef new_ref;
4787 TransactedDirEntry *new_entry;
4789 new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
4790 if (new_ref == DIRENTRY_NULL)
4791 return E_OUTOFMEMORY;
4793 new_entry = &This->entries[new_ref];
4795 new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
4796 new_entry->read = 1;
4797 new_entry->dirty = 1;
4798 memcpy(&new_entry->data, newData, sizeof(DirEntry));
4800 *index = new_ref;
4802 TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
4804 return S_OK;
4807 static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
4808 DirRef index, const DirEntry *data)
4810 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4811 HRESULT hr;
4813 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4815 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4816 if (FAILED(hr)) return hr;
4818 memcpy(&This->entries[index].data, data, sizeof(DirEntry));
4820 if (index != This->base.storageDirEntry)
4822 This->entries[index].dirty = 1;
4824 if (data->size.QuadPart == 0 &&
4825 This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4827 /* Since this entry is modified, and we aren't using its stream data, we
4828 * no longer care about the original entry. */
4829 DirRef delete_ref;
4830 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4832 if (delete_ref != DIRENTRY_NULL)
4833 This->entries[delete_ref].deleted = 1;
4835 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4839 return S_OK;
4842 static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
4843 DirRef index, DirEntry *data)
4845 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4846 HRESULT hr;
4848 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4849 if (FAILED(hr)) return hr;
4851 memcpy(data, &This->entries[index].data, sizeof(DirEntry));
4853 TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
4855 return S_OK;
4858 static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
4859 DirRef index)
4861 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4863 if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
4864 This->entries[index].data.size.QuadPart != 0)
4866 /* If we deleted this entry while it has stream data. We must have left the
4867 * data because some other entry is using it, and we need to leave the
4868 * original entry alone. */
4869 memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
4870 This->firstFreeEntry = min(index, This->firstFreeEntry);
4872 else
4874 This->entries[index].deleted = 1;
4877 return S_OK;
4880 static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
4881 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4883 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4885 if (This->entries[index].stream_dirty)
4887 return StorageBaseImpl_StreamReadAt(This->scratch,
4888 This->entries[index].stream_entry, offset, size, buffer, bytesRead);
4890 else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
4892 /* This stream doesn't live in the parent, and we haven't allocated storage
4893 * for it yet */
4894 *bytesRead = 0;
4895 return S_OK;
4897 else
4899 return StorageBaseImpl_StreamReadAt(This->transactedParent,
4900 This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
4904 static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
4905 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
4907 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4908 HRESULT hr;
4910 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4911 if (FAILED(hr)) return hr;
4913 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4914 if (FAILED(hr)) return hr;
4916 hr = StorageBaseImpl_StreamWriteAt(This->scratch,
4917 This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
4919 if (SUCCEEDED(hr) && size != 0)
4920 This->entries[index].data.size.QuadPart = max(
4921 This->entries[index].data.size.QuadPart,
4922 offset.QuadPart + size);
4924 return hr;
4927 static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
4928 DirRef index, ULARGE_INTEGER newsize)
4930 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4931 HRESULT hr;
4933 hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
4934 if (FAILED(hr)) return hr;
4936 if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
4937 return S_OK;
4939 if (newsize.QuadPart == 0)
4941 /* Destroy any parent references or entries in the scratch file. */
4942 if (This->entries[index].stream_dirty)
4944 ULARGE_INTEGER zero;
4945 zero.QuadPart = 0;
4946 StorageBaseImpl_StreamSetSize(This->scratch,
4947 This->entries[index].stream_entry, zero);
4948 StorageBaseImpl_DestroyDirEntry(This->scratch,
4949 This->entries[index].stream_entry);
4950 This->entries[index].stream_dirty = 0;
4952 else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
4954 DirRef delete_ref;
4955 delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
4957 if (delete_ref != DIRENTRY_NULL)
4958 This->entries[delete_ref].deleted = 1;
4960 This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
4963 else
4965 hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
4966 if (FAILED(hr)) return hr;
4968 hr = StorageBaseImpl_StreamSetSize(This->scratch,
4969 This->entries[index].stream_entry, newsize);
4972 if (SUCCEEDED(hr))
4973 This->entries[index].data.size = newsize;
4975 return hr;
4978 static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
4979 DirRef dst, DirRef src)
4981 TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
4982 HRESULT hr;
4983 TransactedDirEntry *dst_entry, *src_entry;
4985 hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
4986 if (FAILED(hr)) return hr;
4988 hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
4989 if (FAILED(hr)) return hr;
4991 dst_entry = &This->entries[dst];
4992 src_entry = &This->entries[src];
4994 dst_entry->stream_dirty = src_entry->stream_dirty;
4995 dst_entry->stream_entry = src_entry->stream_entry;
4996 dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
4997 dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
4998 dst_entry->data.size = src_entry->data.size;
5000 return S_OK;
5003 static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
5005 StorageBaseImpl_QueryInterface,
5006 StorageBaseImpl_AddRef,
5007 StorageBaseImpl_Release,
5008 StorageBaseImpl_CreateStream,
5009 StorageBaseImpl_OpenStream,
5010 StorageBaseImpl_CreateStorage,
5011 StorageBaseImpl_OpenStorage,
5012 StorageBaseImpl_CopyTo,
5013 StorageBaseImpl_MoveElementTo,
5014 TransactedSnapshotImpl_Commit,
5015 TransactedSnapshotImpl_Revert,
5016 StorageBaseImpl_EnumElements,
5017 StorageBaseImpl_DestroyElement,
5018 StorageBaseImpl_RenameElement,
5019 StorageBaseImpl_SetElementTimes,
5020 StorageBaseImpl_SetClass,
5021 StorageBaseImpl_SetStateBits,
5022 StorageBaseImpl_Stat
5025 static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
5027 TransactedSnapshotImpl_Destroy,
5028 TransactedSnapshotImpl_Invalidate,
5029 TransactedSnapshotImpl_Flush,
5030 TransactedSnapshotImpl_GetFilename,
5031 TransactedSnapshotImpl_CreateDirEntry,
5032 TransactedSnapshotImpl_WriteDirEntry,
5033 TransactedSnapshotImpl_ReadDirEntry,
5034 TransactedSnapshotImpl_DestroyDirEntry,
5035 TransactedSnapshotImpl_StreamReadAt,
5036 TransactedSnapshotImpl_StreamWriteAt,
5037 TransactedSnapshotImpl_StreamSetSize,
5038 TransactedSnapshotImpl_StreamLink
5041 static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
5042 TransactedSnapshotImpl** result)
5044 HRESULT hr;
5046 *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
5047 if (*result)
5049 (*result)->base.lpVtbl = &TransactedSnapshotImpl_Vtbl;
5051 /* This is OK because the property set storage functions use the IStorage functions. */
5052 (*result)->base.pssVtbl = parentStorage->pssVtbl;
5054 (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
5056 list_init(&(*result)->base.strmHead);
5058 list_init(&(*result)->base.storageHead);
5060 (*result)->base.ref = 1;
5062 (*result)->base.openFlags = parentStorage->openFlags;
5064 /* Create a new temporary storage to act as the scratch file. */
5065 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
5066 0, (IStorage**)&(*result)->scratch);
5068 if (SUCCEEDED(hr))
5070 ULONG num_entries = 20;
5072 (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
5074 (*result)->entries_size = num_entries;
5076 (*result)->firstFreeEntry = 0;
5078 if ((*result)->entries)
5080 /* parentStorage already has 1 reference, which we take over here. */
5081 (*result)->transactedParent = parentStorage;
5083 parentStorage->transactedChild = (StorageBaseImpl*)*result;
5085 (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
5087 else
5089 IStorage_Release((IStorage*)(*result)->scratch);
5091 hr = E_OUTOFMEMORY;
5095 if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
5097 return hr;
5099 else
5100 return E_OUTOFMEMORY;
5103 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
5104 StorageBaseImpl** result)
5106 static int fixme=0;
5108 if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
5110 FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
5113 return TransactedSnapshotImpl_Construct(parentStorage,
5114 (TransactedSnapshotImpl**)result);
5117 static HRESULT Storage_Construct(
5118 HANDLE hFile,
5119 LPCOLESTR pwcsName,
5120 ILockBytes* pLkbyt,
5121 DWORD openFlags,
5122 BOOL fileBased,
5123 BOOL create,
5124 ULONG sector_size,
5125 StorageBaseImpl** result)
5127 StorageImpl *newStorage;
5128 StorageBaseImpl *newTransactedStorage;
5129 HRESULT hr;
5131 hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
5132 if (FAILED(hr)) goto end;
5134 if (openFlags & STGM_TRANSACTED)
5136 hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
5137 if (FAILED(hr))
5138 IStorage_Release((IStorage*)newStorage);
5139 else
5140 *result = newTransactedStorage;
5142 else
5143 *result = &newStorage->base;
5145 end:
5146 return hr;
5149 static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
5151 StorageInternalImpl* This = (StorageInternalImpl*) base;
5153 if (!This->base.reverted)
5155 TRACE("Storage invalidated (stg=%p)\n", This);
5157 This->base.reverted = 1;
5159 This->parentStorage = NULL;
5161 StorageBaseImpl_DeleteAll(&This->base);
5163 list_remove(&This->ParentListEntry);
5167 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
5169 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5171 StorageInternalImpl_Invalidate(&This->base);
5173 HeapFree(GetProcessHeap(), 0, This);
5176 static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
5178 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5180 return StorageBaseImpl_Flush(This->parentStorage);
5183 static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
5185 StorageInternalImpl* This = (StorageInternalImpl*) iface;
5187 return StorageBaseImpl_GetFilename(This->parentStorage, result);
5190 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
5191 const DirEntry *newData, DirRef *index)
5193 StorageInternalImpl* This = (StorageInternalImpl*) base;
5195 return StorageBaseImpl_CreateDirEntry(This->parentStorage,
5196 newData, index);
5199 static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
5200 DirRef index, const DirEntry *data)
5202 StorageInternalImpl* This = (StorageInternalImpl*) base;
5204 return StorageBaseImpl_WriteDirEntry(This->parentStorage,
5205 index, data);
5208 static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
5209 DirRef index, DirEntry *data)
5211 StorageInternalImpl* This = (StorageInternalImpl*) base;
5213 return StorageBaseImpl_ReadDirEntry(This->parentStorage,
5214 index, data);
5217 static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
5218 DirRef index)
5220 StorageInternalImpl* This = (StorageInternalImpl*) base;
5222 return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
5223 index);
5226 static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
5227 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
5229 StorageInternalImpl* This = (StorageInternalImpl*) base;
5231 return StorageBaseImpl_StreamReadAt(This->parentStorage,
5232 index, offset, size, buffer, bytesRead);
5235 static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
5236 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
5238 StorageInternalImpl* This = (StorageInternalImpl*) base;
5240 return StorageBaseImpl_StreamWriteAt(This->parentStorage,
5241 index, offset, size, buffer, bytesWritten);
5244 static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
5245 DirRef index, ULARGE_INTEGER newsize)
5247 StorageInternalImpl* This = (StorageInternalImpl*) base;
5249 return StorageBaseImpl_StreamSetSize(This->parentStorage,
5250 index, newsize);
5253 static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
5254 DirRef dst, DirRef src)
5256 StorageInternalImpl* This = (StorageInternalImpl*) base;
5258 return StorageBaseImpl_StreamLink(This->parentStorage,
5259 dst, src);
5262 /******************************************************************************
5264 ** Storage32InternalImpl_Commit
5267 static HRESULT WINAPI StorageInternalImpl_Commit(
5268 IStorage* iface,
5269 DWORD grfCommitFlags) /* [in] */
5271 StorageBaseImpl* base = (StorageBaseImpl*) iface;
5272 TRACE("(%p,%x)\n", iface, grfCommitFlags);
5273 return StorageBaseImpl_Flush(base);
5276 /******************************************************************************
5278 ** Storage32InternalImpl_Revert
5281 static HRESULT WINAPI StorageInternalImpl_Revert(
5282 IStorage* iface)
5284 FIXME("(%p): stub\n", iface);
5285 return S_OK;
5288 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
5290 IStorage_Release((IStorage*)This->parentStorage);
5291 HeapFree(GetProcessHeap(), 0, This);
5294 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
5295 IEnumSTATSTG* iface,
5296 REFIID riid,
5297 void** ppvObject)
5299 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5301 if (ppvObject==0)
5302 return E_INVALIDARG;
5304 *ppvObject = 0;
5306 if (IsEqualGUID(&IID_IUnknown, riid) ||
5307 IsEqualGUID(&IID_IEnumSTATSTG, riid))
5309 *ppvObject = This;
5310 IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
5311 return S_OK;
5314 return E_NOINTERFACE;
5317 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
5318 IEnumSTATSTG* iface)
5320 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5321 return InterlockedIncrement(&This->ref);
5324 static ULONG WINAPI IEnumSTATSTGImpl_Release(
5325 IEnumSTATSTG* iface)
5327 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5329 ULONG newRef;
5331 newRef = InterlockedDecrement(&This->ref);
5333 if (newRef==0)
5335 IEnumSTATSTGImpl_Destroy(This);
5338 return newRef;
5341 static HRESULT IEnumSTATSTGImpl_GetNextRef(
5342 IEnumSTATSTGImpl* This,
5343 DirRef *ref)
5345 DirRef result = DIRENTRY_NULL;
5346 DirRef searchNode;
5347 DirEntry entry;
5348 HRESULT hr;
5349 WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
5351 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
5352 This->parentStorage->storageDirEntry, &entry);
5353 searchNode = entry.dirRootEntry;
5355 while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
5357 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
5359 if (SUCCEEDED(hr))
5361 LONG diff = entryNameCmp( entry.name, This->name);
5363 if (diff <= 0)
5365 searchNode = entry.rightChild;
5367 else
5369 result = searchNode;
5370 memcpy(result_name, entry.name, sizeof(result_name));
5371 searchNode = entry.leftChild;
5376 if (SUCCEEDED(hr))
5378 *ref = result;
5379 if (result != DIRENTRY_NULL)
5380 memcpy(This->name, result_name, sizeof(result_name));
5383 return hr;
5386 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
5387 IEnumSTATSTG* iface,
5388 ULONG celt,
5389 STATSTG* rgelt,
5390 ULONG* pceltFetched)
5392 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5394 DirEntry currentEntry;
5395 STATSTG* currentReturnStruct = rgelt;
5396 ULONG objectFetched = 0;
5397 DirRef currentSearchNode;
5398 HRESULT hr=S_OK;
5400 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
5401 return E_INVALIDARG;
5403 if (This->parentStorage->reverted)
5404 return STG_E_REVERTED;
5407 * To avoid the special case, get another pointer to a ULONG value if
5408 * the caller didn't supply one.
5410 if (pceltFetched==0)
5411 pceltFetched = &objectFetched;
5414 * Start the iteration, we will iterate until we hit the end of the
5415 * linked list or until we hit the number of items to iterate through
5417 *pceltFetched = 0;
5419 while ( *pceltFetched < celt )
5421 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5423 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5424 break;
5427 * Read the entry from the storage.
5429 StorageBaseImpl_ReadDirEntry(This->parentStorage,
5430 currentSearchNode,
5431 &currentEntry);
5434 * Copy the information to the return buffer.
5436 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
5437 currentReturnStruct,
5438 &currentEntry,
5439 STATFLAG_DEFAULT);
5442 * Step to the next item in the iteration
5444 (*pceltFetched)++;
5445 currentReturnStruct++;
5448 if (SUCCEEDED(hr) && *pceltFetched != celt)
5449 hr = S_FALSE;
5451 return hr;
5455 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
5456 IEnumSTATSTG* iface,
5457 ULONG celt)
5459 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5461 ULONG objectFetched = 0;
5462 DirRef currentSearchNode;
5463 HRESULT hr=S_OK;
5465 if (This->parentStorage->reverted)
5466 return STG_E_REVERTED;
5468 while ( (objectFetched < celt) )
5470 hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
5472 if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
5473 break;
5475 objectFetched++;
5478 if (SUCCEEDED(hr) && objectFetched != celt)
5479 return S_FALSE;
5481 return hr;
5484 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
5485 IEnumSTATSTG* iface)
5487 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5489 if (This->parentStorage->reverted)
5490 return STG_E_REVERTED;
5492 This->name[0] = 0;
5494 return S_OK;
5497 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
5498 IEnumSTATSTG* iface,
5499 IEnumSTATSTG** ppenum)
5501 IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
5503 IEnumSTATSTGImpl* newClone;
5505 if (This->parentStorage->reverted)
5506 return STG_E_REVERTED;
5509 * Perform a sanity check on the parameters.
5511 if (ppenum==0)
5512 return E_INVALIDARG;
5514 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
5515 This->storageDirEntry);
5519 * The new clone enumeration must point to the same current node as
5520 * the ole one.
5522 memcpy(newClone->name, This->name, sizeof(newClone->name));
5524 *ppenum = &newClone->IEnumSTATSTG_iface;
5527 * Don't forget to nail down a reference to the clone before
5528 * returning it.
5530 IEnumSTATSTGImpl_AddRef(*ppenum);
5532 return S_OK;
5536 * Virtual function table for the IEnumSTATSTGImpl class.
5538 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
5540 IEnumSTATSTGImpl_QueryInterface,
5541 IEnumSTATSTGImpl_AddRef,
5542 IEnumSTATSTGImpl_Release,
5543 IEnumSTATSTGImpl_Next,
5544 IEnumSTATSTGImpl_Skip,
5545 IEnumSTATSTGImpl_Reset,
5546 IEnumSTATSTGImpl_Clone
5549 /******************************************************************************
5550 ** IEnumSTATSTGImpl implementation
5553 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
5554 StorageBaseImpl* parentStorage,
5555 DirRef storageDirEntry)
5557 IEnumSTATSTGImpl* newEnumeration;
5559 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
5561 if (newEnumeration!=0)
5564 * Set-up the virtual function table and reference count.
5566 newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
5567 newEnumeration->ref = 0;
5570 * We want to nail-down the reference to the storage in case the
5571 * enumeration out-lives the storage in the client application.
5573 newEnumeration->parentStorage = parentStorage;
5574 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
5576 newEnumeration->storageDirEntry = storageDirEntry;
5579 * Make sure the current node of the iterator is the first one.
5581 IEnumSTATSTGImpl_Reset(&newEnumeration->IEnumSTATSTG_iface);
5584 return newEnumeration;
5588 * Virtual function table for the Storage32InternalImpl class.
5590 static const IStorageVtbl Storage32InternalImpl_Vtbl =
5592 StorageBaseImpl_QueryInterface,
5593 StorageBaseImpl_AddRef,
5594 StorageBaseImpl_Release,
5595 StorageBaseImpl_CreateStream,
5596 StorageBaseImpl_OpenStream,
5597 StorageBaseImpl_CreateStorage,
5598 StorageBaseImpl_OpenStorage,
5599 StorageBaseImpl_CopyTo,
5600 StorageBaseImpl_MoveElementTo,
5601 StorageInternalImpl_Commit,
5602 StorageInternalImpl_Revert,
5603 StorageBaseImpl_EnumElements,
5604 StorageBaseImpl_DestroyElement,
5605 StorageBaseImpl_RenameElement,
5606 StorageBaseImpl_SetElementTimes,
5607 StorageBaseImpl_SetClass,
5608 StorageBaseImpl_SetStateBits,
5609 StorageBaseImpl_Stat
5612 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
5614 StorageInternalImpl_Destroy,
5615 StorageInternalImpl_Invalidate,
5616 StorageInternalImpl_Flush,
5617 StorageInternalImpl_GetFilename,
5618 StorageInternalImpl_CreateDirEntry,
5619 StorageInternalImpl_WriteDirEntry,
5620 StorageInternalImpl_ReadDirEntry,
5621 StorageInternalImpl_DestroyDirEntry,
5622 StorageInternalImpl_StreamReadAt,
5623 StorageInternalImpl_StreamWriteAt,
5624 StorageInternalImpl_StreamSetSize,
5625 StorageInternalImpl_StreamLink
5628 /******************************************************************************
5629 ** Storage32InternalImpl implementation
5632 static StorageInternalImpl* StorageInternalImpl_Construct(
5633 StorageBaseImpl* parentStorage,
5634 DWORD openFlags,
5635 DirRef storageDirEntry)
5637 StorageInternalImpl* newStorage;
5639 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
5641 if (newStorage!=0)
5643 list_init(&newStorage->base.strmHead);
5645 list_init(&newStorage->base.storageHead);
5648 * Initialize the virtual function table.
5650 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
5651 newStorage->base.pssVtbl = &IPropertySetStorage_Vtbl;
5652 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
5653 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
5655 newStorage->base.reverted = 0;
5657 newStorage->base.ref = 1;
5659 newStorage->parentStorage = parentStorage;
5662 * Keep a reference to the directory entry of this storage
5664 newStorage->base.storageDirEntry = storageDirEntry;
5666 newStorage->base.create = 0;
5668 return newStorage;
5671 return 0;
5674 /******************************************************************************
5675 ** StorageUtl implementation
5678 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
5680 WORD tmp;
5682 memcpy(&tmp, buffer+offset, sizeof(WORD));
5683 *value = lendian16toh(tmp);
5686 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
5688 value = htole16(value);
5689 memcpy(buffer+offset, &value, sizeof(WORD));
5692 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
5694 DWORD tmp;
5696 memcpy(&tmp, buffer+offset, sizeof(DWORD));
5697 *value = lendian32toh(tmp);
5700 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
5702 value = htole32(value);
5703 memcpy(buffer+offset, &value, sizeof(DWORD));
5706 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
5707 ULARGE_INTEGER* value)
5709 #ifdef WORDS_BIGENDIAN
5710 ULARGE_INTEGER tmp;
5712 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
5713 value->u.LowPart = htole32(tmp.u.HighPart);
5714 value->u.HighPart = htole32(tmp.u.LowPart);
5715 #else
5716 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
5717 #endif
5720 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
5721 const ULARGE_INTEGER *value)
5723 #ifdef WORDS_BIGENDIAN
5724 ULARGE_INTEGER tmp;
5726 tmp.u.LowPart = htole32(value->u.HighPart);
5727 tmp.u.HighPart = htole32(value->u.LowPart);
5728 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
5729 #else
5730 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
5731 #endif
5734 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
5736 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
5737 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
5738 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
5740 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
5743 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
5745 StorageUtl_WriteDWord(buffer, offset, value->Data1);
5746 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
5747 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
5749 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
5752 void StorageUtl_CopyDirEntryToSTATSTG(
5753 StorageBaseImpl* storage,
5754 STATSTG* destination,
5755 const DirEntry* source,
5756 int statFlags)
5759 * The copy of the string occurs only when the flag is not set
5761 if (!(statFlags & STATFLAG_NONAME) && source->stgType == STGTY_ROOT)
5763 /* Use the filename for the root storage. */
5764 destination->pwcsName = 0;
5765 StorageBaseImpl_GetFilename(storage, &destination->pwcsName);
5767 else if( ((statFlags & STATFLAG_NONAME) != 0) ||
5768 (source->name[0] == 0) )
5770 destination->pwcsName = 0;
5772 else
5774 destination->pwcsName =
5775 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
5777 strcpyW(destination->pwcsName, source->name);
5780 switch (source->stgType)
5782 case STGTY_STORAGE:
5783 case STGTY_ROOT:
5784 destination->type = STGTY_STORAGE;
5785 break;
5786 case STGTY_STREAM:
5787 destination->type = STGTY_STREAM;
5788 break;
5789 default:
5790 destination->type = STGTY_STREAM;
5791 break;
5794 destination->cbSize = source->size;
5796 currentReturnStruct->mtime = {0}; TODO
5797 currentReturnStruct->ctime = {0};
5798 currentReturnStruct->atime = {0};
5800 destination->grfMode = 0;
5801 destination->grfLocksSupported = 0;
5802 destination->clsid = source->clsid;
5803 destination->grfStateBits = 0;
5804 destination->reserved = 0;
5807 /******************************************************************************
5808 ** BlockChainStream implementation
5811 /* Read and save the index of all blocks in this stream. */
5812 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
5814 ULONG next_sector, next_offset;
5815 HRESULT hr;
5816 struct BlockChainRun *last_run;
5818 if (This->indexCacheLen == 0)
5820 last_run = NULL;
5821 next_offset = 0;
5822 next_sector = BlockChainStream_GetHeadOfChain(This);
5824 else
5826 last_run = &This->indexCache[This->indexCacheLen-1];
5827 next_offset = last_run->lastOffset+1;
5828 hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
5829 last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
5830 &next_sector);
5831 if (FAILED(hr)) return hr;
5834 while (next_sector != BLOCK_END_OF_CHAIN)
5836 if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
5838 /* Add the current block to the cache. */
5839 if (This->indexCacheSize == 0)
5841 This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
5842 if (!This->indexCache) return E_OUTOFMEMORY;
5843 This->indexCacheSize = 16;
5845 else if (This->indexCacheSize == This->indexCacheLen)
5847 struct BlockChainRun *new_cache;
5848 ULONG new_size;
5850 new_size = This->indexCacheSize * 2;
5851 new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
5852 if (!new_cache) return E_OUTOFMEMORY;
5853 memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
5855 HeapFree(GetProcessHeap(), 0, This->indexCache);
5856 This->indexCache = new_cache;
5857 This->indexCacheSize = new_size;
5860 This->indexCacheLen++;
5861 last_run = &This->indexCache[This->indexCacheLen-1];
5862 last_run->firstSector = next_sector;
5863 last_run->firstOffset = next_offset;
5866 last_run->lastOffset = next_offset;
5868 /* Find the next block. */
5869 next_offset++;
5870 hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
5871 if (FAILED(hr)) return hr;
5874 if (This->indexCacheLen)
5876 This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
5877 This->numBlocks = last_run->lastOffset+1;
5879 else
5881 This->tailIndex = BLOCK_END_OF_CHAIN;
5882 This->numBlocks = 0;
5885 return S_OK;
5888 /* Locate the nth block in this stream. */
5889 ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
5891 ULONG min_offset = 0, max_offset = This->numBlocks-1;
5892 ULONG min_run = 0, max_run = This->indexCacheLen-1;
5894 if (offset >= This->numBlocks)
5895 return BLOCK_END_OF_CHAIN;
5897 while (min_run < max_run)
5899 ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
5900 if (offset < This->indexCache[run_to_check].firstOffset)
5902 max_offset = This->indexCache[run_to_check].firstOffset-1;
5903 max_run = run_to_check-1;
5905 else if (offset > This->indexCache[run_to_check].lastOffset)
5907 min_offset = This->indexCache[run_to_check].lastOffset+1;
5908 min_run = run_to_check+1;
5910 else
5911 /* Block is in this run. */
5912 min_run = max_run = run_to_check;
5915 return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
5918 HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This,
5919 ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create)
5921 BlockChainBlock *result=NULL;
5922 int i;
5924 for (i=0; i<2; i++)
5925 if (This->cachedBlocks[i].index == index)
5927 *sector = This->cachedBlocks[i].sector;
5928 *block = &This->cachedBlocks[i];
5929 return S_OK;
5932 *sector = BlockChainStream_GetSectorOfOffset(This, index);
5933 if (*sector == BLOCK_END_OF_CHAIN)
5934 return STG_E_DOCFILECORRUPT;
5936 if (create)
5938 if (This->cachedBlocks[0].index == 0xffffffff)
5939 result = &This->cachedBlocks[0];
5940 else if (This->cachedBlocks[1].index == 0xffffffff)
5941 result = &This->cachedBlocks[1];
5942 else
5944 result = &This->cachedBlocks[This->blockToEvict++];
5945 if (This->blockToEvict == 2)
5946 This->blockToEvict = 0;
5949 if (result->dirty)
5951 if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data))
5952 return STG_E_WRITEFAULT;
5953 result->dirty = 0;
5956 result->read = 0;
5957 result->index = index;
5958 result->sector = *sector;
5961 *block = result;
5962 return S_OK;
5965 BlockChainStream* BlockChainStream_Construct(
5966 StorageImpl* parentStorage,
5967 ULONG* headOfStreamPlaceHolder,
5968 DirRef dirEntry)
5970 BlockChainStream* newStream;
5972 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
5974 newStream->parentStorage = parentStorage;
5975 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
5976 newStream->ownerDirEntry = dirEntry;
5977 newStream->indexCache = NULL;
5978 newStream->indexCacheLen = 0;
5979 newStream->indexCacheSize = 0;
5980 newStream->cachedBlocks[0].index = 0xffffffff;
5981 newStream->cachedBlocks[0].dirty = 0;
5982 newStream->cachedBlocks[1].index = 0xffffffff;
5983 newStream->cachedBlocks[1].dirty = 0;
5984 newStream->blockToEvict = 0;
5986 if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
5988 HeapFree(GetProcessHeap(), 0, newStream->indexCache);
5989 HeapFree(GetProcessHeap(), 0, newStream);
5990 return NULL;
5993 return newStream;
5996 HRESULT BlockChainStream_Flush(BlockChainStream* This)
5998 int i;
5999 if (!This) return S_OK;
6000 for (i=0; i<2; i++)
6002 if (This->cachedBlocks[i].dirty)
6004 if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data))
6005 This->cachedBlocks[i].dirty = 0;
6006 else
6007 return STG_E_WRITEFAULT;
6010 return S_OK;
6013 void BlockChainStream_Destroy(BlockChainStream* This)
6015 if (This)
6017 BlockChainStream_Flush(This);
6018 HeapFree(GetProcessHeap(), 0, This->indexCache);
6020 HeapFree(GetProcessHeap(), 0, This);
6023 /******************************************************************************
6024 * BlockChainStream_GetHeadOfChain
6026 * Returns the head of this stream chain.
6027 * Some special chains don't have directory entries, their heads are kept in
6028 * This->headOfStreamPlaceHolder.
6031 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
6033 DirEntry chainEntry;
6034 HRESULT hr;
6036 if (This->headOfStreamPlaceHolder != 0)
6037 return *(This->headOfStreamPlaceHolder);
6039 if (This->ownerDirEntry != DIRENTRY_NULL)
6041 hr = StorageImpl_ReadDirEntry(
6042 This->parentStorage,
6043 This->ownerDirEntry,
6044 &chainEntry);
6046 if (SUCCEEDED(hr))
6048 return chainEntry.startingBlock;
6052 return BLOCK_END_OF_CHAIN;
6055 /******************************************************************************
6056 * BlockChainStream_GetCount
6058 * Returns the number of blocks that comprises this chain.
6059 * This is not the size of the stream as the last block may not be full!
6061 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
6063 return This->numBlocks;
6066 /******************************************************************************
6067 * BlockChainStream_ReadAt
6069 * Reads a specified number of bytes from this chain at the specified offset.
6070 * bytesRead may be NULL.
6071 * Failure will be returned if the specified number of bytes has not been read.
6073 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
6074 ULARGE_INTEGER offset,
6075 ULONG size,
6076 void* buffer,
6077 ULONG* bytesRead)
6079 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6080 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6081 ULONG bytesToReadInBuffer;
6082 ULONG blockIndex;
6083 BYTE* bufferWalker;
6084 ULARGE_INTEGER stream_size;
6085 HRESULT hr;
6086 BlockChainBlock *cachedBlock;
6088 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
6091 * Find the first block in the stream that contains part of the buffer.
6093 blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
6095 *bytesRead = 0;
6097 stream_size = BlockChainStream_GetSize(This);
6098 if (stream_size.QuadPart > offset.QuadPart)
6099 size = min(stream_size.QuadPart - offset.QuadPart, size);
6100 else
6101 return S_OK;
6104 * Start reading the buffer.
6106 bufferWalker = buffer;
6108 while (size > 0)
6110 ULARGE_INTEGER ulOffset;
6111 DWORD bytesReadAt;
6114 * Calculate how many bytes we can copy from this big block.
6116 bytesToReadInBuffer =
6117 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6119 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
6121 if (FAILED(hr))
6122 return hr;
6124 if (!cachedBlock)
6126 /* Not in cache, and we're going to read past the end of the block. */
6127 ulOffset.u.HighPart = 0;
6128 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6129 offsetInBlock;
6131 StorageImpl_ReadAt(This->parentStorage,
6132 ulOffset,
6133 bufferWalker,
6134 bytesToReadInBuffer,
6135 &bytesReadAt);
6137 else
6139 if (!cachedBlock->read)
6141 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6142 return STG_E_READFAULT;
6144 cachedBlock->read = 1;
6147 memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
6148 bytesReadAt = bytesToReadInBuffer;
6151 blockNoInSequence++;
6152 bufferWalker += bytesReadAt;
6153 size -= bytesReadAt;
6154 *bytesRead += bytesReadAt;
6155 offsetInBlock = 0; /* There is no offset on the next block */
6157 if (bytesToReadInBuffer != bytesReadAt)
6158 break;
6161 return S_OK;
6164 /******************************************************************************
6165 * BlockChainStream_WriteAt
6167 * Writes the specified number of bytes to this chain at the specified offset.
6168 * Will fail if not all specified number of bytes have been written.
6170 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
6171 ULARGE_INTEGER offset,
6172 ULONG size,
6173 const void* buffer,
6174 ULONG* bytesWritten)
6176 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
6177 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
6178 ULONG bytesToWrite;
6179 ULONG blockIndex;
6180 const BYTE* bufferWalker;
6181 HRESULT hr;
6182 BlockChainBlock *cachedBlock;
6184 *bytesWritten = 0;
6185 bufferWalker = buffer;
6187 while (size > 0)
6189 ULARGE_INTEGER ulOffset;
6190 DWORD bytesWrittenAt;
6193 * Calculate how many bytes we can copy to this big block.
6195 bytesToWrite =
6196 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
6198 hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
6200 /* BlockChainStream_SetSize should have already been called to ensure we have
6201 * enough blocks in the chain to write into */
6202 if (FAILED(hr))
6204 ERR("not enough blocks in chain to write data\n");
6205 return hr;
6208 if (!cachedBlock)
6210 /* Not in cache, and we're going to write past the end of the block. */
6211 ulOffset.u.HighPart = 0;
6212 ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
6213 offsetInBlock;
6215 StorageImpl_WriteAt(This->parentStorage,
6216 ulOffset,
6217 bufferWalker,
6218 bytesToWrite,
6219 &bytesWrittenAt);
6221 else
6223 if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
6225 if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data))
6226 return STG_E_READFAULT;
6229 memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
6230 bytesWrittenAt = bytesToWrite;
6231 cachedBlock->read = 1;
6232 cachedBlock->dirty = 1;
6235 blockNoInSequence++;
6236 bufferWalker += bytesWrittenAt;
6237 size -= bytesWrittenAt;
6238 *bytesWritten += bytesWrittenAt;
6239 offsetInBlock = 0; /* There is no offset on the next block */
6241 if (bytesWrittenAt != bytesToWrite)
6242 break;
6245 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6248 /******************************************************************************
6249 * BlockChainStream_Shrink
6251 * Shrinks this chain in the big block depot.
6253 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
6254 ULARGE_INTEGER newSize)
6256 ULONG blockIndex;
6257 ULONG numBlocks;
6258 int i;
6261 * Figure out how many blocks are needed to contain the new size
6263 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6265 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6266 numBlocks++;
6268 if (numBlocks)
6271 * Go to the new end of chain
6273 blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
6275 /* Mark the new end of chain */
6276 StorageImpl_SetNextBlockInChain(
6277 This->parentStorage,
6278 blockIndex,
6279 BLOCK_END_OF_CHAIN);
6281 This->tailIndex = blockIndex;
6283 else
6285 if (This->headOfStreamPlaceHolder != 0)
6287 *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
6289 else
6291 DirEntry chainEntry;
6292 assert(This->ownerDirEntry != DIRENTRY_NULL);
6294 StorageImpl_ReadDirEntry(
6295 This->parentStorage,
6296 This->ownerDirEntry,
6297 &chainEntry);
6299 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
6301 StorageImpl_WriteDirEntry(
6302 This->parentStorage,
6303 This->ownerDirEntry,
6304 &chainEntry);
6307 This->tailIndex = BLOCK_END_OF_CHAIN;
6310 This->numBlocks = numBlocks;
6313 * Mark the extra blocks as free
6315 while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
6317 struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
6318 StorageImpl_FreeBigBlock(This->parentStorage,
6319 last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
6320 if (last_run->lastOffset == last_run->firstOffset)
6321 This->indexCacheLen--;
6322 else
6323 last_run->lastOffset--;
6327 * Reset the last accessed block cache.
6329 for (i=0; i<2; i++)
6331 if (This->cachedBlocks[i].index >= numBlocks)
6333 This->cachedBlocks[i].index = 0xffffffff;
6334 This->cachedBlocks[i].dirty = 0;
6338 return TRUE;
6341 /******************************************************************************
6342 * BlockChainStream_Enlarge
6344 * Grows this chain in the big block depot.
6346 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
6347 ULARGE_INTEGER newSize)
6349 ULONG blockIndex, currentBlock;
6350 ULONG newNumBlocks;
6351 ULONG oldNumBlocks = 0;
6353 blockIndex = BlockChainStream_GetHeadOfChain(This);
6356 * Empty chain. Create the head.
6358 if (blockIndex == BLOCK_END_OF_CHAIN)
6360 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6361 StorageImpl_SetNextBlockInChain(This->parentStorage,
6362 blockIndex,
6363 BLOCK_END_OF_CHAIN);
6365 if (This->headOfStreamPlaceHolder != 0)
6367 *(This->headOfStreamPlaceHolder) = blockIndex;
6369 else
6371 DirEntry chainEntry;
6372 assert(This->ownerDirEntry != DIRENTRY_NULL);
6374 StorageImpl_ReadDirEntry(
6375 This->parentStorage,
6376 This->ownerDirEntry,
6377 &chainEntry);
6379 chainEntry.startingBlock = blockIndex;
6381 StorageImpl_WriteDirEntry(
6382 This->parentStorage,
6383 This->ownerDirEntry,
6384 &chainEntry);
6387 This->tailIndex = blockIndex;
6388 This->numBlocks = 1;
6392 * Figure out how many blocks are needed to contain this stream
6394 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
6396 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
6397 newNumBlocks++;
6400 * Go to the current end of chain
6402 if (This->tailIndex == BLOCK_END_OF_CHAIN)
6404 currentBlock = blockIndex;
6406 while (blockIndex != BLOCK_END_OF_CHAIN)
6408 This->numBlocks++;
6409 currentBlock = blockIndex;
6411 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
6412 &blockIndex)))
6413 return FALSE;
6416 This->tailIndex = currentBlock;
6419 currentBlock = This->tailIndex;
6420 oldNumBlocks = This->numBlocks;
6423 * Add new blocks to the chain
6425 if (oldNumBlocks < newNumBlocks)
6427 while (oldNumBlocks < newNumBlocks)
6429 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
6431 StorageImpl_SetNextBlockInChain(
6432 This->parentStorage,
6433 currentBlock,
6434 blockIndex);
6436 StorageImpl_SetNextBlockInChain(
6437 This->parentStorage,
6438 blockIndex,
6439 BLOCK_END_OF_CHAIN);
6441 currentBlock = blockIndex;
6442 oldNumBlocks++;
6445 This->tailIndex = blockIndex;
6446 This->numBlocks = newNumBlocks;
6449 if (FAILED(BlockChainStream_UpdateIndexCache(This)))
6450 return FALSE;
6452 return TRUE;
6455 /******************************************************************************
6456 * BlockChainStream_SetSize
6458 * Sets the size of this stream. The big block depot will be updated.
6459 * The file will grow if we grow the chain.
6461 * TODO: Free the actual blocks in the file when we shrink the chain.
6462 * Currently, the blocks are still in the file. So the file size
6463 * doesn't shrink even if we shrink streams.
6465 BOOL BlockChainStream_SetSize(
6466 BlockChainStream* This,
6467 ULARGE_INTEGER newSize)
6469 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
6471 if (newSize.u.LowPart == size.u.LowPart)
6472 return TRUE;
6474 if (newSize.u.LowPart < size.u.LowPart)
6476 BlockChainStream_Shrink(This, newSize);
6478 else
6480 BlockChainStream_Enlarge(This, newSize);
6483 return TRUE;
6486 /******************************************************************************
6487 * BlockChainStream_GetSize
6489 * Returns the size of this chain.
6490 * Will return the block count if this chain doesn't have a directory entry.
6492 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
6494 DirEntry chainEntry;
6496 if(This->headOfStreamPlaceHolder == NULL)
6499 * This chain has a directory entry so use the size value from there.
6501 StorageImpl_ReadDirEntry(
6502 This->parentStorage,
6503 This->ownerDirEntry,
6504 &chainEntry);
6506 return chainEntry.size;
6508 else
6511 * this chain is a chain that does not have a directory entry, figure out the
6512 * size by making the product number of used blocks times the
6513 * size of them
6515 ULARGE_INTEGER result;
6516 result.u.HighPart = 0;
6518 result.u.LowPart =
6519 BlockChainStream_GetCount(This) *
6520 This->parentStorage->bigBlockSize;
6522 return result;
6526 /******************************************************************************
6527 ** SmallBlockChainStream implementation
6530 SmallBlockChainStream* SmallBlockChainStream_Construct(
6531 StorageImpl* parentStorage,
6532 ULONG* headOfStreamPlaceHolder,
6533 DirRef dirEntry)
6535 SmallBlockChainStream* newStream;
6537 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
6539 newStream->parentStorage = parentStorage;
6540 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
6541 newStream->ownerDirEntry = dirEntry;
6543 return newStream;
6546 void SmallBlockChainStream_Destroy(
6547 SmallBlockChainStream* This)
6549 HeapFree(GetProcessHeap(), 0, This);
6552 /******************************************************************************
6553 * SmallBlockChainStream_GetHeadOfChain
6555 * Returns the head of this chain of small blocks.
6557 static ULONG SmallBlockChainStream_GetHeadOfChain(
6558 SmallBlockChainStream* This)
6560 DirEntry chainEntry;
6561 HRESULT hr;
6563 if (This->headOfStreamPlaceHolder != NULL)
6564 return *(This->headOfStreamPlaceHolder);
6566 if (This->ownerDirEntry)
6568 hr = StorageImpl_ReadDirEntry(
6569 This->parentStorage,
6570 This->ownerDirEntry,
6571 &chainEntry);
6573 if (SUCCEEDED(hr))
6575 return chainEntry.startingBlock;
6580 return BLOCK_END_OF_CHAIN;
6583 /******************************************************************************
6584 * SmallBlockChainStream_GetNextBlockInChain
6586 * Returns the index of the next small block in this chain.
6588 * Return Values:
6589 * - BLOCK_END_OF_CHAIN: end of this chain
6590 * - BLOCK_UNUSED: small block 'blockIndex' is free
6592 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
6593 SmallBlockChainStream* This,
6594 ULONG blockIndex,
6595 ULONG* nextBlockInChain)
6597 ULARGE_INTEGER offsetOfBlockInDepot;
6598 DWORD buffer;
6599 ULONG bytesRead;
6600 HRESULT res;
6602 *nextBlockInChain = BLOCK_END_OF_CHAIN;
6604 offsetOfBlockInDepot.u.HighPart = 0;
6605 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6608 * Read those bytes in the buffer from the small block file.
6610 res = BlockChainStream_ReadAt(
6611 This->parentStorage->smallBlockDepotChain,
6612 offsetOfBlockInDepot,
6613 sizeof(DWORD),
6614 &buffer,
6615 &bytesRead);
6617 if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
6618 res = STG_E_READFAULT;
6620 if (SUCCEEDED(res))
6622 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
6623 return S_OK;
6626 return res;
6629 /******************************************************************************
6630 * SmallBlockChainStream_SetNextBlockInChain
6632 * Writes the index of the next block of the specified block in the small
6633 * block depot.
6634 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
6635 * To flag a block as free use BLOCK_UNUSED as nextBlock.
6637 static void SmallBlockChainStream_SetNextBlockInChain(
6638 SmallBlockChainStream* This,
6639 ULONG blockIndex,
6640 ULONG nextBlock)
6642 ULARGE_INTEGER offsetOfBlockInDepot;
6643 DWORD buffer;
6644 ULONG bytesWritten;
6646 offsetOfBlockInDepot.u.HighPart = 0;
6647 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6649 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
6652 * Read those bytes in the buffer from the small block file.
6654 BlockChainStream_WriteAt(
6655 This->parentStorage->smallBlockDepotChain,
6656 offsetOfBlockInDepot,
6657 sizeof(DWORD),
6658 &buffer,
6659 &bytesWritten);
6662 /******************************************************************************
6663 * SmallBlockChainStream_FreeBlock
6665 * Flag small block 'blockIndex' as free in the small block depot.
6667 static void SmallBlockChainStream_FreeBlock(
6668 SmallBlockChainStream* This,
6669 ULONG blockIndex)
6671 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
6674 /******************************************************************************
6675 * SmallBlockChainStream_GetNextFreeBlock
6677 * Returns the index of a free small block. The small block depot will be
6678 * enlarged if necessary. The small block chain will also be enlarged if
6679 * necessary.
6681 static ULONG SmallBlockChainStream_GetNextFreeBlock(
6682 SmallBlockChainStream* This)
6684 ULARGE_INTEGER offsetOfBlockInDepot;
6685 DWORD buffer;
6686 ULONG bytesRead;
6687 ULONG blockIndex = This->parentStorage->firstFreeSmallBlock;
6688 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
6689 HRESULT res = S_OK;
6690 ULONG smallBlocksPerBigBlock;
6691 DirEntry rootEntry;
6692 ULONG blocksRequired;
6693 ULARGE_INTEGER old_size, size_required;
6695 offsetOfBlockInDepot.u.HighPart = 0;
6698 * Scan the small block depot for a free block
6700 while (nextBlockIndex != BLOCK_UNUSED)
6702 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
6704 res = BlockChainStream_ReadAt(
6705 This->parentStorage->smallBlockDepotChain,
6706 offsetOfBlockInDepot,
6707 sizeof(DWORD),
6708 &buffer,
6709 &bytesRead);
6712 * If we run out of space for the small block depot, enlarge it
6714 if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
6716 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
6718 if (nextBlockIndex != BLOCK_UNUSED)
6719 blockIndex++;
6721 else
6723 ULONG count =
6724 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
6726 BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
6727 ULARGE_INTEGER newSize, offset;
6728 ULONG bytesWritten;
6730 newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
6731 BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
6734 * Initialize all the small blocks to free
6736 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
6737 offset.QuadPart = count * This->parentStorage->bigBlockSize;
6738 BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
6739 offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
6741 StorageImpl_SaveFileHeader(This->parentStorage);
6745 This->parentStorage->firstFreeSmallBlock = blockIndex+1;
6747 smallBlocksPerBigBlock =
6748 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
6751 * Verify if we have to allocate big blocks to contain small blocks
6753 blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
6755 size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
6757 old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
6759 if (size_required.QuadPart > old_size.QuadPart)
6761 BlockChainStream_SetSize(
6762 This->parentStorage->smallBlockRootChain,
6763 size_required);
6765 StorageImpl_ReadDirEntry(
6766 This->parentStorage,
6767 This->parentStorage->base.storageDirEntry,
6768 &rootEntry);
6770 rootEntry.size = size_required;
6772 StorageImpl_WriteDirEntry(
6773 This->parentStorage,
6774 This->parentStorage->base.storageDirEntry,
6775 &rootEntry);
6778 return blockIndex;
6781 /******************************************************************************
6782 * SmallBlockChainStream_ReadAt
6784 * Reads a specified number of bytes from this chain at the specified offset.
6785 * bytesRead may be NULL.
6786 * Failure will be returned if the specified number of bytes has not been read.
6788 HRESULT SmallBlockChainStream_ReadAt(
6789 SmallBlockChainStream* This,
6790 ULARGE_INTEGER offset,
6791 ULONG size,
6792 void* buffer,
6793 ULONG* bytesRead)
6795 HRESULT rc = S_OK;
6796 ULARGE_INTEGER offsetInBigBlockFile;
6797 ULONG blockNoInSequence =
6798 offset.u.LowPart / This->parentStorage->smallBlockSize;
6800 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6801 ULONG bytesToReadInBuffer;
6802 ULONG blockIndex;
6803 ULONG bytesReadFromBigBlockFile;
6804 BYTE* bufferWalker;
6805 ULARGE_INTEGER stream_size;
6808 * This should never happen on a small block file.
6810 assert(offset.u.HighPart==0);
6812 *bytesRead = 0;
6814 stream_size = SmallBlockChainStream_GetSize(This);
6815 if (stream_size.QuadPart > offset.QuadPart)
6816 size = min(stream_size.QuadPart - offset.QuadPart, size);
6817 else
6818 return S_OK;
6821 * Find the first block in the stream that contains part of the buffer.
6823 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6825 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6827 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6828 if(FAILED(rc))
6829 return rc;
6830 blockNoInSequence--;
6834 * Start reading the buffer.
6836 bufferWalker = buffer;
6838 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6841 * Calculate how many bytes we can copy from this small block.
6843 bytesToReadInBuffer =
6844 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6847 * Calculate the offset of the small block in the small block file.
6849 offsetInBigBlockFile.u.HighPart = 0;
6850 offsetInBigBlockFile.u.LowPart =
6851 blockIndex * This->parentStorage->smallBlockSize;
6853 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6856 * Read those bytes in the buffer from the small block file.
6857 * The small block has already been identified so it shouldn't fail
6858 * unless the file is corrupt.
6860 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
6861 offsetInBigBlockFile,
6862 bytesToReadInBuffer,
6863 bufferWalker,
6864 &bytesReadFromBigBlockFile);
6866 if (FAILED(rc))
6867 return rc;
6869 if (!bytesReadFromBigBlockFile)
6870 return STG_E_DOCFILECORRUPT;
6873 * Step to the next big block.
6875 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
6876 if(FAILED(rc))
6877 return STG_E_DOCFILECORRUPT;
6879 bufferWalker += bytesReadFromBigBlockFile;
6880 size -= bytesReadFromBigBlockFile;
6881 *bytesRead += bytesReadFromBigBlockFile;
6882 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
6885 return S_OK;
6888 /******************************************************************************
6889 * SmallBlockChainStream_WriteAt
6891 * Writes the specified number of bytes to this chain at the specified offset.
6892 * Will fail if not all specified number of bytes have been written.
6894 HRESULT SmallBlockChainStream_WriteAt(
6895 SmallBlockChainStream* This,
6896 ULARGE_INTEGER offset,
6897 ULONG size,
6898 const void* buffer,
6899 ULONG* bytesWritten)
6901 ULARGE_INTEGER offsetInBigBlockFile;
6902 ULONG blockNoInSequence =
6903 offset.u.LowPart / This->parentStorage->smallBlockSize;
6905 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
6906 ULONG bytesToWriteInBuffer;
6907 ULONG blockIndex;
6908 ULONG bytesWrittenToBigBlockFile;
6909 const BYTE* bufferWalker;
6910 HRESULT res;
6913 * This should never happen on a small block file.
6915 assert(offset.u.HighPart==0);
6918 * Find the first block in the stream that contains part of the buffer.
6920 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6922 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
6924 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
6925 return STG_E_DOCFILECORRUPT;
6926 blockNoInSequence--;
6930 * Start writing the buffer.
6932 *bytesWritten = 0;
6933 bufferWalker = buffer;
6934 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
6937 * Calculate how many bytes we can copy to this small block.
6939 bytesToWriteInBuffer =
6940 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
6943 * Calculate the offset of the small block in the small block file.
6945 offsetInBigBlockFile.u.HighPart = 0;
6946 offsetInBigBlockFile.u.LowPart =
6947 blockIndex * This->parentStorage->smallBlockSize;
6949 offsetInBigBlockFile.u.LowPart += offsetInBlock;
6952 * Write those bytes in the buffer to the small block file.
6954 res = BlockChainStream_WriteAt(
6955 This->parentStorage->smallBlockRootChain,
6956 offsetInBigBlockFile,
6957 bytesToWriteInBuffer,
6958 bufferWalker,
6959 &bytesWrittenToBigBlockFile);
6960 if (FAILED(res))
6961 return res;
6964 * Step to the next big block.
6966 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
6967 &blockIndex)))
6968 return FALSE;
6969 bufferWalker += bytesWrittenToBigBlockFile;
6970 size -= bytesWrittenToBigBlockFile;
6971 *bytesWritten += bytesWrittenToBigBlockFile;
6972 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
6975 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
6978 /******************************************************************************
6979 * SmallBlockChainStream_Shrink
6981 * Shrinks this chain in the small block depot.
6983 static BOOL SmallBlockChainStream_Shrink(
6984 SmallBlockChainStream* This,
6985 ULARGE_INTEGER newSize)
6987 ULONG blockIndex, extraBlock;
6988 ULONG numBlocks;
6989 ULONG count = 0;
6991 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
6993 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
6994 numBlocks++;
6996 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
6999 * Go to the new end of chain
7001 while (count < numBlocks)
7003 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7004 &blockIndex)))
7005 return FALSE;
7006 count++;
7010 * If the count is 0, we have a special case, the head of the chain was
7011 * just freed.
7013 if (count == 0)
7015 DirEntry chainEntry;
7017 StorageImpl_ReadDirEntry(This->parentStorage,
7018 This->ownerDirEntry,
7019 &chainEntry);
7021 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
7023 StorageImpl_WriteDirEntry(This->parentStorage,
7024 This->ownerDirEntry,
7025 &chainEntry);
7028 * We start freeing the chain at the head block.
7030 extraBlock = blockIndex;
7032 else
7034 /* Get the next block before marking the new end */
7035 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
7036 &extraBlock)))
7037 return FALSE;
7039 /* Mark the new end of chain */
7040 SmallBlockChainStream_SetNextBlockInChain(
7041 This,
7042 blockIndex,
7043 BLOCK_END_OF_CHAIN);
7047 * Mark the extra blocks as free
7049 while (extraBlock != BLOCK_END_OF_CHAIN)
7051 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
7052 &blockIndex)))
7053 return FALSE;
7054 SmallBlockChainStream_FreeBlock(This, extraBlock);
7055 This->parentStorage->firstFreeSmallBlock = min(This->parentStorage->firstFreeSmallBlock, extraBlock);
7056 extraBlock = blockIndex;
7059 return TRUE;
7062 /******************************************************************************
7063 * SmallBlockChainStream_Enlarge
7065 * Grows this chain in the small block depot.
7067 static BOOL SmallBlockChainStream_Enlarge(
7068 SmallBlockChainStream* This,
7069 ULARGE_INTEGER newSize)
7071 ULONG blockIndex, currentBlock;
7072 ULONG newNumBlocks;
7073 ULONG oldNumBlocks = 0;
7075 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7078 * Empty chain. Create the head.
7080 if (blockIndex == BLOCK_END_OF_CHAIN)
7082 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7083 SmallBlockChainStream_SetNextBlockInChain(
7084 This,
7085 blockIndex,
7086 BLOCK_END_OF_CHAIN);
7088 if (This->headOfStreamPlaceHolder != NULL)
7090 *(This->headOfStreamPlaceHolder) = blockIndex;
7092 else
7094 DirEntry chainEntry;
7096 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
7097 &chainEntry);
7099 chainEntry.startingBlock = blockIndex;
7101 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
7102 &chainEntry);
7106 currentBlock = blockIndex;
7109 * Figure out how many blocks are needed to contain this stream
7111 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
7113 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
7114 newNumBlocks++;
7117 * Go to the current end of chain
7119 while (blockIndex != BLOCK_END_OF_CHAIN)
7121 oldNumBlocks++;
7122 currentBlock = blockIndex;
7123 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
7124 return FALSE;
7128 * Add new blocks to the chain
7130 while (oldNumBlocks < newNumBlocks)
7132 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
7133 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
7135 SmallBlockChainStream_SetNextBlockInChain(
7136 This,
7137 blockIndex,
7138 BLOCK_END_OF_CHAIN);
7140 currentBlock = blockIndex;
7141 oldNumBlocks++;
7144 return TRUE;
7147 /******************************************************************************
7148 * SmallBlockChainStream_SetSize
7150 * Sets the size of this stream.
7151 * The file will grow if we grow the chain.
7153 * TODO: Free the actual blocks in the file when we shrink the chain.
7154 * Currently, the blocks are still in the file. So the file size
7155 * doesn't shrink even if we shrink streams.
7157 BOOL SmallBlockChainStream_SetSize(
7158 SmallBlockChainStream* This,
7159 ULARGE_INTEGER newSize)
7161 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
7163 if (newSize.u.LowPart == size.u.LowPart)
7164 return TRUE;
7166 if (newSize.u.LowPart < size.u.LowPart)
7168 SmallBlockChainStream_Shrink(This, newSize);
7170 else
7172 SmallBlockChainStream_Enlarge(This, newSize);
7175 return TRUE;
7178 /******************************************************************************
7179 * SmallBlockChainStream_GetCount
7181 * Returns the number of small blocks that comprises this chain.
7182 * This is not the size of the stream as the last block may not be full!
7185 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
7187 ULONG blockIndex;
7188 ULONG count = 0;
7190 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
7192 while(blockIndex != BLOCK_END_OF_CHAIN)
7194 count++;
7196 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
7197 blockIndex, &blockIndex)))
7198 return 0;
7201 return count;
7204 /******************************************************************************
7205 * SmallBlockChainStream_GetSize
7207 * Returns the size of this chain.
7209 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
7211 DirEntry chainEntry;
7213 if(This->headOfStreamPlaceHolder != NULL)
7215 ULARGE_INTEGER result;
7216 result.u.HighPart = 0;
7218 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
7219 This->parentStorage->smallBlockSize;
7221 return result;
7224 StorageImpl_ReadDirEntry(
7225 This->parentStorage,
7226 This->ownerDirEntry,
7227 &chainEntry);
7229 return chainEntry.size;
7232 static HRESULT create_storagefile(
7233 LPCOLESTR pwcsName,
7234 DWORD grfMode,
7235 DWORD grfAttrs,
7236 STGOPTIONS* pStgOptions,
7237 REFIID riid,
7238 void** ppstgOpen)
7240 StorageBaseImpl* newStorage = 0;
7241 HANDLE hFile = INVALID_HANDLE_VALUE;
7242 HRESULT hr = STG_E_INVALIDFLAG;
7243 DWORD shareMode;
7244 DWORD accessMode;
7245 DWORD creationMode;
7246 DWORD fileAttributes;
7247 WCHAR tempFileName[MAX_PATH];
7249 if (ppstgOpen == 0)
7250 return STG_E_INVALIDPOINTER;
7252 if (pStgOptions->ulSectorSize != MIN_BIG_BLOCK_SIZE && pStgOptions->ulSectorSize != MAX_BIG_BLOCK_SIZE)
7253 return STG_E_INVALIDPARAMETER;
7255 /* if no share mode given then DENY_NONE is the default */
7256 if (STGM_SHARE_MODE(grfMode) == 0)
7257 grfMode |= STGM_SHARE_DENY_NONE;
7259 if ( FAILED( validateSTGM(grfMode) ))
7260 goto end;
7262 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
7263 switch(STGM_ACCESS_MODE(grfMode))
7265 case STGM_WRITE:
7266 case STGM_READWRITE:
7267 break;
7268 default:
7269 goto end;
7272 /* in direct mode, can only use SHARE_EXCLUSIVE */
7273 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
7274 goto end;
7276 /* but in transacted mode, any share mode is valid */
7279 * Generate a unique name.
7281 if (pwcsName == 0)
7283 WCHAR tempPath[MAX_PATH];
7284 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
7286 memset(tempPath, 0, sizeof(tempPath));
7287 memset(tempFileName, 0, sizeof(tempFileName));
7289 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
7290 tempPath[0] = '.';
7292 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
7293 pwcsName = tempFileName;
7294 else
7296 hr = STG_E_INSUFFICIENTMEMORY;
7297 goto end;
7300 creationMode = TRUNCATE_EXISTING;
7302 else
7304 creationMode = GetCreationModeFromSTGM(grfMode);
7308 * Interpret the STGM value grfMode
7310 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7311 accessMode = GetAccessModeFromSTGM(grfMode);
7313 if (grfMode & STGM_DELETEONRELEASE)
7314 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
7315 else
7316 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
7318 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
7320 static int fixme;
7321 if (!fixme++)
7322 FIXME("Storage share mode not implemented.\n");
7325 *ppstgOpen = 0;
7327 hFile = CreateFileW(pwcsName,
7328 accessMode,
7329 shareMode,
7330 NULL,
7331 creationMode,
7332 fileAttributes,
7335 if (hFile == INVALID_HANDLE_VALUE)
7337 if(GetLastError() == ERROR_FILE_EXISTS)
7338 hr = STG_E_FILEALREADYEXISTS;
7339 else
7340 hr = E_FAIL;
7341 goto end;
7345 * Allocate and initialize the new IStorage32object.
7347 hr = Storage_Construct(
7348 hFile,
7349 pwcsName,
7350 NULL,
7351 grfMode,
7352 TRUE,
7353 TRUE,
7354 pStgOptions->ulSectorSize,
7355 &newStorage);
7357 if (FAILED(hr))
7359 goto end;
7362 hr = IStorage_QueryInterface((IStorage*)newStorage, riid, ppstgOpen);
7364 IStorage_Release((IStorage*)newStorage);
7366 end:
7367 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
7369 return hr;
7372 /******************************************************************************
7373 * StgCreateDocfile [OLE32.@]
7374 * Creates a new compound file storage object
7376 * PARAMS
7377 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
7378 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
7379 * reserved [ ?] unused?, usually 0
7380 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
7382 * RETURNS
7383 * S_OK if the file was successfully created
7384 * some STG_E_ value if error
7385 * NOTES
7386 * if pwcsName is NULL, create file with new unique name
7387 * the function can returns
7388 * STG_S_CONVERTED if the specified file was successfully converted to storage format
7389 * (unrealized now)
7391 HRESULT WINAPI StgCreateDocfile(
7392 LPCOLESTR pwcsName,
7393 DWORD grfMode,
7394 DWORD reserved,
7395 IStorage **ppstgOpen)
7397 STGOPTIONS stgoptions = {1, 0, 512};
7399 TRACE("(%s, %x, %d, %p)\n",
7400 debugstr_w(pwcsName), grfMode,
7401 reserved, ppstgOpen);
7403 if (ppstgOpen == 0)
7404 return STG_E_INVALIDPOINTER;
7405 if (reserved != 0)
7406 return STG_E_INVALIDPARAMETER;
7408 return create_storagefile(pwcsName, grfMode, 0, &stgoptions, &IID_IStorage, (void**)ppstgOpen);
7411 /******************************************************************************
7412 * StgCreateStorageEx [OLE32.@]
7414 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7416 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7417 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7419 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
7421 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
7422 return STG_E_INVALIDPARAMETER;
7425 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
7427 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
7428 return STG_E_INVALIDPARAMETER;
7431 if (stgfmt == STGFMT_FILE)
7433 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7434 return STG_E_INVALIDPARAMETER;
7437 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
7439 STGOPTIONS defaultOptions = {1, 0, 512};
7441 if (!pStgOptions) pStgOptions = &defaultOptions;
7442 return create_storagefile(pwcsName, grfMode, grfAttrs, pStgOptions, riid, ppObjectOpen);
7446 ERR("Invalid stgfmt argument\n");
7447 return STG_E_INVALIDPARAMETER;
7450 /******************************************************************************
7451 * StgCreatePropSetStg [OLE32.@]
7453 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
7454 IPropertySetStorage **ppPropSetStg)
7456 HRESULT hr;
7458 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
7459 if (reserved)
7460 hr = STG_E_INVALIDPARAMETER;
7461 else
7462 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
7463 (void**)ppPropSetStg);
7464 return hr;
7467 /******************************************************************************
7468 * StgOpenStorageEx [OLE32.@]
7470 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
7472 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
7473 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
7475 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
7477 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
7478 return STG_E_INVALIDPARAMETER;
7481 switch (stgfmt)
7483 case STGFMT_FILE:
7484 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
7485 return STG_E_INVALIDPARAMETER;
7487 case STGFMT_STORAGE:
7488 break;
7490 case STGFMT_DOCFILE:
7491 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
7493 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
7494 return STG_E_INVALIDPARAMETER;
7496 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
7497 break;
7499 case STGFMT_ANY:
7500 WARN("STGFMT_ANY assuming storage\n");
7501 break;
7503 default:
7504 return STG_E_INVALIDPARAMETER;
7507 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
7511 /******************************************************************************
7512 * StgOpenStorage [OLE32.@]
7514 HRESULT WINAPI StgOpenStorage(
7515 const OLECHAR *pwcsName,
7516 IStorage *pstgPriority,
7517 DWORD grfMode,
7518 SNB snbExclude,
7519 DWORD reserved,
7520 IStorage **ppstgOpen)
7522 StorageBaseImpl* newStorage = 0;
7523 HRESULT hr = S_OK;
7524 HANDLE hFile = 0;
7525 DWORD shareMode;
7526 DWORD accessMode;
7528 TRACE("(%s, %p, %x, %p, %d, %p)\n",
7529 debugstr_w(pwcsName), pstgPriority, grfMode,
7530 snbExclude, reserved, ppstgOpen);
7532 if (pwcsName == 0)
7534 hr = STG_E_INVALIDNAME;
7535 goto end;
7538 if (ppstgOpen == 0)
7540 hr = STG_E_INVALIDPOINTER;
7541 goto end;
7544 if (reserved)
7546 hr = STG_E_INVALIDPARAMETER;
7547 goto end;
7550 if (grfMode & STGM_PRIORITY)
7552 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
7553 return STG_E_INVALIDFLAG;
7554 if (grfMode & STGM_DELETEONRELEASE)
7555 return STG_E_INVALIDFUNCTION;
7556 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
7557 return STG_E_INVALIDFLAG;
7558 grfMode &= ~0xf0; /* remove the existing sharing mode */
7559 grfMode |= STGM_SHARE_DENY_NONE;
7561 /* STGM_PRIORITY stops other IStorage objects on the same file from
7562 * committing until the STGM_PRIORITY IStorage is closed. it also
7563 * stops non-transacted mode StgOpenStorage calls with write access from
7564 * succeeding. obviously, both of these cannot be achieved through just
7565 * file share flags */
7566 FIXME("STGM_PRIORITY mode not implemented correctly\n");
7570 * Validate the sharing mode
7572 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
7573 switch(STGM_SHARE_MODE(grfMode))
7575 case STGM_SHARE_EXCLUSIVE:
7576 case STGM_SHARE_DENY_WRITE:
7577 break;
7578 default:
7579 hr = STG_E_INVALIDFLAG;
7580 goto end;
7583 if ( FAILED( validateSTGM(grfMode) ) ||
7584 (grfMode&STGM_CREATE))
7586 hr = STG_E_INVALIDFLAG;
7587 goto end;
7590 /* shared reading requires transacted mode */
7591 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
7592 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
7593 !(grfMode&STGM_TRANSACTED) )
7595 hr = STG_E_INVALIDFLAG;
7596 goto end;
7600 * Interpret the STGM value grfMode
7602 shareMode = GetShareModeFromSTGM(grfMode);
7603 accessMode = GetAccessModeFromSTGM(grfMode);
7605 *ppstgOpen = 0;
7607 hFile = CreateFileW( pwcsName,
7608 accessMode,
7609 shareMode,
7610 NULL,
7611 OPEN_EXISTING,
7612 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
7615 if (hFile==INVALID_HANDLE_VALUE)
7617 DWORD last_error = GetLastError();
7619 hr = E_FAIL;
7621 switch (last_error)
7623 case ERROR_FILE_NOT_FOUND:
7624 hr = STG_E_FILENOTFOUND;
7625 break;
7627 case ERROR_PATH_NOT_FOUND:
7628 hr = STG_E_PATHNOTFOUND;
7629 break;
7631 case ERROR_ACCESS_DENIED:
7632 case ERROR_WRITE_PROTECT:
7633 hr = STG_E_ACCESSDENIED;
7634 break;
7636 case ERROR_SHARING_VIOLATION:
7637 hr = STG_E_SHAREVIOLATION;
7638 break;
7640 default:
7641 hr = E_FAIL;
7644 goto end;
7648 * Refuse to open the file if it's too small to be a structured storage file
7649 * FIXME: verify the file when reading instead of here
7651 if (GetFileSize(hFile, NULL) < 0x100)
7653 CloseHandle(hFile);
7654 hr = STG_E_FILEALREADYEXISTS;
7655 goto end;
7659 * Allocate and initialize the new IStorage32object.
7661 hr = Storage_Construct(
7662 hFile,
7663 pwcsName,
7664 NULL,
7665 grfMode,
7666 TRUE,
7667 FALSE,
7668 512,
7669 &newStorage);
7671 if (FAILED(hr))
7674 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
7676 if(hr == STG_E_INVALIDHEADER)
7677 hr = STG_E_FILEALREADYEXISTS;
7678 goto end;
7682 * Get an "out" pointer for the caller.
7684 *ppstgOpen = (IStorage*)newStorage;
7686 end:
7687 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
7688 return hr;
7691 /******************************************************************************
7692 * StgCreateDocfileOnILockBytes [OLE32.@]
7694 HRESULT WINAPI StgCreateDocfileOnILockBytes(
7695 ILockBytes *plkbyt,
7696 DWORD grfMode,
7697 DWORD reserved,
7698 IStorage** ppstgOpen)
7700 StorageBaseImpl* newStorage = 0;
7701 HRESULT hr = S_OK;
7703 if ((ppstgOpen == 0) || (plkbyt == 0))
7704 return STG_E_INVALIDPOINTER;
7707 * Allocate and initialize the new IStorage object.
7709 hr = Storage_Construct(
7712 plkbyt,
7713 grfMode,
7714 FALSE,
7715 TRUE,
7716 512,
7717 &newStorage);
7719 if (FAILED(hr))
7721 return hr;
7725 * Get an "out" pointer for the caller.
7727 *ppstgOpen = (IStorage*)newStorage;
7729 return hr;
7732 /******************************************************************************
7733 * StgOpenStorageOnILockBytes [OLE32.@]
7735 HRESULT WINAPI StgOpenStorageOnILockBytes(
7736 ILockBytes *plkbyt,
7737 IStorage *pstgPriority,
7738 DWORD grfMode,
7739 SNB snbExclude,
7740 DWORD reserved,
7741 IStorage **ppstgOpen)
7743 StorageBaseImpl* newStorage = 0;
7744 HRESULT hr = S_OK;
7746 if ((plkbyt == 0) || (ppstgOpen == 0))
7747 return STG_E_INVALIDPOINTER;
7749 if ( FAILED( validateSTGM(grfMode) ))
7750 return STG_E_INVALIDFLAG;
7752 *ppstgOpen = 0;
7755 * Allocate and initialize the new IStorage object.
7757 hr = Storage_Construct(
7760 plkbyt,
7761 grfMode,
7762 FALSE,
7763 FALSE,
7764 512,
7765 &newStorage);
7767 if (FAILED(hr))
7769 return hr;
7773 * Get an "out" pointer for the caller.
7775 *ppstgOpen = (IStorage*)newStorage;
7777 return hr;
7780 /******************************************************************************
7781 * StgSetTimes [ole32.@]
7782 * StgSetTimes [OLE32.@]
7786 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
7787 FILETIME const *patime, FILETIME const *pmtime)
7789 IStorage *stg = NULL;
7790 HRESULT r;
7792 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
7794 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
7795 0, 0, &stg);
7796 if( SUCCEEDED(r) )
7798 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
7799 IStorage_Release(stg);
7802 return r;
7805 /******************************************************************************
7806 * StgIsStorageILockBytes [OLE32.@]
7808 * Determines if the ILockBytes contains a storage object.
7810 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
7812 BYTE sig[8];
7813 ULARGE_INTEGER offset;
7815 offset.u.HighPart = 0;
7816 offset.u.LowPart = 0;
7818 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
7820 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
7821 return S_OK;
7823 return S_FALSE;
7826 /******************************************************************************
7827 * WriteClassStg [OLE32.@]
7829 * This method will store the specified CLSID in the specified storage object
7831 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
7833 HRESULT hRes;
7835 if(!pStg)
7836 return E_INVALIDARG;
7838 if(!rclsid)
7839 return STG_E_INVALIDPOINTER;
7841 hRes = IStorage_SetClass(pStg, rclsid);
7843 return hRes;
7846 /***********************************************************************
7847 * ReadClassStg (OLE32.@)
7849 * This method reads the CLSID previously written to a storage object with
7850 * the WriteClassStg.
7852 * PARAMS
7853 * pstg [I] IStorage pointer
7854 * pclsid [O] Pointer to where the CLSID is written
7856 * RETURNS
7857 * Success: S_OK.
7858 * Failure: HRESULT code.
7860 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
7862 STATSTG pstatstg;
7863 HRESULT hRes;
7865 TRACE("(%p, %p)\n", pstg, pclsid);
7867 if(!pstg || !pclsid)
7868 return E_INVALIDARG;
7871 * read a STATSTG structure (contains the clsid) from the storage
7873 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
7875 if(SUCCEEDED(hRes))
7876 *pclsid=pstatstg.clsid;
7878 return hRes;
7881 /***********************************************************************
7882 * OleLoadFromStream (OLE32.@)
7884 * This function loads an object from stream
7886 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
7888 CLSID clsid;
7889 HRESULT res;
7890 LPPERSISTSTREAM xstm;
7892 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
7894 res=ReadClassStm(pStm,&clsid);
7895 if (FAILED(res))
7896 return res;
7897 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
7898 if (FAILED(res))
7899 return res;
7900 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
7901 if (FAILED(res)) {
7902 IUnknown_Release((IUnknown*)*ppvObj);
7903 return res;
7905 res=IPersistStream_Load(xstm,pStm);
7906 IPersistStream_Release(xstm);
7907 /* FIXME: all refcounts ok at this point? I think they should be:
7908 * pStm : unchanged
7909 * ppvObj : 1
7910 * xstm : 0 (released)
7912 return res;
7915 /***********************************************************************
7916 * OleSaveToStream (OLE32.@)
7918 * This function saves an object with the IPersistStream interface on it
7919 * to the specified stream.
7921 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
7924 CLSID clsid;
7925 HRESULT res;
7927 TRACE("(%p,%p)\n",pPStm,pStm);
7929 res=IPersistStream_GetClassID(pPStm,&clsid);
7931 if (SUCCEEDED(res)){
7933 res=WriteClassStm(pStm,&clsid);
7935 if (SUCCEEDED(res))
7937 res=IPersistStream_Save(pPStm,pStm,TRUE);
7940 TRACE("Finished Save\n");
7941 return res;
7944 /****************************************************************************
7945 * This method validate a STGM parameter that can contain the values below
7947 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
7948 * The stgm values contained in 0xffff0000 are bitmasks.
7950 * STGM_DIRECT 0x00000000
7951 * STGM_TRANSACTED 0x00010000
7952 * STGM_SIMPLE 0x08000000
7954 * STGM_READ 0x00000000
7955 * STGM_WRITE 0x00000001
7956 * STGM_READWRITE 0x00000002
7958 * STGM_SHARE_DENY_NONE 0x00000040
7959 * STGM_SHARE_DENY_READ 0x00000030
7960 * STGM_SHARE_DENY_WRITE 0x00000020
7961 * STGM_SHARE_EXCLUSIVE 0x00000010
7963 * STGM_PRIORITY 0x00040000
7964 * STGM_DELETEONRELEASE 0x04000000
7966 * STGM_CREATE 0x00001000
7967 * STGM_CONVERT 0x00020000
7968 * STGM_FAILIFTHERE 0x00000000
7970 * STGM_NOSCRATCH 0x00100000
7971 * STGM_NOSNAPSHOT 0x00200000
7973 static HRESULT validateSTGM(DWORD stgm)
7975 DWORD access = STGM_ACCESS_MODE(stgm);
7976 DWORD share = STGM_SHARE_MODE(stgm);
7977 DWORD create = STGM_CREATE_MODE(stgm);
7979 if (stgm&~STGM_KNOWN_FLAGS)
7981 ERR("unknown flags %08x\n", stgm);
7982 return E_FAIL;
7985 switch (access)
7987 case STGM_READ:
7988 case STGM_WRITE:
7989 case STGM_READWRITE:
7990 break;
7991 default:
7992 return E_FAIL;
7995 switch (share)
7997 case STGM_SHARE_DENY_NONE:
7998 case STGM_SHARE_DENY_READ:
7999 case STGM_SHARE_DENY_WRITE:
8000 case STGM_SHARE_EXCLUSIVE:
8001 break;
8002 default:
8003 return E_FAIL;
8006 switch (create)
8008 case STGM_CREATE:
8009 case STGM_FAILIFTHERE:
8010 break;
8011 default:
8012 return E_FAIL;
8016 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
8018 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
8019 return E_FAIL;
8022 * STGM_CREATE | STGM_CONVERT
8023 * if both are false, STGM_FAILIFTHERE is set to TRUE
8025 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
8026 return E_FAIL;
8029 * STGM_NOSCRATCH requires STGM_TRANSACTED
8031 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
8032 return E_FAIL;
8035 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
8036 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
8038 if ( (stgm & STGM_NOSNAPSHOT) &&
8039 (!(stgm & STGM_TRANSACTED) ||
8040 share == STGM_SHARE_EXCLUSIVE ||
8041 share == STGM_SHARE_DENY_WRITE) )
8042 return E_FAIL;
8044 return S_OK;
8047 /****************************************************************************
8048 * GetShareModeFromSTGM
8050 * This method will return a share mode flag from a STGM value.
8051 * The STGM value is assumed valid.
8053 static DWORD GetShareModeFromSTGM(DWORD stgm)
8055 switch (STGM_SHARE_MODE(stgm))
8057 case STGM_SHARE_DENY_NONE:
8058 return FILE_SHARE_READ | FILE_SHARE_WRITE;
8059 case STGM_SHARE_DENY_READ:
8060 return FILE_SHARE_WRITE;
8061 case STGM_SHARE_DENY_WRITE:
8062 return FILE_SHARE_READ;
8063 case STGM_SHARE_EXCLUSIVE:
8064 return 0;
8066 ERR("Invalid share mode!\n");
8067 assert(0);
8068 return 0;
8071 /****************************************************************************
8072 * GetAccessModeFromSTGM
8074 * This method will return an access mode flag from a STGM value.
8075 * The STGM value is assumed valid.
8077 static DWORD GetAccessModeFromSTGM(DWORD stgm)
8079 switch (STGM_ACCESS_MODE(stgm))
8081 case STGM_READ:
8082 return GENERIC_READ;
8083 case STGM_WRITE:
8084 case STGM_READWRITE:
8085 return GENERIC_READ | GENERIC_WRITE;
8087 ERR("Invalid access mode!\n");
8088 assert(0);
8089 return 0;
8092 /****************************************************************************
8093 * GetCreationModeFromSTGM
8095 * This method will return a creation mode flag from a STGM value.
8096 * The STGM value is assumed valid.
8098 static DWORD GetCreationModeFromSTGM(DWORD stgm)
8100 switch(STGM_CREATE_MODE(stgm))
8102 case STGM_CREATE:
8103 return CREATE_ALWAYS;
8104 case STGM_CONVERT:
8105 FIXME("STGM_CONVERT not implemented!\n");
8106 return CREATE_NEW;
8107 case STGM_FAILIFTHERE:
8108 return CREATE_NEW;
8110 ERR("Invalid create mode!\n");
8111 assert(0);
8112 return 0;
8116 /*************************************************************************
8117 * OLECONVERT_LoadOLE10 [Internal]
8119 * Loads the OLE10 STREAM to memory
8121 * PARAMS
8122 * pOleStream [I] The OLESTREAM
8123 * pData [I] Data Structure for the OLESTREAM Data
8125 * RETURNS
8126 * Success: S_OK
8127 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
8128 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
8130 * NOTES
8131 * This function is used by OleConvertOLESTREAMToIStorage only.
8133 * Memory allocated for pData must be freed by the caller
8135 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
8137 DWORD dwSize;
8138 HRESULT hRes = S_OK;
8139 int nTryCnt=0;
8140 int max_try = 6;
8142 pData->pData = NULL;
8143 pData->pstrOleObjFileName = NULL;
8145 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
8147 /* Get the OleID */
8148 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8149 if(dwSize != sizeof(pData->dwOleID))
8151 hRes = CONVERT10_E_OLESTREAM_GET;
8153 else if(pData->dwOleID != OLESTREAM_ID)
8155 hRes = CONVERT10_E_OLESTREAM_FMT;
8157 else
8159 hRes = S_OK;
8160 break;
8164 if(hRes == S_OK)
8166 /* Get the TypeID... more info needed for this field */
8167 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8168 if(dwSize != sizeof(pData->dwTypeID))
8170 hRes = CONVERT10_E_OLESTREAM_GET;
8173 if(hRes == S_OK)
8175 if(pData->dwTypeID != 0)
8177 /* Get the length of the OleTypeName */
8178 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8179 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8181 hRes = CONVERT10_E_OLESTREAM_GET;
8184 if(hRes == S_OK)
8186 if(pData->dwOleTypeNameLength > 0)
8188 /* Get the OleTypeName */
8189 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8190 if(dwSize != pData->dwOleTypeNameLength)
8192 hRes = CONVERT10_E_OLESTREAM_GET;
8196 if(bStrem1)
8198 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
8199 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
8201 hRes = CONVERT10_E_OLESTREAM_GET;
8203 if(hRes == S_OK)
8205 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
8206 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
8207 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
8208 if(pData->pstrOleObjFileName)
8210 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
8211 if(dwSize != pData->dwOleObjFileNameLength)
8213 hRes = CONVERT10_E_OLESTREAM_GET;
8216 else
8217 hRes = CONVERT10_E_OLESTREAM_GET;
8220 else
8222 /* Get the Width of the Metafile */
8223 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8224 if(dwSize != sizeof(pData->dwMetaFileWidth))
8226 hRes = CONVERT10_E_OLESTREAM_GET;
8228 if(hRes == S_OK)
8230 /* Get the Height of the Metafile */
8231 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8232 if(dwSize != sizeof(pData->dwMetaFileHeight))
8234 hRes = CONVERT10_E_OLESTREAM_GET;
8238 if(hRes == S_OK)
8240 /* Get the Length of the Data */
8241 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8242 if(dwSize != sizeof(pData->dwDataLength))
8244 hRes = CONVERT10_E_OLESTREAM_GET;
8248 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
8250 if(!bStrem1) /* if it is a second OLE stream data */
8252 pData->dwDataLength -= 8;
8253 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
8254 if(dwSize != sizeof(pData->strUnknown))
8256 hRes = CONVERT10_E_OLESTREAM_GET;
8260 if(hRes == S_OK)
8262 if(pData->dwDataLength > 0)
8264 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
8266 /* Get Data (ex. IStorage, Metafile, or BMP) */
8267 if(pData->pData)
8269 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
8270 if(dwSize != pData->dwDataLength)
8272 hRes = CONVERT10_E_OLESTREAM_GET;
8275 else
8277 hRes = CONVERT10_E_OLESTREAM_GET;
8283 return hRes;
8286 /*************************************************************************
8287 * OLECONVERT_SaveOLE10 [Internal]
8289 * Saves the OLE10 STREAM From memory
8291 * PARAMS
8292 * pData [I] Data Structure for the OLESTREAM Data
8293 * pOleStream [I] The OLESTREAM to save
8295 * RETURNS
8296 * Success: S_OK
8297 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8299 * NOTES
8300 * This function is used by OleConvertIStorageToOLESTREAM only.
8303 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
8305 DWORD dwSize;
8306 HRESULT hRes = S_OK;
8309 /* Set the OleID */
8310 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
8311 if(dwSize != sizeof(pData->dwOleID))
8313 hRes = CONVERT10_E_OLESTREAM_PUT;
8316 if(hRes == S_OK)
8318 /* Set the TypeID */
8319 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
8320 if(dwSize != sizeof(pData->dwTypeID))
8322 hRes = CONVERT10_E_OLESTREAM_PUT;
8326 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
8328 /* Set the Length of the OleTypeName */
8329 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
8330 if(dwSize != sizeof(pData->dwOleTypeNameLength))
8332 hRes = CONVERT10_E_OLESTREAM_PUT;
8335 if(hRes == S_OK)
8337 if(pData->dwOleTypeNameLength > 0)
8339 /* Set the OleTypeName */
8340 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
8341 if(dwSize != pData->dwOleTypeNameLength)
8343 hRes = CONVERT10_E_OLESTREAM_PUT;
8348 if(hRes == S_OK)
8350 /* Set the width of the Metafile */
8351 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
8352 if(dwSize != sizeof(pData->dwMetaFileWidth))
8354 hRes = CONVERT10_E_OLESTREAM_PUT;
8358 if(hRes == S_OK)
8360 /* Set the height of the Metafile */
8361 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
8362 if(dwSize != sizeof(pData->dwMetaFileHeight))
8364 hRes = CONVERT10_E_OLESTREAM_PUT;
8368 if(hRes == S_OK)
8370 /* Set the length of the Data */
8371 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
8372 if(dwSize != sizeof(pData->dwDataLength))
8374 hRes = CONVERT10_E_OLESTREAM_PUT;
8378 if(hRes == S_OK)
8380 if(pData->dwDataLength > 0)
8382 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
8383 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
8384 if(dwSize != pData->dwDataLength)
8386 hRes = CONVERT10_E_OLESTREAM_PUT;
8391 return hRes;
8394 /*************************************************************************
8395 * OLECONVERT_GetOLE20FromOLE10[Internal]
8397 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
8398 * opens it, and copies the content to the dest IStorage for
8399 * OleConvertOLESTREAMToIStorage
8402 * PARAMS
8403 * pDestStorage [I] The IStorage to copy the data to
8404 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
8405 * nBufferLength [I] The size of the buffer
8407 * RETURNS
8408 * Nothing
8410 * NOTES
8414 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
8416 HRESULT hRes;
8417 HANDLE hFile;
8418 IStorage *pTempStorage;
8419 DWORD dwNumOfBytesWritten;
8420 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8421 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8423 /* Create a temp File */
8424 GetTempPathW(MAX_PATH, wstrTempDir);
8425 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8426 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
8428 if(hFile != INVALID_HANDLE_VALUE)
8430 /* Write IStorage Data to File */
8431 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
8432 CloseHandle(hFile);
8434 /* Open and copy temp storage to the Dest Storage */
8435 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
8436 if(hRes == S_OK)
8438 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
8439 IStorage_Release(pTempStorage);
8441 DeleteFileW(wstrTempFile);
8446 /*************************************************************************
8447 * OLECONVERT_WriteOLE20ToBuffer [Internal]
8449 * Saves the OLE10 STREAM From memory
8451 * PARAMS
8452 * pStorage [I] The Src IStorage to copy
8453 * pData [I] The Dest Memory to write to.
8455 * RETURNS
8456 * The size in bytes allocated for pData
8458 * NOTES
8459 * Memory allocated for pData must be freed by the caller
8461 * Used by OleConvertIStorageToOLESTREAM only.
8464 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
8466 HANDLE hFile;
8467 HRESULT hRes;
8468 DWORD nDataLength = 0;
8469 IStorage *pTempStorage;
8470 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
8471 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
8473 *pData = NULL;
8475 /* Create temp Storage */
8476 GetTempPathW(MAX_PATH, wstrTempDir);
8477 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
8478 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
8480 if(hRes == S_OK)
8482 /* Copy Src Storage to the Temp Storage */
8483 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
8484 IStorage_Release(pTempStorage);
8486 /* Open Temp Storage as a file and copy to memory */
8487 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
8488 if(hFile != INVALID_HANDLE_VALUE)
8490 nDataLength = GetFileSize(hFile, NULL);
8491 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
8492 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
8493 CloseHandle(hFile);
8495 DeleteFileW(wstrTempFile);
8497 return nDataLength;
8500 /*************************************************************************
8501 * OLECONVERT_CreateOleStream [Internal]
8503 * Creates the "\001OLE" stream in the IStorage if necessary.
8505 * PARAMS
8506 * pStorage [I] Dest storage to create the stream in
8508 * RETURNS
8509 * Nothing
8511 * NOTES
8512 * This function is used by OleConvertOLESTREAMToIStorage only.
8514 * This stream is still unknown, MS Word seems to have extra data
8515 * but since the data is stored in the OLESTREAM there should be
8516 * no need to recreate the stream. If the stream is manually
8517 * deleted it will create it with this default data.
8520 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
8522 HRESULT hRes;
8523 IStream *pStream;
8524 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
8525 BYTE pOleStreamHeader [] =
8527 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
8528 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8529 0x00, 0x00, 0x00, 0x00
8532 /* Create stream if not present */
8533 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8534 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8536 if(hRes == S_OK)
8538 /* Write default Data */
8539 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
8540 IStream_Release(pStream);
8544 /* write a string to a stream, preceded by its length */
8545 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
8547 HRESULT r;
8548 LPSTR str;
8549 DWORD len = 0;
8551 if( string )
8552 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
8553 r = IStream_Write( stm, &len, sizeof(len), NULL);
8554 if( FAILED( r ) )
8555 return r;
8556 if(len == 0)
8557 return r;
8558 str = CoTaskMemAlloc( len );
8559 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
8560 r = IStream_Write( stm, str, len, NULL);
8561 CoTaskMemFree( str );
8562 return r;
8565 /* read a string preceded by its length from a stream */
8566 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
8568 HRESULT r;
8569 DWORD len, count = 0;
8570 LPSTR str;
8571 LPWSTR wstr;
8573 r = IStream_Read( stm, &len, sizeof(len), &count );
8574 if( FAILED( r ) )
8575 return r;
8576 if( count != sizeof(len) )
8577 return E_OUTOFMEMORY;
8579 TRACE("%d bytes\n",len);
8581 str = CoTaskMemAlloc( len );
8582 if( !str )
8583 return E_OUTOFMEMORY;
8584 count = 0;
8585 r = IStream_Read( stm, str, len, &count );
8586 if( FAILED( r ) )
8587 return r;
8588 if( count != len )
8590 CoTaskMemFree( str );
8591 return E_OUTOFMEMORY;
8594 TRACE("Read string %s\n",debugstr_an(str,len));
8596 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
8597 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
8598 if( wstr )
8599 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
8600 CoTaskMemFree( str );
8602 *string = wstr;
8604 return r;
8608 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
8609 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
8611 IStream *pstm;
8612 HRESULT r = S_OK;
8613 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8615 static const BYTE unknown1[12] =
8616 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
8617 0xFF, 0xFF, 0xFF, 0xFF};
8618 static const BYTE unknown2[16] =
8619 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
8620 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8622 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
8623 debugstr_w(lpszUserType), debugstr_w(szClipName),
8624 debugstr_w(szProgIDName));
8626 /* Create a CompObj stream */
8627 r = IStorage_CreateStream(pstg, szwStreamName,
8628 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
8629 if( FAILED (r) )
8630 return r;
8632 /* Write CompObj Structure to stream */
8633 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
8635 if( SUCCEEDED( r ) )
8636 r = WriteClassStm( pstm, clsid );
8638 if( SUCCEEDED( r ) )
8639 r = STREAM_WriteString( pstm, lpszUserType );
8640 if( SUCCEEDED( r ) )
8641 r = STREAM_WriteString( pstm, szClipName );
8642 if( SUCCEEDED( r ) )
8643 r = STREAM_WriteString( pstm, szProgIDName );
8644 if( SUCCEEDED( r ) )
8645 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
8647 IStream_Release( pstm );
8649 return r;
8652 /***********************************************************************
8653 * WriteFmtUserTypeStg (OLE32.@)
8655 HRESULT WINAPI WriteFmtUserTypeStg(
8656 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
8658 HRESULT r;
8659 WCHAR szwClipName[0x40];
8660 CLSID clsid = CLSID_NULL;
8661 LPWSTR wstrProgID = NULL;
8662 DWORD n;
8664 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
8666 /* get the clipboard format name */
8667 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
8668 szwClipName[n]=0;
8670 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
8672 /* FIXME: There's room to save a CLSID and its ProgID, but
8673 the CLSID is not looked up in the registry and in all the
8674 tests I wrote it was CLSID_NULL. Where does it come from?
8677 /* get the real program ID. This may fail, but that's fine */
8678 ProgIDFromCLSID(&clsid, &wstrProgID);
8680 TRACE("progid is %s\n",debugstr_w(wstrProgID));
8682 r = STORAGE_WriteCompObj( pstg, &clsid,
8683 lpszUserType, szwClipName, wstrProgID );
8685 CoTaskMemFree(wstrProgID);
8687 return r;
8691 /******************************************************************************
8692 * ReadFmtUserTypeStg [OLE32.@]
8694 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
8696 HRESULT r;
8697 IStream *stm = 0;
8698 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
8699 unsigned char unknown1[12];
8700 unsigned char unknown2[16];
8701 DWORD count;
8702 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
8703 CLSID clsid;
8705 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
8707 r = IStorage_OpenStream( pstg, szCompObj, NULL,
8708 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
8709 if( FAILED ( r ) )
8711 WARN("Failed to open stream r = %08x\n", r);
8712 return r;
8715 /* read the various parts of the structure */
8716 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
8717 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
8718 goto end;
8719 r = ReadClassStm( stm, &clsid );
8720 if( FAILED( r ) )
8721 goto end;
8723 r = STREAM_ReadString( stm, &szCLSIDName );
8724 if( FAILED( r ) )
8725 goto end;
8727 r = STREAM_ReadString( stm, &szOleTypeName );
8728 if( FAILED( r ) )
8729 goto end;
8731 r = STREAM_ReadString( stm, &szProgIDName );
8732 if( FAILED( r ) )
8733 goto end;
8735 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
8736 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
8737 goto end;
8739 /* ok, success... now we just need to store what we found */
8740 if( pcf )
8741 *pcf = RegisterClipboardFormatW( szOleTypeName );
8742 CoTaskMemFree( szOleTypeName );
8744 if( lplpszUserType )
8745 *lplpszUserType = szCLSIDName;
8746 CoTaskMemFree( szProgIDName );
8748 end:
8749 IStream_Release( stm );
8751 return r;
8755 /*************************************************************************
8756 * OLECONVERT_CreateCompObjStream [Internal]
8758 * Creates a "\001CompObj" is the destination IStorage if necessary.
8760 * PARAMS
8761 * pStorage [I] The dest IStorage to create the CompObj Stream
8762 * if necessary.
8763 * strOleTypeName [I] The ProgID
8765 * RETURNS
8766 * Success: S_OK
8767 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8769 * NOTES
8770 * This function is used by OleConvertOLESTREAMToIStorage only.
8772 * The stream data is stored in the OLESTREAM and there should be
8773 * no need to recreate the stream. If the stream is manually
8774 * deleted it will attempt to create it by querying the registry.
8778 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
8780 IStream *pStream;
8781 HRESULT hStorageRes, hRes = S_OK;
8782 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
8783 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
8784 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
8786 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
8787 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
8789 /* Initialize the CompObj structure */
8790 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
8791 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
8792 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
8795 /* Create a CompObj stream if it doesn't exist */
8796 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
8797 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8798 if(hStorageRes == S_OK)
8800 /* copy the OleTypeName to the compobj struct */
8801 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
8802 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
8804 /* copy the OleTypeName to the compobj struct */
8805 /* Note: in the test made, these were Identical */
8806 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
8807 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
8809 /* Get the CLSID */
8810 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
8811 bufferW, OLESTREAM_MAX_STR_LEN );
8812 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
8814 if(hRes == S_OK)
8816 HKEY hKey;
8817 LONG hErr;
8818 /* Get the CLSID Default Name from the Registry */
8819 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
8820 if(hErr == ERROR_SUCCESS)
8822 char strTemp[OLESTREAM_MAX_STR_LEN];
8823 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
8824 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
8825 if(hErr == ERROR_SUCCESS)
8827 strcpy(IStorageCompObj.strCLSIDName, strTemp);
8829 RegCloseKey(hKey);
8833 /* Write CompObj Structure to stream */
8834 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
8836 WriteClassStm(pStream,&(IStorageCompObj.clsid));
8838 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
8839 if(IStorageCompObj.dwCLSIDNameLength > 0)
8841 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
8843 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
8844 if(IStorageCompObj.dwOleTypeNameLength > 0)
8846 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
8848 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
8849 if(IStorageCompObj.dwProgIDNameLength > 0)
8851 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
8853 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
8854 IStream_Release(pStream);
8856 return hRes;
8860 /*************************************************************************
8861 * OLECONVERT_CreateOlePresStream[Internal]
8863 * Creates the "\002OlePres000" Stream with the Metafile data
8865 * PARAMS
8866 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
8867 * dwExtentX [I] Width of the Metafile
8868 * dwExtentY [I] Height of the Metafile
8869 * pData [I] Metafile data
8870 * dwDataLength [I] Size of the Metafile data
8872 * RETURNS
8873 * Success: S_OK
8874 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
8876 * NOTES
8877 * This function is used by OleConvertOLESTREAMToIStorage only.
8880 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
8882 HRESULT hRes;
8883 IStream *pStream;
8884 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
8885 BYTE pOlePresStreamHeader [] =
8887 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
8888 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8889 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8890 0x00, 0x00, 0x00, 0x00
8893 BYTE pOlePresStreamHeaderEmpty [] =
8895 0x00, 0x00, 0x00, 0x00,
8896 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
8897 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
8898 0x00, 0x00, 0x00, 0x00
8901 /* Create the OlePres000 Stream */
8902 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8903 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8905 if(hRes == S_OK)
8907 DWORD nHeaderSize;
8908 OLECONVERT_ISTORAGE_OLEPRES OlePres;
8910 memset(&OlePres, 0, sizeof(OlePres));
8911 /* Do we have any metafile data to save */
8912 if(dwDataLength > 0)
8914 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
8915 nHeaderSize = sizeof(pOlePresStreamHeader);
8917 else
8919 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
8920 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
8922 /* Set width and height of the metafile */
8923 OlePres.dwExtentX = dwExtentX;
8924 OlePres.dwExtentY = -dwExtentY;
8926 /* Set Data and Length */
8927 if(dwDataLength > sizeof(METAFILEPICT16))
8929 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
8930 OlePres.pData = &(pData[8]);
8932 /* Save OlePres000 Data to Stream */
8933 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
8934 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
8935 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
8936 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
8937 if(OlePres.dwSize > 0)
8939 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
8941 IStream_Release(pStream);
8945 /*************************************************************************
8946 * OLECONVERT_CreateOle10NativeStream [Internal]
8948 * Creates the "\001Ole10Native" Stream (should contain a BMP)
8950 * PARAMS
8951 * pStorage [I] Dest storage to create the stream in
8952 * pData [I] Ole10 Native Data (ex. bmp)
8953 * dwDataLength [I] Size of the Ole10 Native Data
8955 * RETURNS
8956 * Nothing
8958 * NOTES
8959 * This function is used by OleConvertOLESTREAMToIStorage only.
8961 * Might need to verify the data and return appropriate error message
8964 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
8966 HRESULT hRes;
8967 IStream *pStream;
8968 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
8970 /* Create the Ole10Native Stream */
8971 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
8972 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
8974 if(hRes == S_OK)
8976 /* Write info to stream */
8977 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
8978 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
8979 IStream_Release(pStream);
8984 /*************************************************************************
8985 * OLECONVERT_GetOLE10ProgID [Internal]
8987 * Finds the ProgID (or OleTypeID) from the IStorage
8989 * PARAMS
8990 * pStorage [I] The Src IStorage to get the ProgID
8991 * strProgID [I] the ProgID string to get
8992 * dwSize [I] the size of the string
8994 * RETURNS
8995 * Success: S_OK
8996 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
8998 * NOTES
8999 * This function is used by OleConvertIStorageToOLESTREAM only.
9003 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
9005 HRESULT hRes;
9006 IStream *pStream;
9007 LARGE_INTEGER iSeekPos;
9008 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
9009 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
9011 /* Open the CompObj Stream */
9012 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9013 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9014 if(hRes == S_OK)
9017 /*Get the OleType from the CompObj Stream */
9018 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
9019 iSeekPos.u.HighPart = 0;
9021 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9022 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
9023 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
9024 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9025 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
9026 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
9027 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
9029 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
9030 if(*dwSize > 0)
9032 IStream_Read(pStream, strProgID, *dwSize, NULL);
9034 IStream_Release(pStream);
9036 else
9038 STATSTG stat;
9039 LPOLESTR wstrProgID;
9041 /* Get the OleType from the registry */
9042 REFCLSID clsid = &(stat.clsid);
9043 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
9044 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
9045 if(hRes == S_OK)
9047 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
9051 return hRes;
9054 /*************************************************************************
9055 * OLECONVERT_GetOle10PresData [Internal]
9057 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
9059 * PARAMS
9060 * pStorage [I] Src IStroage
9061 * pOleStream [I] Dest OleStream Mem Struct
9063 * RETURNS
9064 * Nothing
9066 * NOTES
9067 * This function is used by OleConvertIStorageToOLESTREAM only.
9069 * Memory allocated for pData must be freed by the caller
9073 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9076 HRESULT hRes;
9077 IStream *pStream;
9078 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9080 /* Initialize Default data for OLESTREAM */
9081 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9082 pOleStreamData[0].dwTypeID = 2;
9083 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9084 pOleStreamData[1].dwTypeID = 0;
9085 pOleStreamData[0].dwMetaFileWidth = 0;
9086 pOleStreamData[0].dwMetaFileHeight = 0;
9087 pOleStreamData[0].pData = NULL;
9088 pOleStreamData[1].pData = NULL;
9090 /* Open Ole10Native Stream */
9091 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9092 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9093 if(hRes == S_OK)
9096 /* Read Size and Data */
9097 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
9098 if(pOleStreamData->dwDataLength > 0)
9100 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
9101 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
9103 IStream_Release(pStream);
9109 /*************************************************************************
9110 * OLECONVERT_GetOle20PresData[Internal]
9112 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
9114 * PARAMS
9115 * pStorage [I] Src IStroage
9116 * pOleStreamData [I] Dest OleStream Mem Struct
9118 * RETURNS
9119 * Nothing
9121 * NOTES
9122 * This function is used by OleConvertIStorageToOLESTREAM only.
9124 * Memory allocated for pData must be freed by the caller
9126 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
9128 HRESULT hRes;
9129 IStream *pStream;
9130 OLECONVERT_ISTORAGE_OLEPRES olePress;
9131 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
9133 /* Initialize Default data for OLESTREAM */
9134 pOleStreamData[0].dwOleID = OLESTREAM_ID;
9135 pOleStreamData[0].dwTypeID = 2;
9136 pOleStreamData[0].dwMetaFileWidth = 0;
9137 pOleStreamData[0].dwMetaFileHeight = 0;
9138 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
9139 pOleStreamData[1].dwOleID = OLESTREAM_ID;
9140 pOleStreamData[1].dwTypeID = 0;
9141 pOleStreamData[1].dwOleTypeNameLength = 0;
9142 pOleStreamData[1].strOleTypeName[0] = 0;
9143 pOleStreamData[1].dwMetaFileWidth = 0;
9144 pOleStreamData[1].dwMetaFileHeight = 0;
9145 pOleStreamData[1].pData = NULL;
9146 pOleStreamData[1].dwDataLength = 0;
9149 /* Open OlePress000 stream */
9150 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
9151 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
9152 if(hRes == S_OK)
9154 LARGE_INTEGER iSeekPos;
9155 METAFILEPICT16 MetaFilePict;
9156 static const char strMetafilePictName[] = "METAFILEPICT";
9158 /* Set the TypeID for a Metafile */
9159 pOleStreamData[1].dwTypeID = 5;
9161 /* Set the OleTypeName to Metafile */
9162 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
9163 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
9165 iSeekPos.u.HighPart = 0;
9166 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
9168 /* Get Presentation Data */
9169 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
9170 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
9171 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
9172 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
9174 /*Set width and Height */
9175 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
9176 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
9177 if(olePress.dwSize > 0)
9179 /* Set Length */
9180 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
9182 /* Set MetaFilePict struct */
9183 MetaFilePict.mm = 8;
9184 MetaFilePict.xExt = olePress.dwExtentX;
9185 MetaFilePict.yExt = olePress.dwExtentY;
9186 MetaFilePict.hMF = 0;
9188 /* Get Metafile Data */
9189 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
9190 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
9191 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
9193 IStream_Release(pStream);
9197 /*************************************************************************
9198 * OleConvertOLESTREAMToIStorage [OLE32.@]
9200 * Read info on MSDN
9202 * TODO
9203 * DVTARGETDEVICE parameter is not handled
9204 * Still unsure of some mem fields for OLE 10 Stream
9205 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9206 * and "\001OLE" streams
9209 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
9210 LPOLESTREAM pOleStream,
9211 LPSTORAGE pstg,
9212 const DVTARGETDEVICE* ptd)
9214 int i;
9215 HRESULT hRes=S_OK;
9216 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9218 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
9220 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9222 if(ptd != NULL)
9224 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
9227 if(pstg == NULL || pOleStream == NULL)
9229 hRes = E_INVALIDARG;
9232 if(hRes == S_OK)
9234 /* Load the OLESTREAM to Memory */
9235 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
9238 if(hRes == S_OK)
9240 /* Load the OLESTREAM to Memory (part 2)*/
9241 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
9244 if(hRes == S_OK)
9247 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
9249 /* Do we have the IStorage Data in the OLESTREAM */
9250 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
9252 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9253 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
9255 else
9257 /* It must be an original OLE 1.0 source */
9258 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9261 else
9263 /* It must be an original OLE 1.0 source */
9264 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
9267 /* Create CompObj Stream if necessary */
9268 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
9269 if(hRes == S_OK)
9271 /*Create the Ole Stream if necessary */
9272 OLECONVERT_CreateOleStream(pstg);
9277 /* Free allocated memory */
9278 for(i=0; i < 2; i++)
9280 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9281 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
9282 pOleStreamData[i].pstrOleObjFileName = NULL;
9284 return hRes;
9287 /*************************************************************************
9288 * OleConvertIStorageToOLESTREAM [OLE32.@]
9290 * Read info on MSDN
9292 * Read info on MSDN
9294 * TODO
9295 * Still unsure of some mem fields for OLE 10 Stream
9296 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
9297 * and "\001OLE" streams.
9300 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
9301 LPSTORAGE pstg,
9302 LPOLESTREAM pOleStream)
9304 int i;
9305 HRESULT hRes = S_OK;
9306 IStream *pStream;
9307 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
9308 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
9310 TRACE("%p %p\n", pstg, pOleStream);
9312 memset(pOleStreamData, 0, sizeof(pOleStreamData));
9314 if(pstg == NULL || pOleStream == NULL)
9316 hRes = E_INVALIDARG;
9318 if(hRes == S_OK)
9320 /* Get the ProgID */
9321 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
9322 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
9324 if(hRes == S_OK)
9326 /* Was it originally Ole10 */
9327 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
9328 if(hRes == S_OK)
9330 IStream_Release(pStream);
9331 /* Get Presentation Data for Ole10Native */
9332 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
9334 else
9336 /* Get Presentation Data (OLE20) */
9337 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
9340 /* Save OLESTREAM */
9341 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
9342 if(hRes == S_OK)
9344 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
9349 /* Free allocated memory */
9350 for(i=0; i < 2; i++)
9352 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
9355 return hRes;
9358 /***********************************************************************
9359 * GetConvertStg (OLE32.@)
9361 HRESULT WINAPI GetConvertStg(IStorage *stg) {
9362 FIXME("unimplemented stub!\n");
9363 return E_FAIL;
9366 /******************************************************************************
9367 * StgIsStorageFile [OLE32.@]
9368 * Verify if the file contains a storage object
9370 * PARAMS
9371 * fn [ I] Filename
9373 * RETURNS
9374 * S_OK if file has magic bytes as a storage object
9375 * S_FALSE if file is not storage
9377 HRESULT WINAPI
9378 StgIsStorageFile(LPCOLESTR fn)
9380 HANDLE hf;
9381 BYTE magic[8];
9382 DWORD bytes_read;
9384 TRACE("%s\n", debugstr_w(fn));
9385 hf = CreateFileW(fn, GENERIC_READ,
9386 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
9387 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
9389 if (hf == INVALID_HANDLE_VALUE)
9390 return STG_E_FILENOTFOUND;
9392 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
9394 WARN(" unable to read file\n");
9395 CloseHandle(hf);
9396 return S_FALSE;
9399 CloseHandle(hf);
9401 if (bytes_read != 8) {
9402 TRACE(" too short\n");
9403 return S_FALSE;
9406 if (!memcmp(magic,STORAGE_magic,8)) {
9407 TRACE(" -> YES\n");
9408 return S_OK;
9411 TRACE(" -> Invalid header.\n");
9412 return S_FALSE;
9415 /***********************************************************************
9416 * WriteClassStm (OLE32.@)
9418 * Writes a CLSID to a stream.
9420 * PARAMS
9421 * pStm [I] Stream to write to.
9422 * rclsid [I] CLSID to write.
9424 * RETURNS
9425 * Success: S_OK.
9426 * Failure: HRESULT code.
9428 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
9430 TRACE("(%p,%p)\n",pStm,rclsid);
9432 if (!pStm || !rclsid)
9433 return E_INVALIDARG;
9435 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
9438 /***********************************************************************
9439 * ReadClassStm (OLE32.@)
9441 * Reads a CLSID from a stream.
9443 * PARAMS
9444 * pStm [I] Stream to read from.
9445 * rclsid [O] CLSID to read.
9447 * RETURNS
9448 * Success: S_OK.
9449 * Failure: HRESULT code.
9451 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
9453 ULONG nbByte;
9454 HRESULT res;
9456 TRACE("(%p,%p)\n",pStm,pclsid);
9458 if (!pStm || !pclsid)
9459 return E_INVALIDARG;
9461 /* clear the output args */
9462 *pclsid = CLSID_NULL;
9464 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
9466 if (FAILED(res))
9467 return res;
9469 if (nbByte != sizeof(CLSID))
9470 return STG_E_READFAULT;
9471 else
9472 return S_OK;