push 149f0a5527ac85057a8ef03858d34d91c36f97e8
[wine/hacks.git] / dlls / ole32 / storage32.c
blob384722dc027f52294992c92db3b17d75fd4f3535
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 typedef struct StorageInternalImpl StorageInternalImpl;
88 /* Method definitions for the Storage32InternalImpl class. */
89 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
90 DWORD openFlags, DirRef storageDirEntry);
91 static void StorageImpl_Destroy(StorageBaseImpl* iface);
92 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
93 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
94 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
95 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
96 static void StorageImpl_SaveFileHeader(StorageImpl* This);
98 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
100 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
101 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
102 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
104 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
105 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
106 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
108 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
109 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
110 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD value);
112 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
113 ULONG blockIndex, ULONG offset, DWORD* value);
115 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
116 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
117 static void StorageInternalImpl_Invalidate( StorageInternalImpl *This );
119 /* OLESTREAM memory structure to use for Get and Put Routines */
120 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
121 typedef struct
123 DWORD dwOleID;
124 DWORD dwTypeID;
125 DWORD dwOleTypeNameLength;
126 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
127 CHAR *pstrOleObjFileName;
128 DWORD dwOleObjFileNameLength;
129 DWORD dwMetaFileWidth;
130 DWORD dwMetaFileHeight;
131 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
132 DWORD dwDataLength;
133 BYTE *pData;
134 }OLECONVERT_OLESTREAM_DATA;
136 /* CompObj Stream structure */
137 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
138 typedef struct
140 BYTE byUnknown1[12];
141 CLSID clsid;
142 DWORD dwCLSIDNameLength;
143 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
144 DWORD dwOleTypeNameLength;
145 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
146 DWORD dwProgIDNameLength;
147 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
148 BYTE byUnknown2[16];
149 }OLECONVERT_ISTORAGE_COMPOBJ;
152 /* Ole Presentation Stream structure */
153 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
154 typedef struct
156 BYTE byUnknown1[28];
157 DWORD dwExtentX;
158 DWORD dwExtentY;
159 DWORD dwSize;
160 BYTE *pData;
161 }OLECONVERT_ISTORAGE_OLEPRES;
165 /***********************************************************************
166 * Forward declaration of internal functions used by the method DestroyElement
168 static HRESULT deleteStorageContents(
169 StorageBaseImpl *parentStorage,
170 DirRef indexToDelete,
171 DirEntry entryDataToDelete);
173 static HRESULT deleteStreamContents(
174 StorageBaseImpl *parentStorage,
175 DirRef indexToDelete,
176 DirEntry entryDataToDelete);
178 static HRESULT removeFromTree(
179 StorageImpl *This,
180 DirRef parentStorageIndex,
181 DirRef deletedIndex);
183 /***********************************************************************
184 * Declaration of the functions used to manipulate DirEntry
187 static HRESULT createDirEntry(
188 StorageImpl *storage,
189 const DirEntry *newData,
190 DirRef *index);
192 static HRESULT destroyDirEntry(
193 StorageImpl *storage,
194 DirRef index);
196 static HRESULT insertIntoTree(
197 StorageImpl *This,
198 DirRef parentStorageIndex,
199 DirRef newEntryIndex);
201 static LONG entryNameCmp(
202 const OLECHAR *name1,
203 const OLECHAR *name2);
205 static DirRef findElement(
206 StorageImpl *storage,
207 DirRef storageEntry,
208 const OLECHAR *name,
209 DirEntry *data);
211 static HRESULT findTreeParent(
212 StorageImpl *storage,
213 DirRef storageEntry,
214 const OLECHAR *childName,
215 DirEntry *parentData,
216 DirRef *parentEntry,
217 ULONG *relation);
219 /***********************************************************************
220 * Declaration of miscellaneous functions...
222 static HRESULT validateSTGM(DWORD stgmValue);
224 static DWORD GetShareModeFromSTGM(DWORD stgm);
225 static DWORD GetAccessModeFromSTGM(DWORD stgm);
226 static DWORD GetCreationModeFromSTGM(DWORD stgm);
228 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
231 /****************************************************************************
232 * IEnumSTATSTGImpl definitions.
234 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
235 * This class allows iterating through the content of a storage and to find
236 * specific items inside it.
238 struct IEnumSTATSTGImpl
240 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
241 * since we want to cast this in an IEnumSTATSTG pointer */
243 LONG ref; /* Reference count */
244 StorageImpl* parentStorage; /* Reference to the parent storage */
245 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
248 * The current implementation of the IEnumSTATSTGImpl class uses a stack
249 * to walk the directory entries to get the content of a storage. This stack
250 * is implemented by the following 3 data members
252 ULONG stackSize;
253 ULONG stackMaxSize;
254 DirRef* stackToVisit;
256 #define ENUMSTATSGT_SIZE_INCREMENT 10
260 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, DirRef storageDirEntry);
261 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
262 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, DirRef nodeToPush);
263 static DirRef IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
265 /************************************************************************
266 ** Block Functions
269 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
271 if (index == 0xffffffff)
272 index = 0;
273 else
274 index ++;
276 return index * BIG_BLOCK_SIZE;
279 /************************************************************************
280 ** Storage32BaseImpl implementation
282 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
283 ULARGE_INTEGER offset,
284 void* buffer,
285 ULONG size,
286 ULONG* bytesRead)
288 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
291 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
292 ULARGE_INTEGER offset,
293 const void* buffer,
294 const ULONG size,
295 ULONG* bytesWritten)
297 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
300 /************************************************************************
301 * Storage32BaseImpl_QueryInterface (IUnknown)
303 * This method implements the common QueryInterface for all IStorage32
304 * implementations contained in this file.
306 * See Windows documentation for more details on IUnknown methods.
308 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
309 IStorage* iface,
310 REFIID riid,
311 void** ppvObject)
313 StorageBaseImpl *This = (StorageBaseImpl *)iface;
315 if ( (This==0) || (ppvObject==0) )
316 return E_INVALIDARG;
318 *ppvObject = 0;
320 if (IsEqualGUID(&IID_IUnknown, riid) ||
321 IsEqualGUID(&IID_IStorage, riid))
323 *ppvObject = This;
325 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
327 *ppvObject = &This->pssVtbl;
330 if ((*ppvObject)==0)
331 return E_NOINTERFACE;
333 IStorage_AddRef(iface);
335 return S_OK;
338 /************************************************************************
339 * Storage32BaseImpl_AddRef (IUnknown)
341 * This method implements the common AddRef for all IStorage32
342 * implementations contained in this file.
344 * See Windows documentation for more details on IUnknown methods.
346 static ULONG WINAPI StorageBaseImpl_AddRef(
347 IStorage* iface)
349 StorageBaseImpl *This = (StorageBaseImpl *)iface;
350 ULONG ref = InterlockedIncrement(&This->ref);
352 TRACE("(%p) AddRef to %d\n", This, ref);
354 return ref;
357 /************************************************************************
358 * Storage32BaseImpl_Release (IUnknown)
360 * This method implements the common Release for all IStorage32
361 * implementations contained in this file.
363 * See Windows documentation for more details on IUnknown methods.
365 static ULONG WINAPI StorageBaseImpl_Release(
366 IStorage* iface)
368 StorageBaseImpl *This = (StorageBaseImpl *)iface;
370 ULONG ref = InterlockedDecrement(&This->ref);
372 TRACE("(%p) ReleaseRef to %d\n", This, ref);
374 if (ref == 0)
377 * Since we are using a system of base-classes, we want to call the
378 * destructor of the appropriate derived class. To do this, we are
379 * using virtual functions to implement the destructor.
381 StorageBaseImpl_Destroy(This);
384 return ref;
387 /************************************************************************
388 * Storage32BaseImpl_OpenStream (IStorage)
390 * This method will open the specified stream object from the current storage.
392 * See Windows documentation for more details on IStorage methods.
394 static HRESULT WINAPI StorageBaseImpl_OpenStream(
395 IStorage* iface,
396 const OLECHAR* pwcsName, /* [string][in] */
397 void* reserved1, /* [unique][in] */
398 DWORD grfMode, /* [in] */
399 DWORD reserved2, /* [in] */
400 IStream** ppstm) /* [out] */
402 StorageBaseImpl *This = (StorageBaseImpl *)iface;
403 StgStreamImpl* newStream;
404 DirEntry currentEntry;
405 DirRef streamEntryRef;
406 HRESULT res = STG_E_UNKNOWN;
408 TRACE("(%p, %s, %p, %x, %d, %p)\n",
409 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
411 if ( (pwcsName==NULL) || (ppstm==0) )
413 res = E_INVALIDARG;
414 goto end;
417 *ppstm = NULL;
419 if ( FAILED( validateSTGM(grfMode) ) ||
420 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
422 res = STG_E_INVALIDFLAG;
423 goto end;
427 * As documented.
429 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
431 res = STG_E_INVALIDFUNCTION;
432 goto end;
435 if (!This->ancestorStorage)
437 res = STG_E_REVERTED;
438 goto end;
442 * Check that we're compatible with the parent's storage mode, but
443 * only if we are not in transacted mode
445 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
446 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
448 res = STG_E_ACCESSDENIED;
449 goto end;
454 * Search for the element with the given name
456 streamEntryRef = findElement(
457 This->ancestorStorage,
458 This->storageDirEntry,
459 pwcsName,
460 &currentEntry);
463 * If it was found, construct the stream object and return a pointer to it.
465 if ( (streamEntryRef!=DIRENTRY_NULL) &&
466 (currentEntry.stgType==STGTY_STREAM) )
468 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
470 /* A single stream cannot be opened a second time. */
471 res = STG_E_ACCESSDENIED;
472 goto end;
475 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
477 if (newStream!=0)
479 newStream->grfMode = grfMode;
480 *ppstm = (IStream*)newStream;
482 IStream_AddRef(*ppstm);
484 res = S_OK;
485 goto end;
488 res = E_OUTOFMEMORY;
489 goto end;
492 res = STG_E_FILENOTFOUND;
494 end:
495 if (res == S_OK)
496 TRACE("<-- IStream %p\n", *ppstm);
497 TRACE("<-- %08x\n", res);
498 return res;
501 /************************************************************************
502 * Storage32BaseImpl_OpenStorage (IStorage)
504 * This method will open a new storage object from the current storage.
506 * See Windows documentation for more details on IStorage methods.
508 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
509 IStorage* iface,
510 const OLECHAR* pwcsName, /* [string][unique][in] */
511 IStorage* pstgPriority, /* [unique][in] */
512 DWORD grfMode, /* [in] */
513 SNB snbExclude, /* [unique][in] */
514 DWORD reserved, /* [in] */
515 IStorage** ppstg) /* [out] */
517 StorageBaseImpl *This = (StorageBaseImpl *)iface;
518 StorageInternalImpl* newStorage;
519 DirEntry currentEntry;
520 DirRef storageEntryRef;
521 HRESULT res = STG_E_UNKNOWN;
523 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
524 iface, debugstr_w(pwcsName), pstgPriority,
525 grfMode, snbExclude, reserved, ppstg);
527 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
529 res = E_INVALIDARG;
530 goto end;
533 if (This->openFlags & STGM_SIMPLE)
535 res = STG_E_INVALIDFUNCTION;
536 goto end;
539 /* as documented */
540 if (snbExclude != NULL)
542 res = STG_E_INVALIDPARAMETER;
543 goto end;
546 if ( FAILED( validateSTGM(grfMode) ))
548 res = STG_E_INVALIDFLAG;
549 goto end;
553 * As documented.
555 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
556 (grfMode & STGM_DELETEONRELEASE) ||
557 (grfMode & STGM_PRIORITY) )
559 res = STG_E_INVALIDFUNCTION;
560 goto end;
563 if (!This->ancestorStorage)
564 return STG_E_REVERTED;
567 * Check that we're compatible with the parent's storage mode,
568 * but only if we are not transacted
570 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
571 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
573 res = STG_E_ACCESSDENIED;
574 goto end;
578 *ppstg = NULL;
580 storageEntryRef = findElement(
581 This->ancestorStorage,
582 This->storageDirEntry,
583 pwcsName,
584 &currentEntry);
586 if ( (storageEntryRef!=DIRENTRY_NULL) &&
587 (currentEntry.stgType==STGTY_STORAGE) )
589 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
591 /* A single storage cannot be opened a second time. */
592 res = STG_E_ACCESSDENIED;
593 goto end;
596 newStorage = StorageInternalImpl_Construct(
597 This->ancestorStorage,
598 grfMode,
599 storageEntryRef);
601 if (newStorage != 0)
603 *ppstg = (IStorage*)newStorage;
605 StorageBaseImpl_AddRef(*ppstg);
607 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
609 res = S_OK;
610 goto end;
613 res = STG_E_INSUFFICIENTMEMORY;
614 goto end;
617 res = STG_E_FILENOTFOUND;
619 end:
620 TRACE("<-- %08x\n", res);
621 return res;
624 /************************************************************************
625 * Storage32BaseImpl_EnumElements (IStorage)
627 * This method will create an enumerator object that can be used to
628 * retrieve information about all the elements in the storage object.
630 * See Windows documentation for more details on IStorage methods.
632 static HRESULT WINAPI StorageBaseImpl_EnumElements(
633 IStorage* iface,
634 DWORD reserved1, /* [in] */
635 void* reserved2, /* [size_is][unique][in] */
636 DWORD reserved3, /* [in] */
637 IEnumSTATSTG** ppenum) /* [out] */
639 StorageBaseImpl *This = (StorageBaseImpl *)iface;
640 IEnumSTATSTGImpl* newEnum;
642 TRACE("(%p, %d, %p, %d, %p)\n",
643 iface, reserved1, reserved2, reserved3, ppenum);
645 if ( (This==0) || (ppenum==0))
646 return E_INVALIDARG;
648 if (!This->ancestorStorage)
649 return STG_E_REVERTED;
651 newEnum = IEnumSTATSTGImpl_Construct(
652 This->ancestorStorage,
653 This->storageDirEntry);
655 if (newEnum!=0)
657 *ppenum = (IEnumSTATSTG*)newEnum;
659 IEnumSTATSTG_AddRef(*ppenum);
661 return S_OK;
664 return E_OUTOFMEMORY;
667 /************************************************************************
668 * Storage32BaseImpl_Stat (IStorage)
670 * This method will retrieve information about this storage object.
672 * See Windows documentation for more details on IStorage methods.
674 static HRESULT WINAPI StorageBaseImpl_Stat(
675 IStorage* iface,
676 STATSTG* pstatstg, /* [out] */
677 DWORD grfStatFlag) /* [in] */
679 StorageBaseImpl *This = (StorageBaseImpl *)iface;
680 DirEntry currentEntry;
681 BOOL readSuccessful;
682 HRESULT res = STG_E_UNKNOWN;
684 TRACE("(%p, %p, %x)\n",
685 iface, pstatstg, grfStatFlag);
687 if ( (This==0) || (pstatstg==0))
689 res = E_INVALIDARG;
690 goto end;
693 if (!This->ancestorStorage)
695 res = STG_E_REVERTED;
696 goto end;
699 readSuccessful = StorageImpl_ReadDirEntry(
700 This->ancestorStorage,
701 This->storageDirEntry,
702 &currentEntry);
704 if (readSuccessful)
706 StorageUtl_CopyDirEntryToSTATSTG(
707 This,
708 pstatstg,
709 &currentEntry,
710 grfStatFlag);
712 pstatstg->grfMode = This->openFlags;
713 pstatstg->grfStateBits = This->stateBits;
715 res = S_OK;
716 goto end;
719 res = E_FAIL;
721 end:
722 if (res == S_OK)
724 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);
726 TRACE("<-- %08x\n", res);
727 return res;
730 /************************************************************************
731 * Storage32BaseImpl_RenameElement (IStorage)
733 * This method will rename the specified element.
735 * See Windows documentation for more details on IStorage methods.
737 static HRESULT WINAPI StorageBaseImpl_RenameElement(
738 IStorage* iface,
739 const OLECHAR* pwcsOldName, /* [in] */
740 const OLECHAR* pwcsNewName) /* [in] */
742 StorageBaseImpl *This = (StorageBaseImpl *)iface;
743 DirEntry currentEntry;
744 DirRef currentEntryRef;
746 TRACE("(%p, %s, %s)\n",
747 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
749 if (!This->ancestorStorage)
750 return STG_E_REVERTED;
752 currentEntryRef = findElement(This->ancestorStorage,
753 This->storageDirEntry,
754 pwcsNewName,
755 &currentEntry);
757 if (currentEntryRef != DIRENTRY_NULL)
760 * There is already an element with the new name
762 return STG_E_FILEALREADYEXISTS;
766 * Search for the old element name
768 currentEntryRef = findElement(This->ancestorStorage,
769 This->storageDirEntry,
770 pwcsOldName,
771 &currentEntry);
773 if (currentEntryRef != DIRENTRY_NULL)
775 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
776 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
778 WARN("Element is already open; cannot rename.\n");
779 return STG_E_ACCESSDENIED;
782 /* Remove the element from its current position in the tree */
783 removeFromTree(This->ancestorStorage, This->storageDirEntry,
784 currentEntryRef);
786 /* Change the name of the element */
787 strcpyW(currentEntry.name, pwcsNewName);
789 StorageImpl_WriteDirEntry(This->ancestorStorage, currentEntryRef,
790 &currentEntry);
792 /* Insert the element in a new position in the tree */
793 insertIntoTree(This->ancestorStorage, This->storageDirEntry,
794 currentEntryRef);
796 else
799 * There is no element with the old name
801 return STG_E_FILENOTFOUND;
804 return S_OK;
807 /************************************************************************
808 * Storage32BaseImpl_CreateStream (IStorage)
810 * This method will create a stream object within this storage
812 * See Windows documentation for more details on IStorage methods.
814 static HRESULT WINAPI StorageBaseImpl_CreateStream(
815 IStorage* iface,
816 const OLECHAR* pwcsName, /* [string][in] */
817 DWORD grfMode, /* [in] */
818 DWORD reserved1, /* [in] */
819 DWORD reserved2, /* [in] */
820 IStream** ppstm) /* [out] */
822 StorageBaseImpl *This = (StorageBaseImpl *)iface;
823 StgStreamImpl* newStream;
824 DirEntry currentEntry, newStreamEntry;
825 DirRef currentEntryRef, newStreamEntryRef;
827 TRACE("(%p, %s, %x, %d, %d, %p)\n",
828 iface, debugstr_w(pwcsName), grfMode,
829 reserved1, reserved2, ppstm);
831 if (ppstm == 0)
832 return STG_E_INVALIDPOINTER;
834 if (pwcsName == 0)
835 return STG_E_INVALIDNAME;
837 if (reserved1 || reserved2)
838 return STG_E_INVALIDPARAMETER;
840 if ( FAILED( validateSTGM(grfMode) ))
841 return STG_E_INVALIDFLAG;
843 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
844 return STG_E_INVALIDFLAG;
846 if (!This->ancestorStorage)
847 return STG_E_REVERTED;
850 * As documented.
852 if ((grfMode & STGM_DELETEONRELEASE) ||
853 (grfMode & STGM_TRANSACTED))
854 return STG_E_INVALIDFUNCTION;
856 /* Can't create a stream on read-only storage */
857 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
858 return STG_E_ACCESSDENIED;
861 * Check that we're compatible with the parent's storage mode
862 * if not in transacted mode
864 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
865 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
866 return STG_E_ACCESSDENIED;
869 if(This->openFlags & STGM_SIMPLE)
870 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
872 *ppstm = 0;
874 currentEntryRef = findElement(This->ancestorStorage,
875 This->storageDirEntry,
876 pwcsName,
877 &currentEntry);
879 if (currentEntryRef != DIRENTRY_NULL)
882 * An element with this name already exists
884 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
886 IStorage_DestroyElement(iface, pwcsName);
888 else
889 return STG_E_FILEALREADYEXISTS;
891 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
893 WARN("read-only storage\n");
894 return STG_E_ACCESSDENIED;
898 * memset the empty entry
900 memset(&newStreamEntry, 0, sizeof(DirEntry));
902 newStreamEntry.sizeOfNameString =
903 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
905 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
906 return STG_E_INVALIDNAME;
908 strcpyW(newStreamEntry.name, pwcsName);
910 newStreamEntry.stgType = STGTY_STREAM;
911 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
912 newStreamEntry.size.u.LowPart = 0;
913 newStreamEntry.size.u.HighPart = 0;
915 newStreamEntry.leftChild = DIRENTRY_NULL;
916 newStreamEntry.rightChild = DIRENTRY_NULL;
917 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
919 /* call CoFileTime to get the current time
920 newStreamEntry.ctime
921 newStreamEntry.mtime
924 /* newStreamEntry.clsid */
927 * Create an entry with the new data
929 createDirEntry(This->ancestorStorage, &newStreamEntry, &newStreamEntryRef);
932 * Insert the new entry in the parent storage's tree.
934 insertIntoTree(
935 This->ancestorStorage,
936 This->storageDirEntry,
937 newStreamEntryRef);
940 * Open the stream to return it.
942 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
944 if (newStream != 0)
946 *ppstm = (IStream*)newStream;
948 IStream_AddRef(*ppstm);
950 else
952 return STG_E_INSUFFICIENTMEMORY;
955 return S_OK;
958 /************************************************************************
959 * Storage32BaseImpl_SetClass (IStorage)
961 * This method will write the specified CLSID in the directory entry of this
962 * storage.
964 * See Windows documentation for more details on IStorage methods.
966 static HRESULT WINAPI StorageBaseImpl_SetClass(
967 IStorage* iface,
968 REFCLSID clsid) /* [in] */
970 StorageBaseImpl *This = (StorageBaseImpl *)iface;
971 HRESULT hRes = E_FAIL;
972 DirEntry currentEntry;
973 BOOL success;
975 TRACE("(%p, %p)\n", iface, clsid);
977 if (!This->ancestorStorage)
978 return STG_E_REVERTED;
980 success = StorageImpl_ReadDirEntry(This->ancestorStorage,
981 This->storageDirEntry,
982 &currentEntry);
983 if (success)
985 currentEntry.clsid = *clsid;
987 success = StorageImpl_WriteDirEntry(This->ancestorStorage,
988 This->storageDirEntry,
989 &currentEntry);
990 if (success)
991 hRes = S_OK;
994 return hRes;
997 /************************************************************************
998 ** Storage32Impl implementation
1001 /************************************************************************
1002 * Storage32BaseImpl_CreateStorage (IStorage)
1004 * This method will create the storage object within the provided storage.
1006 * See Windows documentation for more details on IStorage methods.
1008 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1009 IStorage* iface,
1010 const OLECHAR *pwcsName, /* [string][in] */
1011 DWORD grfMode, /* [in] */
1012 DWORD reserved1, /* [in] */
1013 DWORD reserved2, /* [in] */
1014 IStorage **ppstg) /* [out] */
1016 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1018 DirEntry currentEntry;
1019 DirEntry newEntry;
1020 DirRef currentEntryRef;
1021 DirRef newEntryRef;
1022 HRESULT hr;
1024 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1025 iface, debugstr_w(pwcsName), grfMode,
1026 reserved1, reserved2, ppstg);
1028 if (ppstg == 0)
1029 return STG_E_INVALIDPOINTER;
1031 if (This->openFlags & STGM_SIMPLE)
1033 return STG_E_INVALIDFUNCTION;
1036 if (pwcsName == 0)
1037 return STG_E_INVALIDNAME;
1039 *ppstg = NULL;
1041 if ( FAILED( validateSTGM(grfMode) ) ||
1042 (grfMode & STGM_DELETEONRELEASE) )
1044 WARN("bad grfMode: 0x%x\n", grfMode);
1045 return STG_E_INVALIDFLAG;
1048 if (!This->ancestorStorage)
1049 return STG_E_REVERTED;
1052 * Check that we're compatible with the parent's storage mode
1054 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1056 WARN("access denied\n");
1057 return STG_E_ACCESSDENIED;
1060 currentEntryRef = findElement(This->ancestorStorage,
1061 This->storageDirEntry,
1062 pwcsName,
1063 &currentEntry);
1065 if (currentEntryRef != DIRENTRY_NULL)
1068 * An element with this name already exists
1070 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1071 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1073 hr = IStorage_DestroyElement(iface, pwcsName);
1074 if (FAILED(hr))
1075 return hr;
1077 else
1079 WARN("file already exists\n");
1080 return STG_E_FILEALREADYEXISTS;
1083 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1085 WARN("read-only storage\n");
1086 return STG_E_ACCESSDENIED;
1089 memset(&newEntry, 0, sizeof(DirEntry));
1091 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1093 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1095 FIXME("name too long\n");
1096 return STG_E_INVALIDNAME;
1099 strcpyW(newEntry.name, pwcsName);
1101 newEntry.stgType = STGTY_STORAGE;
1102 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1103 newEntry.size.u.LowPart = 0;
1104 newEntry.size.u.HighPart = 0;
1106 newEntry.leftChild = DIRENTRY_NULL;
1107 newEntry.rightChild = DIRENTRY_NULL;
1108 newEntry.dirRootEntry = DIRENTRY_NULL;
1110 /* call CoFileTime to get the current time
1111 newEntry.ctime
1112 newEntry.mtime
1115 /* newEntry.clsid */
1118 * Create a new directory entry for the storage
1120 createDirEntry(This->ancestorStorage, &newEntry, &newEntryRef);
1123 * Insert the new directory entry into the parent storage's tree
1125 insertIntoTree(
1126 This->ancestorStorage,
1127 This->storageDirEntry,
1128 newEntryRef);
1131 * Open it to get a pointer to return.
1133 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1135 if( (hr != S_OK) || (*ppstg == NULL))
1137 return hr;
1141 return S_OK;
1145 /***************************************************************************
1147 * Internal Method
1149 * Reserve a directory entry in the file and initialize it.
1151 static HRESULT createDirEntry(
1152 StorageImpl *storage,
1153 const DirEntry *newData,
1154 DirRef *index)
1156 ULONG currentEntryIndex = 0;
1157 ULONG newEntryIndex = DIRENTRY_NULL;
1158 HRESULT hr = S_OK;
1159 BYTE currentData[RAW_DIRENTRY_SIZE];
1160 WORD sizeOfNameString;
1164 hr = StorageImpl_ReadRawDirEntry(storage,
1165 currentEntryIndex,
1166 currentData);
1168 if (SUCCEEDED(hr))
1170 StorageUtl_ReadWord(
1171 currentData,
1172 OFFSET_PS_NAMELENGTH,
1173 &sizeOfNameString);
1175 if (sizeOfNameString == 0)
1178 * The entry exists and is available, we found it.
1180 newEntryIndex = currentEntryIndex;
1183 else
1186 * We exhausted the directory entries, we will create more space below
1188 newEntryIndex = currentEntryIndex;
1190 currentEntryIndex++;
1192 } while (newEntryIndex == DIRENTRY_NULL);
1195 * grow the directory stream
1197 if (FAILED(hr))
1199 BYTE emptyData[RAW_DIRENTRY_SIZE];
1200 ULARGE_INTEGER newSize;
1201 ULONG entryIndex;
1202 ULONG lastEntry = 0;
1203 ULONG blockCount = 0;
1206 * obtain the new count of blocks in the directory stream
1208 blockCount = BlockChainStream_GetCount(
1209 storage->rootBlockChain)+1;
1212 * initialize the size used by the directory stream
1214 newSize.u.HighPart = 0;
1215 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1218 * add a block to the directory stream
1220 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1223 * memset the empty entry in order to initialize the unused newly
1224 * created entries
1226 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1229 * initialize them
1231 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1233 for(
1234 entryIndex = newEntryIndex + 1;
1235 entryIndex < lastEntry;
1236 entryIndex++)
1238 StorageImpl_WriteRawDirEntry(
1239 storage,
1240 entryIndex,
1241 emptyData);
1245 UpdateRawDirEntry(currentData, newData);
1247 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1249 if (SUCCEEDED(hr))
1250 *index = newEntryIndex;
1252 return hr;
1255 /***************************************************************************
1257 * Internal Method
1259 * Mark a directory entry in the file as free.
1261 static HRESULT destroyDirEntry(
1262 StorageImpl *storage,
1263 DirRef index)
1265 HRESULT hr;
1266 BYTE emptyData[RAW_DIRENTRY_SIZE];
1268 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1270 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1272 return hr;
1276 /****************************************************************************
1278 * Internal Method
1280 * Case insensitive comparison of DirEntry.name by first considering
1281 * their size.
1283 * Returns <0 when name1 < name2
1284 * >0 when name1 > name2
1285 * 0 when name1 == name2
1287 static LONG entryNameCmp(
1288 const OLECHAR *name1,
1289 const OLECHAR *name2)
1291 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1293 if (diff == 0)
1296 * We compare the string themselves only when they are of the same length
1298 diff = lstrcmpiW( name1, name2);
1301 return diff;
1304 /****************************************************************************
1306 * Internal Method
1308 * Add a directory entry to a storage
1310 static HRESULT insertIntoTree(
1311 StorageImpl *This,
1312 DirRef parentStorageIndex,
1313 DirRef newEntryIndex)
1315 DirEntry currentEntry;
1316 DirEntry newEntry;
1319 * Read the inserted entry
1321 StorageImpl_ReadDirEntry(This,
1322 newEntryIndex,
1323 &newEntry);
1326 * Read the storage entry
1328 StorageImpl_ReadDirEntry(This,
1329 parentStorageIndex,
1330 &currentEntry);
1332 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1335 * The root storage contains some element, therefore, start the research
1336 * for the appropriate location.
1338 BOOL found = 0;
1339 DirRef current, next, previous, currentEntryId;
1342 * Keep a reference to the root of the storage's element tree
1344 currentEntryId = currentEntry.dirRootEntry;
1347 * Read
1349 StorageImpl_ReadDirEntry(This,
1350 currentEntry.dirRootEntry,
1351 &currentEntry);
1353 previous = currentEntry.leftChild;
1354 next = currentEntry.rightChild;
1355 current = currentEntryId;
1357 while (found == 0)
1359 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1361 if (diff < 0)
1363 if (previous != DIRENTRY_NULL)
1365 StorageImpl_ReadDirEntry(This,
1366 previous,
1367 &currentEntry);
1368 current = previous;
1370 else
1372 currentEntry.leftChild = newEntryIndex;
1373 StorageImpl_WriteDirEntry(This,
1374 current,
1375 &currentEntry);
1376 found = 1;
1379 else if (diff > 0)
1381 if (next != DIRENTRY_NULL)
1383 StorageImpl_ReadDirEntry(This,
1384 next,
1385 &currentEntry);
1386 current = next;
1388 else
1390 currentEntry.rightChild = newEntryIndex;
1391 StorageImpl_WriteDirEntry(This,
1392 current,
1393 &currentEntry);
1394 found = 1;
1397 else
1400 * Trying to insert an item with the same name in the
1401 * subtree structure.
1403 return STG_E_FILEALREADYEXISTS;
1406 previous = currentEntry.leftChild;
1407 next = currentEntry.rightChild;
1410 else
1413 * The storage is empty, make the new entry the root of its element tree
1415 currentEntry.dirRootEntry = newEntryIndex;
1416 StorageImpl_WriteDirEntry(This,
1417 parentStorageIndex,
1418 &currentEntry);
1421 return S_OK;
1424 /****************************************************************************
1426 * Internal Method
1428 * Find and read the element of a storage with the given name.
1430 static DirRef findElement(StorageImpl *storage, DirRef storageEntry,
1431 const OLECHAR *name, DirEntry *data)
1433 DirRef currentEntry;
1435 /* Read the storage entry to find the root of the tree. */
1436 StorageImpl_ReadDirEntry(storage, storageEntry, data);
1438 currentEntry = data->dirRootEntry;
1440 while (currentEntry != DIRENTRY_NULL)
1442 LONG cmp;
1444 StorageImpl_ReadDirEntry(storage, currentEntry, data);
1446 cmp = entryNameCmp(name, data->name);
1448 if (cmp == 0)
1449 /* found it */
1450 break;
1452 else if (cmp < 0)
1453 currentEntry = data->leftChild;
1455 else if (cmp > 0)
1456 currentEntry = data->rightChild;
1459 return currentEntry;
1462 /****************************************************************************
1464 * Internal Method
1466 * Find and read the binary tree parent of the element with the given name.
1468 * If there is no such element, find a place where it could be inserted and
1469 * return STG_E_FILENOTFOUND.
1471 static HRESULT findTreeParent(StorageImpl *storage, DirRef storageEntry,
1472 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1473 ULONG *relation)
1475 DirRef childEntry;
1476 DirEntry childData;
1478 /* Read the storage entry to find the root of the tree. */
1479 StorageImpl_ReadDirEntry(storage, storageEntry, parentData);
1481 *parentEntry = storageEntry;
1482 *relation = DIRENTRY_RELATION_DIR;
1484 childEntry = parentData->dirRootEntry;
1486 while (childEntry != DIRENTRY_NULL)
1488 LONG cmp;
1490 StorageImpl_ReadDirEntry(storage, childEntry, &childData);
1492 cmp = entryNameCmp(childName, childData.name);
1494 if (cmp == 0)
1495 /* found it */
1496 break;
1498 else if (cmp < 0)
1500 *parentData = childData;
1501 *parentEntry = childEntry;
1502 *relation = DIRENTRY_RELATION_PREVIOUS;
1504 childEntry = parentData->leftChild;
1507 else if (cmp > 0)
1509 *parentData = childData;
1510 *parentEntry = childEntry;
1511 *relation = DIRENTRY_RELATION_NEXT;
1513 childEntry = parentData->rightChild;
1517 if (childEntry == DIRENTRY_NULL)
1518 return STG_E_FILENOTFOUND;
1519 else
1520 return S_OK;
1524 /*************************************************************************
1525 * CopyTo (IStorage)
1527 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1528 IStorage* iface,
1529 DWORD ciidExclude, /* [in] */
1530 const IID* rgiidExclude, /* [size_is][unique][in] */
1531 SNB snbExclude, /* [unique][in] */
1532 IStorage* pstgDest) /* [unique][in] */
1534 IEnumSTATSTG *elements = 0;
1535 STATSTG curElement, strStat;
1536 HRESULT hr;
1537 IStorage *pstgTmp, *pstgChild;
1538 IStream *pstrTmp, *pstrChild;
1539 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1540 int i;
1542 TRACE("(%p, %d, %p, %p, %p)\n",
1543 iface, ciidExclude, rgiidExclude,
1544 snbExclude, pstgDest);
1546 if ( pstgDest == 0 )
1547 return STG_E_INVALIDPOINTER;
1550 * Enumerate the elements
1552 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1554 if ( hr != S_OK )
1555 return hr;
1558 * set the class ID
1560 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1561 IStorage_SetClass( pstgDest, &curElement.clsid );
1563 for(i = 0; i < ciidExclude; ++i)
1565 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1566 skip_storage = TRUE;
1567 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1568 skip_stream = TRUE;
1569 else
1570 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1576 * Obtain the next element
1578 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1580 if ( hr == S_FALSE )
1582 hr = S_OK; /* done, every element has been copied */
1583 break;
1586 if ( snbExclude )
1588 WCHAR **snb = snbExclude;
1589 skip = FALSE;
1590 while ( *snb != NULL && !skip )
1592 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1593 skip = TRUE;
1594 ++snb;
1598 if ( skip )
1599 continue;
1601 if (curElement.type == STGTY_STORAGE)
1603 if(skip_storage)
1604 continue;
1607 * open child source storage
1609 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1610 STGM_READ|STGM_SHARE_EXCLUSIVE,
1611 NULL, 0, &pstgChild );
1613 if (hr != S_OK)
1614 break;
1617 * Check if destination storage is not a child of the source
1618 * storage, which will cause an infinite loop
1620 if (pstgChild == pstgDest)
1622 IEnumSTATSTG_Release(elements);
1624 return STG_E_ACCESSDENIED;
1628 * create a new storage in destination storage
1630 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1631 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1632 0, 0,
1633 &pstgTmp );
1635 * if it already exist, don't create a new one use this one
1637 if (hr == STG_E_FILEALREADYEXISTS)
1639 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1640 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1641 NULL, 0, &pstgTmp );
1644 if (hr != S_OK)
1645 break;
1649 * do the copy recursively
1651 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1652 NULL, pstgTmp );
1654 IStorage_Release( pstgTmp );
1655 IStorage_Release( pstgChild );
1657 else if (curElement.type == STGTY_STREAM)
1659 if(skip_stream)
1660 continue;
1663 * create a new stream in destination storage. If the stream already
1664 * exist, it will be deleted and a new one will be created.
1666 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1667 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1668 0, 0, &pstrTmp );
1670 if (hr != S_OK)
1671 break;
1674 * open child stream storage
1676 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1677 STGM_READ|STGM_SHARE_EXCLUSIVE,
1678 0, &pstrChild );
1680 if (hr != S_OK)
1681 break;
1684 * Get the size of the source stream
1686 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1689 * Set the size of the destination stream.
1691 IStream_SetSize(pstrTmp, strStat.cbSize);
1694 * do the copy
1696 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1697 NULL, NULL );
1699 IStream_Release( pstrTmp );
1700 IStream_Release( pstrChild );
1702 else
1704 WARN("unknown element type: %d\n", curElement.type);
1707 } while (hr == S_OK);
1710 * Clean-up
1712 IEnumSTATSTG_Release(elements);
1714 return hr;
1717 /*************************************************************************
1718 * MoveElementTo (IStorage)
1720 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1721 IStorage* iface,
1722 const OLECHAR *pwcsName, /* [string][in] */
1723 IStorage *pstgDest, /* [unique][in] */
1724 const OLECHAR *pwcsNewName,/* [string][in] */
1725 DWORD grfFlags) /* [in] */
1727 FIXME("(%p %s %p %s %u): stub\n", iface,
1728 debugstr_w(pwcsName), pstgDest,
1729 debugstr_w(pwcsNewName), grfFlags);
1730 return E_NOTIMPL;
1733 /*************************************************************************
1734 * Commit (IStorage)
1736 * Ensures that any changes made to a storage object open in transacted mode
1737 * are reflected in the parent storage
1739 * NOTES
1740 * Wine doesn't implement transacted mode, which seems to be a basic
1741 * optimization, so we can ignore this stub for now.
1743 static HRESULT WINAPI StorageImpl_Commit(
1744 IStorage* iface,
1745 DWORD grfCommitFlags)/* [in] */
1747 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1748 return S_OK;
1751 /*************************************************************************
1752 * Revert (IStorage)
1754 * Discard all changes that have been made since the last commit operation
1756 static HRESULT WINAPI StorageImpl_Revert(
1757 IStorage* iface)
1759 FIXME("(%p): stub\n", iface);
1760 return E_NOTIMPL;
1763 /*************************************************************************
1764 * DestroyElement (IStorage)
1766 * Strategy: This implementation is built this way for simplicity not for speed.
1767 * I always delete the topmost element of the enumeration and adjust
1768 * the deleted element pointer all the time. This takes longer to
1769 * do but allow to reinvoke DestroyElement whenever we encounter a
1770 * storage object. The optimisation resides in the usage of another
1771 * enumeration strategy that would give all the leaves of a storage
1772 * first. (postfix order)
1774 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1775 IStorage* iface,
1776 const OLECHAR *pwcsName)/* [string][in] */
1778 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1780 HRESULT hr = S_OK;
1781 DirEntry entryToDelete;
1782 DirRef entryToDeleteRef;
1784 TRACE("(%p, %s)\n",
1785 iface, debugstr_w(pwcsName));
1787 if (pwcsName==NULL)
1788 return STG_E_INVALIDPOINTER;
1790 if (!This->ancestorStorage)
1791 return STG_E_REVERTED;
1793 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1794 return STG_E_ACCESSDENIED;
1796 entryToDeleteRef = findElement(
1797 This->ancestorStorage,
1798 This->storageDirEntry,
1799 pwcsName,
1800 &entryToDelete);
1802 if ( entryToDeleteRef == DIRENTRY_NULL )
1804 return STG_E_FILENOTFOUND;
1807 if ( entryToDelete.stgType == STGTY_STORAGE )
1809 hr = deleteStorageContents(
1810 This,
1811 entryToDeleteRef,
1812 entryToDelete);
1814 else if ( entryToDelete.stgType == STGTY_STREAM )
1816 hr = deleteStreamContents(
1817 This,
1818 entryToDeleteRef,
1819 entryToDelete);
1822 if (hr!=S_OK)
1823 return hr;
1826 * Remove the entry from its parent storage
1828 hr = removeFromTree(
1829 This->ancestorStorage,
1830 This->storageDirEntry,
1831 entryToDeleteRef);
1834 * Invalidate the entry
1836 if (SUCCEEDED(hr))
1837 destroyDirEntry(This->ancestorStorage,
1838 entryToDeleteRef);
1840 return hr;
1844 /******************************************************************************
1845 * Internal stream list handlers
1848 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1850 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1851 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1854 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1856 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1857 list_remove(&(strm->StrmListEntry));
1860 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1862 StgStreamImpl *strm;
1864 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1866 if (strm->dirEntry == streamEntry)
1868 return TRUE;
1872 return FALSE;
1875 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1877 StorageInternalImpl *childstg;
1879 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1881 if (childstg->base.storageDirEntry == storageEntry)
1883 return TRUE;
1887 return FALSE;
1890 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1892 struct list *cur, *cur2;
1893 StgStreamImpl *strm=NULL;
1894 StorageInternalImpl *childstg=NULL;
1896 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1897 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1898 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1899 strm->parentStorage = NULL;
1900 list_remove(cur);
1903 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1904 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1905 StorageInternalImpl_Invalidate( childstg );
1910 /*********************************************************************
1912 * Internal Method
1914 * Delete the contents of a storage entry.
1917 static HRESULT deleteStorageContents(
1918 StorageBaseImpl *parentStorage,
1919 DirRef indexToDelete,
1920 DirEntry entryDataToDelete)
1922 IEnumSTATSTG *elements = 0;
1923 IStorage *childStorage = 0;
1924 STATSTG currentElement;
1925 HRESULT hr;
1926 HRESULT destroyHr = S_OK;
1927 StorageInternalImpl *stg, *stg2;
1929 /* Invalidate any open storage objects. */
1930 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1932 if (stg->base.storageDirEntry == indexToDelete)
1934 StorageInternalImpl_Invalidate(stg);
1939 * Open the storage and enumerate it
1941 hr = StorageBaseImpl_OpenStorage(
1942 (IStorage*)parentStorage,
1943 entryDataToDelete.name,
1945 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1948 &childStorage);
1950 if (hr != S_OK)
1952 return hr;
1956 * Enumerate the elements
1958 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1963 * Obtain the next element
1965 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1966 if (hr==S_OK)
1968 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
1970 CoTaskMemFree(currentElement.pwcsName);
1974 * We need to Reset the enumeration every time because we delete elements
1975 * and the enumeration could be invalid
1977 IEnumSTATSTG_Reset(elements);
1979 } while ((hr == S_OK) && (destroyHr == S_OK));
1981 IStorage_Release(childStorage);
1982 IEnumSTATSTG_Release(elements);
1984 return destroyHr;
1987 /*********************************************************************
1989 * Internal Method
1991 * Perform the deletion of a stream's data
1994 static HRESULT deleteStreamContents(
1995 StorageBaseImpl *parentStorage,
1996 DirRef indexToDelete,
1997 DirEntry entryDataToDelete)
1999 IStream *pis;
2000 HRESULT hr;
2001 ULARGE_INTEGER size;
2002 StgStreamImpl *strm, *strm2;
2004 /* Invalidate any open stream objects. */
2005 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2007 if (strm->dirEntry == indexToDelete)
2009 TRACE("Stream deleted %p\n", strm);
2010 strm->parentStorage = NULL;
2011 list_remove(&strm->StrmListEntry);
2015 size.u.HighPart = 0;
2016 size.u.LowPart = 0;
2018 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2019 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2021 if (hr!=S_OK)
2023 return(hr);
2027 * Zap the stream
2029 hr = IStream_SetSize(pis, size);
2031 if(hr != S_OK)
2033 return hr;
2037 * Release the stream object.
2039 IStream_Release(pis);
2041 return S_OK;
2044 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2046 switch (relation)
2048 case DIRENTRY_RELATION_PREVIOUS:
2049 entry->leftChild = new_target;
2050 break;
2051 case DIRENTRY_RELATION_NEXT:
2052 entry->rightChild = new_target;
2053 break;
2054 case DIRENTRY_RELATION_DIR:
2055 entry->dirRootEntry = new_target;
2056 break;
2057 default:
2058 assert(0);
2062 /*************************************************************************
2064 * Internal Method
2066 * This method removes a directory entry from its parent storage tree without
2067 * freeing any resources attached to it.
2069 static HRESULT removeFromTree(
2070 StorageImpl *This,
2071 DirRef parentStorageIndex,
2072 DirRef deletedIndex)
2074 HRESULT hr = S_OK;
2075 BOOL res = TRUE;
2076 DirEntry entryToDelete;
2077 DirEntry parentEntry;
2078 DirRef parentEntryRef;
2079 ULONG typeOfRelation;
2081 res = StorageImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2084 * Find the element that links to the one we want to delete.
2086 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2087 &parentEntry, &parentEntryRef, &typeOfRelation);
2089 if (hr != S_OK)
2090 return hr;
2092 if (entryToDelete.leftChild != DIRENTRY_NULL)
2095 * Replace the deleted entry with its left child
2097 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2099 res = StorageImpl_WriteDirEntry(
2100 This,
2101 parentEntryRef,
2102 &parentEntry);
2103 if(!res)
2105 return E_FAIL;
2108 if (entryToDelete.rightChild != DIRENTRY_NULL)
2111 * We need to reinsert the right child somewhere. We already know it and
2112 * its children are greater than everything in the left tree, so we
2113 * insert it at the rightmost point in the left tree.
2115 DirRef newRightChildParent = entryToDelete.leftChild;
2116 DirEntry newRightChildParentEntry;
2120 res = StorageImpl_ReadDirEntry(
2121 This,
2122 newRightChildParent,
2123 &newRightChildParentEntry);
2124 if (!res)
2126 return E_FAIL;
2129 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2130 newRightChildParent = newRightChildParentEntry.rightChild;
2131 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2133 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2135 res = StorageImpl_WriteDirEntry(
2136 This,
2137 newRightChildParent,
2138 &newRightChildParentEntry);
2139 if (!res)
2141 return E_FAIL;
2145 else
2148 * Replace the deleted entry with its right child
2150 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2152 res = StorageImpl_WriteDirEntry(
2153 This,
2154 parentEntryRef,
2155 &parentEntry);
2156 if(!res)
2158 return E_FAIL;
2162 return hr;
2166 /******************************************************************************
2167 * SetElementTimes (IStorage)
2169 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2170 IStorage* iface,
2171 const OLECHAR *pwcsName,/* [string][in] */
2172 const FILETIME *pctime, /* [in] */
2173 const FILETIME *patime, /* [in] */
2174 const FILETIME *pmtime) /* [in] */
2176 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2177 return S_OK;
2180 /******************************************************************************
2181 * SetStateBits (IStorage)
2183 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2184 IStorage* iface,
2185 DWORD grfStateBits,/* [in] */
2186 DWORD grfMask) /* [in] */
2188 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2190 if (!This->ancestorStorage)
2191 return STG_E_REVERTED;
2193 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2194 return S_OK;
2198 * Virtual function table for the IStorage32Impl class.
2200 static const IStorageVtbl Storage32Impl_Vtbl =
2202 StorageBaseImpl_QueryInterface,
2203 StorageBaseImpl_AddRef,
2204 StorageBaseImpl_Release,
2205 StorageBaseImpl_CreateStream,
2206 StorageBaseImpl_OpenStream,
2207 StorageBaseImpl_CreateStorage,
2208 StorageBaseImpl_OpenStorage,
2209 StorageBaseImpl_CopyTo,
2210 StorageBaseImpl_MoveElementTo,
2211 StorageImpl_Commit,
2212 StorageImpl_Revert,
2213 StorageBaseImpl_EnumElements,
2214 StorageBaseImpl_DestroyElement,
2215 StorageBaseImpl_RenameElement,
2216 StorageBaseImpl_SetElementTimes,
2217 StorageBaseImpl_SetClass,
2218 StorageBaseImpl_SetStateBits,
2219 StorageBaseImpl_Stat
2222 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2224 StorageImpl_Destroy
2227 static HRESULT StorageImpl_Construct(
2228 HANDLE hFile,
2229 LPCOLESTR pwcsName,
2230 ILockBytes* pLkbyt,
2231 DWORD openFlags,
2232 BOOL fileBased,
2233 BOOL create,
2234 StorageImpl** result)
2236 StorageImpl* This;
2237 HRESULT hr = S_OK;
2238 DirEntry currentEntry;
2239 BOOL readSuccessful;
2240 DirRef currentEntryRef;
2242 if ( FAILED( validateSTGM(openFlags) ))
2243 return STG_E_INVALIDFLAG;
2245 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2246 if (!This)
2247 return E_OUTOFMEMORY;
2249 memset(This, 0, sizeof(StorageImpl));
2251 list_init(&This->base.strmHead);
2253 list_init(&This->base.storageHead);
2255 This->base.lpVtbl = &Storage32Impl_Vtbl;
2256 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2257 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2258 This->base.openFlags = (openFlags & ~STGM_CREATE);
2259 This->base.ref = 1;
2260 This->base.create = create;
2263 * This is the top-level storage so initialize the ancestor pointer
2264 * to this.
2266 This->base.ancestorStorage = This;
2268 This->hFile = hFile;
2270 if(pwcsName) {
2271 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2272 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2273 if (!This->pwcsName)
2275 hr = STG_E_INSUFFICIENTMEMORY;
2276 goto end;
2278 strcpyW(This->pwcsName, pwcsName);
2280 memcpy(This->base.filename, pwcsName, DIRENTRY_NAME_BUFFER_LEN-1);
2281 This->base.filename[DIRENTRY_NAME_BUFFER_LEN-1] = 0;
2285 * Initialize the big block cache.
2287 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2288 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2289 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2290 pLkbyt,
2291 openFlags,
2292 This->bigBlockSize,
2293 fileBased);
2295 if (This->bigBlockFile == 0)
2297 hr = E_FAIL;
2298 goto end;
2301 if (create)
2303 ULARGE_INTEGER size;
2304 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2307 * Initialize all header variables:
2308 * - The big block depot consists of one block and it is at block 0
2309 * - The directory table starts at block 1
2310 * - There is no small block depot
2312 memset( This->bigBlockDepotStart,
2313 BLOCK_UNUSED,
2314 sizeof(This->bigBlockDepotStart));
2316 This->bigBlockDepotCount = 1;
2317 This->bigBlockDepotStart[0] = 0;
2318 This->rootStartBlock = 1;
2319 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2320 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2321 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2322 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2323 This->extBigBlockDepotCount = 0;
2325 StorageImpl_SaveFileHeader(This);
2328 * Add one block for the big block depot and one block for the directory table
2330 size.u.HighPart = 0;
2331 size.u.LowPart = This->bigBlockSize * 3;
2332 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2335 * Initialize the big block depot
2337 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2338 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2339 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2340 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2342 else
2345 * Load the header for the file.
2347 hr = StorageImpl_LoadFileHeader(This);
2349 if (FAILED(hr))
2351 goto end;
2356 * There is no block depot cached yet.
2358 This->indexBlockDepotCached = 0xFFFFFFFF;
2361 * Start searching for free blocks with block 0.
2363 This->prevFreeBlock = 0;
2366 * Create the block chain abstractions.
2368 if(!(This->rootBlockChain =
2369 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2371 hr = STG_E_READFAULT;
2372 goto end;
2375 if(!(This->smallBlockDepotChain =
2376 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2377 DIRENTRY_NULL)))
2379 hr = STG_E_READFAULT;
2380 goto end;
2384 * Write the root storage entry (memory only)
2386 if (create)
2388 DirEntry rootEntry;
2390 * Initialize the directory table
2392 memset(&rootEntry, 0, sizeof(rootEntry));
2393 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2394 sizeof(rootEntry.name)/sizeof(WCHAR) );
2395 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2396 rootEntry.stgType = STGTY_ROOT;
2397 rootEntry.leftChild = DIRENTRY_NULL;
2398 rootEntry.rightChild = DIRENTRY_NULL;
2399 rootEntry.dirRootEntry = DIRENTRY_NULL;
2400 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2401 rootEntry.size.u.HighPart = 0;
2402 rootEntry.size.u.LowPart = 0;
2404 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2408 * Find the ID of the root storage.
2410 currentEntryRef = 0;
2414 readSuccessful = StorageImpl_ReadDirEntry(
2415 This,
2416 currentEntryRef,
2417 &currentEntry);
2419 if (readSuccessful)
2421 if ( (currentEntry.sizeOfNameString != 0 ) &&
2422 (currentEntry.stgType == STGTY_ROOT) )
2424 This->base.storageDirEntry = currentEntryRef;
2428 currentEntryRef++;
2430 } while (readSuccessful && (This->base.storageDirEntry == DIRENTRY_NULL) );
2432 if (!readSuccessful)
2434 hr = STG_E_READFAULT;
2435 goto end;
2439 * Create the block chain abstraction for the small block root chain.
2441 if(!(This->smallBlockRootChain =
2442 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2444 hr = STG_E_READFAULT;
2447 end:
2448 if (FAILED(hr))
2450 IStorage_Release((IStorage*)This);
2451 *result = NULL;
2453 else
2454 *result = This;
2456 return hr;
2459 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2461 StorageImpl *This = (StorageImpl*) iface;
2462 TRACE("(%p)\n", This);
2464 StorageBaseImpl_DeleteAll(&This->base);
2466 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2468 BlockChainStream_Destroy(This->smallBlockRootChain);
2469 BlockChainStream_Destroy(This->rootBlockChain);
2470 BlockChainStream_Destroy(This->smallBlockDepotChain);
2472 if (This->bigBlockFile)
2473 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2474 HeapFree(GetProcessHeap(), 0, This);
2477 /******************************************************************************
2478 * Storage32Impl_GetNextFreeBigBlock
2480 * Returns the index of the next free big block.
2481 * If the big block depot is filled, this method will enlarge it.
2484 static ULONG StorageImpl_GetNextFreeBigBlock(
2485 StorageImpl* This)
2487 ULONG depotBlockIndexPos;
2488 BYTE depotBuffer[BIG_BLOCK_SIZE];
2489 BOOL success;
2490 ULONG depotBlockOffset;
2491 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2492 ULONG nextBlockIndex = BLOCK_SPECIAL;
2493 int depotIndex = 0;
2494 ULONG freeBlock = BLOCK_UNUSED;
2496 depotIndex = This->prevFreeBlock / blocksPerDepot;
2497 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2500 * Scan the entire big block depot until we find a block marked free
2502 while (nextBlockIndex != BLOCK_UNUSED)
2504 if (depotIndex < COUNT_BBDEPOTINHEADER)
2506 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2509 * Grow the primary depot.
2511 if (depotBlockIndexPos == BLOCK_UNUSED)
2513 depotBlockIndexPos = depotIndex*blocksPerDepot;
2516 * Add a block depot.
2518 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2519 This->bigBlockDepotCount++;
2520 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2523 * Flag it as a block depot.
2525 StorageImpl_SetNextBlockInChain(This,
2526 depotBlockIndexPos,
2527 BLOCK_SPECIAL);
2529 /* Save new header information.
2531 StorageImpl_SaveFileHeader(This);
2534 else
2536 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2538 if (depotBlockIndexPos == BLOCK_UNUSED)
2541 * Grow the extended depot.
2543 ULONG extIndex = BLOCK_UNUSED;
2544 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2545 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2547 if (extBlockOffset == 0)
2549 /* We need an extended block.
2551 extIndex = Storage32Impl_AddExtBlockDepot(This);
2552 This->extBigBlockDepotCount++;
2553 depotBlockIndexPos = extIndex + 1;
2555 else
2556 depotBlockIndexPos = depotIndex * blocksPerDepot;
2559 * Add a block depot and mark it in the extended block.
2561 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2562 This->bigBlockDepotCount++;
2563 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2565 /* Flag the block depot.
2567 StorageImpl_SetNextBlockInChain(This,
2568 depotBlockIndexPos,
2569 BLOCK_SPECIAL);
2571 /* If necessary, flag the extended depot block.
2573 if (extIndex != BLOCK_UNUSED)
2574 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2576 /* Save header information.
2578 StorageImpl_SaveFileHeader(This);
2582 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2584 if (success)
2586 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2587 ( nextBlockIndex != BLOCK_UNUSED))
2589 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2591 if (nextBlockIndex == BLOCK_UNUSED)
2593 freeBlock = (depotIndex * blocksPerDepot) +
2594 (depotBlockOffset/sizeof(ULONG));
2597 depotBlockOffset += sizeof(ULONG);
2601 depotIndex++;
2602 depotBlockOffset = 0;
2606 * make sure that the block physically exists before using it
2608 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2610 This->prevFreeBlock = freeBlock;
2612 return freeBlock;
2615 /******************************************************************************
2616 * Storage32Impl_AddBlockDepot
2618 * This will create a depot block, essentially it is a block initialized
2619 * to BLOCK_UNUSEDs.
2621 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2623 BYTE blockBuffer[BIG_BLOCK_SIZE];
2626 * Initialize blocks as free
2628 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2629 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2632 /******************************************************************************
2633 * Storage32Impl_GetExtDepotBlock
2635 * Returns the index of the block that corresponds to the specified depot
2636 * index. This method is only for depot indexes equal or greater than
2637 * COUNT_BBDEPOTINHEADER.
2639 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2641 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2642 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2643 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2644 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2645 ULONG blockIndex = BLOCK_UNUSED;
2646 ULONG extBlockIndex = This->extBigBlockDepotStart;
2648 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2650 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2651 return BLOCK_UNUSED;
2653 while (extBlockCount > 0)
2655 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2656 extBlockCount--;
2659 if (extBlockIndex != BLOCK_UNUSED)
2660 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2661 extBlockOffset * sizeof(ULONG), &blockIndex);
2663 return blockIndex;
2666 /******************************************************************************
2667 * Storage32Impl_SetExtDepotBlock
2669 * Associates the specified block index to the specified depot index.
2670 * This method is only for depot indexes equal or greater than
2671 * COUNT_BBDEPOTINHEADER.
2673 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2675 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2676 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2677 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2678 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2679 ULONG extBlockIndex = This->extBigBlockDepotStart;
2681 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2683 while (extBlockCount > 0)
2685 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2686 extBlockCount--;
2689 if (extBlockIndex != BLOCK_UNUSED)
2691 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2692 extBlockOffset * sizeof(ULONG),
2693 blockIndex);
2697 /******************************************************************************
2698 * Storage32Impl_AddExtBlockDepot
2700 * Creates an extended depot block.
2702 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2704 ULONG numExtBlocks = This->extBigBlockDepotCount;
2705 ULONG nextExtBlock = This->extBigBlockDepotStart;
2706 BYTE depotBuffer[BIG_BLOCK_SIZE];
2707 ULONG index = BLOCK_UNUSED;
2708 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2709 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2710 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2712 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2713 blocksPerDepotBlock;
2715 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2718 * The first extended block.
2720 This->extBigBlockDepotStart = index;
2722 else
2724 unsigned int i;
2726 * Follow the chain to the last one.
2728 for (i = 0; i < (numExtBlocks - 1); i++)
2730 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2734 * Add the new extended block to the chain.
2736 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2737 index);
2741 * Initialize this block.
2743 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2744 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2746 return index;
2749 /******************************************************************************
2750 * Storage32Impl_FreeBigBlock
2752 * This method will flag the specified block as free in the big block depot.
2754 static void StorageImpl_FreeBigBlock(
2755 StorageImpl* This,
2756 ULONG blockIndex)
2758 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2760 if (blockIndex < This->prevFreeBlock)
2761 This->prevFreeBlock = blockIndex;
2764 /************************************************************************
2765 * Storage32Impl_GetNextBlockInChain
2767 * This method will retrieve the block index of the next big block in
2768 * in the chain.
2770 * Params: This - Pointer to the Storage object.
2771 * blockIndex - Index of the block to retrieve the chain
2772 * for.
2773 * nextBlockIndex - receives the return value.
2775 * Returns: This method returns the index of the next block in the chain.
2776 * It will return the constants:
2777 * BLOCK_SPECIAL - If the block given was not part of a
2778 * chain.
2779 * BLOCK_END_OF_CHAIN - If the block given was the last in
2780 * a chain.
2781 * BLOCK_UNUSED - If the block given was not past of a chain
2782 * and is available.
2783 * BLOCK_EXTBBDEPOT - This block is part of the extended
2784 * big block depot.
2786 * See Windows documentation for more details on IStorage methods.
2788 static HRESULT StorageImpl_GetNextBlockInChain(
2789 StorageImpl* This,
2790 ULONG blockIndex,
2791 ULONG* nextBlockIndex)
2793 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2794 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2795 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2796 BYTE depotBuffer[BIG_BLOCK_SIZE];
2797 BOOL success;
2798 ULONG depotBlockIndexPos;
2799 int index;
2801 *nextBlockIndex = BLOCK_SPECIAL;
2803 if(depotBlockCount >= This->bigBlockDepotCount)
2805 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2806 This->bigBlockDepotCount);
2807 return STG_E_READFAULT;
2811 * Cache the currently accessed depot block.
2813 if (depotBlockCount != This->indexBlockDepotCached)
2815 This->indexBlockDepotCached = depotBlockCount;
2817 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2819 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2821 else
2824 * We have to look in the extended depot.
2826 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2829 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2831 if (!success)
2832 return STG_E_READFAULT;
2834 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2836 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2837 This->blockDepotCached[index] = *nextBlockIndex;
2841 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2843 return S_OK;
2846 /******************************************************************************
2847 * Storage32Impl_GetNextExtendedBlock
2849 * Given an extended block this method will return the next extended block.
2851 * NOTES:
2852 * The last ULONG of an extended block is the block index of the next
2853 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2854 * depot.
2856 * Return values:
2857 * - The index of the next extended block
2858 * - BLOCK_UNUSED: there is no next extended block.
2859 * - Any other return values denotes failure.
2861 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2863 ULONG nextBlockIndex = BLOCK_SPECIAL;
2864 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2866 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2867 &nextBlockIndex);
2869 return nextBlockIndex;
2872 /******************************************************************************
2873 * Storage32Impl_SetNextBlockInChain
2875 * This method will write the index of the specified block's next block
2876 * in the big block depot.
2878 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2879 * do the following
2881 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2882 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2883 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2886 static void StorageImpl_SetNextBlockInChain(
2887 StorageImpl* This,
2888 ULONG blockIndex,
2889 ULONG nextBlock)
2891 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2892 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2893 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2894 ULONG depotBlockIndexPos;
2896 assert(depotBlockCount < This->bigBlockDepotCount);
2897 assert(blockIndex != nextBlock);
2899 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2901 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2903 else
2906 * We have to look in the extended depot.
2908 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2911 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2912 nextBlock);
2914 * Update the cached block depot, if necessary.
2916 if (depotBlockCount == This->indexBlockDepotCached)
2918 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2922 /******************************************************************************
2923 * Storage32Impl_LoadFileHeader
2925 * This method will read in the file header, i.e. big block index -1.
2927 static HRESULT StorageImpl_LoadFileHeader(
2928 StorageImpl* This)
2930 HRESULT hr = STG_E_FILENOTFOUND;
2931 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2932 BOOL success;
2933 int index;
2935 TRACE("\n");
2937 * Get a pointer to the big block of data containing the header.
2939 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2942 * Extract the information from the header.
2944 if (success)
2947 * Check for the "magic number" signature and return an error if it is not
2948 * found.
2950 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2952 return STG_E_OLDFORMAT;
2955 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2957 return STG_E_INVALIDHEADER;
2960 StorageUtl_ReadWord(
2961 headerBigBlock,
2962 OFFSET_BIGBLOCKSIZEBITS,
2963 &This->bigBlockSizeBits);
2965 StorageUtl_ReadWord(
2966 headerBigBlock,
2967 OFFSET_SMALLBLOCKSIZEBITS,
2968 &This->smallBlockSizeBits);
2970 StorageUtl_ReadDWord(
2971 headerBigBlock,
2972 OFFSET_BBDEPOTCOUNT,
2973 &This->bigBlockDepotCount);
2975 StorageUtl_ReadDWord(
2976 headerBigBlock,
2977 OFFSET_ROOTSTARTBLOCK,
2978 &This->rootStartBlock);
2980 StorageUtl_ReadDWord(
2981 headerBigBlock,
2982 OFFSET_SBDEPOTSTART,
2983 &This->smallBlockDepotStart);
2985 StorageUtl_ReadDWord(
2986 headerBigBlock,
2987 OFFSET_EXTBBDEPOTSTART,
2988 &This->extBigBlockDepotStart);
2990 StorageUtl_ReadDWord(
2991 headerBigBlock,
2992 OFFSET_EXTBBDEPOTCOUNT,
2993 &This->extBigBlockDepotCount);
2995 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2997 StorageUtl_ReadDWord(
2998 headerBigBlock,
2999 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3000 &(This->bigBlockDepotStart[index]));
3004 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3006 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3007 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3010 * Right now, the code is making some assumptions about the size of the
3011 * blocks, just make sure they are what we're expecting.
3013 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3014 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3016 WARN("Broken OLE storage file\n");
3017 hr = STG_E_INVALIDHEADER;
3019 else
3020 hr = S_OK;
3023 return hr;
3026 /******************************************************************************
3027 * Storage32Impl_SaveFileHeader
3029 * This method will save to the file the header, i.e. big block -1.
3031 static void StorageImpl_SaveFileHeader(
3032 StorageImpl* This)
3034 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3035 int index;
3036 BOOL success;
3039 * Get a pointer to the big block of data containing the header.
3041 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3044 * If the block read failed, the file is probably new.
3046 if (!success)
3049 * Initialize for all unknown fields.
3051 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3054 * Initialize the magic number.
3056 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3059 * And a bunch of things we don't know what they mean
3061 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3062 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3063 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3064 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3068 * Write the information to the header.
3070 StorageUtl_WriteWord(
3071 headerBigBlock,
3072 OFFSET_BIGBLOCKSIZEBITS,
3073 This->bigBlockSizeBits);
3075 StorageUtl_WriteWord(
3076 headerBigBlock,
3077 OFFSET_SMALLBLOCKSIZEBITS,
3078 This->smallBlockSizeBits);
3080 StorageUtl_WriteDWord(
3081 headerBigBlock,
3082 OFFSET_BBDEPOTCOUNT,
3083 This->bigBlockDepotCount);
3085 StorageUtl_WriteDWord(
3086 headerBigBlock,
3087 OFFSET_ROOTSTARTBLOCK,
3088 This->rootStartBlock);
3090 StorageUtl_WriteDWord(
3091 headerBigBlock,
3092 OFFSET_SBDEPOTSTART,
3093 This->smallBlockDepotStart);
3095 StorageUtl_WriteDWord(
3096 headerBigBlock,
3097 OFFSET_SBDEPOTCOUNT,
3098 This->smallBlockDepotChain ?
3099 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3101 StorageUtl_WriteDWord(
3102 headerBigBlock,
3103 OFFSET_EXTBBDEPOTSTART,
3104 This->extBigBlockDepotStart);
3106 StorageUtl_WriteDWord(
3107 headerBigBlock,
3108 OFFSET_EXTBBDEPOTCOUNT,
3109 This->extBigBlockDepotCount);
3111 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3113 StorageUtl_WriteDWord(
3114 headerBigBlock,
3115 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3116 (This->bigBlockDepotStart[index]));
3120 * Write the big block back to the file.
3122 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3125 /******************************************************************************
3126 * StorageImpl_ReadRawDirEntry
3128 * This method will read the raw data from a directory entry in the file.
3130 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3132 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3134 ULARGE_INTEGER offset;
3135 HRESULT hr;
3136 ULONG bytesRead;
3138 offset.u.HighPart = 0;
3139 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3141 hr = BlockChainStream_ReadAt(
3142 This->rootBlockChain,
3143 offset,
3144 RAW_DIRENTRY_SIZE,
3145 buffer,
3146 &bytesRead);
3148 return hr;
3151 /******************************************************************************
3152 * StorageImpl_WriteRawDirEntry
3154 * This method will write the raw data from a directory entry in the file.
3156 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3158 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3160 ULARGE_INTEGER offset;
3161 HRESULT hr;
3162 ULONG bytesRead;
3164 offset.u.HighPart = 0;
3165 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3167 hr = BlockChainStream_WriteAt(
3168 This->rootBlockChain,
3169 offset,
3170 RAW_DIRENTRY_SIZE,
3171 buffer,
3172 &bytesRead);
3174 return hr;
3177 /******************************************************************************
3178 * UpdateRawDirEntry
3180 * Update raw directory entry data from the fields in newData.
3182 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3184 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3186 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3188 memcpy(
3189 buffer + OFFSET_PS_NAME,
3190 newData->name,
3191 DIRENTRY_NAME_BUFFER_LEN );
3193 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3195 StorageUtl_WriteWord(
3196 buffer,
3197 OFFSET_PS_NAMELENGTH,
3198 newData->sizeOfNameString);
3200 StorageUtl_WriteDWord(
3201 buffer,
3202 OFFSET_PS_LEFTCHILD,
3203 newData->leftChild);
3205 StorageUtl_WriteDWord(
3206 buffer,
3207 OFFSET_PS_RIGHTCHILD,
3208 newData->rightChild);
3210 StorageUtl_WriteDWord(
3211 buffer,
3212 OFFSET_PS_DIRROOT,
3213 newData->dirRootEntry);
3215 StorageUtl_WriteGUID(
3216 buffer,
3217 OFFSET_PS_GUID,
3218 &newData->clsid);
3220 StorageUtl_WriteDWord(
3221 buffer,
3222 OFFSET_PS_CTIMELOW,
3223 newData->ctime.dwLowDateTime);
3225 StorageUtl_WriteDWord(
3226 buffer,
3227 OFFSET_PS_CTIMEHIGH,
3228 newData->ctime.dwHighDateTime);
3230 StorageUtl_WriteDWord(
3231 buffer,
3232 OFFSET_PS_MTIMELOW,
3233 newData->mtime.dwLowDateTime);
3235 StorageUtl_WriteDWord(
3236 buffer,
3237 OFFSET_PS_MTIMEHIGH,
3238 newData->ctime.dwHighDateTime);
3240 StorageUtl_WriteDWord(
3241 buffer,
3242 OFFSET_PS_STARTBLOCK,
3243 newData->startingBlock);
3245 StorageUtl_WriteDWord(
3246 buffer,
3247 OFFSET_PS_SIZE,
3248 newData->size.u.LowPart);
3251 /******************************************************************************
3252 * Storage32Impl_ReadDirEntry
3254 * This method will read the specified directory entry.
3256 BOOL StorageImpl_ReadDirEntry(
3257 StorageImpl* This,
3258 DirRef index,
3259 DirEntry* buffer)
3261 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3262 HRESULT readRes;
3264 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3266 if (SUCCEEDED(readRes))
3268 memset(buffer->name, 0, sizeof(buffer->name));
3269 memcpy(
3270 buffer->name,
3271 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3272 DIRENTRY_NAME_BUFFER_LEN );
3273 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3275 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3277 StorageUtl_ReadWord(
3278 currentEntry,
3279 OFFSET_PS_NAMELENGTH,
3280 &buffer->sizeOfNameString);
3282 StorageUtl_ReadDWord(
3283 currentEntry,
3284 OFFSET_PS_LEFTCHILD,
3285 &buffer->leftChild);
3287 StorageUtl_ReadDWord(
3288 currentEntry,
3289 OFFSET_PS_RIGHTCHILD,
3290 &buffer->rightChild);
3292 StorageUtl_ReadDWord(
3293 currentEntry,
3294 OFFSET_PS_DIRROOT,
3295 &buffer->dirRootEntry);
3297 StorageUtl_ReadGUID(
3298 currentEntry,
3299 OFFSET_PS_GUID,
3300 &buffer->clsid);
3302 StorageUtl_ReadDWord(
3303 currentEntry,
3304 OFFSET_PS_CTIMELOW,
3305 &buffer->ctime.dwLowDateTime);
3307 StorageUtl_ReadDWord(
3308 currentEntry,
3309 OFFSET_PS_CTIMEHIGH,
3310 &buffer->ctime.dwHighDateTime);
3312 StorageUtl_ReadDWord(
3313 currentEntry,
3314 OFFSET_PS_MTIMELOW,
3315 &buffer->mtime.dwLowDateTime);
3317 StorageUtl_ReadDWord(
3318 currentEntry,
3319 OFFSET_PS_MTIMEHIGH,
3320 &buffer->mtime.dwHighDateTime);
3322 StorageUtl_ReadDWord(
3323 currentEntry,
3324 OFFSET_PS_STARTBLOCK,
3325 &buffer->startingBlock);
3327 StorageUtl_ReadDWord(
3328 currentEntry,
3329 OFFSET_PS_SIZE,
3330 &buffer->size.u.LowPart);
3332 buffer->size.u.HighPart = 0;
3335 return SUCCEEDED(readRes) ? TRUE : FALSE;
3338 /*********************************************************************
3339 * Write the specified directory entry to the file
3341 BOOL StorageImpl_WriteDirEntry(
3342 StorageImpl* This,
3343 DirRef index,
3344 const DirEntry* buffer)
3346 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3347 HRESULT writeRes;
3349 UpdateRawDirEntry(currentEntry, buffer);
3351 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3352 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3355 static BOOL StorageImpl_ReadBigBlock(
3356 StorageImpl* This,
3357 ULONG blockIndex,
3358 void* buffer)
3360 ULARGE_INTEGER ulOffset;
3361 DWORD read;
3363 ulOffset.u.HighPart = 0;
3364 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3366 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3367 return (read == This->bigBlockSize);
3370 static BOOL StorageImpl_ReadDWordFromBigBlock(
3371 StorageImpl* This,
3372 ULONG blockIndex,
3373 ULONG offset,
3374 DWORD* value)
3376 ULARGE_INTEGER ulOffset;
3377 DWORD read;
3378 DWORD tmp;
3380 ulOffset.u.HighPart = 0;
3381 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3382 ulOffset.u.LowPart += offset;
3384 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3385 *value = lendian32toh(tmp);
3386 return (read == sizeof(DWORD));
3389 static BOOL StorageImpl_WriteBigBlock(
3390 StorageImpl* This,
3391 ULONG blockIndex,
3392 const void* buffer)
3394 ULARGE_INTEGER ulOffset;
3395 DWORD wrote;
3397 ulOffset.u.HighPart = 0;
3398 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3400 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3401 return (wrote == This->bigBlockSize);
3404 static BOOL StorageImpl_WriteDWordToBigBlock(
3405 StorageImpl* This,
3406 ULONG blockIndex,
3407 ULONG offset,
3408 DWORD value)
3410 ULARGE_INTEGER ulOffset;
3411 DWORD wrote;
3413 ulOffset.u.HighPart = 0;
3414 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3415 ulOffset.u.LowPart += offset;
3417 value = htole32(value);
3418 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3419 return (wrote == sizeof(DWORD));
3422 /******************************************************************************
3423 * Storage32Impl_SmallBlocksToBigBlocks
3425 * This method will convert a small block chain to a big block chain.
3426 * The small block chain will be destroyed.
3428 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3429 StorageImpl* This,
3430 SmallBlockChainStream** ppsbChain)
3432 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3433 ULARGE_INTEGER size, offset;
3434 ULONG cbRead, cbWritten;
3435 ULARGE_INTEGER cbTotalRead;
3436 DirRef streamEntryRef;
3437 HRESULT resWrite = S_OK;
3438 HRESULT resRead;
3439 DirEntry streamEntry;
3440 BYTE *buffer;
3441 BlockChainStream *bbTempChain = NULL;
3442 BlockChainStream *bigBlockChain = NULL;
3445 * Create a temporary big block chain that doesn't have
3446 * an associated directory entry. This temporary chain will be
3447 * used to copy data from small blocks to big blocks.
3449 bbTempChain = BlockChainStream_Construct(This,
3450 &bbHeadOfChain,
3451 DIRENTRY_NULL);
3452 if(!bbTempChain) return NULL;
3454 * Grow the big block chain.
3456 size = SmallBlockChainStream_GetSize(*ppsbChain);
3457 BlockChainStream_SetSize(bbTempChain, size);
3460 * Copy the contents of the small block chain to the big block chain
3461 * by small block size increments.
3463 offset.u.LowPart = 0;
3464 offset.u.HighPart = 0;
3465 cbTotalRead.QuadPart = 0;
3467 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3470 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3471 offset,
3472 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3473 buffer,
3474 &cbRead);
3475 if (FAILED(resRead))
3476 break;
3478 if (cbRead > 0)
3480 cbTotalRead.QuadPart += cbRead;
3482 resWrite = BlockChainStream_WriteAt(bbTempChain,
3483 offset,
3484 cbRead,
3485 buffer,
3486 &cbWritten);
3488 if (FAILED(resWrite))
3489 break;
3491 offset.u.LowPart += cbRead;
3493 } while (cbTotalRead.QuadPart < size.QuadPart);
3494 HeapFree(GetProcessHeap(),0,buffer);
3496 size.u.HighPart = 0;
3497 size.u.LowPart = 0;
3499 if (FAILED(resRead) || FAILED(resWrite))
3501 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3502 BlockChainStream_SetSize(bbTempChain, size);
3503 BlockChainStream_Destroy(bbTempChain);
3504 return NULL;
3508 * Destroy the small block chain.
3510 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3511 SmallBlockChainStream_SetSize(*ppsbChain, size);
3512 SmallBlockChainStream_Destroy(*ppsbChain);
3513 *ppsbChain = 0;
3516 * Change the directory entry. This chain is now a big block chain
3517 * and it doesn't reside in the small blocks chain anymore.
3519 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3521 streamEntry.startingBlock = bbHeadOfChain;
3523 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3526 * Destroy the temporary entryless big block chain.
3527 * Create a new big block chain associated with this entry.
3529 BlockChainStream_Destroy(bbTempChain);
3530 bigBlockChain = BlockChainStream_Construct(This,
3531 NULL,
3532 streamEntryRef);
3534 return bigBlockChain;
3537 /******************************************************************************
3538 * Storage32Impl_BigBlocksToSmallBlocks
3540 * This method will convert a big block chain to a small block chain.
3541 * The big block chain will be destroyed on success.
3543 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3544 StorageImpl* This,
3545 BlockChainStream** ppbbChain)
3547 ULARGE_INTEGER size, offset, cbTotalRead;
3548 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3549 DirRef streamEntryRef;
3550 HRESULT resWrite = S_OK, resRead;
3551 DirEntry streamEntry;
3552 BYTE* buffer;
3553 SmallBlockChainStream* sbTempChain;
3555 TRACE("%p %p\n", This, ppbbChain);
3557 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3558 DIRENTRY_NULL);
3560 if(!sbTempChain)
3561 return NULL;
3563 size = BlockChainStream_GetSize(*ppbbChain);
3564 SmallBlockChainStream_SetSize(sbTempChain, size);
3566 offset.u.HighPart = 0;
3567 offset.u.LowPart = 0;
3568 cbTotalRead.QuadPart = 0;
3569 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3572 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3573 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3574 buffer, &cbRead);
3576 if(FAILED(resRead))
3577 break;
3579 if(cbRead > 0)
3581 cbTotalRead.QuadPart += cbRead;
3583 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3584 cbRead, buffer, &cbWritten);
3586 if(FAILED(resWrite))
3587 break;
3589 offset.u.LowPart += cbRead;
3591 }while(cbTotalRead.QuadPart < size.QuadPart);
3592 HeapFree(GetProcessHeap(), 0, buffer);
3594 size.u.HighPart = 0;
3595 size.u.LowPart = 0;
3597 if(FAILED(resRead) || FAILED(resWrite))
3599 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3600 SmallBlockChainStream_SetSize(sbTempChain, size);
3601 SmallBlockChainStream_Destroy(sbTempChain);
3602 return NULL;
3605 /* destroy the original big block chain */
3606 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3607 BlockChainStream_SetSize(*ppbbChain, size);
3608 BlockChainStream_Destroy(*ppbbChain);
3609 *ppbbChain = NULL;
3611 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3612 streamEntry.startingBlock = sbHeadOfChain;
3613 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3615 SmallBlockChainStream_Destroy(sbTempChain);
3616 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3619 static void StorageInternalImpl_Invalidate( StorageInternalImpl *This )
3621 if (This->base.ancestorStorage)
3623 TRACE("Storage invalidated (stg=%p)\n", This);
3625 This->base.ancestorStorage = NULL;
3627 StorageBaseImpl_DeleteAll(&This->base);
3629 list_remove(&This->ParentListEntry);
3633 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3635 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3637 StorageInternalImpl_Invalidate(This);
3639 HeapFree(GetProcessHeap(), 0, This);
3642 /******************************************************************************
3644 ** Storage32InternalImpl_Commit
3647 static HRESULT WINAPI StorageInternalImpl_Commit(
3648 IStorage* iface,
3649 DWORD grfCommitFlags) /* [in] */
3651 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3652 return S_OK;
3655 /******************************************************************************
3657 ** Storage32InternalImpl_Revert
3660 static HRESULT WINAPI StorageInternalImpl_Revert(
3661 IStorage* iface)
3663 FIXME("(%p): stub\n", iface);
3664 return S_OK;
3667 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3669 IStorage_Release((IStorage*)This->parentStorage);
3670 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3671 HeapFree(GetProcessHeap(), 0, This);
3674 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3675 IEnumSTATSTG* iface,
3676 REFIID riid,
3677 void** ppvObject)
3679 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3681 if (ppvObject==0)
3682 return E_INVALIDARG;
3684 *ppvObject = 0;
3686 if (IsEqualGUID(&IID_IUnknown, riid) ||
3687 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3689 *ppvObject = This;
3690 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3691 return S_OK;
3694 return E_NOINTERFACE;
3697 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3698 IEnumSTATSTG* iface)
3700 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3701 return InterlockedIncrement(&This->ref);
3704 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3705 IEnumSTATSTG* iface)
3707 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3709 ULONG newRef;
3711 newRef = InterlockedDecrement(&This->ref);
3713 if (newRef==0)
3715 IEnumSTATSTGImpl_Destroy(This);
3718 return newRef;
3721 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3722 IEnumSTATSTG* iface,
3723 ULONG celt,
3724 STATSTG* rgelt,
3725 ULONG* pceltFetched)
3727 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3729 DirEntry currentEntry;
3730 STATSTG* currentReturnStruct = rgelt;
3731 ULONG objectFetched = 0;
3732 DirRef currentSearchNode;
3734 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3735 return E_INVALIDARG;
3738 * To avoid the special case, get another pointer to a ULONG value if
3739 * the caller didn't supply one.
3741 if (pceltFetched==0)
3742 pceltFetched = &objectFetched;
3745 * Start the iteration, we will iterate until we hit the end of the
3746 * linked list or until we hit the number of items to iterate through
3748 *pceltFetched = 0;
3751 * Start with the node at the top of the stack.
3753 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3755 while ( ( *pceltFetched < celt) &&
3756 ( currentSearchNode!=DIRENTRY_NULL) )
3759 * Remove the top node from the stack
3761 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3764 * Read the entry from the storage.
3766 StorageImpl_ReadDirEntry(This->parentStorage,
3767 currentSearchNode,
3768 &currentEntry);
3771 * Copy the information to the return buffer.
3773 StorageUtl_CopyDirEntryToSTATSTG(&This->parentStorage->base,
3774 currentReturnStruct,
3775 &currentEntry,
3776 STATFLAG_DEFAULT);
3779 * Step to the next item in the iteration
3781 (*pceltFetched)++;
3782 currentReturnStruct++;
3785 * Push the next search node in the search stack.
3787 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
3790 * continue the iteration.
3792 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3795 if (*pceltFetched == celt)
3796 return S_OK;
3798 return S_FALSE;
3802 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3803 IEnumSTATSTG* iface,
3804 ULONG celt)
3806 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3808 DirEntry currentEntry;
3809 ULONG objectFetched = 0;
3810 DirRef currentSearchNode;
3813 * Start with the node at the top of the stack.
3815 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3817 while ( (objectFetched < celt) &&
3818 (currentSearchNode!=DIRENTRY_NULL) )
3821 * Remove the top node from the stack
3823 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3826 * Read the entry from the storage.
3828 StorageImpl_ReadDirEntry(This->parentStorage,
3829 currentSearchNode,
3830 &currentEntry);
3833 * Step to the next item in the iteration
3835 objectFetched++;
3838 * Push the next search node in the search stack.
3840 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
3843 * continue the iteration.
3845 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3848 if (objectFetched == celt)
3849 return S_OK;
3851 return S_FALSE;
3854 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3855 IEnumSTATSTG* iface)
3857 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3859 DirEntry storageEntry;
3860 BOOL readSuccessful;
3863 * Re-initialize the search stack to an empty stack
3865 This->stackSize = 0;
3868 * Read the storage entry from the top-level storage.
3870 readSuccessful = StorageImpl_ReadDirEntry(
3871 This->parentStorage,
3872 This->storageDirEntry,
3873 &storageEntry);
3875 if (readSuccessful)
3877 assert(storageEntry.sizeOfNameString!=0);
3880 * Push the search node in the search stack.
3882 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.dirRootEntry);
3885 return S_OK;
3888 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3889 IEnumSTATSTG* iface,
3890 IEnumSTATSTG** ppenum)
3892 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3894 IEnumSTATSTGImpl* newClone;
3897 * Perform a sanity check on the parameters.
3899 if (ppenum==0)
3900 return E_INVALIDARG;
3902 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3903 This->storageDirEntry);
3907 * The new clone enumeration must point to the same current node as
3908 * the ole one.
3910 newClone->stackSize = This->stackSize ;
3911 newClone->stackMaxSize = This->stackMaxSize ;
3912 newClone->stackToVisit =
3913 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3915 memcpy(
3916 newClone->stackToVisit,
3917 This->stackToVisit,
3918 sizeof(DirRef) * newClone->stackSize);
3920 *ppenum = (IEnumSTATSTG*)newClone;
3923 * Don't forget to nail down a reference to the clone before
3924 * returning it.
3926 IEnumSTATSTGImpl_AddRef(*ppenum);
3928 return S_OK;
3931 static void IEnumSTATSTGImpl_PushSearchNode(
3932 IEnumSTATSTGImpl* This,
3933 DirRef nodeToPush)
3935 DirEntry storageEntry;
3936 BOOL readSuccessful;
3939 * First, make sure we're not trying to push an unexisting node.
3941 if (nodeToPush==DIRENTRY_NULL)
3942 return;
3945 * First push the node to the stack
3947 if (This->stackSize == This->stackMaxSize)
3949 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3951 This->stackToVisit = HeapReAlloc(
3952 GetProcessHeap(),
3954 This->stackToVisit,
3955 sizeof(DirRef) * This->stackMaxSize);
3958 This->stackToVisit[This->stackSize] = nodeToPush;
3959 This->stackSize++;
3962 * Read the storage entry from the top-level storage.
3964 readSuccessful = StorageImpl_ReadDirEntry(
3965 This->parentStorage,
3966 nodeToPush,
3967 &storageEntry);
3969 if (readSuccessful)
3971 assert(storageEntry.sizeOfNameString!=0);
3974 * Push the previous search node in the search stack.
3976 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.leftChild);
3980 static DirRef IEnumSTATSTGImpl_PopSearchNode(
3981 IEnumSTATSTGImpl* This,
3982 BOOL remove)
3984 DirRef topNode;
3986 if (This->stackSize == 0)
3987 return DIRENTRY_NULL;
3989 topNode = This->stackToVisit[This->stackSize-1];
3991 if (remove)
3992 This->stackSize--;
3994 return topNode;
3998 * Virtual function table for the IEnumSTATSTGImpl class.
4000 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4002 IEnumSTATSTGImpl_QueryInterface,
4003 IEnumSTATSTGImpl_AddRef,
4004 IEnumSTATSTGImpl_Release,
4005 IEnumSTATSTGImpl_Next,
4006 IEnumSTATSTGImpl_Skip,
4007 IEnumSTATSTGImpl_Reset,
4008 IEnumSTATSTGImpl_Clone
4011 /******************************************************************************
4012 ** IEnumSTATSTGImpl implementation
4015 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4016 StorageImpl* parentStorage,
4017 DirRef storageDirEntry)
4019 IEnumSTATSTGImpl* newEnumeration;
4021 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4023 if (newEnumeration!=0)
4026 * Set-up the virtual function table and reference count.
4028 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4029 newEnumeration->ref = 0;
4032 * We want to nail-down the reference to the storage in case the
4033 * enumeration out-lives the storage in the client application.
4035 newEnumeration->parentStorage = parentStorage;
4036 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4038 newEnumeration->storageDirEntry = storageDirEntry;
4041 * Initialize the search stack
4043 newEnumeration->stackSize = 0;
4044 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4045 newEnumeration->stackToVisit =
4046 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef)*ENUMSTATSGT_SIZE_INCREMENT);
4049 * Make sure the current node of the iterator is the first one.
4051 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4054 return newEnumeration;
4058 * Virtual function table for the Storage32InternalImpl class.
4060 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4062 StorageBaseImpl_QueryInterface,
4063 StorageBaseImpl_AddRef,
4064 StorageBaseImpl_Release,
4065 StorageBaseImpl_CreateStream,
4066 StorageBaseImpl_OpenStream,
4067 StorageBaseImpl_CreateStorage,
4068 StorageBaseImpl_OpenStorage,
4069 StorageBaseImpl_CopyTo,
4070 StorageBaseImpl_MoveElementTo,
4071 StorageInternalImpl_Commit,
4072 StorageInternalImpl_Revert,
4073 StorageBaseImpl_EnumElements,
4074 StorageBaseImpl_DestroyElement,
4075 StorageBaseImpl_RenameElement,
4076 StorageBaseImpl_SetElementTimes,
4077 StorageBaseImpl_SetClass,
4078 StorageBaseImpl_SetStateBits,
4079 StorageBaseImpl_Stat
4082 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4084 StorageInternalImpl_Destroy
4087 /******************************************************************************
4088 ** Storage32InternalImpl implementation
4091 static StorageInternalImpl* StorageInternalImpl_Construct(
4092 StorageImpl* ancestorStorage,
4093 DWORD openFlags,
4094 DirRef storageDirEntry)
4096 StorageInternalImpl* newStorage;
4098 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4100 if (newStorage!=0)
4102 list_init(&newStorage->base.strmHead);
4104 list_init(&newStorage->base.storageHead);
4107 * Initialize the virtual function table.
4109 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4110 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4111 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4114 * Keep the ancestor storage pointer but do not nail a reference to it.
4116 newStorage->base.ancestorStorage = ancestorStorage;
4119 * Keep a reference to the directory entry of this storage
4121 newStorage->base.storageDirEntry = storageDirEntry;
4123 newStorage->base.create = 0;
4125 return newStorage;
4128 return 0;
4131 /******************************************************************************
4132 ** StorageUtl implementation
4135 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4137 WORD tmp;
4139 memcpy(&tmp, buffer+offset, sizeof(WORD));
4140 *value = lendian16toh(tmp);
4143 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4145 value = htole16(value);
4146 memcpy(buffer+offset, &value, sizeof(WORD));
4149 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4151 DWORD tmp;
4153 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4154 *value = lendian32toh(tmp);
4157 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4159 value = htole32(value);
4160 memcpy(buffer+offset, &value, sizeof(DWORD));
4163 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4164 ULARGE_INTEGER* value)
4166 #ifdef WORDS_BIGENDIAN
4167 ULARGE_INTEGER tmp;
4169 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4170 value->u.LowPart = htole32(tmp.u.HighPart);
4171 value->u.HighPart = htole32(tmp.u.LowPart);
4172 #else
4173 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4174 #endif
4177 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4178 const ULARGE_INTEGER *value)
4180 #ifdef WORDS_BIGENDIAN
4181 ULARGE_INTEGER tmp;
4183 tmp.u.LowPart = htole32(value->u.HighPart);
4184 tmp.u.HighPart = htole32(value->u.LowPart);
4185 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4186 #else
4187 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4188 #endif
4191 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4193 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4194 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4195 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4197 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4200 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4202 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4203 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4204 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4206 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4209 void StorageUtl_CopyDirEntryToSTATSTG(
4210 StorageBaseImpl* storage,
4211 STATSTG* destination,
4212 const DirEntry* source,
4213 int statFlags)
4215 LPCWSTR entryName;
4217 if (source->stgType == STGTY_ROOT)
4219 /* replace the name of root entry (often "Root Entry") by the file name */
4220 entryName = storage->filename;
4222 else
4224 entryName = source->name;
4228 * The copy of the string occurs only when the flag is not set
4230 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4231 (entryName == NULL) ||
4232 (entryName[0] == 0) )
4234 destination->pwcsName = 0;
4236 else
4238 destination->pwcsName =
4239 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4241 strcpyW(destination->pwcsName, entryName);
4244 switch (source->stgType)
4246 case STGTY_STORAGE:
4247 case STGTY_ROOT:
4248 destination->type = STGTY_STORAGE;
4249 break;
4250 case STGTY_STREAM:
4251 destination->type = STGTY_STREAM;
4252 break;
4253 default:
4254 destination->type = STGTY_STREAM;
4255 break;
4258 destination->cbSize = source->size;
4260 currentReturnStruct->mtime = {0}; TODO
4261 currentReturnStruct->ctime = {0};
4262 currentReturnStruct->atime = {0};
4264 destination->grfMode = 0;
4265 destination->grfLocksSupported = 0;
4266 destination->clsid = source->clsid;
4267 destination->grfStateBits = 0;
4268 destination->reserved = 0;
4271 /******************************************************************************
4272 ** BlockChainStream implementation
4275 BlockChainStream* BlockChainStream_Construct(
4276 StorageImpl* parentStorage,
4277 ULONG* headOfStreamPlaceHolder,
4278 DirRef dirEntry)
4280 BlockChainStream* newStream;
4281 ULONG blockIndex;
4283 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4285 newStream->parentStorage = parentStorage;
4286 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4287 newStream->ownerDirEntry = dirEntry;
4288 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4289 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4290 newStream->numBlocks = 0;
4292 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4294 while (blockIndex != BLOCK_END_OF_CHAIN)
4296 newStream->numBlocks++;
4297 newStream->tailIndex = blockIndex;
4299 if(FAILED(StorageImpl_GetNextBlockInChain(
4300 parentStorage,
4301 blockIndex,
4302 &blockIndex)))
4304 HeapFree(GetProcessHeap(), 0, newStream);
4305 return NULL;
4309 return newStream;
4312 void BlockChainStream_Destroy(BlockChainStream* This)
4314 HeapFree(GetProcessHeap(), 0, This);
4317 /******************************************************************************
4318 * BlockChainStream_GetHeadOfChain
4320 * Returns the head of this stream chain.
4321 * Some special chains don't have directory entries, their heads are kept in
4322 * This->headOfStreamPlaceHolder.
4325 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4327 DirEntry chainEntry;
4328 BOOL readSuccessful;
4330 if (This->headOfStreamPlaceHolder != 0)
4331 return *(This->headOfStreamPlaceHolder);
4333 if (This->ownerDirEntry != DIRENTRY_NULL)
4335 readSuccessful = StorageImpl_ReadDirEntry(
4336 This->parentStorage,
4337 This->ownerDirEntry,
4338 &chainEntry);
4340 if (readSuccessful)
4342 return chainEntry.startingBlock;
4346 return BLOCK_END_OF_CHAIN;
4349 /******************************************************************************
4350 * BlockChainStream_GetCount
4352 * Returns the number of blocks that comprises this chain.
4353 * This is not the size of the stream as the last block may not be full!
4356 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4358 ULONG blockIndex;
4359 ULONG count = 0;
4361 blockIndex = BlockChainStream_GetHeadOfChain(This);
4363 while (blockIndex != BLOCK_END_OF_CHAIN)
4365 count++;
4367 if(FAILED(StorageImpl_GetNextBlockInChain(
4368 This->parentStorage,
4369 blockIndex,
4370 &blockIndex)))
4371 return 0;
4374 return count;
4377 /******************************************************************************
4378 * BlockChainStream_ReadAt
4380 * Reads a specified number of bytes from this chain at the specified offset.
4381 * bytesRead may be NULL.
4382 * Failure will be returned if the specified number of bytes has not been read.
4384 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4385 ULARGE_INTEGER offset,
4386 ULONG size,
4387 void* buffer,
4388 ULONG* bytesRead)
4390 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4391 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4392 ULONG bytesToReadInBuffer;
4393 ULONG blockIndex;
4394 BYTE* bufferWalker;
4396 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4399 * Find the first block in the stream that contains part of the buffer.
4401 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4402 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4403 (blockNoInSequence < This->lastBlockNoInSequence) )
4405 blockIndex = BlockChainStream_GetHeadOfChain(This);
4406 This->lastBlockNoInSequence = blockNoInSequence;
4408 else
4410 ULONG temp = blockNoInSequence;
4412 blockIndex = This->lastBlockNoInSequenceIndex;
4413 blockNoInSequence -= This->lastBlockNoInSequence;
4414 This->lastBlockNoInSequence = temp;
4417 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4419 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4420 return STG_E_DOCFILECORRUPT;
4421 blockNoInSequence--;
4424 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4425 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4427 This->lastBlockNoInSequenceIndex = blockIndex;
4430 * Start reading the buffer.
4432 *bytesRead = 0;
4433 bufferWalker = buffer;
4435 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4437 ULARGE_INTEGER ulOffset;
4438 DWORD bytesReadAt;
4440 * Calculate how many bytes we can copy from this big block.
4442 bytesToReadInBuffer =
4443 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4445 TRACE("block %i\n",blockIndex);
4446 ulOffset.u.HighPart = 0;
4447 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4448 offsetInBlock;
4450 StorageImpl_ReadAt(This->parentStorage,
4451 ulOffset,
4452 bufferWalker,
4453 bytesToReadInBuffer,
4454 &bytesReadAt);
4456 * Step to the next big block.
4458 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4459 return STG_E_DOCFILECORRUPT;
4461 bufferWalker += bytesReadAt;
4462 size -= bytesReadAt;
4463 *bytesRead += bytesReadAt;
4464 offsetInBlock = 0; /* There is no offset on the next block */
4466 if (bytesToReadInBuffer != bytesReadAt)
4467 break;
4470 return (size == 0) ? S_OK : STG_E_READFAULT;
4473 /******************************************************************************
4474 * BlockChainStream_WriteAt
4476 * Writes the specified number of bytes to this chain at the specified offset.
4477 * Will fail if not all specified number of bytes have been written.
4479 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4480 ULARGE_INTEGER offset,
4481 ULONG size,
4482 const void* buffer,
4483 ULONG* bytesWritten)
4485 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4486 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4487 ULONG bytesToWrite;
4488 ULONG blockIndex;
4489 const BYTE* bufferWalker;
4492 * Find the first block in the stream that contains part of the buffer.
4494 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4495 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4496 (blockNoInSequence < This->lastBlockNoInSequence) )
4498 blockIndex = BlockChainStream_GetHeadOfChain(This);
4499 This->lastBlockNoInSequence = blockNoInSequence;
4501 else
4503 ULONG temp = blockNoInSequence;
4505 blockIndex = This->lastBlockNoInSequenceIndex;
4506 blockNoInSequence -= This->lastBlockNoInSequence;
4507 This->lastBlockNoInSequence = temp;
4510 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4512 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4513 &blockIndex)))
4514 return STG_E_DOCFILECORRUPT;
4515 blockNoInSequence--;
4518 This->lastBlockNoInSequenceIndex = blockIndex;
4520 /* BlockChainStream_SetSize should have already been called to ensure we have
4521 * enough blocks in the chain to write into */
4522 if (blockIndex == BLOCK_END_OF_CHAIN)
4524 ERR("not enough blocks in chain to write data\n");
4525 return STG_E_DOCFILECORRUPT;
4528 *bytesWritten = 0;
4529 bufferWalker = buffer;
4531 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4533 ULARGE_INTEGER ulOffset;
4534 DWORD bytesWrittenAt;
4536 * Calculate how many bytes we can copy from this big block.
4538 bytesToWrite =
4539 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4541 TRACE("block %i\n",blockIndex);
4542 ulOffset.u.HighPart = 0;
4543 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4544 offsetInBlock;
4546 StorageImpl_WriteAt(This->parentStorage,
4547 ulOffset,
4548 bufferWalker,
4549 bytesToWrite,
4550 &bytesWrittenAt);
4553 * Step to the next big block.
4555 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4556 &blockIndex)))
4557 return STG_E_DOCFILECORRUPT;
4559 bufferWalker += bytesWrittenAt;
4560 size -= bytesWrittenAt;
4561 *bytesWritten += bytesWrittenAt;
4562 offsetInBlock = 0; /* There is no offset on the next block */
4564 if (bytesWrittenAt != bytesToWrite)
4565 break;
4568 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4571 /******************************************************************************
4572 * BlockChainStream_Shrink
4574 * Shrinks this chain in the big block depot.
4576 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4577 ULARGE_INTEGER newSize)
4579 ULONG blockIndex, extraBlock;
4580 ULONG numBlocks;
4581 ULONG count = 1;
4584 * Reset the last accessed block cache.
4586 This->lastBlockNoInSequence = 0xFFFFFFFF;
4587 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4590 * Figure out how many blocks are needed to contain the new size
4592 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4594 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4595 numBlocks++;
4597 blockIndex = BlockChainStream_GetHeadOfChain(This);
4600 * Go to the new end of chain
4602 while (count < numBlocks)
4604 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4605 &blockIndex)))
4606 return FALSE;
4607 count++;
4610 /* Get the next block before marking the new end */
4611 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4612 &extraBlock)))
4613 return FALSE;
4615 /* Mark the new end of chain */
4616 StorageImpl_SetNextBlockInChain(
4617 This->parentStorage,
4618 blockIndex,
4619 BLOCK_END_OF_CHAIN);
4621 This->tailIndex = blockIndex;
4622 This->numBlocks = numBlocks;
4625 * Mark the extra blocks as free
4627 while (extraBlock != BLOCK_END_OF_CHAIN)
4629 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4630 &blockIndex)))
4631 return FALSE;
4632 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4633 extraBlock = blockIndex;
4636 return TRUE;
4639 /******************************************************************************
4640 * BlockChainStream_Enlarge
4642 * Grows this chain in the big block depot.
4644 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4645 ULARGE_INTEGER newSize)
4647 ULONG blockIndex, currentBlock;
4648 ULONG newNumBlocks;
4649 ULONG oldNumBlocks = 0;
4651 blockIndex = BlockChainStream_GetHeadOfChain(This);
4654 * Empty chain. Create the head.
4656 if (blockIndex == BLOCK_END_OF_CHAIN)
4658 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4659 StorageImpl_SetNextBlockInChain(This->parentStorage,
4660 blockIndex,
4661 BLOCK_END_OF_CHAIN);
4663 if (This->headOfStreamPlaceHolder != 0)
4665 *(This->headOfStreamPlaceHolder) = blockIndex;
4667 else
4669 DirEntry chainEntry;
4670 assert(This->ownerDirEntry != DIRENTRY_NULL);
4672 StorageImpl_ReadDirEntry(
4673 This->parentStorage,
4674 This->ownerDirEntry,
4675 &chainEntry);
4677 chainEntry.startingBlock = blockIndex;
4679 StorageImpl_WriteDirEntry(
4680 This->parentStorage,
4681 This->ownerDirEntry,
4682 &chainEntry);
4685 This->tailIndex = blockIndex;
4686 This->numBlocks = 1;
4690 * Figure out how many blocks are needed to contain this stream
4692 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4694 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4695 newNumBlocks++;
4698 * Go to the current end of chain
4700 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4702 currentBlock = blockIndex;
4704 while (blockIndex != BLOCK_END_OF_CHAIN)
4706 This->numBlocks++;
4707 currentBlock = blockIndex;
4709 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4710 &blockIndex)))
4711 return FALSE;
4714 This->tailIndex = currentBlock;
4717 currentBlock = This->tailIndex;
4718 oldNumBlocks = This->numBlocks;
4721 * Add new blocks to the chain
4723 if (oldNumBlocks < newNumBlocks)
4725 while (oldNumBlocks < newNumBlocks)
4727 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4729 StorageImpl_SetNextBlockInChain(
4730 This->parentStorage,
4731 currentBlock,
4732 blockIndex);
4734 StorageImpl_SetNextBlockInChain(
4735 This->parentStorage,
4736 blockIndex,
4737 BLOCK_END_OF_CHAIN);
4739 currentBlock = blockIndex;
4740 oldNumBlocks++;
4743 This->tailIndex = blockIndex;
4744 This->numBlocks = newNumBlocks;
4747 return TRUE;
4750 /******************************************************************************
4751 * BlockChainStream_SetSize
4753 * Sets the size of this stream. The big block depot will be updated.
4754 * The file will grow if we grow the chain.
4756 * TODO: Free the actual blocks in the file when we shrink the chain.
4757 * Currently, the blocks are still in the file. So the file size
4758 * doesn't shrink even if we shrink streams.
4760 BOOL BlockChainStream_SetSize(
4761 BlockChainStream* This,
4762 ULARGE_INTEGER newSize)
4764 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4766 if (newSize.u.LowPart == size.u.LowPart)
4767 return TRUE;
4769 if (newSize.u.LowPart < size.u.LowPart)
4771 BlockChainStream_Shrink(This, newSize);
4773 else
4775 BlockChainStream_Enlarge(This, newSize);
4778 return TRUE;
4781 /******************************************************************************
4782 * BlockChainStream_GetSize
4784 * Returns the size of this chain.
4785 * Will return the block count if this chain doesn't have a directory entry.
4787 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4789 DirEntry chainEntry;
4791 if(This->headOfStreamPlaceHolder == NULL)
4794 * This chain has a directory entry so use the size value from there.
4796 StorageImpl_ReadDirEntry(
4797 This->parentStorage,
4798 This->ownerDirEntry,
4799 &chainEntry);
4801 return chainEntry.size;
4803 else
4806 * this chain is a chain that does not have a directory entry, figure out the
4807 * size by making the product number of used blocks times the
4808 * size of them
4810 ULARGE_INTEGER result;
4811 result.u.HighPart = 0;
4813 result.u.LowPart =
4814 BlockChainStream_GetCount(This) *
4815 This->parentStorage->bigBlockSize;
4817 return result;
4821 /******************************************************************************
4822 ** SmallBlockChainStream implementation
4825 SmallBlockChainStream* SmallBlockChainStream_Construct(
4826 StorageImpl* parentStorage,
4827 ULONG* headOfStreamPlaceHolder,
4828 DirRef dirEntry)
4830 SmallBlockChainStream* newStream;
4832 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4834 newStream->parentStorage = parentStorage;
4835 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4836 newStream->ownerDirEntry = dirEntry;
4838 return newStream;
4841 void SmallBlockChainStream_Destroy(
4842 SmallBlockChainStream* This)
4844 HeapFree(GetProcessHeap(), 0, This);
4847 /******************************************************************************
4848 * SmallBlockChainStream_GetHeadOfChain
4850 * Returns the head of this chain of small blocks.
4852 static ULONG SmallBlockChainStream_GetHeadOfChain(
4853 SmallBlockChainStream* This)
4855 DirEntry chainEntry;
4856 BOOL readSuccessful;
4858 if (This->headOfStreamPlaceHolder != NULL)
4859 return *(This->headOfStreamPlaceHolder);
4861 if (This->ownerDirEntry)
4863 readSuccessful = StorageImpl_ReadDirEntry(
4864 This->parentStorage,
4865 This->ownerDirEntry,
4866 &chainEntry);
4868 if (readSuccessful)
4870 return chainEntry.startingBlock;
4875 return BLOCK_END_OF_CHAIN;
4878 /******************************************************************************
4879 * SmallBlockChainStream_GetNextBlockInChain
4881 * Returns the index of the next small block in this chain.
4883 * Return Values:
4884 * - BLOCK_END_OF_CHAIN: end of this chain
4885 * - BLOCK_UNUSED: small block 'blockIndex' is free
4887 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4888 SmallBlockChainStream* This,
4889 ULONG blockIndex,
4890 ULONG* nextBlockInChain)
4892 ULARGE_INTEGER offsetOfBlockInDepot;
4893 DWORD buffer;
4894 ULONG bytesRead;
4895 HRESULT res;
4897 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4899 offsetOfBlockInDepot.u.HighPart = 0;
4900 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4903 * Read those bytes in the buffer from the small block file.
4905 res = BlockChainStream_ReadAt(
4906 This->parentStorage->smallBlockDepotChain,
4907 offsetOfBlockInDepot,
4908 sizeof(DWORD),
4909 &buffer,
4910 &bytesRead);
4912 if (SUCCEEDED(res))
4914 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4915 return S_OK;
4918 return res;
4921 /******************************************************************************
4922 * SmallBlockChainStream_SetNextBlockInChain
4924 * Writes the index of the next block of the specified block in the small
4925 * block depot.
4926 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4927 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4929 static void SmallBlockChainStream_SetNextBlockInChain(
4930 SmallBlockChainStream* This,
4931 ULONG blockIndex,
4932 ULONG nextBlock)
4934 ULARGE_INTEGER offsetOfBlockInDepot;
4935 DWORD buffer;
4936 ULONG bytesWritten;
4938 offsetOfBlockInDepot.u.HighPart = 0;
4939 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4941 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4944 * Read those bytes in the buffer from the small block file.
4946 BlockChainStream_WriteAt(
4947 This->parentStorage->smallBlockDepotChain,
4948 offsetOfBlockInDepot,
4949 sizeof(DWORD),
4950 &buffer,
4951 &bytesWritten);
4954 /******************************************************************************
4955 * SmallBlockChainStream_FreeBlock
4957 * Flag small block 'blockIndex' as free in the small block depot.
4959 static void SmallBlockChainStream_FreeBlock(
4960 SmallBlockChainStream* This,
4961 ULONG blockIndex)
4963 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4966 /******************************************************************************
4967 * SmallBlockChainStream_GetNextFreeBlock
4969 * Returns the index of a free small block. The small block depot will be
4970 * enlarged if necessary. The small block chain will also be enlarged if
4971 * necessary.
4973 static ULONG SmallBlockChainStream_GetNextFreeBlock(
4974 SmallBlockChainStream* This)
4976 ULARGE_INTEGER offsetOfBlockInDepot;
4977 DWORD buffer;
4978 ULONG bytesRead;
4979 ULONG blockIndex = 0;
4980 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4981 HRESULT res = S_OK;
4982 ULONG smallBlocksPerBigBlock;
4984 offsetOfBlockInDepot.u.HighPart = 0;
4987 * Scan the small block depot for a free block
4989 while (nextBlockIndex != BLOCK_UNUSED)
4991 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4993 res = BlockChainStream_ReadAt(
4994 This->parentStorage->smallBlockDepotChain,
4995 offsetOfBlockInDepot,
4996 sizeof(DWORD),
4997 &buffer,
4998 &bytesRead);
5001 * If we run out of space for the small block depot, enlarge it
5003 if (SUCCEEDED(res))
5005 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5007 if (nextBlockIndex != BLOCK_UNUSED)
5008 blockIndex++;
5010 else
5012 ULONG count =
5013 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5015 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5016 ULONG nextBlock, newsbdIndex;
5017 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5019 nextBlock = sbdIndex;
5020 while (nextBlock != BLOCK_END_OF_CHAIN)
5022 sbdIndex = nextBlock;
5023 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5026 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5027 if (sbdIndex != BLOCK_END_OF_CHAIN)
5028 StorageImpl_SetNextBlockInChain(
5029 This->parentStorage,
5030 sbdIndex,
5031 newsbdIndex);
5033 StorageImpl_SetNextBlockInChain(
5034 This->parentStorage,
5035 newsbdIndex,
5036 BLOCK_END_OF_CHAIN);
5039 * Initialize all the small blocks to free
5041 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5042 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5044 if (count == 0)
5047 * We have just created the small block depot.
5049 DirEntry rootEntry;
5050 ULONG sbStartIndex;
5053 * Save it in the header
5055 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5056 StorageImpl_SaveFileHeader(This->parentStorage);
5059 * And allocate the first big block that will contain small blocks
5061 sbStartIndex =
5062 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5064 StorageImpl_SetNextBlockInChain(
5065 This->parentStorage,
5066 sbStartIndex,
5067 BLOCK_END_OF_CHAIN);
5069 StorageImpl_ReadDirEntry(
5070 This->parentStorage,
5071 This->parentStorage->base.storageDirEntry,
5072 &rootEntry);
5074 rootEntry.startingBlock = sbStartIndex;
5075 rootEntry.size.u.HighPart = 0;
5076 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5078 StorageImpl_WriteDirEntry(
5079 This->parentStorage,
5080 This->parentStorage->base.storageDirEntry,
5081 &rootEntry);
5083 else
5084 StorageImpl_SaveFileHeader(This->parentStorage);
5088 smallBlocksPerBigBlock =
5089 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5092 * Verify if we have to allocate big blocks to contain small blocks
5094 if (blockIndex % smallBlocksPerBigBlock == 0)
5096 DirEntry rootEntry;
5097 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5099 StorageImpl_ReadDirEntry(
5100 This->parentStorage,
5101 This->parentStorage->base.storageDirEntry,
5102 &rootEntry);
5104 if (rootEntry.size.u.LowPart <
5105 (blocksRequired * This->parentStorage->bigBlockSize))
5107 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5109 BlockChainStream_SetSize(
5110 This->parentStorage->smallBlockRootChain,
5111 rootEntry.size);
5113 StorageImpl_WriteDirEntry(
5114 This->parentStorage,
5115 This->parentStorage->base.storageDirEntry,
5116 &rootEntry);
5120 return blockIndex;
5123 /******************************************************************************
5124 * SmallBlockChainStream_ReadAt
5126 * Reads a specified number of bytes from this chain at the specified offset.
5127 * bytesRead may be NULL.
5128 * Failure will be returned if the specified number of bytes has not been read.
5130 HRESULT SmallBlockChainStream_ReadAt(
5131 SmallBlockChainStream* This,
5132 ULARGE_INTEGER offset,
5133 ULONG size,
5134 void* buffer,
5135 ULONG* bytesRead)
5137 HRESULT rc = S_OK;
5138 ULARGE_INTEGER offsetInBigBlockFile;
5139 ULONG blockNoInSequence =
5140 offset.u.LowPart / This->parentStorage->smallBlockSize;
5142 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5143 ULONG bytesToReadInBuffer;
5144 ULONG blockIndex;
5145 ULONG bytesReadFromBigBlockFile;
5146 BYTE* bufferWalker;
5149 * This should never happen on a small block file.
5151 assert(offset.u.HighPart==0);
5154 * Find the first block in the stream that contains part of the buffer.
5156 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5158 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5160 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5161 if(FAILED(rc))
5162 return rc;
5163 blockNoInSequence--;
5167 * Start reading the buffer.
5169 *bytesRead = 0;
5170 bufferWalker = buffer;
5172 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5175 * Calculate how many bytes we can copy from this small block.
5177 bytesToReadInBuffer =
5178 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5181 * Calculate the offset of the small block in the small block file.
5183 offsetInBigBlockFile.u.HighPart = 0;
5184 offsetInBigBlockFile.u.LowPart =
5185 blockIndex * This->parentStorage->smallBlockSize;
5187 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5190 * Read those bytes in the buffer from the small block file.
5191 * The small block has already been identified so it shouldn't fail
5192 * unless the file is corrupt.
5194 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5195 offsetInBigBlockFile,
5196 bytesToReadInBuffer,
5197 bufferWalker,
5198 &bytesReadFromBigBlockFile);
5200 if (FAILED(rc))
5201 return rc;
5204 * Step to the next big block.
5206 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5207 if(FAILED(rc))
5208 return STG_E_DOCFILECORRUPT;
5210 bufferWalker += bytesReadFromBigBlockFile;
5211 size -= bytesReadFromBigBlockFile;
5212 *bytesRead += bytesReadFromBigBlockFile;
5213 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5216 return (size == 0) ? S_OK : STG_E_READFAULT;
5219 /******************************************************************************
5220 * SmallBlockChainStream_WriteAt
5222 * Writes the specified number of bytes to this chain at the specified offset.
5223 * Will fail if not all specified number of bytes have been written.
5225 HRESULT SmallBlockChainStream_WriteAt(
5226 SmallBlockChainStream* This,
5227 ULARGE_INTEGER offset,
5228 ULONG size,
5229 const void* buffer,
5230 ULONG* bytesWritten)
5232 ULARGE_INTEGER offsetInBigBlockFile;
5233 ULONG blockNoInSequence =
5234 offset.u.LowPart / This->parentStorage->smallBlockSize;
5236 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5237 ULONG bytesToWriteInBuffer;
5238 ULONG blockIndex;
5239 ULONG bytesWrittenToBigBlockFile;
5240 const BYTE* bufferWalker;
5241 HRESULT res;
5244 * This should never happen on a small block file.
5246 assert(offset.u.HighPart==0);
5249 * Find the first block in the stream that contains part of the buffer.
5251 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5253 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5255 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5256 return STG_E_DOCFILECORRUPT;
5257 blockNoInSequence--;
5261 * Start writing the buffer.
5263 *bytesWritten = 0;
5264 bufferWalker = buffer;
5265 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5268 * Calculate how many bytes we can copy to this small block.
5270 bytesToWriteInBuffer =
5271 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5274 * Calculate the offset of the small block in the small block file.
5276 offsetInBigBlockFile.u.HighPart = 0;
5277 offsetInBigBlockFile.u.LowPart =
5278 blockIndex * This->parentStorage->smallBlockSize;
5280 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5283 * Write those bytes in the buffer to the small block file.
5285 res = BlockChainStream_WriteAt(
5286 This->parentStorage->smallBlockRootChain,
5287 offsetInBigBlockFile,
5288 bytesToWriteInBuffer,
5289 bufferWalker,
5290 &bytesWrittenToBigBlockFile);
5291 if (FAILED(res))
5292 return res;
5295 * Step to the next big block.
5297 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5298 &blockIndex)))
5299 return FALSE;
5300 bufferWalker += bytesWrittenToBigBlockFile;
5301 size -= bytesWrittenToBigBlockFile;
5302 *bytesWritten += bytesWrittenToBigBlockFile;
5303 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5306 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5309 /******************************************************************************
5310 * SmallBlockChainStream_Shrink
5312 * Shrinks this chain in the small block depot.
5314 static BOOL SmallBlockChainStream_Shrink(
5315 SmallBlockChainStream* This,
5316 ULARGE_INTEGER newSize)
5318 ULONG blockIndex, extraBlock;
5319 ULONG numBlocks;
5320 ULONG count = 0;
5322 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5324 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5325 numBlocks++;
5327 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5330 * Go to the new end of chain
5332 while (count < numBlocks)
5334 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5335 &blockIndex)))
5336 return FALSE;
5337 count++;
5341 * If the count is 0, we have a special case, the head of the chain was
5342 * just freed.
5344 if (count == 0)
5346 DirEntry chainEntry;
5348 StorageImpl_ReadDirEntry(This->parentStorage,
5349 This->ownerDirEntry,
5350 &chainEntry);
5352 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
5354 StorageImpl_WriteDirEntry(This->parentStorage,
5355 This->ownerDirEntry,
5356 &chainEntry);
5359 * We start freeing the chain at the head block.
5361 extraBlock = blockIndex;
5363 else
5365 /* Get the next block before marking the new end */
5366 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5367 &extraBlock)))
5368 return FALSE;
5370 /* Mark the new end of chain */
5371 SmallBlockChainStream_SetNextBlockInChain(
5372 This,
5373 blockIndex,
5374 BLOCK_END_OF_CHAIN);
5378 * Mark the extra blocks as free
5380 while (extraBlock != BLOCK_END_OF_CHAIN)
5382 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5383 &blockIndex)))
5384 return FALSE;
5385 SmallBlockChainStream_FreeBlock(This, extraBlock);
5386 extraBlock = blockIndex;
5389 return TRUE;
5392 /******************************************************************************
5393 * SmallBlockChainStream_Enlarge
5395 * Grows this chain in the small block depot.
5397 static BOOL SmallBlockChainStream_Enlarge(
5398 SmallBlockChainStream* This,
5399 ULARGE_INTEGER newSize)
5401 ULONG blockIndex, currentBlock;
5402 ULONG newNumBlocks;
5403 ULONG oldNumBlocks = 0;
5405 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5408 * Empty chain. Create the head.
5410 if (blockIndex == BLOCK_END_OF_CHAIN)
5412 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5413 SmallBlockChainStream_SetNextBlockInChain(
5414 This,
5415 blockIndex,
5416 BLOCK_END_OF_CHAIN);
5418 if (This->headOfStreamPlaceHolder != NULL)
5420 *(This->headOfStreamPlaceHolder) = blockIndex;
5422 else
5424 DirEntry chainEntry;
5426 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
5427 &chainEntry);
5429 chainEntry.startingBlock = blockIndex;
5431 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
5432 &chainEntry);
5436 currentBlock = blockIndex;
5439 * Figure out how many blocks are needed to contain this stream
5441 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5443 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5444 newNumBlocks++;
5447 * Go to the current end of chain
5449 while (blockIndex != BLOCK_END_OF_CHAIN)
5451 oldNumBlocks++;
5452 currentBlock = blockIndex;
5453 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5454 return FALSE;
5458 * Add new blocks to the chain
5460 while (oldNumBlocks < newNumBlocks)
5462 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5463 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5465 SmallBlockChainStream_SetNextBlockInChain(
5466 This,
5467 blockIndex,
5468 BLOCK_END_OF_CHAIN);
5470 currentBlock = blockIndex;
5471 oldNumBlocks++;
5474 return TRUE;
5477 /******************************************************************************
5478 * SmallBlockChainStream_SetSize
5480 * Sets the size of this stream.
5481 * The file will grow if we grow the chain.
5483 * TODO: Free the actual blocks in the file when we shrink the chain.
5484 * Currently, the blocks are still in the file. So the file size
5485 * doesn't shrink even if we shrink streams.
5487 BOOL SmallBlockChainStream_SetSize(
5488 SmallBlockChainStream* This,
5489 ULARGE_INTEGER newSize)
5491 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5493 if (newSize.u.LowPart == size.u.LowPart)
5494 return TRUE;
5496 if (newSize.u.LowPart < size.u.LowPart)
5498 SmallBlockChainStream_Shrink(This, newSize);
5500 else
5502 SmallBlockChainStream_Enlarge(This, newSize);
5505 return TRUE;
5508 /******************************************************************************
5509 * SmallBlockChainStream_GetCount
5511 * Returns the number of small blocks that comprises this chain.
5512 * This is not the size of the stream as the last block may not be full!
5515 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5517 ULONG blockIndex;
5518 ULONG count = 0;
5520 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5522 while(blockIndex != BLOCK_END_OF_CHAIN)
5524 count++;
5526 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5527 blockIndex, &blockIndex)))
5528 return 0;
5531 return count;
5534 /******************************************************************************
5535 * SmallBlockChainStream_GetSize
5537 * Returns the size of this chain.
5539 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5541 DirEntry chainEntry;
5543 if(This->headOfStreamPlaceHolder != NULL)
5545 ULARGE_INTEGER result;
5546 result.u.HighPart = 0;
5548 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5549 This->parentStorage->smallBlockSize;
5551 return result;
5554 StorageImpl_ReadDirEntry(
5555 This->parentStorage,
5556 This->ownerDirEntry,
5557 &chainEntry);
5559 return chainEntry.size;
5562 /******************************************************************************
5563 * StgCreateDocfile [OLE32.@]
5564 * Creates a new compound file storage object
5566 * PARAMS
5567 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5568 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5569 * reserved [ ?] unused?, usually 0
5570 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5572 * RETURNS
5573 * S_OK if the file was successfully created
5574 * some STG_E_ value if error
5575 * NOTES
5576 * if pwcsName is NULL, create file with new unique name
5577 * the function can returns
5578 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5579 * (unrealized now)
5581 HRESULT WINAPI StgCreateDocfile(
5582 LPCOLESTR pwcsName,
5583 DWORD grfMode,
5584 DWORD reserved,
5585 IStorage **ppstgOpen)
5587 StorageImpl* newStorage = 0;
5588 HANDLE hFile = INVALID_HANDLE_VALUE;
5589 HRESULT hr = STG_E_INVALIDFLAG;
5590 DWORD shareMode;
5591 DWORD accessMode;
5592 DWORD creationMode;
5593 DWORD fileAttributes;
5594 WCHAR tempFileName[MAX_PATH];
5596 TRACE("(%s, %x, %d, %p)\n",
5597 debugstr_w(pwcsName), grfMode,
5598 reserved, ppstgOpen);
5600 if (ppstgOpen == 0)
5601 return STG_E_INVALIDPOINTER;
5602 if (reserved != 0)
5603 return STG_E_INVALIDPARAMETER;
5605 /* if no share mode given then DENY_NONE is the default */
5606 if (STGM_SHARE_MODE(grfMode) == 0)
5607 grfMode |= STGM_SHARE_DENY_NONE;
5609 if ( FAILED( validateSTGM(grfMode) ))
5610 goto end;
5612 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5613 switch(STGM_ACCESS_MODE(grfMode))
5615 case STGM_WRITE:
5616 case STGM_READWRITE:
5617 break;
5618 default:
5619 goto end;
5622 /* in direct mode, can only use SHARE_EXCLUSIVE */
5623 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5624 goto end;
5626 /* but in transacted mode, any share mode is valid */
5629 * Generate a unique name.
5631 if (pwcsName == 0)
5633 WCHAR tempPath[MAX_PATH];
5634 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5636 memset(tempPath, 0, sizeof(tempPath));
5637 memset(tempFileName, 0, sizeof(tempFileName));
5639 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5640 tempPath[0] = '.';
5642 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5643 pwcsName = tempFileName;
5644 else
5646 hr = STG_E_INSUFFICIENTMEMORY;
5647 goto end;
5650 creationMode = TRUNCATE_EXISTING;
5652 else
5654 creationMode = GetCreationModeFromSTGM(grfMode);
5658 * Interpret the STGM value grfMode
5660 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5661 accessMode = GetAccessModeFromSTGM(grfMode);
5663 if (grfMode & STGM_DELETEONRELEASE)
5664 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5665 else
5666 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5668 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5669 FIXME("Storage share mode not implemented.\n");
5671 if (grfMode & STGM_TRANSACTED)
5672 FIXME("Transacted mode not implemented.\n");
5674 *ppstgOpen = 0;
5676 hFile = CreateFileW(pwcsName,
5677 accessMode,
5678 shareMode,
5679 NULL,
5680 creationMode,
5681 fileAttributes,
5684 if (hFile == INVALID_HANDLE_VALUE)
5686 if(GetLastError() == ERROR_FILE_EXISTS)
5687 hr = STG_E_FILEALREADYEXISTS;
5688 else
5689 hr = E_FAIL;
5690 goto end;
5694 * Allocate and initialize the new IStorage32object.
5696 hr = StorageImpl_Construct(
5697 hFile,
5698 pwcsName,
5699 NULL,
5700 grfMode,
5701 TRUE,
5702 TRUE,
5703 &newStorage);
5705 if (FAILED(hr))
5707 goto end;
5711 * Get an "out" pointer for the caller.
5713 *ppstgOpen = (IStorage*)newStorage;
5715 end:
5716 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5718 return hr;
5721 /******************************************************************************
5722 * StgCreateStorageEx [OLE32.@]
5724 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5726 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5727 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5729 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5731 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5732 return STG_E_INVALIDPARAMETER;
5735 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5737 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5738 return STG_E_INVALIDPARAMETER;
5741 if (stgfmt == STGFMT_FILE)
5743 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5744 return STG_E_INVALIDPARAMETER;
5747 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5749 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5750 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5753 ERR("Invalid stgfmt argument\n");
5754 return STG_E_INVALIDPARAMETER;
5757 /******************************************************************************
5758 * StgCreatePropSetStg [OLE32.@]
5760 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5761 IPropertySetStorage **ppPropSetStg)
5763 HRESULT hr;
5765 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5766 if (reserved)
5767 hr = STG_E_INVALIDPARAMETER;
5768 else
5769 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5770 (void**)ppPropSetStg);
5771 return hr;
5774 /******************************************************************************
5775 * StgOpenStorageEx [OLE32.@]
5777 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5779 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5780 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5782 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5784 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5785 return STG_E_INVALIDPARAMETER;
5788 switch (stgfmt)
5790 case STGFMT_FILE:
5791 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5792 return STG_E_INVALIDPARAMETER;
5794 case STGFMT_STORAGE:
5795 break;
5797 case STGFMT_DOCFILE:
5798 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5800 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5801 return STG_E_INVALIDPARAMETER;
5803 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5804 break;
5806 case STGFMT_ANY:
5807 WARN("STGFMT_ANY assuming storage\n");
5808 break;
5810 default:
5811 return STG_E_INVALIDPARAMETER;
5814 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5818 /******************************************************************************
5819 * StgOpenStorage [OLE32.@]
5821 HRESULT WINAPI StgOpenStorage(
5822 const OLECHAR *pwcsName,
5823 IStorage *pstgPriority,
5824 DWORD grfMode,
5825 SNB snbExclude,
5826 DWORD reserved,
5827 IStorage **ppstgOpen)
5829 StorageImpl* newStorage = 0;
5830 HRESULT hr = S_OK;
5831 HANDLE hFile = 0;
5832 DWORD shareMode;
5833 DWORD accessMode;
5834 WCHAR fullname[MAX_PATH];
5836 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5837 debugstr_w(pwcsName), pstgPriority, grfMode,
5838 snbExclude, reserved, ppstgOpen);
5840 if (pwcsName == 0)
5842 hr = STG_E_INVALIDNAME;
5843 goto end;
5846 if (ppstgOpen == 0)
5848 hr = STG_E_INVALIDPOINTER;
5849 goto end;
5852 if (reserved)
5854 hr = STG_E_INVALIDPARAMETER;
5855 goto end;
5858 if (grfMode & STGM_PRIORITY)
5860 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5861 return STG_E_INVALIDFLAG;
5862 if (grfMode & STGM_DELETEONRELEASE)
5863 return STG_E_INVALIDFUNCTION;
5864 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5865 return STG_E_INVALIDFLAG;
5866 grfMode &= ~0xf0; /* remove the existing sharing mode */
5867 grfMode |= STGM_SHARE_DENY_NONE;
5869 /* STGM_PRIORITY stops other IStorage objects on the same file from
5870 * committing until the STGM_PRIORITY IStorage is closed. it also
5871 * stops non-transacted mode StgOpenStorage calls with write access from
5872 * succeeding. obviously, both of these cannot be achieved through just
5873 * file share flags */
5874 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5878 * Validate the sharing mode
5880 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5881 switch(STGM_SHARE_MODE(grfMode))
5883 case STGM_SHARE_EXCLUSIVE:
5884 case STGM_SHARE_DENY_WRITE:
5885 break;
5886 default:
5887 hr = STG_E_INVALIDFLAG;
5888 goto end;
5891 if ( FAILED( validateSTGM(grfMode) ) ||
5892 (grfMode&STGM_CREATE))
5894 hr = STG_E_INVALIDFLAG;
5895 goto end;
5898 /* shared reading requires transacted mode */
5899 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5900 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5901 !(grfMode&STGM_TRANSACTED) )
5903 hr = STG_E_INVALIDFLAG;
5904 goto end;
5908 * Interpret the STGM value grfMode
5910 shareMode = GetShareModeFromSTGM(grfMode);
5911 accessMode = GetAccessModeFromSTGM(grfMode);
5913 *ppstgOpen = 0;
5915 hFile = CreateFileW( pwcsName,
5916 accessMode,
5917 shareMode,
5918 NULL,
5919 OPEN_EXISTING,
5920 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5923 if (hFile==INVALID_HANDLE_VALUE)
5925 DWORD last_error = GetLastError();
5927 hr = E_FAIL;
5929 switch (last_error)
5931 case ERROR_FILE_NOT_FOUND:
5932 hr = STG_E_FILENOTFOUND;
5933 break;
5935 case ERROR_PATH_NOT_FOUND:
5936 hr = STG_E_PATHNOTFOUND;
5937 break;
5939 case ERROR_ACCESS_DENIED:
5940 case ERROR_WRITE_PROTECT:
5941 hr = STG_E_ACCESSDENIED;
5942 break;
5944 case ERROR_SHARING_VIOLATION:
5945 hr = STG_E_SHAREVIOLATION;
5946 break;
5948 default:
5949 hr = E_FAIL;
5952 goto end;
5956 * Refuse to open the file if it's too small to be a structured storage file
5957 * FIXME: verify the file when reading instead of here
5959 if (GetFileSize(hFile, NULL) < 0x100)
5961 CloseHandle(hFile);
5962 hr = STG_E_FILEALREADYEXISTS;
5963 goto end;
5967 * Allocate and initialize the new IStorage32object.
5969 hr = StorageImpl_Construct(
5970 hFile,
5971 pwcsName,
5972 NULL,
5973 grfMode,
5974 TRUE,
5975 FALSE,
5976 &newStorage);
5978 if (FAILED(hr))
5981 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5983 if(hr == STG_E_INVALIDHEADER)
5984 hr = STG_E_FILEALREADYEXISTS;
5985 goto end;
5988 /* prepare the file name string given in lieu of the root property name */
5989 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5990 memcpy(newStorage->base.filename, fullname, DIRENTRY_NAME_BUFFER_LEN);
5991 newStorage->base.filename[DIRENTRY_NAME_BUFFER_LEN-1] = '\0';
5994 * Get an "out" pointer for the caller.
5996 *ppstgOpen = (IStorage*)newStorage;
5998 end:
5999 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6000 return hr;
6003 /******************************************************************************
6004 * StgCreateDocfileOnILockBytes [OLE32.@]
6006 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6007 ILockBytes *plkbyt,
6008 DWORD grfMode,
6009 DWORD reserved,
6010 IStorage** ppstgOpen)
6012 StorageImpl* newStorage = 0;
6013 HRESULT hr = S_OK;
6015 if ((ppstgOpen == 0) || (plkbyt == 0))
6016 return STG_E_INVALIDPOINTER;
6019 * Allocate and initialize the new IStorage object.
6021 hr = StorageImpl_Construct(
6024 plkbyt,
6025 grfMode,
6026 FALSE,
6027 TRUE,
6028 &newStorage);
6030 if (FAILED(hr))
6032 return hr;
6036 * Get an "out" pointer for the caller.
6038 *ppstgOpen = (IStorage*)newStorage;
6040 return hr;
6043 /******************************************************************************
6044 * StgOpenStorageOnILockBytes [OLE32.@]
6046 HRESULT WINAPI StgOpenStorageOnILockBytes(
6047 ILockBytes *plkbyt,
6048 IStorage *pstgPriority,
6049 DWORD grfMode,
6050 SNB snbExclude,
6051 DWORD reserved,
6052 IStorage **ppstgOpen)
6054 StorageImpl* newStorage = 0;
6055 HRESULT hr = S_OK;
6057 if ((plkbyt == 0) || (ppstgOpen == 0))
6058 return STG_E_INVALIDPOINTER;
6060 if ( FAILED( validateSTGM(grfMode) ))
6061 return STG_E_INVALIDFLAG;
6063 *ppstgOpen = 0;
6066 * Allocate and initialize the new IStorage object.
6068 hr = StorageImpl_Construct(
6071 plkbyt,
6072 grfMode,
6073 FALSE,
6074 FALSE,
6075 &newStorage);
6077 if (FAILED(hr))
6079 return hr;
6083 * Get an "out" pointer for the caller.
6085 *ppstgOpen = (IStorage*)newStorage;
6087 return hr;
6090 /******************************************************************************
6091 * StgSetTimes [ole32.@]
6092 * StgSetTimes [OLE32.@]
6096 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6097 FILETIME const *patime, FILETIME const *pmtime)
6099 IStorage *stg = NULL;
6100 HRESULT r;
6102 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6104 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6105 0, 0, &stg);
6106 if( SUCCEEDED(r) )
6108 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6109 IStorage_Release(stg);
6112 return r;
6115 /******************************************************************************
6116 * StgIsStorageILockBytes [OLE32.@]
6118 * Determines if the ILockBytes contains a storage object.
6120 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6122 BYTE sig[8];
6123 ULARGE_INTEGER offset;
6125 offset.u.HighPart = 0;
6126 offset.u.LowPart = 0;
6128 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6130 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6131 return S_OK;
6133 return S_FALSE;
6136 /******************************************************************************
6137 * WriteClassStg [OLE32.@]
6139 * This method will store the specified CLSID in the specified storage object
6141 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6143 HRESULT hRes;
6145 if(!pStg)
6146 return E_INVALIDARG;
6148 if(!rclsid)
6149 return STG_E_INVALIDPOINTER;
6151 hRes = IStorage_SetClass(pStg, rclsid);
6153 return hRes;
6156 /***********************************************************************
6157 * ReadClassStg (OLE32.@)
6159 * This method reads the CLSID previously written to a storage object with
6160 * the WriteClassStg.
6162 * PARAMS
6163 * pstg [I] IStorage pointer
6164 * pclsid [O] Pointer to where the CLSID is written
6166 * RETURNS
6167 * Success: S_OK.
6168 * Failure: HRESULT code.
6170 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6172 STATSTG pstatstg;
6173 HRESULT hRes;
6175 TRACE("(%p, %p)\n", pstg, pclsid);
6177 if(!pstg || !pclsid)
6178 return E_INVALIDARG;
6181 * read a STATSTG structure (contains the clsid) from the storage
6183 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6185 if(SUCCEEDED(hRes))
6186 *pclsid=pstatstg.clsid;
6188 return hRes;
6191 /***********************************************************************
6192 * OleLoadFromStream (OLE32.@)
6194 * This function loads an object from stream
6196 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6198 CLSID clsid;
6199 HRESULT res;
6200 LPPERSISTSTREAM xstm;
6202 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6204 res=ReadClassStm(pStm,&clsid);
6205 if (FAILED(res))
6206 return res;
6207 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6208 if (FAILED(res))
6209 return res;
6210 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6211 if (FAILED(res)) {
6212 IUnknown_Release((IUnknown*)*ppvObj);
6213 return res;
6215 res=IPersistStream_Load(xstm,pStm);
6216 IPersistStream_Release(xstm);
6217 /* FIXME: all refcounts ok at this point? I think they should be:
6218 * pStm : unchanged
6219 * ppvObj : 1
6220 * xstm : 0 (released)
6222 return res;
6225 /***********************************************************************
6226 * OleSaveToStream (OLE32.@)
6228 * This function saves an object with the IPersistStream interface on it
6229 * to the specified stream.
6231 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6234 CLSID clsid;
6235 HRESULT res;
6237 TRACE("(%p,%p)\n",pPStm,pStm);
6239 res=IPersistStream_GetClassID(pPStm,&clsid);
6241 if (SUCCEEDED(res)){
6243 res=WriteClassStm(pStm,&clsid);
6245 if (SUCCEEDED(res))
6247 res=IPersistStream_Save(pPStm,pStm,TRUE);
6250 TRACE("Finished Save\n");
6251 return res;
6254 /****************************************************************************
6255 * This method validate a STGM parameter that can contain the values below
6257 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6258 * The stgm values contained in 0xffff0000 are bitmasks.
6260 * STGM_DIRECT 0x00000000
6261 * STGM_TRANSACTED 0x00010000
6262 * STGM_SIMPLE 0x08000000
6264 * STGM_READ 0x00000000
6265 * STGM_WRITE 0x00000001
6266 * STGM_READWRITE 0x00000002
6268 * STGM_SHARE_DENY_NONE 0x00000040
6269 * STGM_SHARE_DENY_READ 0x00000030
6270 * STGM_SHARE_DENY_WRITE 0x00000020
6271 * STGM_SHARE_EXCLUSIVE 0x00000010
6273 * STGM_PRIORITY 0x00040000
6274 * STGM_DELETEONRELEASE 0x04000000
6276 * STGM_CREATE 0x00001000
6277 * STGM_CONVERT 0x00020000
6278 * STGM_FAILIFTHERE 0x00000000
6280 * STGM_NOSCRATCH 0x00100000
6281 * STGM_NOSNAPSHOT 0x00200000
6283 static HRESULT validateSTGM(DWORD stgm)
6285 DWORD access = STGM_ACCESS_MODE(stgm);
6286 DWORD share = STGM_SHARE_MODE(stgm);
6287 DWORD create = STGM_CREATE_MODE(stgm);
6289 if (stgm&~STGM_KNOWN_FLAGS)
6291 ERR("unknown flags %08x\n", stgm);
6292 return E_FAIL;
6295 switch (access)
6297 case STGM_READ:
6298 case STGM_WRITE:
6299 case STGM_READWRITE:
6300 break;
6301 default:
6302 return E_FAIL;
6305 switch (share)
6307 case STGM_SHARE_DENY_NONE:
6308 case STGM_SHARE_DENY_READ:
6309 case STGM_SHARE_DENY_WRITE:
6310 case STGM_SHARE_EXCLUSIVE:
6311 break;
6312 default:
6313 return E_FAIL;
6316 switch (create)
6318 case STGM_CREATE:
6319 case STGM_FAILIFTHERE:
6320 break;
6321 default:
6322 return E_FAIL;
6326 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6328 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6329 return E_FAIL;
6332 * STGM_CREATE | STGM_CONVERT
6333 * if both are false, STGM_FAILIFTHERE is set to TRUE
6335 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6336 return E_FAIL;
6339 * STGM_NOSCRATCH requires STGM_TRANSACTED
6341 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6342 return E_FAIL;
6345 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6346 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6348 if ( (stgm & STGM_NOSNAPSHOT) &&
6349 (!(stgm & STGM_TRANSACTED) ||
6350 share == STGM_SHARE_EXCLUSIVE ||
6351 share == STGM_SHARE_DENY_WRITE) )
6352 return E_FAIL;
6354 return S_OK;
6357 /****************************************************************************
6358 * GetShareModeFromSTGM
6360 * This method will return a share mode flag from a STGM value.
6361 * The STGM value is assumed valid.
6363 static DWORD GetShareModeFromSTGM(DWORD stgm)
6365 switch (STGM_SHARE_MODE(stgm))
6367 case STGM_SHARE_DENY_NONE:
6368 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6369 case STGM_SHARE_DENY_READ:
6370 return FILE_SHARE_WRITE;
6371 case STGM_SHARE_DENY_WRITE:
6372 return FILE_SHARE_READ;
6373 case STGM_SHARE_EXCLUSIVE:
6374 return 0;
6376 ERR("Invalid share mode!\n");
6377 assert(0);
6378 return 0;
6381 /****************************************************************************
6382 * GetAccessModeFromSTGM
6384 * This method will return an access mode flag from a STGM value.
6385 * The STGM value is assumed valid.
6387 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6389 switch (STGM_ACCESS_MODE(stgm))
6391 case STGM_READ:
6392 return GENERIC_READ;
6393 case STGM_WRITE:
6394 case STGM_READWRITE:
6395 return GENERIC_READ | GENERIC_WRITE;
6397 ERR("Invalid access mode!\n");
6398 assert(0);
6399 return 0;
6402 /****************************************************************************
6403 * GetCreationModeFromSTGM
6405 * This method will return a creation mode flag from a STGM value.
6406 * The STGM value is assumed valid.
6408 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6410 switch(STGM_CREATE_MODE(stgm))
6412 case STGM_CREATE:
6413 return CREATE_ALWAYS;
6414 case STGM_CONVERT:
6415 FIXME("STGM_CONVERT not implemented!\n");
6416 return CREATE_NEW;
6417 case STGM_FAILIFTHERE:
6418 return CREATE_NEW;
6420 ERR("Invalid create mode!\n");
6421 assert(0);
6422 return 0;
6426 /*************************************************************************
6427 * OLECONVERT_LoadOLE10 [Internal]
6429 * Loads the OLE10 STREAM to memory
6431 * PARAMS
6432 * pOleStream [I] The OLESTREAM
6433 * pData [I] Data Structure for the OLESTREAM Data
6435 * RETURNS
6436 * Success: S_OK
6437 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6438 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6440 * NOTES
6441 * This function is used by OleConvertOLESTREAMToIStorage only.
6443 * Memory allocated for pData must be freed by the caller
6445 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6447 DWORD dwSize;
6448 HRESULT hRes = S_OK;
6449 int nTryCnt=0;
6450 int max_try = 6;
6452 pData->pData = NULL;
6453 pData->pstrOleObjFileName = NULL;
6455 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6457 /* Get the OleID */
6458 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6459 if(dwSize != sizeof(pData->dwOleID))
6461 hRes = CONVERT10_E_OLESTREAM_GET;
6463 else if(pData->dwOleID != OLESTREAM_ID)
6465 hRes = CONVERT10_E_OLESTREAM_FMT;
6467 else
6469 hRes = S_OK;
6470 break;
6474 if(hRes == S_OK)
6476 /* Get the TypeID... more info needed for this field */
6477 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6478 if(dwSize != sizeof(pData->dwTypeID))
6480 hRes = CONVERT10_E_OLESTREAM_GET;
6483 if(hRes == S_OK)
6485 if(pData->dwTypeID != 0)
6487 /* Get the length of the OleTypeName */
6488 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6489 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6491 hRes = CONVERT10_E_OLESTREAM_GET;
6494 if(hRes == S_OK)
6496 if(pData->dwOleTypeNameLength > 0)
6498 /* Get the OleTypeName */
6499 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6500 if(dwSize != pData->dwOleTypeNameLength)
6502 hRes = CONVERT10_E_OLESTREAM_GET;
6506 if(bStrem1)
6508 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6509 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6511 hRes = CONVERT10_E_OLESTREAM_GET;
6513 if(hRes == S_OK)
6515 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6516 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6517 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6518 if(pData->pstrOleObjFileName)
6520 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6521 if(dwSize != pData->dwOleObjFileNameLength)
6523 hRes = CONVERT10_E_OLESTREAM_GET;
6526 else
6527 hRes = CONVERT10_E_OLESTREAM_GET;
6530 else
6532 /* Get the Width of the Metafile */
6533 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6534 if(dwSize != sizeof(pData->dwMetaFileWidth))
6536 hRes = CONVERT10_E_OLESTREAM_GET;
6538 if(hRes == S_OK)
6540 /* Get the Height of the Metafile */
6541 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6542 if(dwSize != sizeof(pData->dwMetaFileHeight))
6544 hRes = CONVERT10_E_OLESTREAM_GET;
6548 if(hRes == S_OK)
6550 /* Get the Length of the Data */
6551 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6552 if(dwSize != sizeof(pData->dwDataLength))
6554 hRes = CONVERT10_E_OLESTREAM_GET;
6558 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6560 if(!bStrem1) /* if it is a second OLE stream data */
6562 pData->dwDataLength -= 8;
6563 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6564 if(dwSize != sizeof(pData->strUnknown))
6566 hRes = CONVERT10_E_OLESTREAM_GET;
6570 if(hRes == S_OK)
6572 if(pData->dwDataLength > 0)
6574 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6576 /* Get Data (ex. IStorage, Metafile, or BMP) */
6577 if(pData->pData)
6579 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6580 if(dwSize != pData->dwDataLength)
6582 hRes = CONVERT10_E_OLESTREAM_GET;
6585 else
6587 hRes = CONVERT10_E_OLESTREAM_GET;
6593 return hRes;
6596 /*************************************************************************
6597 * OLECONVERT_SaveOLE10 [Internal]
6599 * Saves the OLE10 STREAM From memory
6601 * PARAMS
6602 * pData [I] Data Structure for the OLESTREAM Data
6603 * pOleStream [I] The OLESTREAM to save
6605 * RETURNS
6606 * Success: S_OK
6607 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6609 * NOTES
6610 * This function is used by OleConvertIStorageToOLESTREAM only.
6613 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6615 DWORD dwSize;
6616 HRESULT hRes = S_OK;
6619 /* Set the OleID */
6620 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6621 if(dwSize != sizeof(pData->dwOleID))
6623 hRes = CONVERT10_E_OLESTREAM_PUT;
6626 if(hRes == S_OK)
6628 /* Set the TypeID */
6629 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6630 if(dwSize != sizeof(pData->dwTypeID))
6632 hRes = CONVERT10_E_OLESTREAM_PUT;
6636 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6638 /* Set the Length of the OleTypeName */
6639 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6640 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6642 hRes = CONVERT10_E_OLESTREAM_PUT;
6645 if(hRes == S_OK)
6647 if(pData->dwOleTypeNameLength > 0)
6649 /* Set the OleTypeName */
6650 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6651 if(dwSize != pData->dwOleTypeNameLength)
6653 hRes = CONVERT10_E_OLESTREAM_PUT;
6658 if(hRes == S_OK)
6660 /* Set the width of the Metafile */
6661 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6662 if(dwSize != sizeof(pData->dwMetaFileWidth))
6664 hRes = CONVERT10_E_OLESTREAM_PUT;
6668 if(hRes == S_OK)
6670 /* Set the height of the Metafile */
6671 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6672 if(dwSize != sizeof(pData->dwMetaFileHeight))
6674 hRes = CONVERT10_E_OLESTREAM_PUT;
6678 if(hRes == S_OK)
6680 /* Set the length of the Data */
6681 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6682 if(dwSize != sizeof(pData->dwDataLength))
6684 hRes = CONVERT10_E_OLESTREAM_PUT;
6688 if(hRes == S_OK)
6690 if(pData->dwDataLength > 0)
6692 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6693 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6694 if(dwSize != pData->dwDataLength)
6696 hRes = CONVERT10_E_OLESTREAM_PUT;
6701 return hRes;
6704 /*************************************************************************
6705 * OLECONVERT_GetOLE20FromOLE10[Internal]
6707 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6708 * opens it, and copies the content to the dest IStorage for
6709 * OleConvertOLESTREAMToIStorage
6712 * PARAMS
6713 * pDestStorage [I] The IStorage to copy the data to
6714 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6715 * nBufferLength [I] The size of the buffer
6717 * RETURNS
6718 * Nothing
6720 * NOTES
6724 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6726 HRESULT hRes;
6727 HANDLE hFile;
6728 IStorage *pTempStorage;
6729 DWORD dwNumOfBytesWritten;
6730 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6731 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6733 /* Create a temp File */
6734 GetTempPathW(MAX_PATH, wstrTempDir);
6735 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6736 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6738 if(hFile != INVALID_HANDLE_VALUE)
6740 /* Write IStorage Data to File */
6741 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6742 CloseHandle(hFile);
6744 /* Open and copy temp storage to the Dest Storage */
6745 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6746 if(hRes == S_OK)
6748 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6749 IStorage_Release(pTempStorage);
6751 DeleteFileW(wstrTempFile);
6756 /*************************************************************************
6757 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6759 * Saves the OLE10 STREAM From memory
6761 * PARAMS
6762 * pStorage [I] The Src IStorage to copy
6763 * pData [I] The Dest Memory to write to.
6765 * RETURNS
6766 * The size in bytes allocated for pData
6768 * NOTES
6769 * Memory allocated for pData must be freed by the caller
6771 * Used by OleConvertIStorageToOLESTREAM only.
6774 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6776 HANDLE hFile;
6777 HRESULT hRes;
6778 DWORD nDataLength = 0;
6779 IStorage *pTempStorage;
6780 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6781 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6783 *pData = NULL;
6785 /* Create temp Storage */
6786 GetTempPathW(MAX_PATH, wstrTempDir);
6787 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6788 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6790 if(hRes == S_OK)
6792 /* Copy Src Storage to the Temp Storage */
6793 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6794 IStorage_Release(pTempStorage);
6796 /* Open Temp Storage as a file and copy to memory */
6797 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6798 if(hFile != INVALID_HANDLE_VALUE)
6800 nDataLength = GetFileSize(hFile, NULL);
6801 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6802 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6803 CloseHandle(hFile);
6805 DeleteFileW(wstrTempFile);
6807 return nDataLength;
6810 /*************************************************************************
6811 * OLECONVERT_CreateOleStream [Internal]
6813 * Creates the "\001OLE" stream in the IStorage if necessary.
6815 * PARAMS
6816 * pStorage [I] Dest storage to create the stream in
6818 * RETURNS
6819 * Nothing
6821 * NOTES
6822 * This function is used by OleConvertOLESTREAMToIStorage only.
6824 * This stream is still unknown, MS Word seems to have extra data
6825 * but since the data is stored in the OLESTREAM there should be
6826 * no need to recreate the stream. If the stream is manually
6827 * deleted it will create it with this default data.
6830 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6832 HRESULT hRes;
6833 IStream *pStream;
6834 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6835 BYTE pOleStreamHeader [] =
6837 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6838 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6839 0x00, 0x00, 0x00, 0x00
6842 /* Create stream if not present */
6843 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6844 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6846 if(hRes == S_OK)
6848 /* Write default Data */
6849 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6850 IStream_Release(pStream);
6854 /* write a string to a stream, preceded by its length */
6855 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6857 HRESULT r;
6858 LPSTR str;
6859 DWORD len = 0;
6861 if( string )
6862 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6863 r = IStream_Write( stm, &len, sizeof(len), NULL);
6864 if( FAILED( r ) )
6865 return r;
6866 if(len == 0)
6867 return r;
6868 str = CoTaskMemAlloc( len );
6869 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6870 r = IStream_Write( stm, str, len, NULL);
6871 CoTaskMemFree( str );
6872 return r;
6875 /* read a string preceded by its length from a stream */
6876 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6878 HRESULT r;
6879 DWORD len, count = 0;
6880 LPSTR str;
6881 LPWSTR wstr;
6883 r = IStream_Read( stm, &len, sizeof(len), &count );
6884 if( FAILED( r ) )
6885 return r;
6886 if( count != sizeof(len) )
6887 return E_OUTOFMEMORY;
6889 TRACE("%d bytes\n",len);
6891 str = CoTaskMemAlloc( len );
6892 if( !str )
6893 return E_OUTOFMEMORY;
6894 count = 0;
6895 r = IStream_Read( stm, str, len, &count );
6896 if( FAILED( r ) )
6897 return r;
6898 if( count != len )
6900 CoTaskMemFree( str );
6901 return E_OUTOFMEMORY;
6904 TRACE("Read string %s\n",debugstr_an(str,len));
6906 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6907 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6908 if( wstr )
6909 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6910 CoTaskMemFree( str );
6912 *string = wstr;
6914 return r;
6918 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6919 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6921 IStream *pstm;
6922 HRESULT r = S_OK;
6923 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6925 static const BYTE unknown1[12] =
6926 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6927 0xFF, 0xFF, 0xFF, 0xFF};
6928 static const BYTE unknown2[16] =
6929 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6930 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6932 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6933 debugstr_w(lpszUserType), debugstr_w(szClipName),
6934 debugstr_w(szProgIDName));
6936 /* Create a CompObj stream */
6937 r = IStorage_CreateStream(pstg, szwStreamName,
6938 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6939 if( FAILED (r) )
6940 return r;
6942 /* Write CompObj Structure to stream */
6943 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6945 if( SUCCEEDED( r ) )
6946 r = WriteClassStm( pstm, clsid );
6948 if( SUCCEEDED( r ) )
6949 r = STREAM_WriteString( pstm, lpszUserType );
6950 if( SUCCEEDED( r ) )
6951 r = STREAM_WriteString( pstm, szClipName );
6952 if( SUCCEEDED( r ) )
6953 r = STREAM_WriteString( pstm, szProgIDName );
6954 if( SUCCEEDED( r ) )
6955 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6957 IStream_Release( pstm );
6959 return r;
6962 /***********************************************************************
6963 * WriteFmtUserTypeStg (OLE32.@)
6965 HRESULT WINAPI WriteFmtUserTypeStg(
6966 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6968 HRESULT r;
6969 WCHAR szwClipName[0x40];
6970 CLSID clsid = CLSID_NULL;
6971 LPWSTR wstrProgID = NULL;
6972 DWORD n;
6974 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6976 /* get the clipboard format name */
6977 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
6978 szwClipName[n]=0;
6980 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6982 /* FIXME: There's room to save a CLSID and its ProgID, but
6983 the CLSID is not looked up in the registry and in all the
6984 tests I wrote it was CLSID_NULL. Where does it come from?
6987 /* get the real program ID. This may fail, but that's fine */
6988 ProgIDFromCLSID(&clsid, &wstrProgID);
6990 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6992 r = STORAGE_WriteCompObj( pstg, &clsid,
6993 lpszUserType, szwClipName, wstrProgID );
6995 CoTaskMemFree(wstrProgID);
6997 return r;
7001 /******************************************************************************
7002 * ReadFmtUserTypeStg [OLE32.@]
7004 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7006 HRESULT r;
7007 IStream *stm = 0;
7008 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7009 unsigned char unknown1[12];
7010 unsigned char unknown2[16];
7011 DWORD count;
7012 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7013 CLSID clsid;
7015 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7017 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7018 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7019 if( FAILED ( r ) )
7021 WARN("Failed to open stream r = %08x\n", r);
7022 return r;
7025 /* read the various parts of the structure */
7026 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7027 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7028 goto end;
7029 r = ReadClassStm( stm, &clsid );
7030 if( FAILED( r ) )
7031 goto end;
7033 r = STREAM_ReadString( stm, &szCLSIDName );
7034 if( FAILED( r ) )
7035 goto end;
7037 r = STREAM_ReadString( stm, &szOleTypeName );
7038 if( FAILED( r ) )
7039 goto end;
7041 r = STREAM_ReadString( stm, &szProgIDName );
7042 if( FAILED( r ) )
7043 goto end;
7045 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7046 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7047 goto end;
7049 /* ok, success... now we just need to store what we found */
7050 if( pcf )
7051 *pcf = RegisterClipboardFormatW( szOleTypeName );
7052 CoTaskMemFree( szOleTypeName );
7054 if( lplpszUserType )
7055 *lplpszUserType = szCLSIDName;
7056 CoTaskMemFree( szProgIDName );
7058 end:
7059 IStream_Release( stm );
7061 return r;
7065 /*************************************************************************
7066 * OLECONVERT_CreateCompObjStream [Internal]
7068 * Creates a "\001CompObj" is the destination IStorage if necessary.
7070 * PARAMS
7071 * pStorage [I] The dest IStorage to create the CompObj Stream
7072 * if necessary.
7073 * strOleTypeName [I] The ProgID
7075 * RETURNS
7076 * Success: S_OK
7077 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7079 * NOTES
7080 * This function is used by OleConvertOLESTREAMToIStorage only.
7082 * The stream data is stored in the OLESTREAM and there should be
7083 * no need to recreate the stream. If the stream is manually
7084 * deleted it will attempt to create it by querying the registry.
7088 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7090 IStream *pStream;
7091 HRESULT hStorageRes, hRes = S_OK;
7092 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7093 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7094 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7096 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7097 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7099 /* Initialize the CompObj structure */
7100 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7101 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7102 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7105 /* Create a CompObj stream if it doesn't exist */
7106 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7107 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7108 if(hStorageRes == S_OK)
7110 /* copy the OleTypeName to the compobj struct */
7111 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7112 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7114 /* copy the OleTypeName to the compobj struct */
7115 /* Note: in the test made, these were Identical */
7116 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7117 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7119 /* Get the CLSID */
7120 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7121 bufferW, OLESTREAM_MAX_STR_LEN );
7122 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7124 if(hRes == S_OK)
7126 HKEY hKey;
7127 LONG hErr;
7128 /* Get the CLSID Default Name from the Registry */
7129 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7130 if(hErr == ERROR_SUCCESS)
7132 char strTemp[OLESTREAM_MAX_STR_LEN];
7133 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7134 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7135 if(hErr == ERROR_SUCCESS)
7137 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7139 RegCloseKey(hKey);
7143 /* Write CompObj Structure to stream */
7144 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7146 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7148 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7149 if(IStorageCompObj.dwCLSIDNameLength > 0)
7151 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7153 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7154 if(IStorageCompObj.dwOleTypeNameLength > 0)
7156 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7158 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7159 if(IStorageCompObj.dwProgIDNameLength > 0)
7161 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7163 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7164 IStream_Release(pStream);
7166 return hRes;
7170 /*************************************************************************
7171 * OLECONVERT_CreateOlePresStream[Internal]
7173 * Creates the "\002OlePres000" Stream with the Metafile data
7175 * PARAMS
7176 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7177 * dwExtentX [I] Width of the Metafile
7178 * dwExtentY [I] Height of the Metafile
7179 * pData [I] Metafile data
7180 * dwDataLength [I] Size of the Metafile data
7182 * RETURNS
7183 * Success: S_OK
7184 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7186 * NOTES
7187 * This function is used by OleConvertOLESTREAMToIStorage only.
7190 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7192 HRESULT hRes;
7193 IStream *pStream;
7194 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7195 BYTE pOlePresStreamHeader [] =
7197 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7198 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7199 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7200 0x00, 0x00, 0x00, 0x00
7203 BYTE pOlePresStreamHeaderEmpty [] =
7205 0x00, 0x00, 0x00, 0x00,
7206 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7207 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7208 0x00, 0x00, 0x00, 0x00
7211 /* Create the OlePres000 Stream */
7212 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7213 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7215 if(hRes == S_OK)
7217 DWORD nHeaderSize;
7218 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7220 memset(&OlePres, 0, sizeof(OlePres));
7221 /* Do we have any metafile data to save */
7222 if(dwDataLength > 0)
7224 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7225 nHeaderSize = sizeof(pOlePresStreamHeader);
7227 else
7229 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7230 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7232 /* Set width and height of the metafile */
7233 OlePres.dwExtentX = dwExtentX;
7234 OlePres.dwExtentY = -dwExtentY;
7236 /* Set Data and Length */
7237 if(dwDataLength > sizeof(METAFILEPICT16))
7239 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7240 OlePres.pData = &(pData[8]);
7242 /* Save OlePres000 Data to Stream */
7243 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7244 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7245 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7246 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7247 if(OlePres.dwSize > 0)
7249 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7251 IStream_Release(pStream);
7255 /*************************************************************************
7256 * OLECONVERT_CreateOle10NativeStream [Internal]
7258 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7260 * PARAMS
7261 * pStorage [I] Dest storage to create the stream in
7262 * pData [I] Ole10 Native Data (ex. bmp)
7263 * dwDataLength [I] Size of the Ole10 Native Data
7265 * RETURNS
7266 * Nothing
7268 * NOTES
7269 * This function is used by OleConvertOLESTREAMToIStorage only.
7271 * Might need to verify the data and return appropriate error message
7274 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7276 HRESULT hRes;
7277 IStream *pStream;
7278 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7280 /* Create the Ole10Native Stream */
7281 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7282 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7284 if(hRes == S_OK)
7286 /* Write info to stream */
7287 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7288 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7289 IStream_Release(pStream);
7294 /*************************************************************************
7295 * OLECONVERT_GetOLE10ProgID [Internal]
7297 * Finds the ProgID (or OleTypeID) from the IStorage
7299 * PARAMS
7300 * pStorage [I] The Src IStorage to get the ProgID
7301 * strProgID [I] the ProgID string to get
7302 * dwSize [I] the size of the string
7304 * RETURNS
7305 * Success: S_OK
7306 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7308 * NOTES
7309 * This function is used by OleConvertIStorageToOLESTREAM only.
7313 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7315 HRESULT hRes;
7316 IStream *pStream;
7317 LARGE_INTEGER iSeekPos;
7318 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7319 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7321 /* Open the CompObj Stream */
7322 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7323 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7324 if(hRes == S_OK)
7327 /*Get the OleType from the CompObj Stream */
7328 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7329 iSeekPos.u.HighPart = 0;
7331 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7332 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7333 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7334 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7335 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7336 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7337 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7339 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7340 if(*dwSize > 0)
7342 IStream_Read(pStream, strProgID, *dwSize, NULL);
7344 IStream_Release(pStream);
7346 else
7348 STATSTG stat;
7349 LPOLESTR wstrProgID;
7351 /* Get the OleType from the registry */
7352 REFCLSID clsid = &(stat.clsid);
7353 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7354 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7355 if(hRes == S_OK)
7357 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7361 return hRes;
7364 /*************************************************************************
7365 * OLECONVERT_GetOle10PresData [Internal]
7367 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7369 * PARAMS
7370 * pStorage [I] Src IStroage
7371 * pOleStream [I] Dest OleStream Mem Struct
7373 * RETURNS
7374 * Nothing
7376 * NOTES
7377 * This function is used by OleConvertIStorageToOLESTREAM only.
7379 * Memory allocated for pData must be freed by the caller
7383 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7386 HRESULT hRes;
7387 IStream *pStream;
7388 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7390 /* Initialize Default data for OLESTREAM */
7391 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7392 pOleStreamData[0].dwTypeID = 2;
7393 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7394 pOleStreamData[1].dwTypeID = 0;
7395 pOleStreamData[0].dwMetaFileWidth = 0;
7396 pOleStreamData[0].dwMetaFileHeight = 0;
7397 pOleStreamData[0].pData = NULL;
7398 pOleStreamData[1].pData = NULL;
7400 /* Open Ole10Native Stream */
7401 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7402 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7403 if(hRes == S_OK)
7406 /* Read Size and Data */
7407 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7408 if(pOleStreamData->dwDataLength > 0)
7410 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7411 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7413 IStream_Release(pStream);
7419 /*************************************************************************
7420 * OLECONVERT_GetOle20PresData[Internal]
7422 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7424 * PARAMS
7425 * pStorage [I] Src IStroage
7426 * pOleStreamData [I] Dest OleStream Mem Struct
7428 * RETURNS
7429 * Nothing
7431 * NOTES
7432 * This function is used by OleConvertIStorageToOLESTREAM only.
7434 * Memory allocated for pData must be freed by the caller
7436 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7438 HRESULT hRes;
7439 IStream *pStream;
7440 OLECONVERT_ISTORAGE_OLEPRES olePress;
7441 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7443 /* Initialize Default data for OLESTREAM */
7444 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7445 pOleStreamData[0].dwTypeID = 2;
7446 pOleStreamData[0].dwMetaFileWidth = 0;
7447 pOleStreamData[0].dwMetaFileHeight = 0;
7448 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7449 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7450 pOleStreamData[1].dwTypeID = 0;
7451 pOleStreamData[1].dwOleTypeNameLength = 0;
7452 pOleStreamData[1].strOleTypeName[0] = 0;
7453 pOleStreamData[1].dwMetaFileWidth = 0;
7454 pOleStreamData[1].dwMetaFileHeight = 0;
7455 pOleStreamData[1].pData = NULL;
7456 pOleStreamData[1].dwDataLength = 0;
7459 /* Open OlePress000 stream */
7460 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7461 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7462 if(hRes == S_OK)
7464 LARGE_INTEGER iSeekPos;
7465 METAFILEPICT16 MetaFilePict;
7466 static const char strMetafilePictName[] = "METAFILEPICT";
7468 /* Set the TypeID for a Metafile */
7469 pOleStreamData[1].dwTypeID = 5;
7471 /* Set the OleTypeName to Metafile */
7472 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7473 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7475 iSeekPos.u.HighPart = 0;
7476 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7478 /* Get Presentation Data */
7479 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7480 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7481 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7482 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7484 /*Set width and Height */
7485 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7486 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7487 if(olePress.dwSize > 0)
7489 /* Set Length */
7490 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7492 /* Set MetaFilePict struct */
7493 MetaFilePict.mm = 8;
7494 MetaFilePict.xExt = olePress.dwExtentX;
7495 MetaFilePict.yExt = olePress.dwExtentY;
7496 MetaFilePict.hMF = 0;
7498 /* Get Metafile Data */
7499 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7500 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7501 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7503 IStream_Release(pStream);
7507 /*************************************************************************
7508 * OleConvertOLESTREAMToIStorage [OLE32.@]
7510 * Read info on MSDN
7512 * TODO
7513 * DVTARGETDEVICE parameter is not handled
7514 * Still unsure of some mem fields for OLE 10 Stream
7515 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7516 * and "\001OLE" streams
7519 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7520 LPOLESTREAM pOleStream,
7521 LPSTORAGE pstg,
7522 const DVTARGETDEVICE* ptd)
7524 int i;
7525 HRESULT hRes=S_OK;
7526 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7528 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7530 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7532 if(ptd != NULL)
7534 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7537 if(pstg == NULL || pOleStream == NULL)
7539 hRes = E_INVALIDARG;
7542 if(hRes == S_OK)
7544 /* Load the OLESTREAM to Memory */
7545 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7548 if(hRes == S_OK)
7550 /* Load the OLESTREAM to Memory (part 2)*/
7551 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7554 if(hRes == S_OK)
7557 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7559 /* Do we have the IStorage Data in the OLESTREAM */
7560 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7562 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7563 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7565 else
7567 /* It must be an original OLE 1.0 source */
7568 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7571 else
7573 /* It must be an original OLE 1.0 source */
7574 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7577 /* Create CompObj Stream if necessary */
7578 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7579 if(hRes == S_OK)
7581 /*Create the Ole Stream if necessary */
7582 OLECONVERT_CreateOleStream(pstg);
7587 /* Free allocated memory */
7588 for(i=0; i < 2; i++)
7590 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7591 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7592 pOleStreamData[i].pstrOleObjFileName = NULL;
7594 return hRes;
7597 /*************************************************************************
7598 * OleConvertIStorageToOLESTREAM [OLE32.@]
7600 * Read info on MSDN
7602 * Read info on MSDN
7604 * TODO
7605 * Still unsure of some mem fields for OLE 10 Stream
7606 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7607 * and "\001OLE" streams.
7610 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7611 LPSTORAGE pstg,
7612 LPOLESTREAM pOleStream)
7614 int i;
7615 HRESULT hRes = S_OK;
7616 IStream *pStream;
7617 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7618 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7620 TRACE("%p %p\n", pstg, pOleStream);
7622 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7624 if(pstg == NULL || pOleStream == NULL)
7626 hRes = E_INVALIDARG;
7628 if(hRes == S_OK)
7630 /* Get the ProgID */
7631 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7632 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7634 if(hRes == S_OK)
7636 /* Was it originally Ole10 */
7637 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7638 if(hRes == S_OK)
7640 IStream_Release(pStream);
7641 /* Get Presentation Data for Ole10Native */
7642 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7644 else
7646 /* Get Presentation Data (OLE20) */
7647 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7650 /* Save OLESTREAM */
7651 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7652 if(hRes == S_OK)
7654 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7659 /* Free allocated memory */
7660 for(i=0; i < 2; i++)
7662 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7665 return hRes;
7668 /***********************************************************************
7669 * GetConvertStg (OLE32.@)
7671 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7672 FIXME("unimplemented stub!\n");
7673 return E_FAIL;
7676 /******************************************************************************
7677 * StgIsStorageFile [OLE32.@]
7678 * Verify if the file contains a storage object
7680 * PARAMS
7681 * fn [ I] Filename
7683 * RETURNS
7684 * S_OK if file has magic bytes as a storage object
7685 * S_FALSE if file is not storage
7687 HRESULT WINAPI
7688 StgIsStorageFile(LPCOLESTR fn)
7690 HANDLE hf;
7691 BYTE magic[8];
7692 DWORD bytes_read;
7694 TRACE("%s\n", debugstr_w(fn));
7695 hf = CreateFileW(fn, GENERIC_READ,
7696 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7697 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7699 if (hf == INVALID_HANDLE_VALUE)
7700 return STG_E_FILENOTFOUND;
7702 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7704 WARN(" unable to read file\n");
7705 CloseHandle(hf);
7706 return S_FALSE;
7709 CloseHandle(hf);
7711 if (bytes_read != 8) {
7712 WARN(" too short\n");
7713 return S_FALSE;
7716 if (!memcmp(magic,STORAGE_magic,8)) {
7717 WARN(" -> YES\n");
7718 return S_OK;
7721 WARN(" -> Invalid header.\n");
7722 return S_FALSE;
7725 /***********************************************************************
7726 * WriteClassStm (OLE32.@)
7728 * Writes a CLSID to a stream.
7730 * PARAMS
7731 * pStm [I] Stream to write to.
7732 * rclsid [I] CLSID to write.
7734 * RETURNS
7735 * Success: S_OK.
7736 * Failure: HRESULT code.
7738 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7740 TRACE("(%p,%p)\n",pStm,rclsid);
7742 if (!pStm || !rclsid)
7743 return E_INVALIDARG;
7745 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7748 /***********************************************************************
7749 * ReadClassStm (OLE32.@)
7751 * Reads a CLSID from a stream.
7753 * PARAMS
7754 * pStm [I] Stream to read from.
7755 * rclsid [O] CLSID to read.
7757 * RETURNS
7758 * Success: S_OK.
7759 * Failure: HRESULT code.
7761 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7763 ULONG nbByte;
7764 HRESULT res;
7766 TRACE("(%p,%p)\n",pStm,pclsid);
7768 if (!pStm || !pclsid)
7769 return E_INVALIDARG;
7771 /* clear the output args */
7772 *pclsid = CLSID_NULL;
7774 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7776 if (FAILED(res))
7777 return res;
7779 if (nbByte != sizeof(CLSID))
7780 return STG_E_READFAULT;
7781 else
7782 return S_OK;