ole32: Change WriteDirEntry return type to HRESULT.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob8173b84233fefe46fc92f6143121bbd1b77f4c3e
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 destroyDirEntry(
188 StorageImpl *storage,
189 DirRef index);
191 static HRESULT insertIntoTree(
192 StorageImpl *This,
193 DirRef parentStorageIndex,
194 DirRef newEntryIndex);
196 static LONG entryNameCmp(
197 const OLECHAR *name1,
198 const OLECHAR *name2);
200 static DirRef findElement(
201 StorageImpl *storage,
202 DirRef storageEntry,
203 const OLECHAR *name,
204 DirEntry *data);
206 static HRESULT findTreeParent(
207 StorageImpl *storage,
208 DirRef storageEntry,
209 const OLECHAR *childName,
210 DirEntry *parentData,
211 DirRef *parentEntry,
212 ULONG *relation);
214 /***********************************************************************
215 * Declaration of miscellaneous functions...
217 static HRESULT validateSTGM(DWORD stgmValue);
219 static DWORD GetShareModeFromSTGM(DWORD stgm);
220 static DWORD GetAccessModeFromSTGM(DWORD stgm);
221 static DWORD GetCreationModeFromSTGM(DWORD stgm);
223 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
226 /****************************************************************************
227 * IEnumSTATSTGImpl definitions.
229 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
230 * This class allows iterating through the content of a storage and to find
231 * specific items inside it.
233 struct IEnumSTATSTGImpl
235 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
236 * since we want to cast this in an IEnumSTATSTG pointer */
238 LONG ref; /* Reference count */
239 StorageImpl* parentStorage; /* Reference to the parent storage */
240 DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
243 * The current implementation of the IEnumSTATSTGImpl class uses a stack
244 * to walk the directory entries to get the content of a storage. This stack
245 * is implemented by the following 3 data members
247 ULONG stackSize;
248 ULONG stackMaxSize;
249 DirRef* stackToVisit;
251 #define ENUMSTATSGT_SIZE_INCREMENT 10
255 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, DirRef storageDirEntry);
256 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
257 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, DirRef nodeToPush);
258 static DirRef IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
260 /************************************************************************
261 ** Block Functions
264 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
266 if (index == 0xffffffff)
267 index = 0;
268 else
269 index ++;
271 return index * BIG_BLOCK_SIZE;
274 /************************************************************************
275 ** Storage32BaseImpl implementation
277 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
278 ULARGE_INTEGER offset,
279 void* buffer,
280 ULONG size,
281 ULONG* bytesRead)
283 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
286 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
287 ULARGE_INTEGER offset,
288 const void* buffer,
289 const ULONG size,
290 ULONG* bytesWritten)
292 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
295 /************************************************************************
296 * Storage32BaseImpl_QueryInterface (IUnknown)
298 * This method implements the common QueryInterface for all IStorage32
299 * implementations contained in this file.
301 * See Windows documentation for more details on IUnknown methods.
303 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
304 IStorage* iface,
305 REFIID riid,
306 void** ppvObject)
308 StorageBaseImpl *This = (StorageBaseImpl *)iface;
310 if ( (This==0) || (ppvObject==0) )
311 return E_INVALIDARG;
313 *ppvObject = 0;
315 if (IsEqualGUID(&IID_IUnknown, riid) ||
316 IsEqualGUID(&IID_IStorage, riid))
318 *ppvObject = This;
320 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
322 *ppvObject = &This->pssVtbl;
325 if ((*ppvObject)==0)
326 return E_NOINTERFACE;
328 IStorage_AddRef(iface);
330 return S_OK;
333 /************************************************************************
334 * Storage32BaseImpl_AddRef (IUnknown)
336 * This method implements the common AddRef for all IStorage32
337 * implementations contained in this file.
339 * See Windows documentation for more details on IUnknown methods.
341 static ULONG WINAPI StorageBaseImpl_AddRef(
342 IStorage* iface)
344 StorageBaseImpl *This = (StorageBaseImpl *)iface;
345 ULONG ref = InterlockedIncrement(&This->ref);
347 TRACE("(%p) AddRef to %d\n", This, ref);
349 return ref;
352 /************************************************************************
353 * Storage32BaseImpl_Release (IUnknown)
355 * This method implements the common Release for all IStorage32
356 * implementations contained in this file.
358 * See Windows documentation for more details on IUnknown methods.
360 static ULONG WINAPI StorageBaseImpl_Release(
361 IStorage* iface)
363 StorageBaseImpl *This = (StorageBaseImpl *)iface;
365 ULONG ref = InterlockedDecrement(&This->ref);
367 TRACE("(%p) ReleaseRef to %d\n", This, ref);
369 if (ref == 0)
372 * Since we are using a system of base-classes, we want to call the
373 * destructor of the appropriate derived class. To do this, we are
374 * using virtual functions to implement the destructor.
376 StorageBaseImpl_Destroy(This);
379 return ref;
382 /************************************************************************
383 * Storage32BaseImpl_OpenStream (IStorage)
385 * This method will open the specified stream object from the current storage.
387 * See Windows documentation for more details on IStorage methods.
389 static HRESULT WINAPI StorageBaseImpl_OpenStream(
390 IStorage* iface,
391 const OLECHAR* pwcsName, /* [string][in] */
392 void* reserved1, /* [unique][in] */
393 DWORD grfMode, /* [in] */
394 DWORD reserved2, /* [in] */
395 IStream** ppstm) /* [out] */
397 StorageBaseImpl *This = (StorageBaseImpl *)iface;
398 StgStreamImpl* newStream;
399 DirEntry currentEntry;
400 DirRef streamEntryRef;
401 HRESULT res = STG_E_UNKNOWN;
403 TRACE("(%p, %s, %p, %x, %d, %p)\n",
404 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
406 if ( (pwcsName==NULL) || (ppstm==0) )
408 res = E_INVALIDARG;
409 goto end;
412 *ppstm = NULL;
414 if ( FAILED( validateSTGM(grfMode) ) ||
415 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
417 res = STG_E_INVALIDFLAG;
418 goto end;
422 * As documented.
424 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
426 res = STG_E_INVALIDFUNCTION;
427 goto end;
430 if (!This->ancestorStorage)
432 res = STG_E_REVERTED;
433 goto end;
437 * Check that we're compatible with the parent's storage mode, but
438 * only if we are not in transacted mode
440 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
441 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
443 res = STG_E_ACCESSDENIED;
444 goto end;
449 * Search for the element with the given name
451 streamEntryRef = findElement(
452 This->ancestorStorage,
453 This->storageDirEntry,
454 pwcsName,
455 &currentEntry);
458 * If it was found, construct the stream object and return a pointer to it.
460 if ( (streamEntryRef!=DIRENTRY_NULL) &&
461 (currentEntry.stgType==STGTY_STREAM) )
463 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
465 /* A single stream cannot be opened a second time. */
466 res = STG_E_ACCESSDENIED;
467 goto end;
470 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
472 if (newStream!=0)
474 newStream->grfMode = grfMode;
475 *ppstm = (IStream*)newStream;
477 IStream_AddRef(*ppstm);
479 res = S_OK;
480 goto end;
483 res = E_OUTOFMEMORY;
484 goto end;
487 res = STG_E_FILENOTFOUND;
489 end:
490 if (res == S_OK)
491 TRACE("<-- IStream %p\n", *ppstm);
492 TRACE("<-- %08x\n", res);
493 return res;
496 /************************************************************************
497 * Storage32BaseImpl_OpenStorage (IStorage)
499 * This method will open a new storage object from the current storage.
501 * See Windows documentation for more details on IStorage methods.
503 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
504 IStorage* iface,
505 const OLECHAR* pwcsName, /* [string][unique][in] */
506 IStorage* pstgPriority, /* [unique][in] */
507 DWORD grfMode, /* [in] */
508 SNB snbExclude, /* [unique][in] */
509 DWORD reserved, /* [in] */
510 IStorage** ppstg) /* [out] */
512 StorageBaseImpl *This = (StorageBaseImpl *)iface;
513 StorageInternalImpl* newStorage;
514 DirEntry currentEntry;
515 DirRef storageEntryRef;
516 HRESULT res = STG_E_UNKNOWN;
518 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
519 iface, debugstr_w(pwcsName), pstgPriority,
520 grfMode, snbExclude, reserved, ppstg);
522 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
524 res = E_INVALIDARG;
525 goto end;
528 if (This->openFlags & STGM_SIMPLE)
530 res = STG_E_INVALIDFUNCTION;
531 goto end;
534 /* as documented */
535 if (snbExclude != NULL)
537 res = STG_E_INVALIDPARAMETER;
538 goto end;
541 if ( FAILED( validateSTGM(grfMode) ))
543 res = STG_E_INVALIDFLAG;
544 goto end;
548 * As documented.
550 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
551 (grfMode & STGM_DELETEONRELEASE) ||
552 (grfMode & STGM_PRIORITY) )
554 res = STG_E_INVALIDFUNCTION;
555 goto end;
558 if (!This->ancestorStorage)
559 return STG_E_REVERTED;
562 * Check that we're compatible with the parent's storage mode,
563 * but only if we are not transacted
565 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
566 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
568 res = STG_E_ACCESSDENIED;
569 goto end;
573 *ppstg = NULL;
575 storageEntryRef = findElement(
576 This->ancestorStorage,
577 This->storageDirEntry,
578 pwcsName,
579 &currentEntry);
581 if ( (storageEntryRef!=DIRENTRY_NULL) &&
582 (currentEntry.stgType==STGTY_STORAGE) )
584 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
586 /* A single storage cannot be opened a second time. */
587 res = STG_E_ACCESSDENIED;
588 goto end;
591 newStorage = StorageInternalImpl_Construct(
592 This->ancestorStorage,
593 grfMode,
594 storageEntryRef);
596 if (newStorage != 0)
598 *ppstg = (IStorage*)newStorage;
600 StorageBaseImpl_AddRef(*ppstg);
602 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
604 res = S_OK;
605 goto end;
608 res = STG_E_INSUFFICIENTMEMORY;
609 goto end;
612 res = STG_E_FILENOTFOUND;
614 end:
615 TRACE("<-- %08x\n", res);
616 return res;
619 /************************************************************************
620 * Storage32BaseImpl_EnumElements (IStorage)
622 * This method will create an enumerator object that can be used to
623 * retrieve information about all the elements in the storage object.
625 * See Windows documentation for more details on IStorage methods.
627 static HRESULT WINAPI StorageBaseImpl_EnumElements(
628 IStorage* iface,
629 DWORD reserved1, /* [in] */
630 void* reserved2, /* [size_is][unique][in] */
631 DWORD reserved3, /* [in] */
632 IEnumSTATSTG** ppenum) /* [out] */
634 StorageBaseImpl *This = (StorageBaseImpl *)iface;
635 IEnumSTATSTGImpl* newEnum;
637 TRACE("(%p, %d, %p, %d, %p)\n",
638 iface, reserved1, reserved2, reserved3, ppenum);
640 if ( (This==0) || (ppenum==0))
641 return E_INVALIDARG;
643 if (!This->ancestorStorage)
644 return STG_E_REVERTED;
646 newEnum = IEnumSTATSTGImpl_Construct(
647 This->ancestorStorage,
648 This->storageDirEntry);
650 if (newEnum!=0)
652 *ppenum = (IEnumSTATSTG*)newEnum;
654 IEnumSTATSTG_AddRef(*ppenum);
656 return S_OK;
659 return E_OUTOFMEMORY;
662 /************************************************************************
663 * Storage32BaseImpl_Stat (IStorage)
665 * This method will retrieve information about this storage object.
667 * See Windows documentation for more details on IStorage methods.
669 static HRESULT WINAPI StorageBaseImpl_Stat(
670 IStorage* iface,
671 STATSTG* pstatstg, /* [out] */
672 DWORD grfStatFlag) /* [in] */
674 StorageBaseImpl *This = (StorageBaseImpl *)iface;
675 DirEntry currentEntry;
676 BOOL readSuccessful;
677 HRESULT res = STG_E_UNKNOWN;
679 TRACE("(%p, %p, %x)\n",
680 iface, pstatstg, grfStatFlag);
682 if ( (This==0) || (pstatstg==0))
684 res = E_INVALIDARG;
685 goto end;
688 if (!This->ancestorStorage)
690 res = STG_E_REVERTED;
691 goto end;
694 readSuccessful = StorageImpl_ReadDirEntry(
695 This->ancestorStorage,
696 This->storageDirEntry,
697 &currentEntry);
699 if (readSuccessful)
701 StorageUtl_CopyDirEntryToSTATSTG(
702 This,
703 pstatstg,
704 &currentEntry,
705 grfStatFlag);
707 pstatstg->grfMode = This->openFlags;
708 pstatstg->grfStateBits = This->stateBits;
710 res = S_OK;
711 goto end;
714 res = E_FAIL;
716 end:
717 if (res == S_OK)
719 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);
721 TRACE("<-- %08x\n", res);
722 return res;
725 /************************************************************************
726 * Storage32BaseImpl_RenameElement (IStorage)
728 * This method will rename the specified element.
730 * See Windows documentation for more details on IStorage methods.
732 static HRESULT WINAPI StorageBaseImpl_RenameElement(
733 IStorage* iface,
734 const OLECHAR* pwcsOldName, /* [in] */
735 const OLECHAR* pwcsNewName) /* [in] */
737 StorageBaseImpl *This = (StorageBaseImpl *)iface;
738 DirEntry currentEntry;
739 DirRef currentEntryRef;
741 TRACE("(%p, %s, %s)\n",
742 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
744 if (!This->ancestorStorage)
745 return STG_E_REVERTED;
747 currentEntryRef = findElement(This->ancestorStorage,
748 This->storageDirEntry,
749 pwcsNewName,
750 &currentEntry);
752 if (currentEntryRef != DIRENTRY_NULL)
755 * There is already an element with the new name
757 return STG_E_FILEALREADYEXISTS;
761 * Search for the old element name
763 currentEntryRef = findElement(This->ancestorStorage,
764 This->storageDirEntry,
765 pwcsOldName,
766 &currentEntry);
768 if (currentEntryRef != DIRENTRY_NULL)
770 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
771 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
773 WARN("Element is already open; cannot rename.\n");
774 return STG_E_ACCESSDENIED;
777 /* Remove the element from its current position in the tree */
778 removeFromTree(This->ancestorStorage, This->storageDirEntry,
779 currentEntryRef);
781 /* Change the name of the element */
782 strcpyW(currentEntry.name, pwcsNewName);
784 StorageImpl_WriteDirEntry(This->ancestorStorage, currentEntryRef,
785 &currentEntry);
787 /* Insert the element in a new position in the tree */
788 insertIntoTree(This->ancestorStorage, This->storageDirEntry,
789 currentEntryRef);
791 else
794 * There is no element with the old name
796 return STG_E_FILENOTFOUND;
799 return S_OK;
802 /************************************************************************
803 * Storage32BaseImpl_CreateStream (IStorage)
805 * This method will create a stream object within this storage
807 * See Windows documentation for more details on IStorage methods.
809 static HRESULT WINAPI StorageBaseImpl_CreateStream(
810 IStorage* iface,
811 const OLECHAR* pwcsName, /* [string][in] */
812 DWORD grfMode, /* [in] */
813 DWORD reserved1, /* [in] */
814 DWORD reserved2, /* [in] */
815 IStream** ppstm) /* [out] */
817 StorageBaseImpl *This = (StorageBaseImpl *)iface;
818 StgStreamImpl* newStream;
819 DirEntry currentEntry, newStreamEntry;
820 DirRef currentEntryRef, newStreamEntryRef;
822 TRACE("(%p, %s, %x, %d, %d, %p)\n",
823 iface, debugstr_w(pwcsName), grfMode,
824 reserved1, reserved2, ppstm);
826 if (ppstm == 0)
827 return STG_E_INVALIDPOINTER;
829 if (pwcsName == 0)
830 return STG_E_INVALIDNAME;
832 if (reserved1 || reserved2)
833 return STG_E_INVALIDPARAMETER;
835 if ( FAILED( validateSTGM(grfMode) ))
836 return STG_E_INVALIDFLAG;
838 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
839 return STG_E_INVALIDFLAG;
841 if (!This->ancestorStorage)
842 return STG_E_REVERTED;
845 * As documented.
847 if ((grfMode & STGM_DELETEONRELEASE) ||
848 (grfMode & STGM_TRANSACTED))
849 return STG_E_INVALIDFUNCTION;
851 /* Can't create a stream on read-only storage */
852 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
853 return STG_E_ACCESSDENIED;
856 * Check that we're compatible with the parent's storage mode
857 * if not in transacted mode
859 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
860 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
861 return STG_E_ACCESSDENIED;
864 if(This->openFlags & STGM_SIMPLE)
865 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
867 *ppstm = 0;
869 currentEntryRef = findElement(This->ancestorStorage,
870 This->storageDirEntry,
871 pwcsName,
872 &currentEntry);
874 if (currentEntryRef != DIRENTRY_NULL)
877 * An element with this name already exists
879 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
881 IStorage_DestroyElement(iface, pwcsName);
883 else
884 return STG_E_FILEALREADYEXISTS;
886 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
888 WARN("read-only storage\n");
889 return STG_E_ACCESSDENIED;
893 * memset the empty entry
895 memset(&newStreamEntry, 0, sizeof(DirEntry));
897 newStreamEntry.sizeOfNameString =
898 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
900 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
901 return STG_E_INVALIDNAME;
903 strcpyW(newStreamEntry.name, pwcsName);
905 newStreamEntry.stgType = STGTY_STREAM;
906 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
907 newStreamEntry.size.u.LowPart = 0;
908 newStreamEntry.size.u.HighPart = 0;
910 newStreamEntry.leftChild = DIRENTRY_NULL;
911 newStreamEntry.rightChild = DIRENTRY_NULL;
912 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
914 /* call CoFileTime to get the current time
915 newStreamEntry.ctime
916 newStreamEntry.mtime
919 /* newStreamEntry.clsid */
922 * Create an entry with the new data
924 StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
926 * Insert the new entry in the parent storage's tree.
928 insertIntoTree(
929 This->ancestorStorage,
930 This->storageDirEntry,
931 newStreamEntryRef);
934 * Open the stream to return it.
936 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
938 if (newStream != 0)
940 *ppstm = (IStream*)newStream;
942 IStream_AddRef(*ppstm);
944 else
946 return STG_E_INSUFFICIENTMEMORY;
949 return S_OK;
952 /************************************************************************
953 * Storage32BaseImpl_SetClass (IStorage)
955 * This method will write the specified CLSID in the directory entry of this
956 * storage.
958 * See Windows documentation for more details on IStorage methods.
960 static HRESULT WINAPI StorageBaseImpl_SetClass(
961 IStorage* iface,
962 REFCLSID clsid) /* [in] */
964 StorageBaseImpl *This = (StorageBaseImpl *)iface;
965 HRESULT hRes = E_FAIL;
966 DirEntry currentEntry;
967 BOOL success;
969 TRACE("(%p, %p)\n", iface, clsid);
971 if (!This->ancestorStorage)
972 return STG_E_REVERTED;
974 success = StorageImpl_ReadDirEntry(This->ancestorStorage,
975 This->storageDirEntry,
976 &currentEntry);
977 if (success)
979 currentEntry.clsid = *clsid;
981 hRes = StorageImpl_WriteDirEntry(This->ancestorStorage,
982 This->storageDirEntry,
983 &currentEntry);
986 return hRes;
989 /************************************************************************
990 ** Storage32Impl implementation
993 /************************************************************************
994 * Storage32BaseImpl_CreateStorage (IStorage)
996 * This method will create the storage object within the provided storage.
998 * See Windows documentation for more details on IStorage methods.
1000 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1001 IStorage* iface,
1002 const OLECHAR *pwcsName, /* [string][in] */
1003 DWORD grfMode, /* [in] */
1004 DWORD reserved1, /* [in] */
1005 DWORD reserved2, /* [in] */
1006 IStorage **ppstg) /* [out] */
1008 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1010 DirEntry currentEntry;
1011 DirEntry newEntry;
1012 DirRef currentEntryRef;
1013 DirRef newEntryRef;
1014 HRESULT hr;
1016 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1017 iface, debugstr_w(pwcsName), grfMode,
1018 reserved1, reserved2, ppstg);
1020 if (ppstg == 0)
1021 return STG_E_INVALIDPOINTER;
1023 if (This->openFlags & STGM_SIMPLE)
1025 return STG_E_INVALIDFUNCTION;
1028 if (pwcsName == 0)
1029 return STG_E_INVALIDNAME;
1031 *ppstg = NULL;
1033 if ( FAILED( validateSTGM(grfMode) ) ||
1034 (grfMode & STGM_DELETEONRELEASE) )
1036 WARN("bad grfMode: 0x%x\n", grfMode);
1037 return STG_E_INVALIDFLAG;
1040 if (!This->ancestorStorage)
1041 return STG_E_REVERTED;
1044 * Check that we're compatible with the parent's storage mode
1046 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1048 WARN("access denied\n");
1049 return STG_E_ACCESSDENIED;
1052 currentEntryRef = findElement(This->ancestorStorage,
1053 This->storageDirEntry,
1054 pwcsName,
1055 &currentEntry);
1057 if (currentEntryRef != DIRENTRY_NULL)
1060 * An element with this name already exists
1062 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1063 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1065 hr = IStorage_DestroyElement(iface, pwcsName);
1066 if (FAILED(hr))
1067 return hr;
1069 else
1071 WARN("file already exists\n");
1072 return STG_E_FILEALREADYEXISTS;
1075 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1077 WARN("read-only storage\n");
1078 return STG_E_ACCESSDENIED;
1081 memset(&newEntry, 0, sizeof(DirEntry));
1083 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1085 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1087 FIXME("name too long\n");
1088 return STG_E_INVALIDNAME;
1091 strcpyW(newEntry.name, pwcsName);
1093 newEntry.stgType = STGTY_STORAGE;
1094 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1095 newEntry.size.u.LowPart = 0;
1096 newEntry.size.u.HighPart = 0;
1098 newEntry.leftChild = DIRENTRY_NULL;
1099 newEntry.rightChild = DIRENTRY_NULL;
1100 newEntry.dirRootEntry = DIRENTRY_NULL;
1102 /* call CoFileTime to get the current time
1103 newEntry.ctime
1104 newEntry.mtime
1107 /* newEntry.clsid */
1110 * Create a new directory entry for the storage
1112 StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1115 * Insert the new directory entry into the parent storage's tree
1117 insertIntoTree(
1118 This->ancestorStorage,
1119 This->storageDirEntry,
1120 newEntryRef);
1123 * Open it to get a pointer to return.
1125 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1127 if( (hr != S_OK) || (*ppstg == NULL))
1129 return hr;
1133 return S_OK;
1137 /***************************************************************************
1139 * Internal Method
1141 * Reserve a directory entry in the file and initialize it.
1143 static HRESULT StorageImpl_CreateDirEntry(
1144 StorageBaseImpl *base,
1145 const DirEntry *newData,
1146 DirRef *index)
1148 StorageImpl *storage = (StorageImpl*)base;
1149 ULONG currentEntryIndex = 0;
1150 ULONG newEntryIndex = DIRENTRY_NULL;
1151 HRESULT hr = S_OK;
1152 BYTE currentData[RAW_DIRENTRY_SIZE];
1153 WORD sizeOfNameString;
1157 hr = StorageImpl_ReadRawDirEntry(storage,
1158 currentEntryIndex,
1159 currentData);
1161 if (SUCCEEDED(hr))
1163 StorageUtl_ReadWord(
1164 currentData,
1165 OFFSET_PS_NAMELENGTH,
1166 &sizeOfNameString);
1168 if (sizeOfNameString == 0)
1171 * The entry exists and is available, we found it.
1173 newEntryIndex = currentEntryIndex;
1176 else
1179 * We exhausted the directory entries, we will create more space below
1181 newEntryIndex = currentEntryIndex;
1183 currentEntryIndex++;
1185 } while (newEntryIndex == DIRENTRY_NULL);
1188 * grow the directory stream
1190 if (FAILED(hr))
1192 BYTE emptyData[RAW_DIRENTRY_SIZE];
1193 ULARGE_INTEGER newSize;
1194 ULONG entryIndex;
1195 ULONG lastEntry = 0;
1196 ULONG blockCount = 0;
1199 * obtain the new count of blocks in the directory stream
1201 blockCount = BlockChainStream_GetCount(
1202 storage->rootBlockChain)+1;
1205 * initialize the size used by the directory stream
1207 newSize.u.HighPart = 0;
1208 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1211 * add a block to the directory stream
1213 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1216 * memset the empty entry in order to initialize the unused newly
1217 * created entries
1219 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1222 * initialize them
1224 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1226 for(
1227 entryIndex = newEntryIndex + 1;
1228 entryIndex < lastEntry;
1229 entryIndex++)
1231 StorageImpl_WriteRawDirEntry(
1232 storage,
1233 entryIndex,
1234 emptyData);
1238 UpdateRawDirEntry(currentData, newData);
1240 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1242 if (SUCCEEDED(hr))
1243 *index = newEntryIndex;
1245 return hr;
1248 /***************************************************************************
1250 * Internal Method
1252 * Mark a directory entry in the file as free.
1254 static HRESULT destroyDirEntry(
1255 StorageImpl *storage,
1256 DirRef index)
1258 HRESULT hr;
1259 BYTE emptyData[RAW_DIRENTRY_SIZE];
1261 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1263 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1265 return hr;
1269 /****************************************************************************
1271 * Internal Method
1273 * Case insensitive comparison of DirEntry.name by first considering
1274 * their size.
1276 * Returns <0 when name1 < name2
1277 * >0 when name1 > name2
1278 * 0 when name1 == name2
1280 static LONG entryNameCmp(
1281 const OLECHAR *name1,
1282 const OLECHAR *name2)
1284 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1286 if (diff == 0)
1289 * We compare the string themselves only when they are of the same length
1291 diff = lstrcmpiW( name1, name2);
1294 return diff;
1297 /****************************************************************************
1299 * Internal Method
1301 * Add a directory entry to a storage
1303 static HRESULT insertIntoTree(
1304 StorageImpl *This,
1305 DirRef parentStorageIndex,
1306 DirRef newEntryIndex)
1308 DirEntry currentEntry;
1309 DirEntry newEntry;
1312 * Read the inserted entry
1314 StorageImpl_ReadDirEntry(This,
1315 newEntryIndex,
1316 &newEntry);
1319 * Read the storage entry
1321 StorageImpl_ReadDirEntry(This,
1322 parentStorageIndex,
1323 &currentEntry);
1325 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1328 * The root storage contains some element, therefore, start the research
1329 * for the appropriate location.
1331 BOOL found = 0;
1332 DirRef current, next, previous, currentEntryId;
1335 * Keep a reference to the root of the storage's element tree
1337 currentEntryId = currentEntry.dirRootEntry;
1340 * Read
1342 StorageImpl_ReadDirEntry(This,
1343 currentEntry.dirRootEntry,
1344 &currentEntry);
1346 previous = currentEntry.leftChild;
1347 next = currentEntry.rightChild;
1348 current = currentEntryId;
1350 while (found == 0)
1352 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1354 if (diff < 0)
1356 if (previous != DIRENTRY_NULL)
1358 StorageImpl_ReadDirEntry(This,
1359 previous,
1360 &currentEntry);
1361 current = previous;
1363 else
1365 currentEntry.leftChild = newEntryIndex;
1366 StorageImpl_WriteDirEntry(This,
1367 current,
1368 &currentEntry);
1369 found = 1;
1372 else if (diff > 0)
1374 if (next != DIRENTRY_NULL)
1376 StorageImpl_ReadDirEntry(This,
1377 next,
1378 &currentEntry);
1379 current = next;
1381 else
1383 currentEntry.rightChild = newEntryIndex;
1384 StorageImpl_WriteDirEntry(This,
1385 current,
1386 &currentEntry);
1387 found = 1;
1390 else
1393 * Trying to insert an item with the same name in the
1394 * subtree structure.
1396 return STG_E_FILEALREADYEXISTS;
1399 previous = currentEntry.leftChild;
1400 next = currentEntry.rightChild;
1403 else
1406 * The storage is empty, make the new entry the root of its element tree
1408 currentEntry.dirRootEntry = newEntryIndex;
1409 StorageImpl_WriteDirEntry(This,
1410 parentStorageIndex,
1411 &currentEntry);
1414 return S_OK;
1417 /****************************************************************************
1419 * Internal Method
1421 * Find and read the element of a storage with the given name.
1423 static DirRef findElement(StorageImpl *storage, DirRef storageEntry,
1424 const OLECHAR *name, DirEntry *data)
1426 DirRef currentEntry;
1428 /* Read the storage entry to find the root of the tree. */
1429 StorageImpl_ReadDirEntry(storage, storageEntry, data);
1431 currentEntry = data->dirRootEntry;
1433 while (currentEntry != DIRENTRY_NULL)
1435 LONG cmp;
1437 StorageImpl_ReadDirEntry(storage, currentEntry, data);
1439 cmp = entryNameCmp(name, data->name);
1441 if (cmp == 0)
1442 /* found it */
1443 break;
1445 else if (cmp < 0)
1446 currentEntry = data->leftChild;
1448 else if (cmp > 0)
1449 currentEntry = data->rightChild;
1452 return currentEntry;
1455 /****************************************************************************
1457 * Internal Method
1459 * Find and read the binary tree parent of the element with the given name.
1461 * If there is no such element, find a place where it could be inserted and
1462 * return STG_E_FILENOTFOUND.
1464 static HRESULT findTreeParent(StorageImpl *storage, DirRef storageEntry,
1465 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1466 ULONG *relation)
1468 DirRef childEntry;
1469 DirEntry childData;
1471 /* Read the storage entry to find the root of the tree. */
1472 StorageImpl_ReadDirEntry(storage, storageEntry, parentData);
1474 *parentEntry = storageEntry;
1475 *relation = DIRENTRY_RELATION_DIR;
1477 childEntry = parentData->dirRootEntry;
1479 while (childEntry != DIRENTRY_NULL)
1481 LONG cmp;
1483 StorageImpl_ReadDirEntry(storage, childEntry, &childData);
1485 cmp = entryNameCmp(childName, childData.name);
1487 if (cmp == 0)
1488 /* found it */
1489 break;
1491 else if (cmp < 0)
1493 *parentData = childData;
1494 *parentEntry = childEntry;
1495 *relation = DIRENTRY_RELATION_PREVIOUS;
1497 childEntry = parentData->leftChild;
1500 else if (cmp > 0)
1502 *parentData = childData;
1503 *parentEntry = childEntry;
1504 *relation = DIRENTRY_RELATION_NEXT;
1506 childEntry = parentData->rightChild;
1510 if (childEntry == DIRENTRY_NULL)
1511 return STG_E_FILENOTFOUND;
1512 else
1513 return S_OK;
1517 /*************************************************************************
1518 * CopyTo (IStorage)
1520 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1521 IStorage* iface,
1522 DWORD ciidExclude, /* [in] */
1523 const IID* rgiidExclude, /* [size_is][unique][in] */
1524 SNB snbExclude, /* [unique][in] */
1525 IStorage* pstgDest) /* [unique][in] */
1527 IEnumSTATSTG *elements = 0;
1528 STATSTG curElement, strStat;
1529 HRESULT hr;
1530 IStorage *pstgTmp, *pstgChild;
1531 IStream *pstrTmp, *pstrChild;
1532 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1533 int i;
1535 TRACE("(%p, %d, %p, %p, %p)\n",
1536 iface, ciidExclude, rgiidExclude,
1537 snbExclude, pstgDest);
1539 if ( pstgDest == 0 )
1540 return STG_E_INVALIDPOINTER;
1543 * Enumerate the elements
1545 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1547 if ( hr != S_OK )
1548 return hr;
1551 * set the class ID
1553 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1554 IStorage_SetClass( pstgDest, &curElement.clsid );
1556 for(i = 0; i < ciidExclude; ++i)
1558 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1559 skip_storage = TRUE;
1560 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1561 skip_stream = TRUE;
1562 else
1563 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1569 * Obtain the next element
1571 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1573 if ( hr == S_FALSE )
1575 hr = S_OK; /* done, every element has been copied */
1576 break;
1579 if ( snbExclude )
1581 WCHAR **snb = snbExclude;
1582 skip = FALSE;
1583 while ( *snb != NULL && !skip )
1585 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1586 skip = TRUE;
1587 ++snb;
1591 if ( skip )
1592 continue;
1594 if (curElement.type == STGTY_STORAGE)
1596 if(skip_storage)
1597 continue;
1600 * open child source storage
1602 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1603 STGM_READ|STGM_SHARE_EXCLUSIVE,
1604 NULL, 0, &pstgChild );
1606 if (hr != S_OK)
1607 break;
1610 * Check if destination storage is not a child of the source
1611 * storage, which will cause an infinite loop
1613 if (pstgChild == pstgDest)
1615 IEnumSTATSTG_Release(elements);
1617 return STG_E_ACCESSDENIED;
1621 * create a new storage in destination storage
1623 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1624 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1625 0, 0,
1626 &pstgTmp );
1628 * if it already exist, don't create a new one use this one
1630 if (hr == STG_E_FILEALREADYEXISTS)
1632 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1633 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1634 NULL, 0, &pstgTmp );
1637 if (hr != S_OK)
1638 break;
1642 * do the copy recursively
1644 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1645 NULL, pstgTmp );
1647 IStorage_Release( pstgTmp );
1648 IStorage_Release( pstgChild );
1650 else if (curElement.type == STGTY_STREAM)
1652 if(skip_stream)
1653 continue;
1656 * create a new stream in destination storage. If the stream already
1657 * exist, it will be deleted and a new one will be created.
1659 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1660 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1661 0, 0, &pstrTmp );
1663 if (hr != S_OK)
1664 break;
1667 * open child stream storage
1669 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1670 STGM_READ|STGM_SHARE_EXCLUSIVE,
1671 0, &pstrChild );
1673 if (hr != S_OK)
1674 break;
1677 * Get the size of the source stream
1679 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1682 * Set the size of the destination stream.
1684 IStream_SetSize(pstrTmp, strStat.cbSize);
1687 * do the copy
1689 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1690 NULL, NULL );
1692 IStream_Release( pstrTmp );
1693 IStream_Release( pstrChild );
1695 else
1697 WARN("unknown element type: %d\n", curElement.type);
1700 } while (hr == S_OK);
1703 * Clean-up
1705 IEnumSTATSTG_Release(elements);
1707 return hr;
1710 /*************************************************************************
1711 * MoveElementTo (IStorage)
1713 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1714 IStorage* iface,
1715 const OLECHAR *pwcsName, /* [string][in] */
1716 IStorage *pstgDest, /* [unique][in] */
1717 const OLECHAR *pwcsNewName,/* [string][in] */
1718 DWORD grfFlags) /* [in] */
1720 FIXME("(%p %s %p %s %u): stub\n", iface,
1721 debugstr_w(pwcsName), pstgDest,
1722 debugstr_w(pwcsNewName), grfFlags);
1723 return E_NOTIMPL;
1726 /*************************************************************************
1727 * Commit (IStorage)
1729 * Ensures that any changes made to a storage object open in transacted mode
1730 * are reflected in the parent storage
1732 * NOTES
1733 * Wine doesn't implement transacted mode, which seems to be a basic
1734 * optimization, so we can ignore this stub for now.
1736 static HRESULT WINAPI StorageImpl_Commit(
1737 IStorage* iface,
1738 DWORD grfCommitFlags)/* [in] */
1740 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1741 return S_OK;
1744 /*************************************************************************
1745 * Revert (IStorage)
1747 * Discard all changes that have been made since the last commit operation
1749 static HRESULT WINAPI StorageImpl_Revert(
1750 IStorage* iface)
1752 FIXME("(%p): stub\n", iface);
1753 return E_NOTIMPL;
1756 /*************************************************************************
1757 * DestroyElement (IStorage)
1759 * Strategy: This implementation is built this way for simplicity not for speed.
1760 * I always delete the topmost element of the enumeration and adjust
1761 * the deleted element pointer all the time. This takes longer to
1762 * do but allow to reinvoke DestroyElement whenever we encounter a
1763 * storage object. The optimisation resides in the usage of another
1764 * enumeration strategy that would give all the leaves of a storage
1765 * first. (postfix order)
1767 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1768 IStorage* iface,
1769 const OLECHAR *pwcsName)/* [string][in] */
1771 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1773 HRESULT hr = S_OK;
1774 DirEntry entryToDelete;
1775 DirRef entryToDeleteRef;
1777 TRACE("(%p, %s)\n",
1778 iface, debugstr_w(pwcsName));
1780 if (pwcsName==NULL)
1781 return STG_E_INVALIDPOINTER;
1783 if (!This->ancestorStorage)
1784 return STG_E_REVERTED;
1786 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1787 return STG_E_ACCESSDENIED;
1789 entryToDeleteRef = findElement(
1790 This->ancestorStorage,
1791 This->storageDirEntry,
1792 pwcsName,
1793 &entryToDelete);
1795 if ( entryToDeleteRef == DIRENTRY_NULL )
1797 return STG_E_FILENOTFOUND;
1800 if ( entryToDelete.stgType == STGTY_STORAGE )
1802 hr = deleteStorageContents(
1803 This,
1804 entryToDeleteRef,
1805 entryToDelete);
1807 else if ( entryToDelete.stgType == STGTY_STREAM )
1809 hr = deleteStreamContents(
1810 This,
1811 entryToDeleteRef,
1812 entryToDelete);
1815 if (hr!=S_OK)
1816 return hr;
1819 * Remove the entry from its parent storage
1821 hr = removeFromTree(
1822 This->ancestorStorage,
1823 This->storageDirEntry,
1824 entryToDeleteRef);
1827 * Invalidate the entry
1829 if (SUCCEEDED(hr))
1830 destroyDirEntry(This->ancestorStorage,
1831 entryToDeleteRef);
1833 return hr;
1837 /******************************************************************************
1838 * Internal stream list handlers
1841 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1843 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1844 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1847 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1849 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1850 list_remove(&(strm->StrmListEntry));
1853 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1855 StgStreamImpl *strm;
1857 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1859 if (strm->dirEntry == streamEntry)
1861 return TRUE;
1865 return FALSE;
1868 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1870 StorageInternalImpl *childstg;
1872 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1874 if (childstg->base.storageDirEntry == storageEntry)
1876 return TRUE;
1880 return FALSE;
1883 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1885 struct list *cur, *cur2;
1886 StgStreamImpl *strm=NULL;
1887 StorageInternalImpl *childstg=NULL;
1889 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1890 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1891 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1892 strm->parentStorage = NULL;
1893 list_remove(cur);
1896 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1897 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1898 StorageInternalImpl_Invalidate( childstg );
1903 /*********************************************************************
1905 * Internal Method
1907 * Delete the contents of a storage entry.
1910 static HRESULT deleteStorageContents(
1911 StorageBaseImpl *parentStorage,
1912 DirRef indexToDelete,
1913 DirEntry entryDataToDelete)
1915 IEnumSTATSTG *elements = 0;
1916 IStorage *childStorage = 0;
1917 STATSTG currentElement;
1918 HRESULT hr;
1919 HRESULT destroyHr = S_OK;
1920 StorageInternalImpl *stg, *stg2;
1922 /* Invalidate any open storage objects. */
1923 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1925 if (stg->base.storageDirEntry == indexToDelete)
1927 StorageInternalImpl_Invalidate(stg);
1932 * Open the storage and enumerate it
1934 hr = StorageBaseImpl_OpenStorage(
1935 (IStorage*)parentStorage,
1936 entryDataToDelete.name,
1938 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1941 &childStorage);
1943 if (hr != S_OK)
1945 return hr;
1949 * Enumerate the elements
1951 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1956 * Obtain the next element
1958 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1959 if (hr==S_OK)
1961 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
1963 CoTaskMemFree(currentElement.pwcsName);
1967 * We need to Reset the enumeration every time because we delete elements
1968 * and the enumeration could be invalid
1970 IEnumSTATSTG_Reset(elements);
1972 } while ((hr == S_OK) && (destroyHr == S_OK));
1974 IStorage_Release(childStorage);
1975 IEnumSTATSTG_Release(elements);
1977 return destroyHr;
1980 /*********************************************************************
1982 * Internal Method
1984 * Perform the deletion of a stream's data
1987 static HRESULT deleteStreamContents(
1988 StorageBaseImpl *parentStorage,
1989 DirRef indexToDelete,
1990 DirEntry entryDataToDelete)
1992 IStream *pis;
1993 HRESULT hr;
1994 ULARGE_INTEGER size;
1995 StgStreamImpl *strm, *strm2;
1997 /* Invalidate any open stream objects. */
1998 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2000 if (strm->dirEntry == indexToDelete)
2002 TRACE("Stream deleted %p\n", strm);
2003 strm->parentStorage = NULL;
2004 list_remove(&strm->StrmListEntry);
2008 size.u.HighPart = 0;
2009 size.u.LowPart = 0;
2011 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2012 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2014 if (hr!=S_OK)
2016 return(hr);
2020 * Zap the stream
2022 hr = IStream_SetSize(pis, size);
2024 if(hr != S_OK)
2026 return hr;
2030 * Release the stream object.
2032 IStream_Release(pis);
2034 return S_OK;
2037 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2039 switch (relation)
2041 case DIRENTRY_RELATION_PREVIOUS:
2042 entry->leftChild = new_target;
2043 break;
2044 case DIRENTRY_RELATION_NEXT:
2045 entry->rightChild = new_target;
2046 break;
2047 case DIRENTRY_RELATION_DIR:
2048 entry->dirRootEntry = new_target;
2049 break;
2050 default:
2051 assert(0);
2055 /*************************************************************************
2057 * Internal Method
2059 * This method removes a directory entry from its parent storage tree without
2060 * freeing any resources attached to it.
2062 static HRESULT removeFromTree(
2063 StorageImpl *This,
2064 DirRef parentStorageIndex,
2065 DirRef deletedIndex)
2067 HRESULT hr = S_OK;
2068 BOOL res = TRUE;
2069 DirEntry entryToDelete;
2070 DirEntry parentEntry;
2071 DirRef parentEntryRef;
2072 ULONG typeOfRelation;
2074 res = StorageImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2077 * Find the element that links to the one we want to delete.
2079 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2080 &parentEntry, &parentEntryRef, &typeOfRelation);
2082 if (hr != S_OK)
2083 return hr;
2085 if (entryToDelete.leftChild != DIRENTRY_NULL)
2088 * Replace the deleted entry with its left child
2090 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2092 hr = StorageImpl_WriteDirEntry(
2093 This,
2094 parentEntryRef,
2095 &parentEntry);
2096 if(FAILED(hr))
2098 return hr;
2101 if (entryToDelete.rightChild != DIRENTRY_NULL)
2104 * We need to reinsert the right child somewhere. We already know it and
2105 * its children are greater than everything in the left tree, so we
2106 * insert it at the rightmost point in the left tree.
2108 DirRef newRightChildParent = entryToDelete.leftChild;
2109 DirEntry newRightChildParentEntry;
2113 res = StorageImpl_ReadDirEntry(
2114 This,
2115 newRightChildParent,
2116 &newRightChildParentEntry);
2117 if (!res)
2119 return E_FAIL;
2122 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2123 newRightChildParent = newRightChildParentEntry.rightChild;
2124 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2126 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2128 hr = StorageImpl_WriteDirEntry(
2129 This,
2130 newRightChildParent,
2131 &newRightChildParentEntry);
2132 if (FAILED(hr))
2134 return hr;
2138 else
2141 * Replace the deleted entry with its right child
2143 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2145 hr = StorageImpl_WriteDirEntry(
2146 This,
2147 parentEntryRef,
2148 &parentEntry);
2149 if(FAILED(hr))
2151 return hr;
2155 return hr;
2159 /******************************************************************************
2160 * SetElementTimes (IStorage)
2162 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2163 IStorage* iface,
2164 const OLECHAR *pwcsName,/* [string][in] */
2165 const FILETIME *pctime, /* [in] */
2166 const FILETIME *patime, /* [in] */
2167 const FILETIME *pmtime) /* [in] */
2169 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2170 return S_OK;
2173 /******************************************************************************
2174 * SetStateBits (IStorage)
2176 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2177 IStorage* iface,
2178 DWORD grfStateBits,/* [in] */
2179 DWORD grfMask) /* [in] */
2181 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2183 if (!This->ancestorStorage)
2184 return STG_E_REVERTED;
2186 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2187 return S_OK;
2191 * Virtual function table for the IStorage32Impl class.
2193 static const IStorageVtbl Storage32Impl_Vtbl =
2195 StorageBaseImpl_QueryInterface,
2196 StorageBaseImpl_AddRef,
2197 StorageBaseImpl_Release,
2198 StorageBaseImpl_CreateStream,
2199 StorageBaseImpl_OpenStream,
2200 StorageBaseImpl_CreateStorage,
2201 StorageBaseImpl_OpenStorage,
2202 StorageBaseImpl_CopyTo,
2203 StorageBaseImpl_MoveElementTo,
2204 StorageImpl_Commit,
2205 StorageImpl_Revert,
2206 StorageBaseImpl_EnumElements,
2207 StorageBaseImpl_DestroyElement,
2208 StorageBaseImpl_RenameElement,
2209 StorageBaseImpl_SetElementTimes,
2210 StorageBaseImpl_SetClass,
2211 StorageBaseImpl_SetStateBits,
2212 StorageBaseImpl_Stat
2215 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2217 StorageImpl_Destroy,
2218 StorageImpl_CreateDirEntry
2221 static HRESULT StorageImpl_Construct(
2222 HANDLE hFile,
2223 LPCOLESTR pwcsName,
2224 ILockBytes* pLkbyt,
2225 DWORD openFlags,
2226 BOOL fileBased,
2227 BOOL create,
2228 StorageImpl** result)
2230 StorageImpl* This;
2231 HRESULT hr = S_OK;
2232 DirEntry currentEntry;
2233 BOOL readSuccessful;
2234 DirRef currentEntryRef;
2236 if ( FAILED( validateSTGM(openFlags) ))
2237 return STG_E_INVALIDFLAG;
2239 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2240 if (!This)
2241 return E_OUTOFMEMORY;
2243 memset(This, 0, sizeof(StorageImpl));
2245 list_init(&This->base.strmHead);
2247 list_init(&This->base.storageHead);
2249 This->base.lpVtbl = &Storage32Impl_Vtbl;
2250 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2251 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2252 This->base.openFlags = (openFlags & ~STGM_CREATE);
2253 This->base.ref = 1;
2254 This->base.create = create;
2257 * This is the top-level storage so initialize the ancestor pointer
2258 * to this.
2260 This->base.ancestorStorage = This;
2262 This->hFile = hFile;
2264 if(pwcsName) {
2265 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2266 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2267 if (!This->pwcsName)
2269 hr = STG_E_INSUFFICIENTMEMORY;
2270 goto end;
2272 strcpyW(This->pwcsName, pwcsName);
2274 memcpy(This->base.filename, pwcsName, DIRENTRY_NAME_BUFFER_LEN-1);
2275 This->base.filename[DIRENTRY_NAME_BUFFER_LEN-1] = 0;
2279 * Initialize the big block cache.
2281 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2282 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2283 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2284 pLkbyt,
2285 openFlags,
2286 This->bigBlockSize,
2287 fileBased);
2289 if (This->bigBlockFile == 0)
2291 hr = E_FAIL;
2292 goto end;
2295 if (create)
2297 ULARGE_INTEGER size;
2298 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2301 * Initialize all header variables:
2302 * - The big block depot consists of one block and it is at block 0
2303 * - The directory table starts at block 1
2304 * - There is no small block depot
2306 memset( This->bigBlockDepotStart,
2307 BLOCK_UNUSED,
2308 sizeof(This->bigBlockDepotStart));
2310 This->bigBlockDepotCount = 1;
2311 This->bigBlockDepotStart[0] = 0;
2312 This->rootStartBlock = 1;
2313 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2314 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2315 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2316 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2317 This->extBigBlockDepotCount = 0;
2319 StorageImpl_SaveFileHeader(This);
2322 * Add one block for the big block depot and one block for the directory table
2324 size.u.HighPart = 0;
2325 size.u.LowPart = This->bigBlockSize * 3;
2326 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2329 * Initialize the big block depot
2331 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2332 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2333 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2334 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2336 else
2339 * Load the header for the file.
2341 hr = StorageImpl_LoadFileHeader(This);
2343 if (FAILED(hr))
2345 goto end;
2350 * There is no block depot cached yet.
2352 This->indexBlockDepotCached = 0xFFFFFFFF;
2355 * Start searching for free blocks with block 0.
2357 This->prevFreeBlock = 0;
2360 * Create the block chain abstractions.
2362 if(!(This->rootBlockChain =
2363 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2365 hr = STG_E_READFAULT;
2366 goto end;
2369 if(!(This->smallBlockDepotChain =
2370 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2371 DIRENTRY_NULL)))
2373 hr = STG_E_READFAULT;
2374 goto end;
2378 * Write the root storage entry (memory only)
2380 if (create)
2382 DirEntry rootEntry;
2384 * Initialize the directory table
2386 memset(&rootEntry, 0, sizeof(rootEntry));
2387 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2388 sizeof(rootEntry.name)/sizeof(WCHAR) );
2389 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2390 rootEntry.stgType = STGTY_ROOT;
2391 rootEntry.leftChild = DIRENTRY_NULL;
2392 rootEntry.rightChild = DIRENTRY_NULL;
2393 rootEntry.dirRootEntry = DIRENTRY_NULL;
2394 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2395 rootEntry.size.u.HighPart = 0;
2396 rootEntry.size.u.LowPart = 0;
2398 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2402 * Find the ID of the root storage.
2404 currentEntryRef = 0;
2408 readSuccessful = StorageImpl_ReadDirEntry(
2409 This,
2410 currentEntryRef,
2411 &currentEntry);
2413 if (readSuccessful)
2415 if ( (currentEntry.sizeOfNameString != 0 ) &&
2416 (currentEntry.stgType == STGTY_ROOT) )
2418 This->base.storageDirEntry = currentEntryRef;
2422 currentEntryRef++;
2424 } while (readSuccessful && (This->base.storageDirEntry == DIRENTRY_NULL) );
2426 if (!readSuccessful)
2428 hr = STG_E_READFAULT;
2429 goto end;
2433 * Create the block chain abstraction for the small block root chain.
2435 if(!(This->smallBlockRootChain =
2436 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2438 hr = STG_E_READFAULT;
2441 end:
2442 if (FAILED(hr))
2444 IStorage_Release((IStorage*)This);
2445 *result = NULL;
2447 else
2448 *result = This;
2450 return hr;
2453 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2455 StorageImpl *This = (StorageImpl*) iface;
2456 TRACE("(%p)\n", This);
2458 StorageBaseImpl_DeleteAll(&This->base);
2460 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2462 BlockChainStream_Destroy(This->smallBlockRootChain);
2463 BlockChainStream_Destroy(This->rootBlockChain);
2464 BlockChainStream_Destroy(This->smallBlockDepotChain);
2466 if (This->bigBlockFile)
2467 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2468 HeapFree(GetProcessHeap(), 0, This);
2471 /******************************************************************************
2472 * Storage32Impl_GetNextFreeBigBlock
2474 * Returns the index of the next free big block.
2475 * If the big block depot is filled, this method will enlarge it.
2478 static ULONG StorageImpl_GetNextFreeBigBlock(
2479 StorageImpl* This)
2481 ULONG depotBlockIndexPos;
2482 BYTE depotBuffer[BIG_BLOCK_SIZE];
2483 BOOL success;
2484 ULONG depotBlockOffset;
2485 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2486 ULONG nextBlockIndex = BLOCK_SPECIAL;
2487 int depotIndex = 0;
2488 ULONG freeBlock = BLOCK_UNUSED;
2490 depotIndex = This->prevFreeBlock / blocksPerDepot;
2491 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2494 * Scan the entire big block depot until we find a block marked free
2496 while (nextBlockIndex != BLOCK_UNUSED)
2498 if (depotIndex < COUNT_BBDEPOTINHEADER)
2500 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2503 * Grow the primary depot.
2505 if (depotBlockIndexPos == BLOCK_UNUSED)
2507 depotBlockIndexPos = depotIndex*blocksPerDepot;
2510 * Add a block depot.
2512 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2513 This->bigBlockDepotCount++;
2514 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2517 * Flag it as a block depot.
2519 StorageImpl_SetNextBlockInChain(This,
2520 depotBlockIndexPos,
2521 BLOCK_SPECIAL);
2523 /* Save new header information.
2525 StorageImpl_SaveFileHeader(This);
2528 else
2530 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2532 if (depotBlockIndexPos == BLOCK_UNUSED)
2535 * Grow the extended depot.
2537 ULONG extIndex = BLOCK_UNUSED;
2538 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2539 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2541 if (extBlockOffset == 0)
2543 /* We need an extended block.
2545 extIndex = Storage32Impl_AddExtBlockDepot(This);
2546 This->extBigBlockDepotCount++;
2547 depotBlockIndexPos = extIndex + 1;
2549 else
2550 depotBlockIndexPos = depotIndex * blocksPerDepot;
2553 * Add a block depot and mark it in the extended block.
2555 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2556 This->bigBlockDepotCount++;
2557 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2559 /* Flag the block depot.
2561 StorageImpl_SetNextBlockInChain(This,
2562 depotBlockIndexPos,
2563 BLOCK_SPECIAL);
2565 /* If necessary, flag the extended depot block.
2567 if (extIndex != BLOCK_UNUSED)
2568 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2570 /* Save header information.
2572 StorageImpl_SaveFileHeader(This);
2576 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2578 if (success)
2580 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2581 ( nextBlockIndex != BLOCK_UNUSED))
2583 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2585 if (nextBlockIndex == BLOCK_UNUSED)
2587 freeBlock = (depotIndex * blocksPerDepot) +
2588 (depotBlockOffset/sizeof(ULONG));
2591 depotBlockOffset += sizeof(ULONG);
2595 depotIndex++;
2596 depotBlockOffset = 0;
2600 * make sure that the block physically exists before using it
2602 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2604 This->prevFreeBlock = freeBlock;
2606 return freeBlock;
2609 /******************************************************************************
2610 * Storage32Impl_AddBlockDepot
2612 * This will create a depot block, essentially it is a block initialized
2613 * to BLOCK_UNUSEDs.
2615 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2617 BYTE blockBuffer[BIG_BLOCK_SIZE];
2620 * Initialize blocks as free
2622 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2623 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2626 /******************************************************************************
2627 * Storage32Impl_GetExtDepotBlock
2629 * Returns the index of the block that corresponds to the specified depot
2630 * index. This method is only for depot indexes equal or greater than
2631 * COUNT_BBDEPOTINHEADER.
2633 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2635 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2636 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2637 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2638 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2639 ULONG blockIndex = BLOCK_UNUSED;
2640 ULONG extBlockIndex = This->extBigBlockDepotStart;
2642 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2644 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2645 return BLOCK_UNUSED;
2647 while (extBlockCount > 0)
2649 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2650 extBlockCount--;
2653 if (extBlockIndex != BLOCK_UNUSED)
2654 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2655 extBlockOffset * sizeof(ULONG), &blockIndex);
2657 return blockIndex;
2660 /******************************************************************************
2661 * Storage32Impl_SetExtDepotBlock
2663 * Associates the specified block index to the specified depot index.
2664 * This method is only for depot indexes equal or greater than
2665 * COUNT_BBDEPOTINHEADER.
2667 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2669 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2670 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2671 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2672 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2673 ULONG extBlockIndex = This->extBigBlockDepotStart;
2675 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2677 while (extBlockCount > 0)
2679 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2680 extBlockCount--;
2683 if (extBlockIndex != BLOCK_UNUSED)
2685 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2686 extBlockOffset * sizeof(ULONG),
2687 blockIndex);
2691 /******************************************************************************
2692 * Storage32Impl_AddExtBlockDepot
2694 * Creates an extended depot block.
2696 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2698 ULONG numExtBlocks = This->extBigBlockDepotCount;
2699 ULONG nextExtBlock = This->extBigBlockDepotStart;
2700 BYTE depotBuffer[BIG_BLOCK_SIZE];
2701 ULONG index = BLOCK_UNUSED;
2702 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2703 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2704 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2706 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2707 blocksPerDepotBlock;
2709 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2712 * The first extended block.
2714 This->extBigBlockDepotStart = index;
2716 else
2718 unsigned int i;
2720 * Follow the chain to the last one.
2722 for (i = 0; i < (numExtBlocks - 1); i++)
2724 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2728 * Add the new extended block to the chain.
2730 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2731 index);
2735 * Initialize this block.
2737 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2738 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2740 return index;
2743 /******************************************************************************
2744 * Storage32Impl_FreeBigBlock
2746 * This method will flag the specified block as free in the big block depot.
2748 static void StorageImpl_FreeBigBlock(
2749 StorageImpl* This,
2750 ULONG blockIndex)
2752 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2754 if (blockIndex < This->prevFreeBlock)
2755 This->prevFreeBlock = blockIndex;
2758 /************************************************************************
2759 * Storage32Impl_GetNextBlockInChain
2761 * This method will retrieve the block index of the next big block in
2762 * in the chain.
2764 * Params: This - Pointer to the Storage object.
2765 * blockIndex - Index of the block to retrieve the chain
2766 * for.
2767 * nextBlockIndex - receives the return value.
2769 * Returns: This method returns the index of the next block in the chain.
2770 * It will return the constants:
2771 * BLOCK_SPECIAL - If the block given was not part of a
2772 * chain.
2773 * BLOCK_END_OF_CHAIN - If the block given was the last in
2774 * a chain.
2775 * BLOCK_UNUSED - If the block given was not past of a chain
2776 * and is available.
2777 * BLOCK_EXTBBDEPOT - This block is part of the extended
2778 * big block depot.
2780 * See Windows documentation for more details on IStorage methods.
2782 static HRESULT StorageImpl_GetNextBlockInChain(
2783 StorageImpl* This,
2784 ULONG blockIndex,
2785 ULONG* nextBlockIndex)
2787 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2788 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2789 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2790 BYTE depotBuffer[BIG_BLOCK_SIZE];
2791 BOOL success;
2792 ULONG depotBlockIndexPos;
2793 int index;
2795 *nextBlockIndex = BLOCK_SPECIAL;
2797 if(depotBlockCount >= This->bigBlockDepotCount)
2799 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2800 This->bigBlockDepotCount);
2801 return STG_E_READFAULT;
2805 * Cache the currently accessed depot block.
2807 if (depotBlockCount != This->indexBlockDepotCached)
2809 This->indexBlockDepotCached = depotBlockCount;
2811 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2813 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2815 else
2818 * We have to look in the extended depot.
2820 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2823 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2825 if (!success)
2826 return STG_E_READFAULT;
2828 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2830 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2831 This->blockDepotCached[index] = *nextBlockIndex;
2835 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2837 return S_OK;
2840 /******************************************************************************
2841 * Storage32Impl_GetNextExtendedBlock
2843 * Given an extended block this method will return the next extended block.
2845 * NOTES:
2846 * The last ULONG of an extended block is the block index of the next
2847 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2848 * depot.
2850 * Return values:
2851 * - The index of the next extended block
2852 * - BLOCK_UNUSED: there is no next extended block.
2853 * - Any other return values denotes failure.
2855 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2857 ULONG nextBlockIndex = BLOCK_SPECIAL;
2858 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2860 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2861 &nextBlockIndex);
2863 return nextBlockIndex;
2866 /******************************************************************************
2867 * Storage32Impl_SetNextBlockInChain
2869 * This method will write the index of the specified block's next block
2870 * in the big block depot.
2872 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2873 * do the following
2875 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2876 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2877 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2880 static void StorageImpl_SetNextBlockInChain(
2881 StorageImpl* This,
2882 ULONG blockIndex,
2883 ULONG nextBlock)
2885 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2886 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2887 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2888 ULONG depotBlockIndexPos;
2890 assert(depotBlockCount < This->bigBlockDepotCount);
2891 assert(blockIndex != nextBlock);
2893 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2895 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2897 else
2900 * We have to look in the extended depot.
2902 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2905 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2906 nextBlock);
2908 * Update the cached block depot, if necessary.
2910 if (depotBlockCount == This->indexBlockDepotCached)
2912 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2916 /******************************************************************************
2917 * Storage32Impl_LoadFileHeader
2919 * This method will read in the file header, i.e. big block index -1.
2921 static HRESULT StorageImpl_LoadFileHeader(
2922 StorageImpl* This)
2924 HRESULT hr = STG_E_FILENOTFOUND;
2925 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2926 BOOL success;
2927 int index;
2929 TRACE("\n");
2931 * Get a pointer to the big block of data containing the header.
2933 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2936 * Extract the information from the header.
2938 if (success)
2941 * Check for the "magic number" signature and return an error if it is not
2942 * found.
2944 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2946 return STG_E_OLDFORMAT;
2949 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2951 return STG_E_INVALIDHEADER;
2954 StorageUtl_ReadWord(
2955 headerBigBlock,
2956 OFFSET_BIGBLOCKSIZEBITS,
2957 &This->bigBlockSizeBits);
2959 StorageUtl_ReadWord(
2960 headerBigBlock,
2961 OFFSET_SMALLBLOCKSIZEBITS,
2962 &This->smallBlockSizeBits);
2964 StorageUtl_ReadDWord(
2965 headerBigBlock,
2966 OFFSET_BBDEPOTCOUNT,
2967 &This->bigBlockDepotCount);
2969 StorageUtl_ReadDWord(
2970 headerBigBlock,
2971 OFFSET_ROOTSTARTBLOCK,
2972 &This->rootStartBlock);
2974 StorageUtl_ReadDWord(
2975 headerBigBlock,
2976 OFFSET_SBDEPOTSTART,
2977 &This->smallBlockDepotStart);
2979 StorageUtl_ReadDWord(
2980 headerBigBlock,
2981 OFFSET_EXTBBDEPOTSTART,
2982 &This->extBigBlockDepotStart);
2984 StorageUtl_ReadDWord(
2985 headerBigBlock,
2986 OFFSET_EXTBBDEPOTCOUNT,
2987 &This->extBigBlockDepotCount);
2989 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2991 StorageUtl_ReadDWord(
2992 headerBigBlock,
2993 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2994 &(This->bigBlockDepotStart[index]));
2998 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3000 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3001 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3004 * Right now, the code is making some assumptions about the size of the
3005 * blocks, just make sure they are what we're expecting.
3007 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3008 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3010 WARN("Broken OLE storage file\n");
3011 hr = STG_E_INVALIDHEADER;
3013 else
3014 hr = S_OK;
3017 return hr;
3020 /******************************************************************************
3021 * Storage32Impl_SaveFileHeader
3023 * This method will save to the file the header, i.e. big block -1.
3025 static void StorageImpl_SaveFileHeader(
3026 StorageImpl* This)
3028 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3029 int index;
3030 BOOL success;
3033 * Get a pointer to the big block of data containing the header.
3035 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3038 * If the block read failed, the file is probably new.
3040 if (!success)
3043 * Initialize for all unknown fields.
3045 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3048 * Initialize the magic number.
3050 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3053 * And a bunch of things we don't know what they mean
3055 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3056 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3057 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3058 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3062 * Write the information to the header.
3064 StorageUtl_WriteWord(
3065 headerBigBlock,
3066 OFFSET_BIGBLOCKSIZEBITS,
3067 This->bigBlockSizeBits);
3069 StorageUtl_WriteWord(
3070 headerBigBlock,
3071 OFFSET_SMALLBLOCKSIZEBITS,
3072 This->smallBlockSizeBits);
3074 StorageUtl_WriteDWord(
3075 headerBigBlock,
3076 OFFSET_BBDEPOTCOUNT,
3077 This->bigBlockDepotCount);
3079 StorageUtl_WriteDWord(
3080 headerBigBlock,
3081 OFFSET_ROOTSTARTBLOCK,
3082 This->rootStartBlock);
3084 StorageUtl_WriteDWord(
3085 headerBigBlock,
3086 OFFSET_SBDEPOTSTART,
3087 This->smallBlockDepotStart);
3089 StorageUtl_WriteDWord(
3090 headerBigBlock,
3091 OFFSET_SBDEPOTCOUNT,
3092 This->smallBlockDepotChain ?
3093 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3095 StorageUtl_WriteDWord(
3096 headerBigBlock,
3097 OFFSET_EXTBBDEPOTSTART,
3098 This->extBigBlockDepotStart);
3100 StorageUtl_WriteDWord(
3101 headerBigBlock,
3102 OFFSET_EXTBBDEPOTCOUNT,
3103 This->extBigBlockDepotCount);
3105 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3107 StorageUtl_WriteDWord(
3108 headerBigBlock,
3109 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3110 (This->bigBlockDepotStart[index]));
3114 * Write the big block back to the file.
3116 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3119 /******************************************************************************
3120 * StorageImpl_ReadRawDirEntry
3122 * This method will read the raw data from a directory entry in the file.
3124 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3126 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3128 ULARGE_INTEGER offset;
3129 HRESULT hr;
3130 ULONG bytesRead;
3132 offset.u.HighPart = 0;
3133 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3135 hr = BlockChainStream_ReadAt(
3136 This->rootBlockChain,
3137 offset,
3138 RAW_DIRENTRY_SIZE,
3139 buffer,
3140 &bytesRead);
3142 return hr;
3145 /******************************************************************************
3146 * StorageImpl_WriteRawDirEntry
3148 * This method will write the raw data from a directory entry in the file.
3150 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3152 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3154 ULARGE_INTEGER offset;
3155 HRESULT hr;
3156 ULONG bytesRead;
3158 offset.u.HighPart = 0;
3159 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3161 hr = BlockChainStream_WriteAt(
3162 This->rootBlockChain,
3163 offset,
3164 RAW_DIRENTRY_SIZE,
3165 buffer,
3166 &bytesRead);
3168 return hr;
3171 /******************************************************************************
3172 * UpdateRawDirEntry
3174 * Update raw directory entry data from the fields in newData.
3176 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3178 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3180 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3182 memcpy(
3183 buffer + OFFSET_PS_NAME,
3184 newData->name,
3185 DIRENTRY_NAME_BUFFER_LEN );
3187 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3189 StorageUtl_WriteWord(
3190 buffer,
3191 OFFSET_PS_NAMELENGTH,
3192 newData->sizeOfNameString);
3194 StorageUtl_WriteDWord(
3195 buffer,
3196 OFFSET_PS_LEFTCHILD,
3197 newData->leftChild);
3199 StorageUtl_WriteDWord(
3200 buffer,
3201 OFFSET_PS_RIGHTCHILD,
3202 newData->rightChild);
3204 StorageUtl_WriteDWord(
3205 buffer,
3206 OFFSET_PS_DIRROOT,
3207 newData->dirRootEntry);
3209 StorageUtl_WriteGUID(
3210 buffer,
3211 OFFSET_PS_GUID,
3212 &newData->clsid);
3214 StorageUtl_WriteDWord(
3215 buffer,
3216 OFFSET_PS_CTIMELOW,
3217 newData->ctime.dwLowDateTime);
3219 StorageUtl_WriteDWord(
3220 buffer,
3221 OFFSET_PS_CTIMEHIGH,
3222 newData->ctime.dwHighDateTime);
3224 StorageUtl_WriteDWord(
3225 buffer,
3226 OFFSET_PS_MTIMELOW,
3227 newData->mtime.dwLowDateTime);
3229 StorageUtl_WriteDWord(
3230 buffer,
3231 OFFSET_PS_MTIMEHIGH,
3232 newData->ctime.dwHighDateTime);
3234 StorageUtl_WriteDWord(
3235 buffer,
3236 OFFSET_PS_STARTBLOCK,
3237 newData->startingBlock);
3239 StorageUtl_WriteDWord(
3240 buffer,
3241 OFFSET_PS_SIZE,
3242 newData->size.u.LowPart);
3245 /******************************************************************************
3246 * Storage32Impl_ReadDirEntry
3248 * This method will read the specified directory entry.
3250 BOOL StorageImpl_ReadDirEntry(
3251 StorageImpl* This,
3252 DirRef index,
3253 DirEntry* buffer)
3255 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3256 HRESULT readRes;
3258 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3260 if (SUCCEEDED(readRes))
3262 memset(buffer->name, 0, sizeof(buffer->name));
3263 memcpy(
3264 buffer->name,
3265 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3266 DIRENTRY_NAME_BUFFER_LEN );
3267 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3269 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3271 StorageUtl_ReadWord(
3272 currentEntry,
3273 OFFSET_PS_NAMELENGTH,
3274 &buffer->sizeOfNameString);
3276 StorageUtl_ReadDWord(
3277 currentEntry,
3278 OFFSET_PS_LEFTCHILD,
3279 &buffer->leftChild);
3281 StorageUtl_ReadDWord(
3282 currentEntry,
3283 OFFSET_PS_RIGHTCHILD,
3284 &buffer->rightChild);
3286 StorageUtl_ReadDWord(
3287 currentEntry,
3288 OFFSET_PS_DIRROOT,
3289 &buffer->dirRootEntry);
3291 StorageUtl_ReadGUID(
3292 currentEntry,
3293 OFFSET_PS_GUID,
3294 &buffer->clsid);
3296 StorageUtl_ReadDWord(
3297 currentEntry,
3298 OFFSET_PS_CTIMELOW,
3299 &buffer->ctime.dwLowDateTime);
3301 StorageUtl_ReadDWord(
3302 currentEntry,
3303 OFFSET_PS_CTIMEHIGH,
3304 &buffer->ctime.dwHighDateTime);
3306 StorageUtl_ReadDWord(
3307 currentEntry,
3308 OFFSET_PS_MTIMELOW,
3309 &buffer->mtime.dwLowDateTime);
3311 StorageUtl_ReadDWord(
3312 currentEntry,
3313 OFFSET_PS_MTIMEHIGH,
3314 &buffer->mtime.dwHighDateTime);
3316 StorageUtl_ReadDWord(
3317 currentEntry,
3318 OFFSET_PS_STARTBLOCK,
3319 &buffer->startingBlock);
3321 StorageUtl_ReadDWord(
3322 currentEntry,
3323 OFFSET_PS_SIZE,
3324 &buffer->size.u.LowPart);
3326 buffer->size.u.HighPart = 0;
3329 return SUCCEEDED(readRes) ? TRUE : FALSE;
3332 /*********************************************************************
3333 * Write the specified directory entry to the file
3335 HRESULT StorageImpl_WriteDirEntry(
3336 StorageImpl* This,
3337 DirRef index,
3338 const DirEntry* buffer)
3340 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3341 HRESULT writeRes;
3343 UpdateRawDirEntry(currentEntry, buffer);
3345 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3346 return writeRes;
3349 static BOOL StorageImpl_ReadBigBlock(
3350 StorageImpl* This,
3351 ULONG blockIndex,
3352 void* buffer)
3354 ULARGE_INTEGER ulOffset;
3355 DWORD read;
3357 ulOffset.u.HighPart = 0;
3358 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3360 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3361 return (read == This->bigBlockSize);
3364 static BOOL StorageImpl_ReadDWordFromBigBlock(
3365 StorageImpl* This,
3366 ULONG blockIndex,
3367 ULONG offset,
3368 DWORD* value)
3370 ULARGE_INTEGER ulOffset;
3371 DWORD read;
3372 DWORD tmp;
3374 ulOffset.u.HighPart = 0;
3375 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3376 ulOffset.u.LowPart += offset;
3378 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3379 *value = lendian32toh(tmp);
3380 return (read == sizeof(DWORD));
3383 static BOOL StorageImpl_WriteBigBlock(
3384 StorageImpl* This,
3385 ULONG blockIndex,
3386 const void* buffer)
3388 ULARGE_INTEGER ulOffset;
3389 DWORD wrote;
3391 ulOffset.u.HighPart = 0;
3392 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3394 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3395 return (wrote == This->bigBlockSize);
3398 static BOOL StorageImpl_WriteDWordToBigBlock(
3399 StorageImpl* This,
3400 ULONG blockIndex,
3401 ULONG offset,
3402 DWORD value)
3404 ULARGE_INTEGER ulOffset;
3405 DWORD wrote;
3407 ulOffset.u.HighPart = 0;
3408 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3409 ulOffset.u.LowPart += offset;
3411 value = htole32(value);
3412 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3413 return (wrote == sizeof(DWORD));
3416 /******************************************************************************
3417 * Storage32Impl_SmallBlocksToBigBlocks
3419 * This method will convert a small block chain to a big block chain.
3420 * The small block chain will be destroyed.
3422 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3423 StorageImpl* This,
3424 SmallBlockChainStream** ppsbChain)
3426 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3427 ULARGE_INTEGER size, offset;
3428 ULONG cbRead, cbWritten;
3429 ULARGE_INTEGER cbTotalRead;
3430 DirRef streamEntryRef;
3431 HRESULT resWrite = S_OK;
3432 HRESULT resRead;
3433 DirEntry streamEntry;
3434 BYTE *buffer;
3435 BlockChainStream *bbTempChain = NULL;
3436 BlockChainStream *bigBlockChain = NULL;
3439 * Create a temporary big block chain that doesn't have
3440 * an associated directory entry. This temporary chain will be
3441 * used to copy data from small blocks to big blocks.
3443 bbTempChain = BlockChainStream_Construct(This,
3444 &bbHeadOfChain,
3445 DIRENTRY_NULL);
3446 if(!bbTempChain) return NULL;
3448 * Grow the big block chain.
3450 size = SmallBlockChainStream_GetSize(*ppsbChain);
3451 BlockChainStream_SetSize(bbTempChain, size);
3454 * Copy the contents of the small block chain to the big block chain
3455 * by small block size increments.
3457 offset.u.LowPart = 0;
3458 offset.u.HighPart = 0;
3459 cbTotalRead.QuadPart = 0;
3461 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3464 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3465 offset,
3466 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3467 buffer,
3468 &cbRead);
3469 if (FAILED(resRead))
3470 break;
3472 if (cbRead > 0)
3474 cbTotalRead.QuadPart += cbRead;
3476 resWrite = BlockChainStream_WriteAt(bbTempChain,
3477 offset,
3478 cbRead,
3479 buffer,
3480 &cbWritten);
3482 if (FAILED(resWrite))
3483 break;
3485 offset.u.LowPart += cbRead;
3487 } while (cbTotalRead.QuadPart < size.QuadPart);
3488 HeapFree(GetProcessHeap(),0,buffer);
3490 size.u.HighPart = 0;
3491 size.u.LowPart = 0;
3493 if (FAILED(resRead) || FAILED(resWrite))
3495 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3496 BlockChainStream_SetSize(bbTempChain, size);
3497 BlockChainStream_Destroy(bbTempChain);
3498 return NULL;
3502 * Destroy the small block chain.
3504 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3505 SmallBlockChainStream_SetSize(*ppsbChain, size);
3506 SmallBlockChainStream_Destroy(*ppsbChain);
3507 *ppsbChain = 0;
3510 * Change the directory entry. This chain is now a big block chain
3511 * and it doesn't reside in the small blocks chain anymore.
3513 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3515 streamEntry.startingBlock = bbHeadOfChain;
3517 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3520 * Destroy the temporary entryless big block chain.
3521 * Create a new big block chain associated with this entry.
3523 BlockChainStream_Destroy(bbTempChain);
3524 bigBlockChain = BlockChainStream_Construct(This,
3525 NULL,
3526 streamEntryRef);
3528 return bigBlockChain;
3531 /******************************************************************************
3532 * Storage32Impl_BigBlocksToSmallBlocks
3534 * This method will convert a big block chain to a small block chain.
3535 * The big block chain will be destroyed on success.
3537 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3538 StorageImpl* This,
3539 BlockChainStream** ppbbChain)
3541 ULARGE_INTEGER size, offset, cbTotalRead;
3542 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3543 DirRef streamEntryRef;
3544 HRESULT resWrite = S_OK, resRead;
3545 DirEntry streamEntry;
3546 BYTE* buffer;
3547 SmallBlockChainStream* sbTempChain;
3549 TRACE("%p %p\n", This, ppbbChain);
3551 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3552 DIRENTRY_NULL);
3554 if(!sbTempChain)
3555 return NULL;
3557 size = BlockChainStream_GetSize(*ppbbChain);
3558 SmallBlockChainStream_SetSize(sbTempChain, size);
3560 offset.u.HighPart = 0;
3561 offset.u.LowPart = 0;
3562 cbTotalRead.QuadPart = 0;
3563 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3566 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3567 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3568 buffer, &cbRead);
3570 if(FAILED(resRead))
3571 break;
3573 if(cbRead > 0)
3575 cbTotalRead.QuadPart += cbRead;
3577 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3578 cbRead, buffer, &cbWritten);
3580 if(FAILED(resWrite))
3581 break;
3583 offset.u.LowPart += cbRead;
3585 }while(cbTotalRead.QuadPart < size.QuadPart);
3586 HeapFree(GetProcessHeap(), 0, buffer);
3588 size.u.HighPart = 0;
3589 size.u.LowPart = 0;
3591 if(FAILED(resRead) || FAILED(resWrite))
3593 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3594 SmallBlockChainStream_SetSize(sbTempChain, size);
3595 SmallBlockChainStream_Destroy(sbTempChain);
3596 return NULL;
3599 /* destroy the original big block chain */
3600 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3601 BlockChainStream_SetSize(*ppbbChain, size);
3602 BlockChainStream_Destroy(*ppbbChain);
3603 *ppbbChain = NULL;
3605 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3606 streamEntry.startingBlock = sbHeadOfChain;
3607 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3609 SmallBlockChainStream_Destroy(sbTempChain);
3610 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3613 static void StorageInternalImpl_Invalidate( StorageInternalImpl *This )
3615 if (This->base.ancestorStorage)
3617 TRACE("Storage invalidated (stg=%p)\n", This);
3619 This->base.ancestorStorage = NULL;
3621 StorageBaseImpl_DeleteAll(&This->base);
3623 list_remove(&This->ParentListEntry);
3627 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3629 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3631 StorageInternalImpl_Invalidate(This);
3633 HeapFree(GetProcessHeap(), 0, This);
3636 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
3637 const DirEntry *newData, DirRef *index)
3639 return StorageBaseImpl_CreateDirEntry(&base->ancestorStorage->base,
3640 newData, index);
3643 /******************************************************************************
3645 ** Storage32InternalImpl_Commit
3648 static HRESULT WINAPI StorageInternalImpl_Commit(
3649 IStorage* iface,
3650 DWORD grfCommitFlags) /* [in] */
3652 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3653 return S_OK;
3656 /******************************************************************************
3658 ** Storage32InternalImpl_Revert
3661 static HRESULT WINAPI StorageInternalImpl_Revert(
3662 IStorage* iface)
3664 FIXME("(%p): stub\n", iface);
3665 return S_OK;
3668 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3670 IStorage_Release((IStorage*)This->parentStorage);
3671 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3672 HeapFree(GetProcessHeap(), 0, This);
3675 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3676 IEnumSTATSTG* iface,
3677 REFIID riid,
3678 void** ppvObject)
3680 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3682 if (ppvObject==0)
3683 return E_INVALIDARG;
3685 *ppvObject = 0;
3687 if (IsEqualGUID(&IID_IUnknown, riid) ||
3688 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3690 *ppvObject = This;
3691 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3692 return S_OK;
3695 return E_NOINTERFACE;
3698 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3699 IEnumSTATSTG* iface)
3701 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3702 return InterlockedIncrement(&This->ref);
3705 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3706 IEnumSTATSTG* iface)
3708 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3710 ULONG newRef;
3712 newRef = InterlockedDecrement(&This->ref);
3714 if (newRef==0)
3716 IEnumSTATSTGImpl_Destroy(This);
3719 return newRef;
3722 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3723 IEnumSTATSTG* iface,
3724 ULONG celt,
3725 STATSTG* rgelt,
3726 ULONG* pceltFetched)
3728 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3730 DirEntry currentEntry;
3731 STATSTG* currentReturnStruct = rgelt;
3732 ULONG objectFetched = 0;
3733 DirRef currentSearchNode;
3735 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3736 return E_INVALIDARG;
3739 * To avoid the special case, get another pointer to a ULONG value if
3740 * the caller didn't supply one.
3742 if (pceltFetched==0)
3743 pceltFetched = &objectFetched;
3746 * Start the iteration, we will iterate until we hit the end of the
3747 * linked list or until we hit the number of items to iterate through
3749 *pceltFetched = 0;
3752 * Start with the node at the top of the stack.
3754 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3756 while ( ( *pceltFetched < celt) &&
3757 ( currentSearchNode!=DIRENTRY_NULL) )
3760 * Remove the top node from the stack
3762 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3765 * Read the entry from the storage.
3767 StorageImpl_ReadDirEntry(This->parentStorage,
3768 currentSearchNode,
3769 &currentEntry);
3772 * Copy the information to the return buffer.
3774 StorageUtl_CopyDirEntryToSTATSTG(&This->parentStorage->base,
3775 currentReturnStruct,
3776 &currentEntry,
3777 STATFLAG_DEFAULT);
3780 * Step to the next item in the iteration
3782 (*pceltFetched)++;
3783 currentReturnStruct++;
3786 * Push the next search node in the search stack.
3788 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
3791 * continue the iteration.
3793 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3796 if (*pceltFetched == celt)
3797 return S_OK;
3799 return S_FALSE;
3803 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3804 IEnumSTATSTG* iface,
3805 ULONG celt)
3807 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3809 DirEntry currentEntry;
3810 ULONG objectFetched = 0;
3811 DirRef currentSearchNode;
3814 * Start with the node at the top of the stack.
3816 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3818 while ( (objectFetched < celt) &&
3819 (currentSearchNode!=DIRENTRY_NULL) )
3822 * Remove the top node from the stack
3824 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3827 * Read the entry from the storage.
3829 StorageImpl_ReadDirEntry(This->parentStorage,
3830 currentSearchNode,
3831 &currentEntry);
3834 * Step to the next item in the iteration
3836 objectFetched++;
3839 * Push the next search node in the search stack.
3841 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
3844 * continue the iteration.
3846 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3849 if (objectFetched == celt)
3850 return S_OK;
3852 return S_FALSE;
3855 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3856 IEnumSTATSTG* iface)
3858 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3860 DirEntry storageEntry;
3861 BOOL readSuccessful;
3864 * Re-initialize the search stack to an empty stack
3866 This->stackSize = 0;
3869 * Read the storage entry from the top-level storage.
3871 readSuccessful = StorageImpl_ReadDirEntry(
3872 This->parentStorage,
3873 This->storageDirEntry,
3874 &storageEntry);
3876 if (readSuccessful)
3878 assert(storageEntry.sizeOfNameString!=0);
3881 * Push the search node in the search stack.
3883 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.dirRootEntry);
3886 return S_OK;
3889 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3890 IEnumSTATSTG* iface,
3891 IEnumSTATSTG** ppenum)
3893 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3895 IEnumSTATSTGImpl* newClone;
3898 * Perform a sanity check on the parameters.
3900 if (ppenum==0)
3901 return E_INVALIDARG;
3903 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3904 This->storageDirEntry);
3908 * The new clone enumeration must point to the same current node as
3909 * the ole one.
3911 newClone->stackSize = This->stackSize ;
3912 newClone->stackMaxSize = This->stackMaxSize ;
3913 newClone->stackToVisit =
3914 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3916 memcpy(
3917 newClone->stackToVisit,
3918 This->stackToVisit,
3919 sizeof(DirRef) * newClone->stackSize);
3921 *ppenum = (IEnumSTATSTG*)newClone;
3924 * Don't forget to nail down a reference to the clone before
3925 * returning it.
3927 IEnumSTATSTGImpl_AddRef(*ppenum);
3929 return S_OK;
3932 static void IEnumSTATSTGImpl_PushSearchNode(
3933 IEnumSTATSTGImpl* This,
3934 DirRef nodeToPush)
3936 DirEntry storageEntry;
3937 BOOL readSuccessful;
3940 * First, make sure we're not trying to push an unexisting node.
3942 if (nodeToPush==DIRENTRY_NULL)
3943 return;
3946 * First push the node to the stack
3948 if (This->stackSize == This->stackMaxSize)
3950 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3952 This->stackToVisit = HeapReAlloc(
3953 GetProcessHeap(),
3955 This->stackToVisit,
3956 sizeof(DirRef) * This->stackMaxSize);
3959 This->stackToVisit[This->stackSize] = nodeToPush;
3960 This->stackSize++;
3963 * Read the storage entry from the top-level storage.
3965 readSuccessful = StorageImpl_ReadDirEntry(
3966 This->parentStorage,
3967 nodeToPush,
3968 &storageEntry);
3970 if (readSuccessful)
3972 assert(storageEntry.sizeOfNameString!=0);
3975 * Push the previous search node in the search stack.
3977 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.leftChild);
3981 static DirRef IEnumSTATSTGImpl_PopSearchNode(
3982 IEnumSTATSTGImpl* This,
3983 BOOL remove)
3985 DirRef topNode;
3987 if (This->stackSize == 0)
3988 return DIRENTRY_NULL;
3990 topNode = This->stackToVisit[This->stackSize-1];
3992 if (remove)
3993 This->stackSize--;
3995 return topNode;
3999 * Virtual function table for the IEnumSTATSTGImpl class.
4001 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4003 IEnumSTATSTGImpl_QueryInterface,
4004 IEnumSTATSTGImpl_AddRef,
4005 IEnumSTATSTGImpl_Release,
4006 IEnumSTATSTGImpl_Next,
4007 IEnumSTATSTGImpl_Skip,
4008 IEnumSTATSTGImpl_Reset,
4009 IEnumSTATSTGImpl_Clone
4012 /******************************************************************************
4013 ** IEnumSTATSTGImpl implementation
4016 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4017 StorageImpl* parentStorage,
4018 DirRef storageDirEntry)
4020 IEnumSTATSTGImpl* newEnumeration;
4022 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4024 if (newEnumeration!=0)
4027 * Set-up the virtual function table and reference count.
4029 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4030 newEnumeration->ref = 0;
4033 * We want to nail-down the reference to the storage in case the
4034 * enumeration out-lives the storage in the client application.
4036 newEnumeration->parentStorage = parentStorage;
4037 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4039 newEnumeration->storageDirEntry = storageDirEntry;
4042 * Initialize the search stack
4044 newEnumeration->stackSize = 0;
4045 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4046 newEnumeration->stackToVisit =
4047 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef)*ENUMSTATSGT_SIZE_INCREMENT);
4050 * Make sure the current node of the iterator is the first one.
4052 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4055 return newEnumeration;
4059 * Virtual function table for the Storage32InternalImpl class.
4061 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4063 StorageBaseImpl_QueryInterface,
4064 StorageBaseImpl_AddRef,
4065 StorageBaseImpl_Release,
4066 StorageBaseImpl_CreateStream,
4067 StorageBaseImpl_OpenStream,
4068 StorageBaseImpl_CreateStorage,
4069 StorageBaseImpl_OpenStorage,
4070 StorageBaseImpl_CopyTo,
4071 StorageBaseImpl_MoveElementTo,
4072 StorageInternalImpl_Commit,
4073 StorageInternalImpl_Revert,
4074 StorageBaseImpl_EnumElements,
4075 StorageBaseImpl_DestroyElement,
4076 StorageBaseImpl_RenameElement,
4077 StorageBaseImpl_SetElementTimes,
4078 StorageBaseImpl_SetClass,
4079 StorageBaseImpl_SetStateBits,
4080 StorageBaseImpl_Stat
4083 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4085 StorageInternalImpl_Destroy,
4086 StorageInternalImpl_CreateDirEntry
4089 /******************************************************************************
4090 ** Storage32InternalImpl implementation
4093 static StorageInternalImpl* StorageInternalImpl_Construct(
4094 StorageImpl* ancestorStorage,
4095 DWORD openFlags,
4096 DirRef storageDirEntry)
4098 StorageInternalImpl* newStorage;
4100 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4102 if (newStorage!=0)
4104 list_init(&newStorage->base.strmHead);
4106 list_init(&newStorage->base.storageHead);
4109 * Initialize the virtual function table.
4111 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4112 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4113 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4116 * Keep the ancestor storage pointer but do not nail a reference to it.
4118 newStorage->base.ancestorStorage = ancestorStorage;
4121 * Keep a reference to the directory entry of this storage
4123 newStorage->base.storageDirEntry = storageDirEntry;
4125 newStorage->base.create = 0;
4127 return newStorage;
4130 return 0;
4133 /******************************************************************************
4134 ** StorageUtl implementation
4137 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4139 WORD tmp;
4141 memcpy(&tmp, buffer+offset, sizeof(WORD));
4142 *value = lendian16toh(tmp);
4145 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4147 value = htole16(value);
4148 memcpy(buffer+offset, &value, sizeof(WORD));
4151 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4153 DWORD tmp;
4155 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4156 *value = lendian32toh(tmp);
4159 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4161 value = htole32(value);
4162 memcpy(buffer+offset, &value, sizeof(DWORD));
4165 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4166 ULARGE_INTEGER* value)
4168 #ifdef WORDS_BIGENDIAN
4169 ULARGE_INTEGER tmp;
4171 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4172 value->u.LowPart = htole32(tmp.u.HighPart);
4173 value->u.HighPart = htole32(tmp.u.LowPart);
4174 #else
4175 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4176 #endif
4179 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4180 const ULARGE_INTEGER *value)
4182 #ifdef WORDS_BIGENDIAN
4183 ULARGE_INTEGER tmp;
4185 tmp.u.LowPart = htole32(value->u.HighPart);
4186 tmp.u.HighPart = htole32(value->u.LowPart);
4187 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4188 #else
4189 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4190 #endif
4193 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4195 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4196 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4197 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4199 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4202 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4204 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4205 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4206 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4208 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4211 void StorageUtl_CopyDirEntryToSTATSTG(
4212 StorageBaseImpl* storage,
4213 STATSTG* destination,
4214 const DirEntry* source,
4215 int statFlags)
4217 LPCWSTR entryName;
4219 if (source->stgType == STGTY_ROOT)
4221 /* replace the name of root entry (often "Root Entry") by the file name */
4222 entryName = storage->filename;
4224 else
4226 entryName = source->name;
4230 * The copy of the string occurs only when the flag is not set
4232 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4233 (entryName == NULL) ||
4234 (entryName[0] == 0) )
4236 destination->pwcsName = 0;
4238 else
4240 destination->pwcsName =
4241 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4243 strcpyW(destination->pwcsName, entryName);
4246 switch (source->stgType)
4248 case STGTY_STORAGE:
4249 case STGTY_ROOT:
4250 destination->type = STGTY_STORAGE;
4251 break;
4252 case STGTY_STREAM:
4253 destination->type = STGTY_STREAM;
4254 break;
4255 default:
4256 destination->type = STGTY_STREAM;
4257 break;
4260 destination->cbSize = source->size;
4262 currentReturnStruct->mtime = {0}; TODO
4263 currentReturnStruct->ctime = {0};
4264 currentReturnStruct->atime = {0};
4266 destination->grfMode = 0;
4267 destination->grfLocksSupported = 0;
4268 destination->clsid = source->clsid;
4269 destination->grfStateBits = 0;
4270 destination->reserved = 0;
4273 /******************************************************************************
4274 ** BlockChainStream implementation
4277 BlockChainStream* BlockChainStream_Construct(
4278 StorageImpl* parentStorage,
4279 ULONG* headOfStreamPlaceHolder,
4280 DirRef dirEntry)
4282 BlockChainStream* newStream;
4283 ULONG blockIndex;
4285 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4287 newStream->parentStorage = parentStorage;
4288 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4289 newStream->ownerDirEntry = dirEntry;
4290 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4291 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4292 newStream->numBlocks = 0;
4294 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4296 while (blockIndex != BLOCK_END_OF_CHAIN)
4298 newStream->numBlocks++;
4299 newStream->tailIndex = blockIndex;
4301 if(FAILED(StorageImpl_GetNextBlockInChain(
4302 parentStorage,
4303 blockIndex,
4304 &blockIndex)))
4306 HeapFree(GetProcessHeap(), 0, newStream);
4307 return NULL;
4311 return newStream;
4314 void BlockChainStream_Destroy(BlockChainStream* This)
4316 HeapFree(GetProcessHeap(), 0, This);
4319 /******************************************************************************
4320 * BlockChainStream_GetHeadOfChain
4322 * Returns the head of this stream chain.
4323 * Some special chains don't have directory entries, their heads are kept in
4324 * This->headOfStreamPlaceHolder.
4327 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4329 DirEntry chainEntry;
4330 BOOL readSuccessful;
4332 if (This->headOfStreamPlaceHolder != 0)
4333 return *(This->headOfStreamPlaceHolder);
4335 if (This->ownerDirEntry != DIRENTRY_NULL)
4337 readSuccessful = StorageImpl_ReadDirEntry(
4338 This->parentStorage,
4339 This->ownerDirEntry,
4340 &chainEntry);
4342 if (readSuccessful)
4344 return chainEntry.startingBlock;
4348 return BLOCK_END_OF_CHAIN;
4351 /******************************************************************************
4352 * BlockChainStream_GetCount
4354 * Returns the number of blocks that comprises this chain.
4355 * This is not the size of the stream as the last block may not be full!
4358 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4360 ULONG blockIndex;
4361 ULONG count = 0;
4363 blockIndex = BlockChainStream_GetHeadOfChain(This);
4365 while (blockIndex != BLOCK_END_OF_CHAIN)
4367 count++;
4369 if(FAILED(StorageImpl_GetNextBlockInChain(
4370 This->parentStorage,
4371 blockIndex,
4372 &blockIndex)))
4373 return 0;
4376 return count;
4379 /******************************************************************************
4380 * BlockChainStream_ReadAt
4382 * Reads a specified number of bytes from this chain at the specified offset.
4383 * bytesRead may be NULL.
4384 * Failure will be returned if the specified number of bytes has not been read.
4386 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4387 ULARGE_INTEGER offset,
4388 ULONG size,
4389 void* buffer,
4390 ULONG* bytesRead)
4392 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4393 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4394 ULONG bytesToReadInBuffer;
4395 ULONG blockIndex;
4396 BYTE* bufferWalker;
4398 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4401 * Find the first block in the stream that contains part of the buffer.
4403 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4404 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4405 (blockNoInSequence < This->lastBlockNoInSequence) )
4407 blockIndex = BlockChainStream_GetHeadOfChain(This);
4408 This->lastBlockNoInSequence = blockNoInSequence;
4410 else
4412 ULONG temp = blockNoInSequence;
4414 blockIndex = This->lastBlockNoInSequenceIndex;
4415 blockNoInSequence -= This->lastBlockNoInSequence;
4416 This->lastBlockNoInSequence = temp;
4419 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4421 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4422 return STG_E_DOCFILECORRUPT;
4423 blockNoInSequence--;
4426 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4427 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4429 This->lastBlockNoInSequenceIndex = blockIndex;
4432 * Start reading the buffer.
4434 *bytesRead = 0;
4435 bufferWalker = buffer;
4437 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4439 ULARGE_INTEGER ulOffset;
4440 DWORD bytesReadAt;
4442 * Calculate how many bytes we can copy from this big block.
4444 bytesToReadInBuffer =
4445 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4447 TRACE("block %i\n",blockIndex);
4448 ulOffset.u.HighPart = 0;
4449 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4450 offsetInBlock;
4452 StorageImpl_ReadAt(This->parentStorage,
4453 ulOffset,
4454 bufferWalker,
4455 bytesToReadInBuffer,
4456 &bytesReadAt);
4458 * Step to the next big block.
4460 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4461 return STG_E_DOCFILECORRUPT;
4463 bufferWalker += bytesReadAt;
4464 size -= bytesReadAt;
4465 *bytesRead += bytesReadAt;
4466 offsetInBlock = 0; /* There is no offset on the next block */
4468 if (bytesToReadInBuffer != bytesReadAt)
4469 break;
4472 return (size == 0) ? S_OK : STG_E_READFAULT;
4475 /******************************************************************************
4476 * BlockChainStream_WriteAt
4478 * Writes the specified number of bytes to this chain at the specified offset.
4479 * Will fail if not all specified number of bytes have been written.
4481 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4482 ULARGE_INTEGER offset,
4483 ULONG size,
4484 const void* buffer,
4485 ULONG* bytesWritten)
4487 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4488 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4489 ULONG bytesToWrite;
4490 ULONG blockIndex;
4491 const BYTE* bufferWalker;
4494 * Find the first block in the stream that contains part of the buffer.
4496 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4497 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4498 (blockNoInSequence < This->lastBlockNoInSequence) )
4500 blockIndex = BlockChainStream_GetHeadOfChain(This);
4501 This->lastBlockNoInSequence = blockNoInSequence;
4503 else
4505 ULONG temp = blockNoInSequence;
4507 blockIndex = This->lastBlockNoInSequenceIndex;
4508 blockNoInSequence -= This->lastBlockNoInSequence;
4509 This->lastBlockNoInSequence = temp;
4512 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4514 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4515 &blockIndex)))
4516 return STG_E_DOCFILECORRUPT;
4517 blockNoInSequence--;
4520 This->lastBlockNoInSequenceIndex = blockIndex;
4522 /* BlockChainStream_SetSize should have already been called to ensure we have
4523 * enough blocks in the chain to write into */
4524 if (blockIndex == BLOCK_END_OF_CHAIN)
4526 ERR("not enough blocks in chain to write data\n");
4527 return STG_E_DOCFILECORRUPT;
4530 *bytesWritten = 0;
4531 bufferWalker = buffer;
4533 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4535 ULARGE_INTEGER ulOffset;
4536 DWORD bytesWrittenAt;
4538 * Calculate how many bytes we can copy from this big block.
4540 bytesToWrite =
4541 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4543 TRACE("block %i\n",blockIndex);
4544 ulOffset.u.HighPart = 0;
4545 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4546 offsetInBlock;
4548 StorageImpl_WriteAt(This->parentStorage,
4549 ulOffset,
4550 bufferWalker,
4551 bytesToWrite,
4552 &bytesWrittenAt);
4555 * Step to the next big block.
4557 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4558 &blockIndex)))
4559 return STG_E_DOCFILECORRUPT;
4561 bufferWalker += bytesWrittenAt;
4562 size -= bytesWrittenAt;
4563 *bytesWritten += bytesWrittenAt;
4564 offsetInBlock = 0; /* There is no offset on the next block */
4566 if (bytesWrittenAt != bytesToWrite)
4567 break;
4570 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4573 /******************************************************************************
4574 * BlockChainStream_Shrink
4576 * Shrinks this chain in the big block depot.
4578 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4579 ULARGE_INTEGER newSize)
4581 ULONG blockIndex, extraBlock;
4582 ULONG numBlocks;
4583 ULONG count = 1;
4586 * Reset the last accessed block cache.
4588 This->lastBlockNoInSequence = 0xFFFFFFFF;
4589 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4592 * Figure out how many blocks are needed to contain the new size
4594 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4596 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4597 numBlocks++;
4599 blockIndex = BlockChainStream_GetHeadOfChain(This);
4602 * Go to the new end of chain
4604 while (count < numBlocks)
4606 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4607 &blockIndex)))
4608 return FALSE;
4609 count++;
4612 /* Get the next block before marking the new end */
4613 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4614 &extraBlock)))
4615 return FALSE;
4617 /* Mark the new end of chain */
4618 StorageImpl_SetNextBlockInChain(
4619 This->parentStorage,
4620 blockIndex,
4621 BLOCK_END_OF_CHAIN);
4623 This->tailIndex = blockIndex;
4624 This->numBlocks = numBlocks;
4627 * Mark the extra blocks as free
4629 while (extraBlock != BLOCK_END_OF_CHAIN)
4631 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4632 &blockIndex)))
4633 return FALSE;
4634 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4635 extraBlock = blockIndex;
4638 return TRUE;
4641 /******************************************************************************
4642 * BlockChainStream_Enlarge
4644 * Grows this chain in the big block depot.
4646 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4647 ULARGE_INTEGER newSize)
4649 ULONG blockIndex, currentBlock;
4650 ULONG newNumBlocks;
4651 ULONG oldNumBlocks = 0;
4653 blockIndex = BlockChainStream_GetHeadOfChain(This);
4656 * Empty chain. Create the head.
4658 if (blockIndex == BLOCK_END_OF_CHAIN)
4660 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4661 StorageImpl_SetNextBlockInChain(This->parentStorage,
4662 blockIndex,
4663 BLOCK_END_OF_CHAIN);
4665 if (This->headOfStreamPlaceHolder != 0)
4667 *(This->headOfStreamPlaceHolder) = blockIndex;
4669 else
4671 DirEntry chainEntry;
4672 assert(This->ownerDirEntry != DIRENTRY_NULL);
4674 StorageImpl_ReadDirEntry(
4675 This->parentStorage,
4676 This->ownerDirEntry,
4677 &chainEntry);
4679 chainEntry.startingBlock = blockIndex;
4681 StorageImpl_WriteDirEntry(
4682 This->parentStorage,
4683 This->ownerDirEntry,
4684 &chainEntry);
4687 This->tailIndex = blockIndex;
4688 This->numBlocks = 1;
4692 * Figure out how many blocks are needed to contain this stream
4694 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4696 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4697 newNumBlocks++;
4700 * Go to the current end of chain
4702 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4704 currentBlock = blockIndex;
4706 while (blockIndex != BLOCK_END_OF_CHAIN)
4708 This->numBlocks++;
4709 currentBlock = blockIndex;
4711 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4712 &blockIndex)))
4713 return FALSE;
4716 This->tailIndex = currentBlock;
4719 currentBlock = This->tailIndex;
4720 oldNumBlocks = This->numBlocks;
4723 * Add new blocks to the chain
4725 if (oldNumBlocks < newNumBlocks)
4727 while (oldNumBlocks < newNumBlocks)
4729 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4731 StorageImpl_SetNextBlockInChain(
4732 This->parentStorage,
4733 currentBlock,
4734 blockIndex);
4736 StorageImpl_SetNextBlockInChain(
4737 This->parentStorage,
4738 blockIndex,
4739 BLOCK_END_OF_CHAIN);
4741 currentBlock = blockIndex;
4742 oldNumBlocks++;
4745 This->tailIndex = blockIndex;
4746 This->numBlocks = newNumBlocks;
4749 return TRUE;
4752 /******************************************************************************
4753 * BlockChainStream_SetSize
4755 * Sets the size of this stream. The big block depot will be updated.
4756 * The file will grow if we grow the chain.
4758 * TODO: Free the actual blocks in the file when we shrink the chain.
4759 * Currently, the blocks are still in the file. So the file size
4760 * doesn't shrink even if we shrink streams.
4762 BOOL BlockChainStream_SetSize(
4763 BlockChainStream* This,
4764 ULARGE_INTEGER newSize)
4766 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4768 if (newSize.u.LowPart == size.u.LowPart)
4769 return TRUE;
4771 if (newSize.u.LowPart < size.u.LowPart)
4773 BlockChainStream_Shrink(This, newSize);
4775 else
4777 BlockChainStream_Enlarge(This, newSize);
4780 return TRUE;
4783 /******************************************************************************
4784 * BlockChainStream_GetSize
4786 * Returns the size of this chain.
4787 * Will return the block count if this chain doesn't have a directory entry.
4789 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4791 DirEntry chainEntry;
4793 if(This->headOfStreamPlaceHolder == NULL)
4796 * This chain has a directory entry so use the size value from there.
4798 StorageImpl_ReadDirEntry(
4799 This->parentStorage,
4800 This->ownerDirEntry,
4801 &chainEntry);
4803 return chainEntry.size;
4805 else
4808 * this chain is a chain that does not have a directory entry, figure out the
4809 * size by making the product number of used blocks times the
4810 * size of them
4812 ULARGE_INTEGER result;
4813 result.u.HighPart = 0;
4815 result.u.LowPart =
4816 BlockChainStream_GetCount(This) *
4817 This->parentStorage->bigBlockSize;
4819 return result;
4823 /******************************************************************************
4824 ** SmallBlockChainStream implementation
4827 SmallBlockChainStream* SmallBlockChainStream_Construct(
4828 StorageImpl* parentStorage,
4829 ULONG* headOfStreamPlaceHolder,
4830 DirRef dirEntry)
4832 SmallBlockChainStream* newStream;
4834 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4836 newStream->parentStorage = parentStorage;
4837 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4838 newStream->ownerDirEntry = dirEntry;
4840 return newStream;
4843 void SmallBlockChainStream_Destroy(
4844 SmallBlockChainStream* This)
4846 HeapFree(GetProcessHeap(), 0, This);
4849 /******************************************************************************
4850 * SmallBlockChainStream_GetHeadOfChain
4852 * Returns the head of this chain of small blocks.
4854 static ULONG SmallBlockChainStream_GetHeadOfChain(
4855 SmallBlockChainStream* This)
4857 DirEntry chainEntry;
4858 BOOL readSuccessful;
4860 if (This->headOfStreamPlaceHolder != NULL)
4861 return *(This->headOfStreamPlaceHolder);
4863 if (This->ownerDirEntry)
4865 readSuccessful = StorageImpl_ReadDirEntry(
4866 This->parentStorage,
4867 This->ownerDirEntry,
4868 &chainEntry);
4870 if (readSuccessful)
4872 return chainEntry.startingBlock;
4877 return BLOCK_END_OF_CHAIN;
4880 /******************************************************************************
4881 * SmallBlockChainStream_GetNextBlockInChain
4883 * Returns the index of the next small block in this chain.
4885 * Return Values:
4886 * - BLOCK_END_OF_CHAIN: end of this chain
4887 * - BLOCK_UNUSED: small block 'blockIndex' is free
4889 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4890 SmallBlockChainStream* This,
4891 ULONG blockIndex,
4892 ULONG* nextBlockInChain)
4894 ULARGE_INTEGER offsetOfBlockInDepot;
4895 DWORD buffer;
4896 ULONG bytesRead;
4897 HRESULT res;
4899 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4901 offsetOfBlockInDepot.u.HighPart = 0;
4902 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4905 * Read those bytes in the buffer from the small block file.
4907 res = BlockChainStream_ReadAt(
4908 This->parentStorage->smallBlockDepotChain,
4909 offsetOfBlockInDepot,
4910 sizeof(DWORD),
4911 &buffer,
4912 &bytesRead);
4914 if (SUCCEEDED(res))
4916 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4917 return S_OK;
4920 return res;
4923 /******************************************************************************
4924 * SmallBlockChainStream_SetNextBlockInChain
4926 * Writes the index of the next block of the specified block in the small
4927 * block depot.
4928 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4929 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4931 static void SmallBlockChainStream_SetNextBlockInChain(
4932 SmallBlockChainStream* This,
4933 ULONG blockIndex,
4934 ULONG nextBlock)
4936 ULARGE_INTEGER offsetOfBlockInDepot;
4937 DWORD buffer;
4938 ULONG bytesWritten;
4940 offsetOfBlockInDepot.u.HighPart = 0;
4941 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4943 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4946 * Read those bytes in the buffer from the small block file.
4948 BlockChainStream_WriteAt(
4949 This->parentStorage->smallBlockDepotChain,
4950 offsetOfBlockInDepot,
4951 sizeof(DWORD),
4952 &buffer,
4953 &bytesWritten);
4956 /******************************************************************************
4957 * SmallBlockChainStream_FreeBlock
4959 * Flag small block 'blockIndex' as free in the small block depot.
4961 static void SmallBlockChainStream_FreeBlock(
4962 SmallBlockChainStream* This,
4963 ULONG blockIndex)
4965 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4968 /******************************************************************************
4969 * SmallBlockChainStream_GetNextFreeBlock
4971 * Returns the index of a free small block. The small block depot will be
4972 * enlarged if necessary. The small block chain will also be enlarged if
4973 * necessary.
4975 static ULONG SmallBlockChainStream_GetNextFreeBlock(
4976 SmallBlockChainStream* This)
4978 ULARGE_INTEGER offsetOfBlockInDepot;
4979 DWORD buffer;
4980 ULONG bytesRead;
4981 ULONG blockIndex = 0;
4982 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4983 HRESULT res = S_OK;
4984 ULONG smallBlocksPerBigBlock;
4986 offsetOfBlockInDepot.u.HighPart = 0;
4989 * Scan the small block depot for a free block
4991 while (nextBlockIndex != BLOCK_UNUSED)
4993 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4995 res = BlockChainStream_ReadAt(
4996 This->parentStorage->smallBlockDepotChain,
4997 offsetOfBlockInDepot,
4998 sizeof(DWORD),
4999 &buffer,
5000 &bytesRead);
5003 * If we run out of space for the small block depot, enlarge it
5005 if (SUCCEEDED(res))
5007 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5009 if (nextBlockIndex != BLOCK_UNUSED)
5010 blockIndex++;
5012 else
5014 ULONG count =
5015 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5017 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5018 ULONG nextBlock, newsbdIndex;
5019 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5021 nextBlock = sbdIndex;
5022 while (nextBlock != BLOCK_END_OF_CHAIN)
5024 sbdIndex = nextBlock;
5025 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5028 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5029 if (sbdIndex != BLOCK_END_OF_CHAIN)
5030 StorageImpl_SetNextBlockInChain(
5031 This->parentStorage,
5032 sbdIndex,
5033 newsbdIndex);
5035 StorageImpl_SetNextBlockInChain(
5036 This->parentStorage,
5037 newsbdIndex,
5038 BLOCK_END_OF_CHAIN);
5041 * Initialize all the small blocks to free
5043 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5044 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5046 if (count == 0)
5049 * We have just created the small block depot.
5051 DirEntry rootEntry;
5052 ULONG sbStartIndex;
5055 * Save it in the header
5057 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5058 StorageImpl_SaveFileHeader(This->parentStorage);
5061 * And allocate the first big block that will contain small blocks
5063 sbStartIndex =
5064 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5066 StorageImpl_SetNextBlockInChain(
5067 This->parentStorage,
5068 sbStartIndex,
5069 BLOCK_END_OF_CHAIN);
5071 StorageImpl_ReadDirEntry(
5072 This->parentStorage,
5073 This->parentStorage->base.storageDirEntry,
5074 &rootEntry);
5076 rootEntry.startingBlock = sbStartIndex;
5077 rootEntry.size.u.HighPart = 0;
5078 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5080 StorageImpl_WriteDirEntry(
5081 This->parentStorage,
5082 This->parentStorage->base.storageDirEntry,
5083 &rootEntry);
5085 else
5086 StorageImpl_SaveFileHeader(This->parentStorage);
5090 smallBlocksPerBigBlock =
5091 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5094 * Verify if we have to allocate big blocks to contain small blocks
5096 if (blockIndex % smallBlocksPerBigBlock == 0)
5098 DirEntry rootEntry;
5099 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5101 StorageImpl_ReadDirEntry(
5102 This->parentStorage,
5103 This->parentStorage->base.storageDirEntry,
5104 &rootEntry);
5106 if (rootEntry.size.u.LowPart <
5107 (blocksRequired * This->parentStorage->bigBlockSize))
5109 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5111 BlockChainStream_SetSize(
5112 This->parentStorage->smallBlockRootChain,
5113 rootEntry.size);
5115 StorageImpl_WriteDirEntry(
5116 This->parentStorage,
5117 This->parentStorage->base.storageDirEntry,
5118 &rootEntry);
5122 return blockIndex;
5125 /******************************************************************************
5126 * SmallBlockChainStream_ReadAt
5128 * Reads a specified number of bytes from this chain at the specified offset.
5129 * bytesRead may be NULL.
5130 * Failure will be returned if the specified number of bytes has not been read.
5132 HRESULT SmallBlockChainStream_ReadAt(
5133 SmallBlockChainStream* This,
5134 ULARGE_INTEGER offset,
5135 ULONG size,
5136 void* buffer,
5137 ULONG* bytesRead)
5139 HRESULT rc = S_OK;
5140 ULARGE_INTEGER offsetInBigBlockFile;
5141 ULONG blockNoInSequence =
5142 offset.u.LowPart / This->parentStorage->smallBlockSize;
5144 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5145 ULONG bytesToReadInBuffer;
5146 ULONG blockIndex;
5147 ULONG bytesReadFromBigBlockFile;
5148 BYTE* bufferWalker;
5151 * This should never happen on a small block file.
5153 assert(offset.u.HighPart==0);
5156 * Find the first block in the stream that contains part of the buffer.
5158 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5160 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5162 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5163 if(FAILED(rc))
5164 return rc;
5165 blockNoInSequence--;
5169 * Start reading the buffer.
5171 *bytesRead = 0;
5172 bufferWalker = buffer;
5174 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5177 * Calculate how many bytes we can copy from this small block.
5179 bytesToReadInBuffer =
5180 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5183 * Calculate the offset of the small block in the small block file.
5185 offsetInBigBlockFile.u.HighPart = 0;
5186 offsetInBigBlockFile.u.LowPart =
5187 blockIndex * This->parentStorage->smallBlockSize;
5189 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5192 * Read those bytes in the buffer from the small block file.
5193 * The small block has already been identified so it shouldn't fail
5194 * unless the file is corrupt.
5196 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5197 offsetInBigBlockFile,
5198 bytesToReadInBuffer,
5199 bufferWalker,
5200 &bytesReadFromBigBlockFile);
5202 if (FAILED(rc))
5203 return rc;
5206 * Step to the next big block.
5208 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5209 if(FAILED(rc))
5210 return STG_E_DOCFILECORRUPT;
5212 bufferWalker += bytesReadFromBigBlockFile;
5213 size -= bytesReadFromBigBlockFile;
5214 *bytesRead += bytesReadFromBigBlockFile;
5215 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5218 return (size == 0) ? S_OK : STG_E_READFAULT;
5221 /******************************************************************************
5222 * SmallBlockChainStream_WriteAt
5224 * Writes the specified number of bytes to this chain at the specified offset.
5225 * Will fail if not all specified number of bytes have been written.
5227 HRESULT SmallBlockChainStream_WriteAt(
5228 SmallBlockChainStream* This,
5229 ULARGE_INTEGER offset,
5230 ULONG size,
5231 const void* buffer,
5232 ULONG* bytesWritten)
5234 ULARGE_INTEGER offsetInBigBlockFile;
5235 ULONG blockNoInSequence =
5236 offset.u.LowPart / This->parentStorage->smallBlockSize;
5238 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5239 ULONG bytesToWriteInBuffer;
5240 ULONG blockIndex;
5241 ULONG bytesWrittenToBigBlockFile;
5242 const BYTE* bufferWalker;
5243 HRESULT res;
5246 * This should never happen on a small block file.
5248 assert(offset.u.HighPart==0);
5251 * Find the first block in the stream that contains part of the buffer.
5253 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5255 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5257 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5258 return STG_E_DOCFILECORRUPT;
5259 blockNoInSequence--;
5263 * Start writing the buffer.
5265 *bytesWritten = 0;
5266 bufferWalker = buffer;
5267 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5270 * Calculate how many bytes we can copy to this small block.
5272 bytesToWriteInBuffer =
5273 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5276 * Calculate the offset of the small block in the small block file.
5278 offsetInBigBlockFile.u.HighPart = 0;
5279 offsetInBigBlockFile.u.LowPart =
5280 blockIndex * This->parentStorage->smallBlockSize;
5282 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5285 * Write those bytes in the buffer to the small block file.
5287 res = BlockChainStream_WriteAt(
5288 This->parentStorage->smallBlockRootChain,
5289 offsetInBigBlockFile,
5290 bytesToWriteInBuffer,
5291 bufferWalker,
5292 &bytesWrittenToBigBlockFile);
5293 if (FAILED(res))
5294 return res;
5297 * Step to the next big block.
5299 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5300 &blockIndex)))
5301 return FALSE;
5302 bufferWalker += bytesWrittenToBigBlockFile;
5303 size -= bytesWrittenToBigBlockFile;
5304 *bytesWritten += bytesWrittenToBigBlockFile;
5305 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5308 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5311 /******************************************************************************
5312 * SmallBlockChainStream_Shrink
5314 * Shrinks this chain in the small block depot.
5316 static BOOL SmallBlockChainStream_Shrink(
5317 SmallBlockChainStream* This,
5318 ULARGE_INTEGER newSize)
5320 ULONG blockIndex, extraBlock;
5321 ULONG numBlocks;
5322 ULONG count = 0;
5324 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5326 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5327 numBlocks++;
5329 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5332 * Go to the new end of chain
5334 while (count < numBlocks)
5336 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5337 &blockIndex)))
5338 return FALSE;
5339 count++;
5343 * If the count is 0, we have a special case, the head of the chain was
5344 * just freed.
5346 if (count == 0)
5348 DirEntry chainEntry;
5350 StorageImpl_ReadDirEntry(This->parentStorage,
5351 This->ownerDirEntry,
5352 &chainEntry);
5354 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
5356 StorageImpl_WriteDirEntry(This->parentStorage,
5357 This->ownerDirEntry,
5358 &chainEntry);
5361 * We start freeing the chain at the head block.
5363 extraBlock = blockIndex;
5365 else
5367 /* Get the next block before marking the new end */
5368 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5369 &extraBlock)))
5370 return FALSE;
5372 /* Mark the new end of chain */
5373 SmallBlockChainStream_SetNextBlockInChain(
5374 This,
5375 blockIndex,
5376 BLOCK_END_OF_CHAIN);
5380 * Mark the extra blocks as free
5382 while (extraBlock != BLOCK_END_OF_CHAIN)
5384 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5385 &blockIndex)))
5386 return FALSE;
5387 SmallBlockChainStream_FreeBlock(This, extraBlock);
5388 extraBlock = blockIndex;
5391 return TRUE;
5394 /******************************************************************************
5395 * SmallBlockChainStream_Enlarge
5397 * Grows this chain in the small block depot.
5399 static BOOL SmallBlockChainStream_Enlarge(
5400 SmallBlockChainStream* This,
5401 ULARGE_INTEGER newSize)
5403 ULONG blockIndex, currentBlock;
5404 ULONG newNumBlocks;
5405 ULONG oldNumBlocks = 0;
5407 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5410 * Empty chain. Create the head.
5412 if (blockIndex == BLOCK_END_OF_CHAIN)
5414 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5415 SmallBlockChainStream_SetNextBlockInChain(
5416 This,
5417 blockIndex,
5418 BLOCK_END_OF_CHAIN);
5420 if (This->headOfStreamPlaceHolder != NULL)
5422 *(This->headOfStreamPlaceHolder) = blockIndex;
5424 else
5426 DirEntry chainEntry;
5428 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
5429 &chainEntry);
5431 chainEntry.startingBlock = blockIndex;
5433 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
5434 &chainEntry);
5438 currentBlock = blockIndex;
5441 * Figure out how many blocks are needed to contain this stream
5443 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5445 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5446 newNumBlocks++;
5449 * Go to the current end of chain
5451 while (blockIndex != BLOCK_END_OF_CHAIN)
5453 oldNumBlocks++;
5454 currentBlock = blockIndex;
5455 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5456 return FALSE;
5460 * Add new blocks to the chain
5462 while (oldNumBlocks < newNumBlocks)
5464 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5465 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5467 SmallBlockChainStream_SetNextBlockInChain(
5468 This,
5469 blockIndex,
5470 BLOCK_END_OF_CHAIN);
5472 currentBlock = blockIndex;
5473 oldNumBlocks++;
5476 return TRUE;
5479 /******************************************************************************
5480 * SmallBlockChainStream_SetSize
5482 * Sets the size of this stream.
5483 * The file will grow if we grow the chain.
5485 * TODO: Free the actual blocks in the file when we shrink the chain.
5486 * Currently, the blocks are still in the file. So the file size
5487 * doesn't shrink even if we shrink streams.
5489 BOOL SmallBlockChainStream_SetSize(
5490 SmallBlockChainStream* This,
5491 ULARGE_INTEGER newSize)
5493 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5495 if (newSize.u.LowPart == size.u.LowPart)
5496 return TRUE;
5498 if (newSize.u.LowPart < size.u.LowPart)
5500 SmallBlockChainStream_Shrink(This, newSize);
5502 else
5504 SmallBlockChainStream_Enlarge(This, newSize);
5507 return TRUE;
5510 /******************************************************************************
5511 * SmallBlockChainStream_GetCount
5513 * Returns the number of small blocks that comprises this chain.
5514 * This is not the size of the stream as the last block may not be full!
5517 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5519 ULONG blockIndex;
5520 ULONG count = 0;
5522 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5524 while(blockIndex != BLOCK_END_OF_CHAIN)
5526 count++;
5528 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5529 blockIndex, &blockIndex)))
5530 return 0;
5533 return count;
5536 /******************************************************************************
5537 * SmallBlockChainStream_GetSize
5539 * Returns the size of this chain.
5541 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5543 DirEntry chainEntry;
5545 if(This->headOfStreamPlaceHolder != NULL)
5547 ULARGE_INTEGER result;
5548 result.u.HighPart = 0;
5550 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5551 This->parentStorage->smallBlockSize;
5553 return result;
5556 StorageImpl_ReadDirEntry(
5557 This->parentStorage,
5558 This->ownerDirEntry,
5559 &chainEntry);
5561 return chainEntry.size;
5564 /******************************************************************************
5565 * StgCreateDocfile [OLE32.@]
5566 * Creates a new compound file storage object
5568 * PARAMS
5569 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5570 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5571 * reserved [ ?] unused?, usually 0
5572 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5574 * RETURNS
5575 * S_OK if the file was successfully created
5576 * some STG_E_ value if error
5577 * NOTES
5578 * if pwcsName is NULL, create file with new unique name
5579 * the function can returns
5580 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5581 * (unrealized now)
5583 HRESULT WINAPI StgCreateDocfile(
5584 LPCOLESTR pwcsName,
5585 DWORD grfMode,
5586 DWORD reserved,
5587 IStorage **ppstgOpen)
5589 StorageImpl* newStorage = 0;
5590 HANDLE hFile = INVALID_HANDLE_VALUE;
5591 HRESULT hr = STG_E_INVALIDFLAG;
5592 DWORD shareMode;
5593 DWORD accessMode;
5594 DWORD creationMode;
5595 DWORD fileAttributes;
5596 WCHAR tempFileName[MAX_PATH];
5598 TRACE("(%s, %x, %d, %p)\n",
5599 debugstr_w(pwcsName), grfMode,
5600 reserved, ppstgOpen);
5602 if (ppstgOpen == 0)
5603 return STG_E_INVALIDPOINTER;
5604 if (reserved != 0)
5605 return STG_E_INVALIDPARAMETER;
5607 /* if no share mode given then DENY_NONE is the default */
5608 if (STGM_SHARE_MODE(grfMode) == 0)
5609 grfMode |= STGM_SHARE_DENY_NONE;
5611 if ( FAILED( validateSTGM(grfMode) ))
5612 goto end;
5614 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5615 switch(STGM_ACCESS_MODE(grfMode))
5617 case STGM_WRITE:
5618 case STGM_READWRITE:
5619 break;
5620 default:
5621 goto end;
5624 /* in direct mode, can only use SHARE_EXCLUSIVE */
5625 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5626 goto end;
5628 /* but in transacted mode, any share mode is valid */
5631 * Generate a unique name.
5633 if (pwcsName == 0)
5635 WCHAR tempPath[MAX_PATH];
5636 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5638 memset(tempPath, 0, sizeof(tempPath));
5639 memset(tempFileName, 0, sizeof(tempFileName));
5641 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5642 tempPath[0] = '.';
5644 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5645 pwcsName = tempFileName;
5646 else
5648 hr = STG_E_INSUFFICIENTMEMORY;
5649 goto end;
5652 creationMode = TRUNCATE_EXISTING;
5654 else
5656 creationMode = GetCreationModeFromSTGM(grfMode);
5660 * Interpret the STGM value grfMode
5662 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5663 accessMode = GetAccessModeFromSTGM(grfMode);
5665 if (grfMode & STGM_DELETEONRELEASE)
5666 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5667 else
5668 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5670 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5671 FIXME("Storage share mode not implemented.\n");
5673 if (grfMode & STGM_TRANSACTED)
5674 FIXME("Transacted mode not implemented.\n");
5676 *ppstgOpen = 0;
5678 hFile = CreateFileW(pwcsName,
5679 accessMode,
5680 shareMode,
5681 NULL,
5682 creationMode,
5683 fileAttributes,
5686 if (hFile == INVALID_HANDLE_VALUE)
5688 if(GetLastError() == ERROR_FILE_EXISTS)
5689 hr = STG_E_FILEALREADYEXISTS;
5690 else
5691 hr = E_FAIL;
5692 goto end;
5696 * Allocate and initialize the new IStorage32object.
5698 hr = StorageImpl_Construct(
5699 hFile,
5700 pwcsName,
5701 NULL,
5702 grfMode,
5703 TRUE,
5704 TRUE,
5705 &newStorage);
5707 if (FAILED(hr))
5709 goto end;
5713 * Get an "out" pointer for the caller.
5715 *ppstgOpen = (IStorage*)newStorage;
5717 end:
5718 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5720 return hr;
5723 /******************************************************************************
5724 * StgCreateStorageEx [OLE32.@]
5726 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5728 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5729 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5731 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5733 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5734 return STG_E_INVALIDPARAMETER;
5737 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5739 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5740 return STG_E_INVALIDPARAMETER;
5743 if (stgfmt == STGFMT_FILE)
5745 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5746 return STG_E_INVALIDPARAMETER;
5749 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5751 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5752 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5755 ERR("Invalid stgfmt argument\n");
5756 return STG_E_INVALIDPARAMETER;
5759 /******************************************************************************
5760 * StgCreatePropSetStg [OLE32.@]
5762 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5763 IPropertySetStorage **ppPropSetStg)
5765 HRESULT hr;
5767 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5768 if (reserved)
5769 hr = STG_E_INVALIDPARAMETER;
5770 else
5771 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5772 (void**)ppPropSetStg);
5773 return hr;
5776 /******************************************************************************
5777 * StgOpenStorageEx [OLE32.@]
5779 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5781 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5782 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5784 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5786 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5787 return STG_E_INVALIDPARAMETER;
5790 switch (stgfmt)
5792 case STGFMT_FILE:
5793 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5794 return STG_E_INVALIDPARAMETER;
5796 case STGFMT_STORAGE:
5797 break;
5799 case STGFMT_DOCFILE:
5800 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5802 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5803 return STG_E_INVALIDPARAMETER;
5805 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5806 break;
5808 case STGFMT_ANY:
5809 WARN("STGFMT_ANY assuming storage\n");
5810 break;
5812 default:
5813 return STG_E_INVALIDPARAMETER;
5816 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5820 /******************************************************************************
5821 * StgOpenStorage [OLE32.@]
5823 HRESULT WINAPI StgOpenStorage(
5824 const OLECHAR *pwcsName,
5825 IStorage *pstgPriority,
5826 DWORD grfMode,
5827 SNB snbExclude,
5828 DWORD reserved,
5829 IStorage **ppstgOpen)
5831 StorageImpl* newStorage = 0;
5832 HRESULT hr = S_OK;
5833 HANDLE hFile = 0;
5834 DWORD shareMode;
5835 DWORD accessMode;
5836 WCHAR fullname[MAX_PATH];
5838 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5839 debugstr_w(pwcsName), pstgPriority, grfMode,
5840 snbExclude, reserved, ppstgOpen);
5842 if (pwcsName == 0)
5844 hr = STG_E_INVALIDNAME;
5845 goto end;
5848 if (ppstgOpen == 0)
5850 hr = STG_E_INVALIDPOINTER;
5851 goto end;
5854 if (reserved)
5856 hr = STG_E_INVALIDPARAMETER;
5857 goto end;
5860 if (grfMode & STGM_PRIORITY)
5862 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5863 return STG_E_INVALIDFLAG;
5864 if (grfMode & STGM_DELETEONRELEASE)
5865 return STG_E_INVALIDFUNCTION;
5866 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5867 return STG_E_INVALIDFLAG;
5868 grfMode &= ~0xf0; /* remove the existing sharing mode */
5869 grfMode |= STGM_SHARE_DENY_NONE;
5871 /* STGM_PRIORITY stops other IStorage objects on the same file from
5872 * committing until the STGM_PRIORITY IStorage is closed. it also
5873 * stops non-transacted mode StgOpenStorage calls with write access from
5874 * succeeding. obviously, both of these cannot be achieved through just
5875 * file share flags */
5876 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5880 * Validate the sharing mode
5882 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5883 switch(STGM_SHARE_MODE(grfMode))
5885 case STGM_SHARE_EXCLUSIVE:
5886 case STGM_SHARE_DENY_WRITE:
5887 break;
5888 default:
5889 hr = STG_E_INVALIDFLAG;
5890 goto end;
5893 if ( FAILED( validateSTGM(grfMode) ) ||
5894 (grfMode&STGM_CREATE))
5896 hr = STG_E_INVALIDFLAG;
5897 goto end;
5900 /* shared reading requires transacted mode */
5901 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5902 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5903 !(grfMode&STGM_TRANSACTED) )
5905 hr = STG_E_INVALIDFLAG;
5906 goto end;
5910 * Interpret the STGM value grfMode
5912 shareMode = GetShareModeFromSTGM(grfMode);
5913 accessMode = GetAccessModeFromSTGM(grfMode);
5915 *ppstgOpen = 0;
5917 hFile = CreateFileW( pwcsName,
5918 accessMode,
5919 shareMode,
5920 NULL,
5921 OPEN_EXISTING,
5922 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5925 if (hFile==INVALID_HANDLE_VALUE)
5927 DWORD last_error = GetLastError();
5929 hr = E_FAIL;
5931 switch (last_error)
5933 case ERROR_FILE_NOT_FOUND:
5934 hr = STG_E_FILENOTFOUND;
5935 break;
5937 case ERROR_PATH_NOT_FOUND:
5938 hr = STG_E_PATHNOTFOUND;
5939 break;
5941 case ERROR_ACCESS_DENIED:
5942 case ERROR_WRITE_PROTECT:
5943 hr = STG_E_ACCESSDENIED;
5944 break;
5946 case ERROR_SHARING_VIOLATION:
5947 hr = STG_E_SHAREVIOLATION;
5948 break;
5950 default:
5951 hr = E_FAIL;
5954 goto end;
5958 * Refuse to open the file if it's too small to be a structured storage file
5959 * FIXME: verify the file when reading instead of here
5961 if (GetFileSize(hFile, NULL) < 0x100)
5963 CloseHandle(hFile);
5964 hr = STG_E_FILEALREADYEXISTS;
5965 goto end;
5969 * Allocate and initialize the new IStorage32object.
5971 hr = StorageImpl_Construct(
5972 hFile,
5973 pwcsName,
5974 NULL,
5975 grfMode,
5976 TRUE,
5977 FALSE,
5978 &newStorage);
5980 if (FAILED(hr))
5983 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5985 if(hr == STG_E_INVALIDHEADER)
5986 hr = STG_E_FILEALREADYEXISTS;
5987 goto end;
5990 /* prepare the file name string given in lieu of the root property name */
5991 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5992 memcpy(newStorage->base.filename, fullname, DIRENTRY_NAME_BUFFER_LEN);
5993 newStorage->base.filename[DIRENTRY_NAME_BUFFER_LEN-1] = '\0';
5996 * Get an "out" pointer for the caller.
5998 *ppstgOpen = (IStorage*)newStorage;
6000 end:
6001 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6002 return hr;
6005 /******************************************************************************
6006 * StgCreateDocfileOnILockBytes [OLE32.@]
6008 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6009 ILockBytes *plkbyt,
6010 DWORD grfMode,
6011 DWORD reserved,
6012 IStorage** ppstgOpen)
6014 StorageImpl* newStorage = 0;
6015 HRESULT hr = S_OK;
6017 if ((ppstgOpen == 0) || (plkbyt == 0))
6018 return STG_E_INVALIDPOINTER;
6021 * Allocate and initialize the new IStorage object.
6023 hr = StorageImpl_Construct(
6026 plkbyt,
6027 grfMode,
6028 FALSE,
6029 TRUE,
6030 &newStorage);
6032 if (FAILED(hr))
6034 return hr;
6038 * Get an "out" pointer for the caller.
6040 *ppstgOpen = (IStorage*)newStorage;
6042 return hr;
6045 /******************************************************************************
6046 * StgOpenStorageOnILockBytes [OLE32.@]
6048 HRESULT WINAPI StgOpenStorageOnILockBytes(
6049 ILockBytes *plkbyt,
6050 IStorage *pstgPriority,
6051 DWORD grfMode,
6052 SNB snbExclude,
6053 DWORD reserved,
6054 IStorage **ppstgOpen)
6056 StorageImpl* newStorage = 0;
6057 HRESULT hr = S_OK;
6059 if ((plkbyt == 0) || (ppstgOpen == 0))
6060 return STG_E_INVALIDPOINTER;
6062 if ( FAILED( validateSTGM(grfMode) ))
6063 return STG_E_INVALIDFLAG;
6065 *ppstgOpen = 0;
6068 * Allocate and initialize the new IStorage object.
6070 hr = StorageImpl_Construct(
6073 plkbyt,
6074 grfMode,
6075 FALSE,
6076 FALSE,
6077 &newStorage);
6079 if (FAILED(hr))
6081 return hr;
6085 * Get an "out" pointer for the caller.
6087 *ppstgOpen = (IStorage*)newStorage;
6089 return hr;
6092 /******************************************************************************
6093 * StgSetTimes [ole32.@]
6094 * StgSetTimes [OLE32.@]
6098 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6099 FILETIME const *patime, FILETIME const *pmtime)
6101 IStorage *stg = NULL;
6102 HRESULT r;
6104 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6106 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6107 0, 0, &stg);
6108 if( SUCCEEDED(r) )
6110 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6111 IStorage_Release(stg);
6114 return r;
6117 /******************************************************************************
6118 * StgIsStorageILockBytes [OLE32.@]
6120 * Determines if the ILockBytes contains a storage object.
6122 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6124 BYTE sig[8];
6125 ULARGE_INTEGER offset;
6127 offset.u.HighPart = 0;
6128 offset.u.LowPart = 0;
6130 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6132 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6133 return S_OK;
6135 return S_FALSE;
6138 /******************************************************************************
6139 * WriteClassStg [OLE32.@]
6141 * This method will store the specified CLSID in the specified storage object
6143 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6145 HRESULT hRes;
6147 if(!pStg)
6148 return E_INVALIDARG;
6150 if(!rclsid)
6151 return STG_E_INVALIDPOINTER;
6153 hRes = IStorage_SetClass(pStg, rclsid);
6155 return hRes;
6158 /***********************************************************************
6159 * ReadClassStg (OLE32.@)
6161 * This method reads the CLSID previously written to a storage object with
6162 * the WriteClassStg.
6164 * PARAMS
6165 * pstg [I] IStorage pointer
6166 * pclsid [O] Pointer to where the CLSID is written
6168 * RETURNS
6169 * Success: S_OK.
6170 * Failure: HRESULT code.
6172 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6174 STATSTG pstatstg;
6175 HRESULT hRes;
6177 TRACE("(%p, %p)\n", pstg, pclsid);
6179 if(!pstg || !pclsid)
6180 return E_INVALIDARG;
6183 * read a STATSTG structure (contains the clsid) from the storage
6185 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6187 if(SUCCEEDED(hRes))
6188 *pclsid=pstatstg.clsid;
6190 return hRes;
6193 /***********************************************************************
6194 * OleLoadFromStream (OLE32.@)
6196 * This function loads an object from stream
6198 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6200 CLSID clsid;
6201 HRESULT res;
6202 LPPERSISTSTREAM xstm;
6204 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6206 res=ReadClassStm(pStm,&clsid);
6207 if (FAILED(res))
6208 return res;
6209 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6210 if (FAILED(res))
6211 return res;
6212 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6213 if (FAILED(res)) {
6214 IUnknown_Release((IUnknown*)*ppvObj);
6215 return res;
6217 res=IPersistStream_Load(xstm,pStm);
6218 IPersistStream_Release(xstm);
6219 /* FIXME: all refcounts ok at this point? I think they should be:
6220 * pStm : unchanged
6221 * ppvObj : 1
6222 * xstm : 0 (released)
6224 return res;
6227 /***********************************************************************
6228 * OleSaveToStream (OLE32.@)
6230 * This function saves an object with the IPersistStream interface on it
6231 * to the specified stream.
6233 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6236 CLSID clsid;
6237 HRESULT res;
6239 TRACE("(%p,%p)\n",pPStm,pStm);
6241 res=IPersistStream_GetClassID(pPStm,&clsid);
6243 if (SUCCEEDED(res)){
6245 res=WriteClassStm(pStm,&clsid);
6247 if (SUCCEEDED(res))
6249 res=IPersistStream_Save(pPStm,pStm,TRUE);
6252 TRACE("Finished Save\n");
6253 return res;
6256 /****************************************************************************
6257 * This method validate a STGM parameter that can contain the values below
6259 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6260 * The stgm values contained in 0xffff0000 are bitmasks.
6262 * STGM_DIRECT 0x00000000
6263 * STGM_TRANSACTED 0x00010000
6264 * STGM_SIMPLE 0x08000000
6266 * STGM_READ 0x00000000
6267 * STGM_WRITE 0x00000001
6268 * STGM_READWRITE 0x00000002
6270 * STGM_SHARE_DENY_NONE 0x00000040
6271 * STGM_SHARE_DENY_READ 0x00000030
6272 * STGM_SHARE_DENY_WRITE 0x00000020
6273 * STGM_SHARE_EXCLUSIVE 0x00000010
6275 * STGM_PRIORITY 0x00040000
6276 * STGM_DELETEONRELEASE 0x04000000
6278 * STGM_CREATE 0x00001000
6279 * STGM_CONVERT 0x00020000
6280 * STGM_FAILIFTHERE 0x00000000
6282 * STGM_NOSCRATCH 0x00100000
6283 * STGM_NOSNAPSHOT 0x00200000
6285 static HRESULT validateSTGM(DWORD stgm)
6287 DWORD access = STGM_ACCESS_MODE(stgm);
6288 DWORD share = STGM_SHARE_MODE(stgm);
6289 DWORD create = STGM_CREATE_MODE(stgm);
6291 if (stgm&~STGM_KNOWN_FLAGS)
6293 ERR("unknown flags %08x\n", stgm);
6294 return E_FAIL;
6297 switch (access)
6299 case STGM_READ:
6300 case STGM_WRITE:
6301 case STGM_READWRITE:
6302 break;
6303 default:
6304 return E_FAIL;
6307 switch (share)
6309 case STGM_SHARE_DENY_NONE:
6310 case STGM_SHARE_DENY_READ:
6311 case STGM_SHARE_DENY_WRITE:
6312 case STGM_SHARE_EXCLUSIVE:
6313 break;
6314 default:
6315 return E_FAIL;
6318 switch (create)
6320 case STGM_CREATE:
6321 case STGM_FAILIFTHERE:
6322 break;
6323 default:
6324 return E_FAIL;
6328 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6330 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6331 return E_FAIL;
6334 * STGM_CREATE | STGM_CONVERT
6335 * if both are false, STGM_FAILIFTHERE is set to TRUE
6337 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6338 return E_FAIL;
6341 * STGM_NOSCRATCH requires STGM_TRANSACTED
6343 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6344 return E_FAIL;
6347 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6348 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6350 if ( (stgm & STGM_NOSNAPSHOT) &&
6351 (!(stgm & STGM_TRANSACTED) ||
6352 share == STGM_SHARE_EXCLUSIVE ||
6353 share == STGM_SHARE_DENY_WRITE) )
6354 return E_FAIL;
6356 return S_OK;
6359 /****************************************************************************
6360 * GetShareModeFromSTGM
6362 * This method will return a share mode flag from a STGM value.
6363 * The STGM value is assumed valid.
6365 static DWORD GetShareModeFromSTGM(DWORD stgm)
6367 switch (STGM_SHARE_MODE(stgm))
6369 case STGM_SHARE_DENY_NONE:
6370 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6371 case STGM_SHARE_DENY_READ:
6372 return FILE_SHARE_WRITE;
6373 case STGM_SHARE_DENY_WRITE:
6374 return FILE_SHARE_READ;
6375 case STGM_SHARE_EXCLUSIVE:
6376 return 0;
6378 ERR("Invalid share mode!\n");
6379 assert(0);
6380 return 0;
6383 /****************************************************************************
6384 * GetAccessModeFromSTGM
6386 * This method will return an access mode flag from a STGM value.
6387 * The STGM value is assumed valid.
6389 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6391 switch (STGM_ACCESS_MODE(stgm))
6393 case STGM_READ:
6394 return GENERIC_READ;
6395 case STGM_WRITE:
6396 case STGM_READWRITE:
6397 return GENERIC_READ | GENERIC_WRITE;
6399 ERR("Invalid access mode!\n");
6400 assert(0);
6401 return 0;
6404 /****************************************************************************
6405 * GetCreationModeFromSTGM
6407 * This method will return a creation mode flag from a STGM value.
6408 * The STGM value is assumed valid.
6410 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6412 switch(STGM_CREATE_MODE(stgm))
6414 case STGM_CREATE:
6415 return CREATE_ALWAYS;
6416 case STGM_CONVERT:
6417 FIXME("STGM_CONVERT not implemented!\n");
6418 return CREATE_NEW;
6419 case STGM_FAILIFTHERE:
6420 return CREATE_NEW;
6422 ERR("Invalid create mode!\n");
6423 assert(0);
6424 return 0;
6428 /*************************************************************************
6429 * OLECONVERT_LoadOLE10 [Internal]
6431 * Loads the OLE10 STREAM to memory
6433 * PARAMS
6434 * pOleStream [I] The OLESTREAM
6435 * pData [I] Data Structure for the OLESTREAM Data
6437 * RETURNS
6438 * Success: S_OK
6439 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6440 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6442 * NOTES
6443 * This function is used by OleConvertOLESTREAMToIStorage only.
6445 * Memory allocated for pData must be freed by the caller
6447 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6449 DWORD dwSize;
6450 HRESULT hRes = S_OK;
6451 int nTryCnt=0;
6452 int max_try = 6;
6454 pData->pData = NULL;
6455 pData->pstrOleObjFileName = NULL;
6457 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6459 /* Get the OleID */
6460 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6461 if(dwSize != sizeof(pData->dwOleID))
6463 hRes = CONVERT10_E_OLESTREAM_GET;
6465 else if(pData->dwOleID != OLESTREAM_ID)
6467 hRes = CONVERT10_E_OLESTREAM_FMT;
6469 else
6471 hRes = S_OK;
6472 break;
6476 if(hRes == S_OK)
6478 /* Get the TypeID... more info needed for this field */
6479 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6480 if(dwSize != sizeof(pData->dwTypeID))
6482 hRes = CONVERT10_E_OLESTREAM_GET;
6485 if(hRes == S_OK)
6487 if(pData->dwTypeID != 0)
6489 /* Get the length of the OleTypeName */
6490 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6491 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6493 hRes = CONVERT10_E_OLESTREAM_GET;
6496 if(hRes == S_OK)
6498 if(pData->dwOleTypeNameLength > 0)
6500 /* Get the OleTypeName */
6501 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6502 if(dwSize != pData->dwOleTypeNameLength)
6504 hRes = CONVERT10_E_OLESTREAM_GET;
6508 if(bStrem1)
6510 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6511 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6513 hRes = CONVERT10_E_OLESTREAM_GET;
6515 if(hRes == S_OK)
6517 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6518 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6519 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6520 if(pData->pstrOleObjFileName)
6522 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6523 if(dwSize != pData->dwOleObjFileNameLength)
6525 hRes = CONVERT10_E_OLESTREAM_GET;
6528 else
6529 hRes = CONVERT10_E_OLESTREAM_GET;
6532 else
6534 /* Get the Width of the Metafile */
6535 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6536 if(dwSize != sizeof(pData->dwMetaFileWidth))
6538 hRes = CONVERT10_E_OLESTREAM_GET;
6540 if(hRes == S_OK)
6542 /* Get the Height of the Metafile */
6543 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6544 if(dwSize != sizeof(pData->dwMetaFileHeight))
6546 hRes = CONVERT10_E_OLESTREAM_GET;
6550 if(hRes == S_OK)
6552 /* Get the Length of the Data */
6553 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6554 if(dwSize != sizeof(pData->dwDataLength))
6556 hRes = CONVERT10_E_OLESTREAM_GET;
6560 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6562 if(!bStrem1) /* if it is a second OLE stream data */
6564 pData->dwDataLength -= 8;
6565 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6566 if(dwSize != sizeof(pData->strUnknown))
6568 hRes = CONVERT10_E_OLESTREAM_GET;
6572 if(hRes == S_OK)
6574 if(pData->dwDataLength > 0)
6576 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6578 /* Get Data (ex. IStorage, Metafile, or BMP) */
6579 if(pData->pData)
6581 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6582 if(dwSize != pData->dwDataLength)
6584 hRes = CONVERT10_E_OLESTREAM_GET;
6587 else
6589 hRes = CONVERT10_E_OLESTREAM_GET;
6595 return hRes;
6598 /*************************************************************************
6599 * OLECONVERT_SaveOLE10 [Internal]
6601 * Saves the OLE10 STREAM From memory
6603 * PARAMS
6604 * pData [I] Data Structure for the OLESTREAM Data
6605 * pOleStream [I] The OLESTREAM to save
6607 * RETURNS
6608 * Success: S_OK
6609 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6611 * NOTES
6612 * This function is used by OleConvertIStorageToOLESTREAM only.
6615 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6617 DWORD dwSize;
6618 HRESULT hRes = S_OK;
6621 /* Set the OleID */
6622 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6623 if(dwSize != sizeof(pData->dwOleID))
6625 hRes = CONVERT10_E_OLESTREAM_PUT;
6628 if(hRes == S_OK)
6630 /* Set the TypeID */
6631 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6632 if(dwSize != sizeof(pData->dwTypeID))
6634 hRes = CONVERT10_E_OLESTREAM_PUT;
6638 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6640 /* Set the Length of the OleTypeName */
6641 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6642 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6644 hRes = CONVERT10_E_OLESTREAM_PUT;
6647 if(hRes == S_OK)
6649 if(pData->dwOleTypeNameLength > 0)
6651 /* Set the OleTypeName */
6652 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6653 if(dwSize != pData->dwOleTypeNameLength)
6655 hRes = CONVERT10_E_OLESTREAM_PUT;
6660 if(hRes == S_OK)
6662 /* Set the width of the Metafile */
6663 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6664 if(dwSize != sizeof(pData->dwMetaFileWidth))
6666 hRes = CONVERT10_E_OLESTREAM_PUT;
6670 if(hRes == S_OK)
6672 /* Set the height of the Metafile */
6673 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6674 if(dwSize != sizeof(pData->dwMetaFileHeight))
6676 hRes = CONVERT10_E_OLESTREAM_PUT;
6680 if(hRes == S_OK)
6682 /* Set the length of the Data */
6683 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6684 if(dwSize != sizeof(pData->dwDataLength))
6686 hRes = CONVERT10_E_OLESTREAM_PUT;
6690 if(hRes == S_OK)
6692 if(pData->dwDataLength > 0)
6694 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6695 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6696 if(dwSize != pData->dwDataLength)
6698 hRes = CONVERT10_E_OLESTREAM_PUT;
6703 return hRes;
6706 /*************************************************************************
6707 * OLECONVERT_GetOLE20FromOLE10[Internal]
6709 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6710 * opens it, and copies the content to the dest IStorage for
6711 * OleConvertOLESTREAMToIStorage
6714 * PARAMS
6715 * pDestStorage [I] The IStorage to copy the data to
6716 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6717 * nBufferLength [I] The size of the buffer
6719 * RETURNS
6720 * Nothing
6722 * NOTES
6726 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6728 HRESULT hRes;
6729 HANDLE hFile;
6730 IStorage *pTempStorage;
6731 DWORD dwNumOfBytesWritten;
6732 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6733 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6735 /* Create a temp File */
6736 GetTempPathW(MAX_PATH, wstrTempDir);
6737 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6738 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6740 if(hFile != INVALID_HANDLE_VALUE)
6742 /* Write IStorage Data to File */
6743 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6744 CloseHandle(hFile);
6746 /* Open and copy temp storage to the Dest Storage */
6747 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6748 if(hRes == S_OK)
6750 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6751 IStorage_Release(pTempStorage);
6753 DeleteFileW(wstrTempFile);
6758 /*************************************************************************
6759 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6761 * Saves the OLE10 STREAM From memory
6763 * PARAMS
6764 * pStorage [I] The Src IStorage to copy
6765 * pData [I] The Dest Memory to write to.
6767 * RETURNS
6768 * The size in bytes allocated for pData
6770 * NOTES
6771 * Memory allocated for pData must be freed by the caller
6773 * Used by OleConvertIStorageToOLESTREAM only.
6776 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6778 HANDLE hFile;
6779 HRESULT hRes;
6780 DWORD nDataLength = 0;
6781 IStorage *pTempStorage;
6782 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6783 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6785 *pData = NULL;
6787 /* Create temp Storage */
6788 GetTempPathW(MAX_PATH, wstrTempDir);
6789 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6790 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6792 if(hRes == S_OK)
6794 /* Copy Src Storage to the Temp Storage */
6795 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6796 IStorage_Release(pTempStorage);
6798 /* Open Temp Storage as a file and copy to memory */
6799 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6800 if(hFile != INVALID_HANDLE_VALUE)
6802 nDataLength = GetFileSize(hFile, NULL);
6803 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6804 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6805 CloseHandle(hFile);
6807 DeleteFileW(wstrTempFile);
6809 return nDataLength;
6812 /*************************************************************************
6813 * OLECONVERT_CreateOleStream [Internal]
6815 * Creates the "\001OLE" stream in the IStorage if necessary.
6817 * PARAMS
6818 * pStorage [I] Dest storage to create the stream in
6820 * RETURNS
6821 * Nothing
6823 * NOTES
6824 * This function is used by OleConvertOLESTREAMToIStorage only.
6826 * This stream is still unknown, MS Word seems to have extra data
6827 * but since the data is stored in the OLESTREAM there should be
6828 * no need to recreate the stream. If the stream is manually
6829 * deleted it will create it with this default data.
6832 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6834 HRESULT hRes;
6835 IStream *pStream;
6836 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6837 BYTE pOleStreamHeader [] =
6839 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6840 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6841 0x00, 0x00, 0x00, 0x00
6844 /* Create stream if not present */
6845 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6846 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6848 if(hRes == S_OK)
6850 /* Write default Data */
6851 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6852 IStream_Release(pStream);
6856 /* write a string to a stream, preceded by its length */
6857 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6859 HRESULT r;
6860 LPSTR str;
6861 DWORD len = 0;
6863 if( string )
6864 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6865 r = IStream_Write( stm, &len, sizeof(len), NULL);
6866 if( FAILED( r ) )
6867 return r;
6868 if(len == 0)
6869 return r;
6870 str = CoTaskMemAlloc( len );
6871 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6872 r = IStream_Write( stm, str, len, NULL);
6873 CoTaskMemFree( str );
6874 return r;
6877 /* read a string preceded by its length from a stream */
6878 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6880 HRESULT r;
6881 DWORD len, count = 0;
6882 LPSTR str;
6883 LPWSTR wstr;
6885 r = IStream_Read( stm, &len, sizeof(len), &count );
6886 if( FAILED( r ) )
6887 return r;
6888 if( count != sizeof(len) )
6889 return E_OUTOFMEMORY;
6891 TRACE("%d bytes\n",len);
6893 str = CoTaskMemAlloc( len );
6894 if( !str )
6895 return E_OUTOFMEMORY;
6896 count = 0;
6897 r = IStream_Read( stm, str, len, &count );
6898 if( FAILED( r ) )
6899 return r;
6900 if( count != len )
6902 CoTaskMemFree( str );
6903 return E_OUTOFMEMORY;
6906 TRACE("Read string %s\n",debugstr_an(str,len));
6908 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6909 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6910 if( wstr )
6911 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6912 CoTaskMemFree( str );
6914 *string = wstr;
6916 return r;
6920 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6921 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6923 IStream *pstm;
6924 HRESULT r = S_OK;
6925 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6927 static const BYTE unknown1[12] =
6928 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6929 0xFF, 0xFF, 0xFF, 0xFF};
6930 static const BYTE unknown2[16] =
6931 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6934 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6935 debugstr_w(lpszUserType), debugstr_w(szClipName),
6936 debugstr_w(szProgIDName));
6938 /* Create a CompObj stream */
6939 r = IStorage_CreateStream(pstg, szwStreamName,
6940 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6941 if( FAILED (r) )
6942 return r;
6944 /* Write CompObj Structure to stream */
6945 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6947 if( SUCCEEDED( r ) )
6948 r = WriteClassStm( pstm, clsid );
6950 if( SUCCEEDED( r ) )
6951 r = STREAM_WriteString( pstm, lpszUserType );
6952 if( SUCCEEDED( r ) )
6953 r = STREAM_WriteString( pstm, szClipName );
6954 if( SUCCEEDED( r ) )
6955 r = STREAM_WriteString( pstm, szProgIDName );
6956 if( SUCCEEDED( r ) )
6957 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6959 IStream_Release( pstm );
6961 return r;
6964 /***********************************************************************
6965 * WriteFmtUserTypeStg (OLE32.@)
6967 HRESULT WINAPI WriteFmtUserTypeStg(
6968 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6970 HRESULT r;
6971 WCHAR szwClipName[0x40];
6972 CLSID clsid = CLSID_NULL;
6973 LPWSTR wstrProgID = NULL;
6974 DWORD n;
6976 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6978 /* get the clipboard format name */
6979 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
6980 szwClipName[n]=0;
6982 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6984 /* FIXME: There's room to save a CLSID and its ProgID, but
6985 the CLSID is not looked up in the registry and in all the
6986 tests I wrote it was CLSID_NULL. Where does it come from?
6989 /* get the real program ID. This may fail, but that's fine */
6990 ProgIDFromCLSID(&clsid, &wstrProgID);
6992 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6994 r = STORAGE_WriteCompObj( pstg, &clsid,
6995 lpszUserType, szwClipName, wstrProgID );
6997 CoTaskMemFree(wstrProgID);
6999 return r;
7003 /******************************************************************************
7004 * ReadFmtUserTypeStg [OLE32.@]
7006 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7008 HRESULT r;
7009 IStream *stm = 0;
7010 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7011 unsigned char unknown1[12];
7012 unsigned char unknown2[16];
7013 DWORD count;
7014 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7015 CLSID clsid;
7017 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7019 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7020 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7021 if( FAILED ( r ) )
7023 WARN("Failed to open stream r = %08x\n", r);
7024 return r;
7027 /* read the various parts of the structure */
7028 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7029 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7030 goto end;
7031 r = ReadClassStm( stm, &clsid );
7032 if( FAILED( r ) )
7033 goto end;
7035 r = STREAM_ReadString( stm, &szCLSIDName );
7036 if( FAILED( r ) )
7037 goto end;
7039 r = STREAM_ReadString( stm, &szOleTypeName );
7040 if( FAILED( r ) )
7041 goto end;
7043 r = STREAM_ReadString( stm, &szProgIDName );
7044 if( FAILED( r ) )
7045 goto end;
7047 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7048 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7049 goto end;
7051 /* ok, success... now we just need to store what we found */
7052 if( pcf )
7053 *pcf = RegisterClipboardFormatW( szOleTypeName );
7054 CoTaskMemFree( szOleTypeName );
7056 if( lplpszUserType )
7057 *lplpszUserType = szCLSIDName;
7058 CoTaskMemFree( szProgIDName );
7060 end:
7061 IStream_Release( stm );
7063 return r;
7067 /*************************************************************************
7068 * OLECONVERT_CreateCompObjStream [Internal]
7070 * Creates a "\001CompObj" is the destination IStorage if necessary.
7072 * PARAMS
7073 * pStorage [I] The dest IStorage to create the CompObj Stream
7074 * if necessary.
7075 * strOleTypeName [I] The ProgID
7077 * RETURNS
7078 * Success: S_OK
7079 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7081 * NOTES
7082 * This function is used by OleConvertOLESTREAMToIStorage only.
7084 * The stream data is stored in the OLESTREAM and there should be
7085 * no need to recreate the stream. If the stream is manually
7086 * deleted it will attempt to create it by querying the registry.
7090 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7092 IStream *pStream;
7093 HRESULT hStorageRes, hRes = S_OK;
7094 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7095 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7096 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7098 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7099 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7101 /* Initialize the CompObj structure */
7102 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7103 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7104 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7107 /* Create a CompObj stream if it doesn't exist */
7108 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7109 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7110 if(hStorageRes == S_OK)
7112 /* copy the OleTypeName to the compobj struct */
7113 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7114 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7116 /* copy the OleTypeName to the compobj struct */
7117 /* Note: in the test made, these were Identical */
7118 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7119 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7121 /* Get the CLSID */
7122 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7123 bufferW, OLESTREAM_MAX_STR_LEN );
7124 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7126 if(hRes == S_OK)
7128 HKEY hKey;
7129 LONG hErr;
7130 /* Get the CLSID Default Name from the Registry */
7131 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7132 if(hErr == ERROR_SUCCESS)
7134 char strTemp[OLESTREAM_MAX_STR_LEN];
7135 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7136 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7137 if(hErr == ERROR_SUCCESS)
7139 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7141 RegCloseKey(hKey);
7145 /* Write CompObj Structure to stream */
7146 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7148 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7150 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7151 if(IStorageCompObj.dwCLSIDNameLength > 0)
7153 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7155 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7156 if(IStorageCompObj.dwOleTypeNameLength > 0)
7158 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7160 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7161 if(IStorageCompObj.dwProgIDNameLength > 0)
7163 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7165 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7166 IStream_Release(pStream);
7168 return hRes;
7172 /*************************************************************************
7173 * OLECONVERT_CreateOlePresStream[Internal]
7175 * Creates the "\002OlePres000" Stream with the Metafile data
7177 * PARAMS
7178 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7179 * dwExtentX [I] Width of the Metafile
7180 * dwExtentY [I] Height of the Metafile
7181 * pData [I] Metafile data
7182 * dwDataLength [I] Size of the Metafile data
7184 * RETURNS
7185 * Success: S_OK
7186 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7188 * NOTES
7189 * This function is used by OleConvertOLESTREAMToIStorage only.
7192 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7194 HRESULT hRes;
7195 IStream *pStream;
7196 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7197 BYTE pOlePresStreamHeader [] =
7199 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7200 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7201 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7202 0x00, 0x00, 0x00, 0x00
7205 BYTE pOlePresStreamHeaderEmpty [] =
7207 0x00, 0x00, 0x00, 0x00,
7208 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7209 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7210 0x00, 0x00, 0x00, 0x00
7213 /* Create the OlePres000 Stream */
7214 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7215 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7217 if(hRes == S_OK)
7219 DWORD nHeaderSize;
7220 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7222 memset(&OlePres, 0, sizeof(OlePres));
7223 /* Do we have any metafile data to save */
7224 if(dwDataLength > 0)
7226 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7227 nHeaderSize = sizeof(pOlePresStreamHeader);
7229 else
7231 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7232 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7234 /* Set width and height of the metafile */
7235 OlePres.dwExtentX = dwExtentX;
7236 OlePres.dwExtentY = -dwExtentY;
7238 /* Set Data and Length */
7239 if(dwDataLength > sizeof(METAFILEPICT16))
7241 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7242 OlePres.pData = &(pData[8]);
7244 /* Save OlePres000 Data to Stream */
7245 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7246 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7247 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7248 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7249 if(OlePres.dwSize > 0)
7251 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7253 IStream_Release(pStream);
7257 /*************************************************************************
7258 * OLECONVERT_CreateOle10NativeStream [Internal]
7260 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7262 * PARAMS
7263 * pStorage [I] Dest storage to create the stream in
7264 * pData [I] Ole10 Native Data (ex. bmp)
7265 * dwDataLength [I] Size of the Ole10 Native Data
7267 * RETURNS
7268 * Nothing
7270 * NOTES
7271 * This function is used by OleConvertOLESTREAMToIStorage only.
7273 * Might need to verify the data and return appropriate error message
7276 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7278 HRESULT hRes;
7279 IStream *pStream;
7280 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7282 /* Create the Ole10Native Stream */
7283 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7284 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7286 if(hRes == S_OK)
7288 /* Write info to stream */
7289 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7290 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7291 IStream_Release(pStream);
7296 /*************************************************************************
7297 * OLECONVERT_GetOLE10ProgID [Internal]
7299 * Finds the ProgID (or OleTypeID) from the IStorage
7301 * PARAMS
7302 * pStorage [I] The Src IStorage to get the ProgID
7303 * strProgID [I] the ProgID string to get
7304 * dwSize [I] the size of the string
7306 * RETURNS
7307 * Success: S_OK
7308 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7310 * NOTES
7311 * This function is used by OleConvertIStorageToOLESTREAM only.
7315 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7317 HRESULT hRes;
7318 IStream *pStream;
7319 LARGE_INTEGER iSeekPos;
7320 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7321 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7323 /* Open the CompObj Stream */
7324 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7325 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7326 if(hRes == S_OK)
7329 /*Get the OleType from the CompObj Stream */
7330 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7331 iSeekPos.u.HighPart = 0;
7333 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7334 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7335 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7336 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7337 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7338 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7339 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7341 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7342 if(*dwSize > 0)
7344 IStream_Read(pStream, strProgID, *dwSize, NULL);
7346 IStream_Release(pStream);
7348 else
7350 STATSTG stat;
7351 LPOLESTR wstrProgID;
7353 /* Get the OleType from the registry */
7354 REFCLSID clsid = &(stat.clsid);
7355 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7356 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7357 if(hRes == S_OK)
7359 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7363 return hRes;
7366 /*************************************************************************
7367 * OLECONVERT_GetOle10PresData [Internal]
7369 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7371 * PARAMS
7372 * pStorage [I] Src IStroage
7373 * pOleStream [I] Dest OleStream Mem Struct
7375 * RETURNS
7376 * Nothing
7378 * NOTES
7379 * This function is used by OleConvertIStorageToOLESTREAM only.
7381 * Memory allocated for pData must be freed by the caller
7385 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7388 HRESULT hRes;
7389 IStream *pStream;
7390 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7392 /* Initialize Default data for OLESTREAM */
7393 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7394 pOleStreamData[0].dwTypeID = 2;
7395 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7396 pOleStreamData[1].dwTypeID = 0;
7397 pOleStreamData[0].dwMetaFileWidth = 0;
7398 pOleStreamData[0].dwMetaFileHeight = 0;
7399 pOleStreamData[0].pData = NULL;
7400 pOleStreamData[1].pData = NULL;
7402 /* Open Ole10Native Stream */
7403 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7404 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7405 if(hRes == S_OK)
7408 /* Read Size and Data */
7409 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7410 if(pOleStreamData->dwDataLength > 0)
7412 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7413 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7415 IStream_Release(pStream);
7421 /*************************************************************************
7422 * OLECONVERT_GetOle20PresData[Internal]
7424 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7426 * PARAMS
7427 * pStorage [I] Src IStroage
7428 * pOleStreamData [I] Dest OleStream Mem Struct
7430 * RETURNS
7431 * Nothing
7433 * NOTES
7434 * This function is used by OleConvertIStorageToOLESTREAM only.
7436 * Memory allocated for pData must be freed by the caller
7438 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7440 HRESULT hRes;
7441 IStream *pStream;
7442 OLECONVERT_ISTORAGE_OLEPRES olePress;
7443 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7445 /* Initialize Default data for OLESTREAM */
7446 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7447 pOleStreamData[0].dwTypeID = 2;
7448 pOleStreamData[0].dwMetaFileWidth = 0;
7449 pOleStreamData[0].dwMetaFileHeight = 0;
7450 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7451 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7452 pOleStreamData[1].dwTypeID = 0;
7453 pOleStreamData[1].dwOleTypeNameLength = 0;
7454 pOleStreamData[1].strOleTypeName[0] = 0;
7455 pOleStreamData[1].dwMetaFileWidth = 0;
7456 pOleStreamData[1].dwMetaFileHeight = 0;
7457 pOleStreamData[1].pData = NULL;
7458 pOleStreamData[1].dwDataLength = 0;
7461 /* Open OlePress000 stream */
7462 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7463 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7464 if(hRes == S_OK)
7466 LARGE_INTEGER iSeekPos;
7467 METAFILEPICT16 MetaFilePict;
7468 static const char strMetafilePictName[] = "METAFILEPICT";
7470 /* Set the TypeID for a Metafile */
7471 pOleStreamData[1].dwTypeID = 5;
7473 /* Set the OleTypeName to Metafile */
7474 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7475 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7477 iSeekPos.u.HighPart = 0;
7478 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7480 /* Get Presentation Data */
7481 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7482 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7483 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7484 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7486 /*Set width and Height */
7487 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7488 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7489 if(olePress.dwSize > 0)
7491 /* Set Length */
7492 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7494 /* Set MetaFilePict struct */
7495 MetaFilePict.mm = 8;
7496 MetaFilePict.xExt = olePress.dwExtentX;
7497 MetaFilePict.yExt = olePress.dwExtentY;
7498 MetaFilePict.hMF = 0;
7500 /* Get Metafile Data */
7501 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7502 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7503 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7505 IStream_Release(pStream);
7509 /*************************************************************************
7510 * OleConvertOLESTREAMToIStorage [OLE32.@]
7512 * Read info on MSDN
7514 * TODO
7515 * DVTARGETDEVICE parameter is not handled
7516 * Still unsure of some mem fields for OLE 10 Stream
7517 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7518 * and "\001OLE" streams
7521 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7522 LPOLESTREAM pOleStream,
7523 LPSTORAGE pstg,
7524 const DVTARGETDEVICE* ptd)
7526 int i;
7527 HRESULT hRes=S_OK;
7528 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7530 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7532 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7534 if(ptd != NULL)
7536 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7539 if(pstg == NULL || pOleStream == NULL)
7541 hRes = E_INVALIDARG;
7544 if(hRes == S_OK)
7546 /* Load the OLESTREAM to Memory */
7547 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7550 if(hRes == S_OK)
7552 /* Load the OLESTREAM to Memory (part 2)*/
7553 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7556 if(hRes == S_OK)
7559 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7561 /* Do we have the IStorage Data in the OLESTREAM */
7562 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7564 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7565 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7567 else
7569 /* It must be an original OLE 1.0 source */
7570 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7573 else
7575 /* It must be an original OLE 1.0 source */
7576 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7579 /* Create CompObj Stream if necessary */
7580 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7581 if(hRes == S_OK)
7583 /*Create the Ole Stream if necessary */
7584 OLECONVERT_CreateOleStream(pstg);
7589 /* Free allocated memory */
7590 for(i=0; i < 2; i++)
7592 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7593 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7594 pOleStreamData[i].pstrOleObjFileName = NULL;
7596 return hRes;
7599 /*************************************************************************
7600 * OleConvertIStorageToOLESTREAM [OLE32.@]
7602 * Read info on MSDN
7604 * Read info on MSDN
7606 * TODO
7607 * Still unsure of some mem fields for OLE 10 Stream
7608 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7609 * and "\001OLE" streams.
7612 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7613 LPSTORAGE pstg,
7614 LPOLESTREAM pOleStream)
7616 int i;
7617 HRESULT hRes = S_OK;
7618 IStream *pStream;
7619 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7620 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7622 TRACE("%p %p\n", pstg, pOleStream);
7624 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7626 if(pstg == NULL || pOleStream == NULL)
7628 hRes = E_INVALIDARG;
7630 if(hRes == S_OK)
7632 /* Get the ProgID */
7633 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7634 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7636 if(hRes == S_OK)
7638 /* Was it originally Ole10 */
7639 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7640 if(hRes == S_OK)
7642 IStream_Release(pStream);
7643 /* Get Presentation Data for Ole10Native */
7644 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7646 else
7648 /* Get Presentation Data (OLE20) */
7649 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7652 /* Save OLESTREAM */
7653 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7654 if(hRes == S_OK)
7656 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7661 /* Free allocated memory */
7662 for(i=0; i < 2; i++)
7664 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7667 return hRes;
7670 /***********************************************************************
7671 * GetConvertStg (OLE32.@)
7673 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7674 FIXME("unimplemented stub!\n");
7675 return E_FAIL;
7678 /******************************************************************************
7679 * StgIsStorageFile [OLE32.@]
7680 * Verify if the file contains a storage object
7682 * PARAMS
7683 * fn [ I] Filename
7685 * RETURNS
7686 * S_OK if file has magic bytes as a storage object
7687 * S_FALSE if file is not storage
7689 HRESULT WINAPI
7690 StgIsStorageFile(LPCOLESTR fn)
7692 HANDLE hf;
7693 BYTE magic[8];
7694 DWORD bytes_read;
7696 TRACE("%s\n", debugstr_w(fn));
7697 hf = CreateFileW(fn, GENERIC_READ,
7698 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7699 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7701 if (hf == INVALID_HANDLE_VALUE)
7702 return STG_E_FILENOTFOUND;
7704 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7706 WARN(" unable to read file\n");
7707 CloseHandle(hf);
7708 return S_FALSE;
7711 CloseHandle(hf);
7713 if (bytes_read != 8) {
7714 WARN(" too short\n");
7715 return S_FALSE;
7718 if (!memcmp(magic,STORAGE_magic,8)) {
7719 WARN(" -> YES\n");
7720 return S_OK;
7723 WARN(" -> Invalid header.\n");
7724 return S_FALSE;
7727 /***********************************************************************
7728 * WriteClassStm (OLE32.@)
7730 * Writes a CLSID to a stream.
7732 * PARAMS
7733 * pStm [I] Stream to write to.
7734 * rclsid [I] CLSID to write.
7736 * RETURNS
7737 * Success: S_OK.
7738 * Failure: HRESULT code.
7740 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7742 TRACE("(%p,%p)\n",pStm,rclsid);
7744 if (!pStm || !rclsid)
7745 return E_INVALIDARG;
7747 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7750 /***********************************************************************
7751 * ReadClassStm (OLE32.@)
7753 * Reads a CLSID from a stream.
7755 * PARAMS
7756 * pStm [I] Stream to read from.
7757 * rclsid [O] CLSID to read.
7759 * RETURNS
7760 * Success: S_OK.
7761 * Failure: HRESULT code.
7763 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7765 ULONG nbByte;
7766 HRESULT res;
7768 TRACE("(%p,%p)\n",pStm,pclsid);
7770 if (!pStm || !pclsid)
7771 return E_INVALIDARG;
7773 /* clear the output args */
7774 *pclsid = CLSID_NULL;
7776 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7778 if (FAILED(res))
7779 return res;
7781 if (nbByte != sizeof(CLSID))
7782 return STG_E_READFAULT;
7783 else
7784 return S_OK;