ole32: Make CreateDirEntry a virtual method.
[wine/hacks.git] / dlls / ole32 / storage32.c
blob1df4d9fe07f6411905310867c18ad8f63d1c0ea4
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 success = StorageImpl_WriteDirEntry(This->ancestorStorage,
982 This->storageDirEntry,
983 &currentEntry);
984 if (success)
985 hRes = S_OK;
988 return hRes;
991 /************************************************************************
992 ** Storage32Impl implementation
995 /************************************************************************
996 * Storage32BaseImpl_CreateStorage (IStorage)
998 * This method will create the storage object within the provided storage.
1000 * See Windows documentation for more details on IStorage methods.
1002 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1003 IStorage* iface,
1004 const OLECHAR *pwcsName, /* [string][in] */
1005 DWORD grfMode, /* [in] */
1006 DWORD reserved1, /* [in] */
1007 DWORD reserved2, /* [in] */
1008 IStorage **ppstg) /* [out] */
1010 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1012 DirEntry currentEntry;
1013 DirEntry newEntry;
1014 DirRef currentEntryRef;
1015 DirRef newEntryRef;
1016 HRESULT hr;
1018 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1019 iface, debugstr_w(pwcsName), grfMode,
1020 reserved1, reserved2, ppstg);
1022 if (ppstg == 0)
1023 return STG_E_INVALIDPOINTER;
1025 if (This->openFlags & STGM_SIMPLE)
1027 return STG_E_INVALIDFUNCTION;
1030 if (pwcsName == 0)
1031 return STG_E_INVALIDNAME;
1033 *ppstg = NULL;
1035 if ( FAILED( validateSTGM(grfMode) ) ||
1036 (grfMode & STGM_DELETEONRELEASE) )
1038 WARN("bad grfMode: 0x%x\n", grfMode);
1039 return STG_E_INVALIDFLAG;
1042 if (!This->ancestorStorage)
1043 return STG_E_REVERTED;
1046 * Check that we're compatible with the parent's storage mode
1048 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1050 WARN("access denied\n");
1051 return STG_E_ACCESSDENIED;
1054 currentEntryRef = findElement(This->ancestorStorage,
1055 This->storageDirEntry,
1056 pwcsName,
1057 &currentEntry);
1059 if (currentEntryRef != DIRENTRY_NULL)
1062 * An element with this name already exists
1064 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1065 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1067 hr = IStorage_DestroyElement(iface, pwcsName);
1068 if (FAILED(hr))
1069 return hr;
1071 else
1073 WARN("file already exists\n");
1074 return STG_E_FILEALREADYEXISTS;
1077 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1079 WARN("read-only storage\n");
1080 return STG_E_ACCESSDENIED;
1083 memset(&newEntry, 0, sizeof(DirEntry));
1085 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1087 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1089 FIXME("name too long\n");
1090 return STG_E_INVALIDNAME;
1093 strcpyW(newEntry.name, pwcsName);
1095 newEntry.stgType = STGTY_STORAGE;
1096 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1097 newEntry.size.u.LowPart = 0;
1098 newEntry.size.u.HighPart = 0;
1100 newEntry.leftChild = DIRENTRY_NULL;
1101 newEntry.rightChild = DIRENTRY_NULL;
1102 newEntry.dirRootEntry = DIRENTRY_NULL;
1104 /* call CoFileTime to get the current time
1105 newEntry.ctime
1106 newEntry.mtime
1109 /* newEntry.clsid */
1112 * Create a new directory entry for the storage
1114 StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1117 * Insert the new directory entry into the parent storage's tree
1119 insertIntoTree(
1120 This->ancestorStorage,
1121 This->storageDirEntry,
1122 newEntryRef);
1125 * Open it to get a pointer to return.
1127 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1129 if( (hr != S_OK) || (*ppstg == NULL))
1131 return hr;
1135 return S_OK;
1139 /***************************************************************************
1141 * Internal Method
1143 * Reserve a directory entry in the file and initialize it.
1145 static HRESULT StorageImpl_CreateDirEntry(
1146 StorageBaseImpl *base,
1147 const DirEntry *newData,
1148 DirRef *index)
1150 StorageImpl *storage = (StorageImpl*)base;
1151 ULONG currentEntryIndex = 0;
1152 ULONG newEntryIndex = DIRENTRY_NULL;
1153 HRESULT hr = S_OK;
1154 BYTE currentData[RAW_DIRENTRY_SIZE];
1155 WORD sizeOfNameString;
1159 hr = StorageImpl_ReadRawDirEntry(storage,
1160 currentEntryIndex,
1161 currentData);
1163 if (SUCCEEDED(hr))
1165 StorageUtl_ReadWord(
1166 currentData,
1167 OFFSET_PS_NAMELENGTH,
1168 &sizeOfNameString);
1170 if (sizeOfNameString == 0)
1173 * The entry exists and is available, we found it.
1175 newEntryIndex = currentEntryIndex;
1178 else
1181 * We exhausted the directory entries, we will create more space below
1183 newEntryIndex = currentEntryIndex;
1185 currentEntryIndex++;
1187 } while (newEntryIndex == DIRENTRY_NULL);
1190 * grow the directory stream
1192 if (FAILED(hr))
1194 BYTE emptyData[RAW_DIRENTRY_SIZE];
1195 ULARGE_INTEGER newSize;
1196 ULONG entryIndex;
1197 ULONG lastEntry = 0;
1198 ULONG blockCount = 0;
1201 * obtain the new count of blocks in the directory stream
1203 blockCount = BlockChainStream_GetCount(
1204 storage->rootBlockChain)+1;
1207 * initialize the size used by the directory stream
1209 newSize.u.HighPart = 0;
1210 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1213 * add a block to the directory stream
1215 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1218 * memset the empty entry in order to initialize the unused newly
1219 * created entries
1221 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1224 * initialize them
1226 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1228 for(
1229 entryIndex = newEntryIndex + 1;
1230 entryIndex < lastEntry;
1231 entryIndex++)
1233 StorageImpl_WriteRawDirEntry(
1234 storage,
1235 entryIndex,
1236 emptyData);
1240 UpdateRawDirEntry(currentData, newData);
1242 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1244 if (SUCCEEDED(hr))
1245 *index = newEntryIndex;
1247 return hr;
1250 /***************************************************************************
1252 * Internal Method
1254 * Mark a directory entry in the file as free.
1256 static HRESULT destroyDirEntry(
1257 StorageImpl *storage,
1258 DirRef index)
1260 HRESULT hr;
1261 BYTE emptyData[RAW_DIRENTRY_SIZE];
1263 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1265 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1267 return hr;
1271 /****************************************************************************
1273 * Internal Method
1275 * Case insensitive comparison of DirEntry.name by first considering
1276 * their size.
1278 * Returns <0 when name1 < name2
1279 * >0 when name1 > name2
1280 * 0 when name1 == name2
1282 static LONG entryNameCmp(
1283 const OLECHAR *name1,
1284 const OLECHAR *name2)
1286 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1288 if (diff == 0)
1291 * We compare the string themselves only when they are of the same length
1293 diff = lstrcmpiW( name1, name2);
1296 return diff;
1299 /****************************************************************************
1301 * Internal Method
1303 * Add a directory entry to a storage
1305 static HRESULT insertIntoTree(
1306 StorageImpl *This,
1307 DirRef parentStorageIndex,
1308 DirRef newEntryIndex)
1310 DirEntry currentEntry;
1311 DirEntry newEntry;
1314 * Read the inserted entry
1316 StorageImpl_ReadDirEntry(This,
1317 newEntryIndex,
1318 &newEntry);
1321 * Read the storage entry
1323 StorageImpl_ReadDirEntry(This,
1324 parentStorageIndex,
1325 &currentEntry);
1327 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1330 * The root storage contains some element, therefore, start the research
1331 * for the appropriate location.
1333 BOOL found = 0;
1334 DirRef current, next, previous, currentEntryId;
1337 * Keep a reference to the root of the storage's element tree
1339 currentEntryId = currentEntry.dirRootEntry;
1342 * Read
1344 StorageImpl_ReadDirEntry(This,
1345 currentEntry.dirRootEntry,
1346 &currentEntry);
1348 previous = currentEntry.leftChild;
1349 next = currentEntry.rightChild;
1350 current = currentEntryId;
1352 while (found == 0)
1354 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1356 if (diff < 0)
1358 if (previous != DIRENTRY_NULL)
1360 StorageImpl_ReadDirEntry(This,
1361 previous,
1362 &currentEntry);
1363 current = previous;
1365 else
1367 currentEntry.leftChild = newEntryIndex;
1368 StorageImpl_WriteDirEntry(This,
1369 current,
1370 &currentEntry);
1371 found = 1;
1374 else if (diff > 0)
1376 if (next != DIRENTRY_NULL)
1378 StorageImpl_ReadDirEntry(This,
1379 next,
1380 &currentEntry);
1381 current = next;
1383 else
1385 currentEntry.rightChild = newEntryIndex;
1386 StorageImpl_WriteDirEntry(This,
1387 current,
1388 &currentEntry);
1389 found = 1;
1392 else
1395 * Trying to insert an item with the same name in the
1396 * subtree structure.
1398 return STG_E_FILEALREADYEXISTS;
1401 previous = currentEntry.leftChild;
1402 next = currentEntry.rightChild;
1405 else
1408 * The storage is empty, make the new entry the root of its element tree
1410 currentEntry.dirRootEntry = newEntryIndex;
1411 StorageImpl_WriteDirEntry(This,
1412 parentStorageIndex,
1413 &currentEntry);
1416 return S_OK;
1419 /****************************************************************************
1421 * Internal Method
1423 * Find and read the element of a storage with the given name.
1425 static DirRef findElement(StorageImpl *storage, DirRef storageEntry,
1426 const OLECHAR *name, DirEntry *data)
1428 DirRef currentEntry;
1430 /* Read the storage entry to find the root of the tree. */
1431 StorageImpl_ReadDirEntry(storage, storageEntry, data);
1433 currentEntry = data->dirRootEntry;
1435 while (currentEntry != DIRENTRY_NULL)
1437 LONG cmp;
1439 StorageImpl_ReadDirEntry(storage, currentEntry, data);
1441 cmp = entryNameCmp(name, data->name);
1443 if (cmp == 0)
1444 /* found it */
1445 break;
1447 else if (cmp < 0)
1448 currentEntry = data->leftChild;
1450 else if (cmp > 0)
1451 currentEntry = data->rightChild;
1454 return currentEntry;
1457 /****************************************************************************
1459 * Internal Method
1461 * Find and read the binary tree parent of the element with the given name.
1463 * If there is no such element, find a place where it could be inserted and
1464 * return STG_E_FILENOTFOUND.
1466 static HRESULT findTreeParent(StorageImpl *storage, DirRef storageEntry,
1467 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1468 ULONG *relation)
1470 DirRef childEntry;
1471 DirEntry childData;
1473 /* Read the storage entry to find the root of the tree. */
1474 StorageImpl_ReadDirEntry(storage, storageEntry, parentData);
1476 *parentEntry = storageEntry;
1477 *relation = DIRENTRY_RELATION_DIR;
1479 childEntry = parentData->dirRootEntry;
1481 while (childEntry != DIRENTRY_NULL)
1483 LONG cmp;
1485 StorageImpl_ReadDirEntry(storage, childEntry, &childData);
1487 cmp = entryNameCmp(childName, childData.name);
1489 if (cmp == 0)
1490 /* found it */
1491 break;
1493 else if (cmp < 0)
1495 *parentData = childData;
1496 *parentEntry = childEntry;
1497 *relation = DIRENTRY_RELATION_PREVIOUS;
1499 childEntry = parentData->leftChild;
1502 else if (cmp > 0)
1504 *parentData = childData;
1505 *parentEntry = childEntry;
1506 *relation = DIRENTRY_RELATION_NEXT;
1508 childEntry = parentData->rightChild;
1512 if (childEntry == DIRENTRY_NULL)
1513 return STG_E_FILENOTFOUND;
1514 else
1515 return S_OK;
1519 /*************************************************************************
1520 * CopyTo (IStorage)
1522 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1523 IStorage* iface,
1524 DWORD ciidExclude, /* [in] */
1525 const IID* rgiidExclude, /* [size_is][unique][in] */
1526 SNB snbExclude, /* [unique][in] */
1527 IStorage* pstgDest) /* [unique][in] */
1529 IEnumSTATSTG *elements = 0;
1530 STATSTG curElement, strStat;
1531 HRESULT hr;
1532 IStorage *pstgTmp, *pstgChild;
1533 IStream *pstrTmp, *pstrChild;
1534 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1535 int i;
1537 TRACE("(%p, %d, %p, %p, %p)\n",
1538 iface, ciidExclude, rgiidExclude,
1539 snbExclude, pstgDest);
1541 if ( pstgDest == 0 )
1542 return STG_E_INVALIDPOINTER;
1545 * Enumerate the elements
1547 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1549 if ( hr != S_OK )
1550 return hr;
1553 * set the class ID
1555 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1556 IStorage_SetClass( pstgDest, &curElement.clsid );
1558 for(i = 0; i < ciidExclude; ++i)
1560 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1561 skip_storage = TRUE;
1562 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1563 skip_stream = TRUE;
1564 else
1565 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1571 * Obtain the next element
1573 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1575 if ( hr == S_FALSE )
1577 hr = S_OK; /* done, every element has been copied */
1578 break;
1581 if ( snbExclude )
1583 WCHAR **snb = snbExclude;
1584 skip = FALSE;
1585 while ( *snb != NULL && !skip )
1587 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1588 skip = TRUE;
1589 ++snb;
1593 if ( skip )
1594 continue;
1596 if (curElement.type == STGTY_STORAGE)
1598 if(skip_storage)
1599 continue;
1602 * open child source storage
1604 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1605 STGM_READ|STGM_SHARE_EXCLUSIVE,
1606 NULL, 0, &pstgChild );
1608 if (hr != S_OK)
1609 break;
1612 * Check if destination storage is not a child of the source
1613 * storage, which will cause an infinite loop
1615 if (pstgChild == pstgDest)
1617 IEnumSTATSTG_Release(elements);
1619 return STG_E_ACCESSDENIED;
1623 * create a new storage in destination storage
1625 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1626 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1627 0, 0,
1628 &pstgTmp );
1630 * if it already exist, don't create a new one use this one
1632 if (hr == STG_E_FILEALREADYEXISTS)
1634 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1635 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1636 NULL, 0, &pstgTmp );
1639 if (hr != S_OK)
1640 break;
1644 * do the copy recursively
1646 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1647 NULL, pstgTmp );
1649 IStorage_Release( pstgTmp );
1650 IStorage_Release( pstgChild );
1652 else if (curElement.type == STGTY_STREAM)
1654 if(skip_stream)
1655 continue;
1658 * create a new stream in destination storage. If the stream already
1659 * exist, it will be deleted and a new one will be created.
1661 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1662 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1663 0, 0, &pstrTmp );
1665 if (hr != S_OK)
1666 break;
1669 * open child stream storage
1671 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1672 STGM_READ|STGM_SHARE_EXCLUSIVE,
1673 0, &pstrChild );
1675 if (hr != S_OK)
1676 break;
1679 * Get the size of the source stream
1681 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1684 * Set the size of the destination stream.
1686 IStream_SetSize(pstrTmp, strStat.cbSize);
1689 * do the copy
1691 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1692 NULL, NULL );
1694 IStream_Release( pstrTmp );
1695 IStream_Release( pstrChild );
1697 else
1699 WARN("unknown element type: %d\n", curElement.type);
1702 } while (hr == S_OK);
1705 * Clean-up
1707 IEnumSTATSTG_Release(elements);
1709 return hr;
1712 /*************************************************************************
1713 * MoveElementTo (IStorage)
1715 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1716 IStorage* iface,
1717 const OLECHAR *pwcsName, /* [string][in] */
1718 IStorage *pstgDest, /* [unique][in] */
1719 const OLECHAR *pwcsNewName,/* [string][in] */
1720 DWORD grfFlags) /* [in] */
1722 FIXME("(%p %s %p %s %u): stub\n", iface,
1723 debugstr_w(pwcsName), pstgDest,
1724 debugstr_w(pwcsNewName), grfFlags);
1725 return E_NOTIMPL;
1728 /*************************************************************************
1729 * Commit (IStorage)
1731 * Ensures that any changes made to a storage object open in transacted mode
1732 * are reflected in the parent storage
1734 * NOTES
1735 * Wine doesn't implement transacted mode, which seems to be a basic
1736 * optimization, so we can ignore this stub for now.
1738 static HRESULT WINAPI StorageImpl_Commit(
1739 IStorage* iface,
1740 DWORD grfCommitFlags)/* [in] */
1742 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1743 return S_OK;
1746 /*************************************************************************
1747 * Revert (IStorage)
1749 * Discard all changes that have been made since the last commit operation
1751 static HRESULT WINAPI StorageImpl_Revert(
1752 IStorage* iface)
1754 FIXME("(%p): stub\n", iface);
1755 return E_NOTIMPL;
1758 /*************************************************************************
1759 * DestroyElement (IStorage)
1761 * Strategy: This implementation is built this way for simplicity not for speed.
1762 * I always delete the topmost element of the enumeration and adjust
1763 * the deleted element pointer all the time. This takes longer to
1764 * do but allow to reinvoke DestroyElement whenever we encounter a
1765 * storage object. The optimisation resides in the usage of another
1766 * enumeration strategy that would give all the leaves of a storage
1767 * first. (postfix order)
1769 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1770 IStorage* iface,
1771 const OLECHAR *pwcsName)/* [string][in] */
1773 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1775 HRESULT hr = S_OK;
1776 DirEntry entryToDelete;
1777 DirRef entryToDeleteRef;
1779 TRACE("(%p, %s)\n",
1780 iface, debugstr_w(pwcsName));
1782 if (pwcsName==NULL)
1783 return STG_E_INVALIDPOINTER;
1785 if (!This->ancestorStorage)
1786 return STG_E_REVERTED;
1788 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1789 return STG_E_ACCESSDENIED;
1791 entryToDeleteRef = findElement(
1792 This->ancestorStorage,
1793 This->storageDirEntry,
1794 pwcsName,
1795 &entryToDelete);
1797 if ( entryToDeleteRef == DIRENTRY_NULL )
1799 return STG_E_FILENOTFOUND;
1802 if ( entryToDelete.stgType == STGTY_STORAGE )
1804 hr = deleteStorageContents(
1805 This,
1806 entryToDeleteRef,
1807 entryToDelete);
1809 else if ( entryToDelete.stgType == STGTY_STREAM )
1811 hr = deleteStreamContents(
1812 This,
1813 entryToDeleteRef,
1814 entryToDelete);
1817 if (hr!=S_OK)
1818 return hr;
1821 * Remove the entry from its parent storage
1823 hr = removeFromTree(
1824 This->ancestorStorage,
1825 This->storageDirEntry,
1826 entryToDeleteRef);
1829 * Invalidate the entry
1831 if (SUCCEEDED(hr))
1832 destroyDirEntry(This->ancestorStorage,
1833 entryToDeleteRef);
1835 return hr;
1839 /******************************************************************************
1840 * Internal stream list handlers
1843 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1845 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1846 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1849 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1851 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1852 list_remove(&(strm->StrmListEntry));
1855 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1857 StgStreamImpl *strm;
1859 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1861 if (strm->dirEntry == streamEntry)
1863 return TRUE;
1867 return FALSE;
1870 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1872 StorageInternalImpl *childstg;
1874 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1876 if (childstg->base.storageDirEntry == storageEntry)
1878 return TRUE;
1882 return FALSE;
1885 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1887 struct list *cur, *cur2;
1888 StgStreamImpl *strm=NULL;
1889 StorageInternalImpl *childstg=NULL;
1891 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1892 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1893 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1894 strm->parentStorage = NULL;
1895 list_remove(cur);
1898 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1899 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1900 StorageInternalImpl_Invalidate( childstg );
1905 /*********************************************************************
1907 * Internal Method
1909 * Delete the contents of a storage entry.
1912 static HRESULT deleteStorageContents(
1913 StorageBaseImpl *parentStorage,
1914 DirRef indexToDelete,
1915 DirEntry entryDataToDelete)
1917 IEnumSTATSTG *elements = 0;
1918 IStorage *childStorage = 0;
1919 STATSTG currentElement;
1920 HRESULT hr;
1921 HRESULT destroyHr = S_OK;
1922 StorageInternalImpl *stg, *stg2;
1924 /* Invalidate any open storage objects. */
1925 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1927 if (stg->base.storageDirEntry == indexToDelete)
1929 StorageInternalImpl_Invalidate(stg);
1934 * Open the storage and enumerate it
1936 hr = StorageBaseImpl_OpenStorage(
1937 (IStorage*)parentStorage,
1938 entryDataToDelete.name,
1940 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1943 &childStorage);
1945 if (hr != S_OK)
1947 return hr;
1951 * Enumerate the elements
1953 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1958 * Obtain the next element
1960 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1961 if (hr==S_OK)
1963 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
1965 CoTaskMemFree(currentElement.pwcsName);
1969 * We need to Reset the enumeration every time because we delete elements
1970 * and the enumeration could be invalid
1972 IEnumSTATSTG_Reset(elements);
1974 } while ((hr == S_OK) && (destroyHr == S_OK));
1976 IStorage_Release(childStorage);
1977 IEnumSTATSTG_Release(elements);
1979 return destroyHr;
1982 /*********************************************************************
1984 * Internal Method
1986 * Perform the deletion of a stream's data
1989 static HRESULT deleteStreamContents(
1990 StorageBaseImpl *parentStorage,
1991 DirRef indexToDelete,
1992 DirEntry entryDataToDelete)
1994 IStream *pis;
1995 HRESULT hr;
1996 ULARGE_INTEGER size;
1997 StgStreamImpl *strm, *strm2;
1999 /* Invalidate any open stream objects. */
2000 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2002 if (strm->dirEntry == indexToDelete)
2004 TRACE("Stream deleted %p\n", strm);
2005 strm->parentStorage = NULL;
2006 list_remove(&strm->StrmListEntry);
2010 size.u.HighPart = 0;
2011 size.u.LowPart = 0;
2013 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2014 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2016 if (hr!=S_OK)
2018 return(hr);
2022 * Zap the stream
2024 hr = IStream_SetSize(pis, size);
2026 if(hr != S_OK)
2028 return hr;
2032 * Release the stream object.
2034 IStream_Release(pis);
2036 return S_OK;
2039 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2041 switch (relation)
2043 case DIRENTRY_RELATION_PREVIOUS:
2044 entry->leftChild = new_target;
2045 break;
2046 case DIRENTRY_RELATION_NEXT:
2047 entry->rightChild = new_target;
2048 break;
2049 case DIRENTRY_RELATION_DIR:
2050 entry->dirRootEntry = new_target;
2051 break;
2052 default:
2053 assert(0);
2057 /*************************************************************************
2059 * Internal Method
2061 * This method removes a directory entry from its parent storage tree without
2062 * freeing any resources attached to it.
2064 static HRESULT removeFromTree(
2065 StorageImpl *This,
2066 DirRef parentStorageIndex,
2067 DirRef deletedIndex)
2069 HRESULT hr = S_OK;
2070 BOOL res = TRUE;
2071 DirEntry entryToDelete;
2072 DirEntry parentEntry;
2073 DirRef parentEntryRef;
2074 ULONG typeOfRelation;
2076 res = StorageImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2079 * Find the element that links to the one we want to delete.
2081 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2082 &parentEntry, &parentEntryRef, &typeOfRelation);
2084 if (hr != S_OK)
2085 return hr;
2087 if (entryToDelete.leftChild != DIRENTRY_NULL)
2090 * Replace the deleted entry with its left child
2092 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2094 res = StorageImpl_WriteDirEntry(
2095 This,
2096 parentEntryRef,
2097 &parentEntry);
2098 if(!res)
2100 return E_FAIL;
2103 if (entryToDelete.rightChild != DIRENTRY_NULL)
2106 * We need to reinsert the right child somewhere. We already know it and
2107 * its children are greater than everything in the left tree, so we
2108 * insert it at the rightmost point in the left tree.
2110 DirRef newRightChildParent = entryToDelete.leftChild;
2111 DirEntry newRightChildParentEntry;
2115 res = StorageImpl_ReadDirEntry(
2116 This,
2117 newRightChildParent,
2118 &newRightChildParentEntry);
2119 if (!res)
2121 return E_FAIL;
2124 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2125 newRightChildParent = newRightChildParentEntry.rightChild;
2126 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2128 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2130 res = StorageImpl_WriteDirEntry(
2131 This,
2132 newRightChildParent,
2133 &newRightChildParentEntry);
2134 if (!res)
2136 return E_FAIL;
2140 else
2143 * Replace the deleted entry with its right child
2145 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2147 res = StorageImpl_WriteDirEntry(
2148 This,
2149 parentEntryRef,
2150 &parentEntry);
2151 if(!res)
2153 return E_FAIL;
2157 return hr;
2161 /******************************************************************************
2162 * SetElementTimes (IStorage)
2164 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2165 IStorage* iface,
2166 const OLECHAR *pwcsName,/* [string][in] */
2167 const FILETIME *pctime, /* [in] */
2168 const FILETIME *patime, /* [in] */
2169 const FILETIME *pmtime) /* [in] */
2171 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2172 return S_OK;
2175 /******************************************************************************
2176 * SetStateBits (IStorage)
2178 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2179 IStorage* iface,
2180 DWORD grfStateBits,/* [in] */
2181 DWORD grfMask) /* [in] */
2183 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2185 if (!This->ancestorStorage)
2186 return STG_E_REVERTED;
2188 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2189 return S_OK;
2193 * Virtual function table for the IStorage32Impl class.
2195 static const IStorageVtbl Storage32Impl_Vtbl =
2197 StorageBaseImpl_QueryInterface,
2198 StorageBaseImpl_AddRef,
2199 StorageBaseImpl_Release,
2200 StorageBaseImpl_CreateStream,
2201 StorageBaseImpl_OpenStream,
2202 StorageBaseImpl_CreateStorage,
2203 StorageBaseImpl_OpenStorage,
2204 StorageBaseImpl_CopyTo,
2205 StorageBaseImpl_MoveElementTo,
2206 StorageImpl_Commit,
2207 StorageImpl_Revert,
2208 StorageBaseImpl_EnumElements,
2209 StorageBaseImpl_DestroyElement,
2210 StorageBaseImpl_RenameElement,
2211 StorageBaseImpl_SetElementTimes,
2212 StorageBaseImpl_SetClass,
2213 StorageBaseImpl_SetStateBits,
2214 StorageBaseImpl_Stat
2217 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2219 StorageImpl_Destroy,
2220 StorageImpl_CreateDirEntry
2223 static HRESULT StorageImpl_Construct(
2224 HANDLE hFile,
2225 LPCOLESTR pwcsName,
2226 ILockBytes* pLkbyt,
2227 DWORD openFlags,
2228 BOOL fileBased,
2229 BOOL create,
2230 StorageImpl** result)
2232 StorageImpl* This;
2233 HRESULT hr = S_OK;
2234 DirEntry currentEntry;
2235 BOOL readSuccessful;
2236 DirRef currentEntryRef;
2238 if ( FAILED( validateSTGM(openFlags) ))
2239 return STG_E_INVALIDFLAG;
2241 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2242 if (!This)
2243 return E_OUTOFMEMORY;
2245 memset(This, 0, sizeof(StorageImpl));
2247 list_init(&This->base.strmHead);
2249 list_init(&This->base.storageHead);
2251 This->base.lpVtbl = &Storage32Impl_Vtbl;
2252 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2253 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2254 This->base.openFlags = (openFlags & ~STGM_CREATE);
2255 This->base.ref = 1;
2256 This->base.create = create;
2259 * This is the top-level storage so initialize the ancestor pointer
2260 * to this.
2262 This->base.ancestorStorage = This;
2264 This->hFile = hFile;
2266 if(pwcsName) {
2267 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2268 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2269 if (!This->pwcsName)
2271 hr = STG_E_INSUFFICIENTMEMORY;
2272 goto end;
2274 strcpyW(This->pwcsName, pwcsName);
2276 memcpy(This->base.filename, pwcsName, DIRENTRY_NAME_BUFFER_LEN-1);
2277 This->base.filename[DIRENTRY_NAME_BUFFER_LEN-1] = 0;
2281 * Initialize the big block cache.
2283 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2284 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2285 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2286 pLkbyt,
2287 openFlags,
2288 This->bigBlockSize,
2289 fileBased);
2291 if (This->bigBlockFile == 0)
2293 hr = E_FAIL;
2294 goto end;
2297 if (create)
2299 ULARGE_INTEGER size;
2300 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2303 * Initialize all header variables:
2304 * - The big block depot consists of one block and it is at block 0
2305 * - The directory table starts at block 1
2306 * - There is no small block depot
2308 memset( This->bigBlockDepotStart,
2309 BLOCK_UNUSED,
2310 sizeof(This->bigBlockDepotStart));
2312 This->bigBlockDepotCount = 1;
2313 This->bigBlockDepotStart[0] = 0;
2314 This->rootStartBlock = 1;
2315 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2316 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2317 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2318 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2319 This->extBigBlockDepotCount = 0;
2321 StorageImpl_SaveFileHeader(This);
2324 * Add one block for the big block depot and one block for the directory table
2326 size.u.HighPart = 0;
2327 size.u.LowPart = This->bigBlockSize * 3;
2328 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2331 * Initialize the big block depot
2333 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2334 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2335 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2336 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2338 else
2341 * Load the header for the file.
2343 hr = StorageImpl_LoadFileHeader(This);
2345 if (FAILED(hr))
2347 goto end;
2352 * There is no block depot cached yet.
2354 This->indexBlockDepotCached = 0xFFFFFFFF;
2357 * Start searching for free blocks with block 0.
2359 This->prevFreeBlock = 0;
2362 * Create the block chain abstractions.
2364 if(!(This->rootBlockChain =
2365 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2367 hr = STG_E_READFAULT;
2368 goto end;
2371 if(!(This->smallBlockDepotChain =
2372 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2373 DIRENTRY_NULL)))
2375 hr = STG_E_READFAULT;
2376 goto end;
2380 * Write the root storage entry (memory only)
2382 if (create)
2384 DirEntry rootEntry;
2386 * Initialize the directory table
2388 memset(&rootEntry, 0, sizeof(rootEntry));
2389 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2390 sizeof(rootEntry.name)/sizeof(WCHAR) );
2391 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2392 rootEntry.stgType = STGTY_ROOT;
2393 rootEntry.leftChild = DIRENTRY_NULL;
2394 rootEntry.rightChild = DIRENTRY_NULL;
2395 rootEntry.dirRootEntry = DIRENTRY_NULL;
2396 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2397 rootEntry.size.u.HighPart = 0;
2398 rootEntry.size.u.LowPart = 0;
2400 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2404 * Find the ID of the root storage.
2406 currentEntryRef = 0;
2410 readSuccessful = StorageImpl_ReadDirEntry(
2411 This,
2412 currentEntryRef,
2413 &currentEntry);
2415 if (readSuccessful)
2417 if ( (currentEntry.sizeOfNameString != 0 ) &&
2418 (currentEntry.stgType == STGTY_ROOT) )
2420 This->base.storageDirEntry = currentEntryRef;
2424 currentEntryRef++;
2426 } while (readSuccessful && (This->base.storageDirEntry == DIRENTRY_NULL) );
2428 if (!readSuccessful)
2430 hr = STG_E_READFAULT;
2431 goto end;
2435 * Create the block chain abstraction for the small block root chain.
2437 if(!(This->smallBlockRootChain =
2438 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2440 hr = STG_E_READFAULT;
2443 end:
2444 if (FAILED(hr))
2446 IStorage_Release((IStorage*)This);
2447 *result = NULL;
2449 else
2450 *result = This;
2452 return hr;
2455 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2457 StorageImpl *This = (StorageImpl*) iface;
2458 TRACE("(%p)\n", This);
2460 StorageBaseImpl_DeleteAll(&This->base);
2462 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2464 BlockChainStream_Destroy(This->smallBlockRootChain);
2465 BlockChainStream_Destroy(This->rootBlockChain);
2466 BlockChainStream_Destroy(This->smallBlockDepotChain);
2468 if (This->bigBlockFile)
2469 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2470 HeapFree(GetProcessHeap(), 0, This);
2473 /******************************************************************************
2474 * Storage32Impl_GetNextFreeBigBlock
2476 * Returns the index of the next free big block.
2477 * If the big block depot is filled, this method will enlarge it.
2480 static ULONG StorageImpl_GetNextFreeBigBlock(
2481 StorageImpl* This)
2483 ULONG depotBlockIndexPos;
2484 BYTE depotBuffer[BIG_BLOCK_SIZE];
2485 BOOL success;
2486 ULONG depotBlockOffset;
2487 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2488 ULONG nextBlockIndex = BLOCK_SPECIAL;
2489 int depotIndex = 0;
2490 ULONG freeBlock = BLOCK_UNUSED;
2492 depotIndex = This->prevFreeBlock / blocksPerDepot;
2493 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2496 * Scan the entire big block depot until we find a block marked free
2498 while (nextBlockIndex != BLOCK_UNUSED)
2500 if (depotIndex < COUNT_BBDEPOTINHEADER)
2502 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2505 * Grow the primary depot.
2507 if (depotBlockIndexPos == BLOCK_UNUSED)
2509 depotBlockIndexPos = depotIndex*blocksPerDepot;
2512 * Add a block depot.
2514 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2515 This->bigBlockDepotCount++;
2516 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2519 * Flag it as a block depot.
2521 StorageImpl_SetNextBlockInChain(This,
2522 depotBlockIndexPos,
2523 BLOCK_SPECIAL);
2525 /* Save new header information.
2527 StorageImpl_SaveFileHeader(This);
2530 else
2532 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2534 if (depotBlockIndexPos == BLOCK_UNUSED)
2537 * Grow the extended depot.
2539 ULONG extIndex = BLOCK_UNUSED;
2540 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2541 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2543 if (extBlockOffset == 0)
2545 /* We need an extended block.
2547 extIndex = Storage32Impl_AddExtBlockDepot(This);
2548 This->extBigBlockDepotCount++;
2549 depotBlockIndexPos = extIndex + 1;
2551 else
2552 depotBlockIndexPos = depotIndex * blocksPerDepot;
2555 * Add a block depot and mark it in the extended block.
2557 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2558 This->bigBlockDepotCount++;
2559 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2561 /* Flag the block depot.
2563 StorageImpl_SetNextBlockInChain(This,
2564 depotBlockIndexPos,
2565 BLOCK_SPECIAL);
2567 /* If necessary, flag the extended depot block.
2569 if (extIndex != BLOCK_UNUSED)
2570 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2572 /* Save header information.
2574 StorageImpl_SaveFileHeader(This);
2578 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2580 if (success)
2582 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2583 ( nextBlockIndex != BLOCK_UNUSED))
2585 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2587 if (nextBlockIndex == BLOCK_UNUSED)
2589 freeBlock = (depotIndex * blocksPerDepot) +
2590 (depotBlockOffset/sizeof(ULONG));
2593 depotBlockOffset += sizeof(ULONG);
2597 depotIndex++;
2598 depotBlockOffset = 0;
2602 * make sure that the block physically exists before using it
2604 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2606 This->prevFreeBlock = freeBlock;
2608 return freeBlock;
2611 /******************************************************************************
2612 * Storage32Impl_AddBlockDepot
2614 * This will create a depot block, essentially it is a block initialized
2615 * to BLOCK_UNUSEDs.
2617 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2619 BYTE blockBuffer[BIG_BLOCK_SIZE];
2622 * Initialize blocks as free
2624 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2625 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2628 /******************************************************************************
2629 * Storage32Impl_GetExtDepotBlock
2631 * Returns the index of the block that corresponds to the specified depot
2632 * index. This method is only for depot indexes equal or greater than
2633 * COUNT_BBDEPOTINHEADER.
2635 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2637 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2638 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2639 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2640 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2641 ULONG blockIndex = BLOCK_UNUSED;
2642 ULONG extBlockIndex = This->extBigBlockDepotStart;
2644 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2646 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2647 return BLOCK_UNUSED;
2649 while (extBlockCount > 0)
2651 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2652 extBlockCount--;
2655 if (extBlockIndex != BLOCK_UNUSED)
2656 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2657 extBlockOffset * sizeof(ULONG), &blockIndex);
2659 return blockIndex;
2662 /******************************************************************************
2663 * Storage32Impl_SetExtDepotBlock
2665 * Associates the specified block index to the specified depot index.
2666 * This method is only for depot indexes equal or greater than
2667 * COUNT_BBDEPOTINHEADER.
2669 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2671 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2672 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2673 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2674 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2675 ULONG extBlockIndex = This->extBigBlockDepotStart;
2677 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2679 while (extBlockCount > 0)
2681 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2682 extBlockCount--;
2685 if (extBlockIndex != BLOCK_UNUSED)
2687 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2688 extBlockOffset * sizeof(ULONG),
2689 blockIndex);
2693 /******************************************************************************
2694 * Storage32Impl_AddExtBlockDepot
2696 * Creates an extended depot block.
2698 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2700 ULONG numExtBlocks = This->extBigBlockDepotCount;
2701 ULONG nextExtBlock = This->extBigBlockDepotStart;
2702 BYTE depotBuffer[BIG_BLOCK_SIZE];
2703 ULONG index = BLOCK_UNUSED;
2704 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2705 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2706 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2708 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2709 blocksPerDepotBlock;
2711 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2714 * The first extended block.
2716 This->extBigBlockDepotStart = index;
2718 else
2720 unsigned int i;
2722 * Follow the chain to the last one.
2724 for (i = 0; i < (numExtBlocks - 1); i++)
2726 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2730 * Add the new extended block to the chain.
2732 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2733 index);
2737 * Initialize this block.
2739 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2740 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2742 return index;
2745 /******************************************************************************
2746 * Storage32Impl_FreeBigBlock
2748 * This method will flag the specified block as free in the big block depot.
2750 static void StorageImpl_FreeBigBlock(
2751 StorageImpl* This,
2752 ULONG blockIndex)
2754 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2756 if (blockIndex < This->prevFreeBlock)
2757 This->prevFreeBlock = blockIndex;
2760 /************************************************************************
2761 * Storage32Impl_GetNextBlockInChain
2763 * This method will retrieve the block index of the next big block in
2764 * in the chain.
2766 * Params: This - Pointer to the Storage object.
2767 * blockIndex - Index of the block to retrieve the chain
2768 * for.
2769 * nextBlockIndex - receives the return value.
2771 * Returns: This method returns the index of the next block in the chain.
2772 * It will return the constants:
2773 * BLOCK_SPECIAL - If the block given was not part of a
2774 * chain.
2775 * BLOCK_END_OF_CHAIN - If the block given was the last in
2776 * a chain.
2777 * BLOCK_UNUSED - If the block given was not past of a chain
2778 * and is available.
2779 * BLOCK_EXTBBDEPOT - This block is part of the extended
2780 * big block depot.
2782 * See Windows documentation for more details on IStorage methods.
2784 static HRESULT StorageImpl_GetNextBlockInChain(
2785 StorageImpl* This,
2786 ULONG blockIndex,
2787 ULONG* nextBlockIndex)
2789 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2790 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2791 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2792 BYTE depotBuffer[BIG_BLOCK_SIZE];
2793 BOOL success;
2794 ULONG depotBlockIndexPos;
2795 int index;
2797 *nextBlockIndex = BLOCK_SPECIAL;
2799 if(depotBlockCount >= This->bigBlockDepotCount)
2801 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2802 This->bigBlockDepotCount);
2803 return STG_E_READFAULT;
2807 * Cache the currently accessed depot block.
2809 if (depotBlockCount != This->indexBlockDepotCached)
2811 This->indexBlockDepotCached = depotBlockCount;
2813 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2815 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2817 else
2820 * We have to look in the extended depot.
2822 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2825 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2827 if (!success)
2828 return STG_E_READFAULT;
2830 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2832 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2833 This->blockDepotCached[index] = *nextBlockIndex;
2837 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2839 return S_OK;
2842 /******************************************************************************
2843 * Storage32Impl_GetNextExtendedBlock
2845 * Given an extended block this method will return the next extended block.
2847 * NOTES:
2848 * The last ULONG of an extended block is the block index of the next
2849 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2850 * depot.
2852 * Return values:
2853 * - The index of the next extended block
2854 * - BLOCK_UNUSED: there is no next extended block.
2855 * - Any other return values denotes failure.
2857 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2859 ULONG nextBlockIndex = BLOCK_SPECIAL;
2860 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2862 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2863 &nextBlockIndex);
2865 return nextBlockIndex;
2868 /******************************************************************************
2869 * Storage32Impl_SetNextBlockInChain
2871 * This method will write the index of the specified block's next block
2872 * in the big block depot.
2874 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2875 * do the following
2877 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2878 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2879 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2882 static void StorageImpl_SetNextBlockInChain(
2883 StorageImpl* This,
2884 ULONG blockIndex,
2885 ULONG nextBlock)
2887 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2888 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2889 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2890 ULONG depotBlockIndexPos;
2892 assert(depotBlockCount < This->bigBlockDepotCount);
2893 assert(blockIndex != nextBlock);
2895 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2897 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2899 else
2902 * We have to look in the extended depot.
2904 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2907 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2908 nextBlock);
2910 * Update the cached block depot, if necessary.
2912 if (depotBlockCount == This->indexBlockDepotCached)
2914 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2918 /******************************************************************************
2919 * Storage32Impl_LoadFileHeader
2921 * This method will read in the file header, i.e. big block index -1.
2923 static HRESULT StorageImpl_LoadFileHeader(
2924 StorageImpl* This)
2926 HRESULT hr = STG_E_FILENOTFOUND;
2927 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2928 BOOL success;
2929 int index;
2931 TRACE("\n");
2933 * Get a pointer to the big block of data containing the header.
2935 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2938 * Extract the information from the header.
2940 if (success)
2943 * Check for the "magic number" signature and return an error if it is not
2944 * found.
2946 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2948 return STG_E_OLDFORMAT;
2951 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2953 return STG_E_INVALIDHEADER;
2956 StorageUtl_ReadWord(
2957 headerBigBlock,
2958 OFFSET_BIGBLOCKSIZEBITS,
2959 &This->bigBlockSizeBits);
2961 StorageUtl_ReadWord(
2962 headerBigBlock,
2963 OFFSET_SMALLBLOCKSIZEBITS,
2964 &This->smallBlockSizeBits);
2966 StorageUtl_ReadDWord(
2967 headerBigBlock,
2968 OFFSET_BBDEPOTCOUNT,
2969 &This->bigBlockDepotCount);
2971 StorageUtl_ReadDWord(
2972 headerBigBlock,
2973 OFFSET_ROOTSTARTBLOCK,
2974 &This->rootStartBlock);
2976 StorageUtl_ReadDWord(
2977 headerBigBlock,
2978 OFFSET_SBDEPOTSTART,
2979 &This->smallBlockDepotStart);
2981 StorageUtl_ReadDWord(
2982 headerBigBlock,
2983 OFFSET_EXTBBDEPOTSTART,
2984 &This->extBigBlockDepotStart);
2986 StorageUtl_ReadDWord(
2987 headerBigBlock,
2988 OFFSET_EXTBBDEPOTCOUNT,
2989 &This->extBigBlockDepotCount);
2991 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2993 StorageUtl_ReadDWord(
2994 headerBigBlock,
2995 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2996 &(This->bigBlockDepotStart[index]));
3000 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3002 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3003 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3006 * Right now, the code is making some assumptions about the size of the
3007 * blocks, just make sure they are what we're expecting.
3009 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3010 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3012 WARN("Broken OLE storage file\n");
3013 hr = STG_E_INVALIDHEADER;
3015 else
3016 hr = S_OK;
3019 return hr;
3022 /******************************************************************************
3023 * Storage32Impl_SaveFileHeader
3025 * This method will save to the file the header, i.e. big block -1.
3027 static void StorageImpl_SaveFileHeader(
3028 StorageImpl* This)
3030 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3031 int index;
3032 BOOL success;
3035 * Get a pointer to the big block of data containing the header.
3037 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3040 * If the block read failed, the file is probably new.
3042 if (!success)
3045 * Initialize for all unknown fields.
3047 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3050 * Initialize the magic number.
3052 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3055 * And a bunch of things we don't know what they mean
3057 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3058 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3059 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3060 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3064 * Write the information to the header.
3066 StorageUtl_WriteWord(
3067 headerBigBlock,
3068 OFFSET_BIGBLOCKSIZEBITS,
3069 This->bigBlockSizeBits);
3071 StorageUtl_WriteWord(
3072 headerBigBlock,
3073 OFFSET_SMALLBLOCKSIZEBITS,
3074 This->smallBlockSizeBits);
3076 StorageUtl_WriteDWord(
3077 headerBigBlock,
3078 OFFSET_BBDEPOTCOUNT,
3079 This->bigBlockDepotCount);
3081 StorageUtl_WriteDWord(
3082 headerBigBlock,
3083 OFFSET_ROOTSTARTBLOCK,
3084 This->rootStartBlock);
3086 StorageUtl_WriteDWord(
3087 headerBigBlock,
3088 OFFSET_SBDEPOTSTART,
3089 This->smallBlockDepotStart);
3091 StorageUtl_WriteDWord(
3092 headerBigBlock,
3093 OFFSET_SBDEPOTCOUNT,
3094 This->smallBlockDepotChain ?
3095 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3097 StorageUtl_WriteDWord(
3098 headerBigBlock,
3099 OFFSET_EXTBBDEPOTSTART,
3100 This->extBigBlockDepotStart);
3102 StorageUtl_WriteDWord(
3103 headerBigBlock,
3104 OFFSET_EXTBBDEPOTCOUNT,
3105 This->extBigBlockDepotCount);
3107 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3109 StorageUtl_WriteDWord(
3110 headerBigBlock,
3111 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3112 (This->bigBlockDepotStart[index]));
3116 * Write the big block back to the file.
3118 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3121 /******************************************************************************
3122 * StorageImpl_ReadRawDirEntry
3124 * This method will read the raw data from a directory entry in the file.
3126 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3128 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3130 ULARGE_INTEGER offset;
3131 HRESULT hr;
3132 ULONG bytesRead;
3134 offset.u.HighPart = 0;
3135 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3137 hr = BlockChainStream_ReadAt(
3138 This->rootBlockChain,
3139 offset,
3140 RAW_DIRENTRY_SIZE,
3141 buffer,
3142 &bytesRead);
3144 return hr;
3147 /******************************************************************************
3148 * StorageImpl_WriteRawDirEntry
3150 * This method will write the raw data from a directory entry in the file.
3152 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3154 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3156 ULARGE_INTEGER offset;
3157 HRESULT hr;
3158 ULONG bytesRead;
3160 offset.u.HighPart = 0;
3161 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3163 hr = BlockChainStream_WriteAt(
3164 This->rootBlockChain,
3165 offset,
3166 RAW_DIRENTRY_SIZE,
3167 buffer,
3168 &bytesRead);
3170 return hr;
3173 /******************************************************************************
3174 * UpdateRawDirEntry
3176 * Update raw directory entry data from the fields in newData.
3178 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3180 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3182 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3184 memcpy(
3185 buffer + OFFSET_PS_NAME,
3186 newData->name,
3187 DIRENTRY_NAME_BUFFER_LEN );
3189 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3191 StorageUtl_WriteWord(
3192 buffer,
3193 OFFSET_PS_NAMELENGTH,
3194 newData->sizeOfNameString);
3196 StorageUtl_WriteDWord(
3197 buffer,
3198 OFFSET_PS_LEFTCHILD,
3199 newData->leftChild);
3201 StorageUtl_WriteDWord(
3202 buffer,
3203 OFFSET_PS_RIGHTCHILD,
3204 newData->rightChild);
3206 StorageUtl_WriteDWord(
3207 buffer,
3208 OFFSET_PS_DIRROOT,
3209 newData->dirRootEntry);
3211 StorageUtl_WriteGUID(
3212 buffer,
3213 OFFSET_PS_GUID,
3214 &newData->clsid);
3216 StorageUtl_WriteDWord(
3217 buffer,
3218 OFFSET_PS_CTIMELOW,
3219 newData->ctime.dwLowDateTime);
3221 StorageUtl_WriteDWord(
3222 buffer,
3223 OFFSET_PS_CTIMEHIGH,
3224 newData->ctime.dwHighDateTime);
3226 StorageUtl_WriteDWord(
3227 buffer,
3228 OFFSET_PS_MTIMELOW,
3229 newData->mtime.dwLowDateTime);
3231 StorageUtl_WriteDWord(
3232 buffer,
3233 OFFSET_PS_MTIMEHIGH,
3234 newData->ctime.dwHighDateTime);
3236 StorageUtl_WriteDWord(
3237 buffer,
3238 OFFSET_PS_STARTBLOCK,
3239 newData->startingBlock);
3241 StorageUtl_WriteDWord(
3242 buffer,
3243 OFFSET_PS_SIZE,
3244 newData->size.u.LowPart);
3247 /******************************************************************************
3248 * Storage32Impl_ReadDirEntry
3250 * This method will read the specified directory entry.
3252 BOOL StorageImpl_ReadDirEntry(
3253 StorageImpl* This,
3254 DirRef index,
3255 DirEntry* buffer)
3257 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3258 HRESULT readRes;
3260 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3262 if (SUCCEEDED(readRes))
3264 memset(buffer->name, 0, sizeof(buffer->name));
3265 memcpy(
3266 buffer->name,
3267 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3268 DIRENTRY_NAME_BUFFER_LEN );
3269 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3271 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3273 StorageUtl_ReadWord(
3274 currentEntry,
3275 OFFSET_PS_NAMELENGTH,
3276 &buffer->sizeOfNameString);
3278 StorageUtl_ReadDWord(
3279 currentEntry,
3280 OFFSET_PS_LEFTCHILD,
3281 &buffer->leftChild);
3283 StorageUtl_ReadDWord(
3284 currentEntry,
3285 OFFSET_PS_RIGHTCHILD,
3286 &buffer->rightChild);
3288 StorageUtl_ReadDWord(
3289 currentEntry,
3290 OFFSET_PS_DIRROOT,
3291 &buffer->dirRootEntry);
3293 StorageUtl_ReadGUID(
3294 currentEntry,
3295 OFFSET_PS_GUID,
3296 &buffer->clsid);
3298 StorageUtl_ReadDWord(
3299 currentEntry,
3300 OFFSET_PS_CTIMELOW,
3301 &buffer->ctime.dwLowDateTime);
3303 StorageUtl_ReadDWord(
3304 currentEntry,
3305 OFFSET_PS_CTIMEHIGH,
3306 &buffer->ctime.dwHighDateTime);
3308 StorageUtl_ReadDWord(
3309 currentEntry,
3310 OFFSET_PS_MTIMELOW,
3311 &buffer->mtime.dwLowDateTime);
3313 StorageUtl_ReadDWord(
3314 currentEntry,
3315 OFFSET_PS_MTIMEHIGH,
3316 &buffer->mtime.dwHighDateTime);
3318 StorageUtl_ReadDWord(
3319 currentEntry,
3320 OFFSET_PS_STARTBLOCK,
3321 &buffer->startingBlock);
3323 StorageUtl_ReadDWord(
3324 currentEntry,
3325 OFFSET_PS_SIZE,
3326 &buffer->size.u.LowPart);
3328 buffer->size.u.HighPart = 0;
3331 return SUCCEEDED(readRes) ? TRUE : FALSE;
3334 /*********************************************************************
3335 * Write the specified directory entry to the file
3337 BOOL StorageImpl_WriteDirEntry(
3338 StorageImpl* This,
3339 DirRef index,
3340 const DirEntry* buffer)
3342 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3343 HRESULT writeRes;
3345 UpdateRawDirEntry(currentEntry, buffer);
3347 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3348 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3351 static BOOL StorageImpl_ReadBigBlock(
3352 StorageImpl* This,
3353 ULONG blockIndex,
3354 void* buffer)
3356 ULARGE_INTEGER ulOffset;
3357 DWORD read;
3359 ulOffset.u.HighPart = 0;
3360 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3362 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3363 return (read == This->bigBlockSize);
3366 static BOOL StorageImpl_ReadDWordFromBigBlock(
3367 StorageImpl* This,
3368 ULONG blockIndex,
3369 ULONG offset,
3370 DWORD* value)
3372 ULARGE_INTEGER ulOffset;
3373 DWORD read;
3374 DWORD tmp;
3376 ulOffset.u.HighPart = 0;
3377 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3378 ulOffset.u.LowPart += offset;
3380 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3381 *value = lendian32toh(tmp);
3382 return (read == sizeof(DWORD));
3385 static BOOL StorageImpl_WriteBigBlock(
3386 StorageImpl* This,
3387 ULONG blockIndex,
3388 const void* buffer)
3390 ULARGE_INTEGER ulOffset;
3391 DWORD wrote;
3393 ulOffset.u.HighPart = 0;
3394 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3396 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3397 return (wrote == This->bigBlockSize);
3400 static BOOL StorageImpl_WriteDWordToBigBlock(
3401 StorageImpl* This,
3402 ULONG blockIndex,
3403 ULONG offset,
3404 DWORD value)
3406 ULARGE_INTEGER ulOffset;
3407 DWORD wrote;
3409 ulOffset.u.HighPart = 0;
3410 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3411 ulOffset.u.LowPart += offset;
3413 value = htole32(value);
3414 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3415 return (wrote == sizeof(DWORD));
3418 /******************************************************************************
3419 * Storage32Impl_SmallBlocksToBigBlocks
3421 * This method will convert a small block chain to a big block chain.
3422 * The small block chain will be destroyed.
3424 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3425 StorageImpl* This,
3426 SmallBlockChainStream** ppsbChain)
3428 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3429 ULARGE_INTEGER size, offset;
3430 ULONG cbRead, cbWritten;
3431 ULARGE_INTEGER cbTotalRead;
3432 DirRef streamEntryRef;
3433 HRESULT resWrite = S_OK;
3434 HRESULT resRead;
3435 DirEntry streamEntry;
3436 BYTE *buffer;
3437 BlockChainStream *bbTempChain = NULL;
3438 BlockChainStream *bigBlockChain = NULL;
3441 * Create a temporary big block chain that doesn't have
3442 * an associated directory entry. This temporary chain will be
3443 * used to copy data from small blocks to big blocks.
3445 bbTempChain = BlockChainStream_Construct(This,
3446 &bbHeadOfChain,
3447 DIRENTRY_NULL);
3448 if(!bbTempChain) return NULL;
3450 * Grow the big block chain.
3452 size = SmallBlockChainStream_GetSize(*ppsbChain);
3453 BlockChainStream_SetSize(bbTempChain, size);
3456 * Copy the contents of the small block chain to the big block chain
3457 * by small block size increments.
3459 offset.u.LowPart = 0;
3460 offset.u.HighPart = 0;
3461 cbTotalRead.QuadPart = 0;
3463 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3466 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3467 offset,
3468 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3469 buffer,
3470 &cbRead);
3471 if (FAILED(resRead))
3472 break;
3474 if (cbRead > 0)
3476 cbTotalRead.QuadPart += cbRead;
3478 resWrite = BlockChainStream_WriteAt(bbTempChain,
3479 offset,
3480 cbRead,
3481 buffer,
3482 &cbWritten);
3484 if (FAILED(resWrite))
3485 break;
3487 offset.u.LowPart += cbRead;
3489 } while (cbTotalRead.QuadPart < size.QuadPart);
3490 HeapFree(GetProcessHeap(),0,buffer);
3492 size.u.HighPart = 0;
3493 size.u.LowPart = 0;
3495 if (FAILED(resRead) || FAILED(resWrite))
3497 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3498 BlockChainStream_SetSize(bbTempChain, size);
3499 BlockChainStream_Destroy(bbTempChain);
3500 return NULL;
3504 * Destroy the small block chain.
3506 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3507 SmallBlockChainStream_SetSize(*ppsbChain, size);
3508 SmallBlockChainStream_Destroy(*ppsbChain);
3509 *ppsbChain = 0;
3512 * Change the directory entry. This chain is now a big block chain
3513 * and it doesn't reside in the small blocks chain anymore.
3515 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3517 streamEntry.startingBlock = bbHeadOfChain;
3519 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3522 * Destroy the temporary entryless big block chain.
3523 * Create a new big block chain associated with this entry.
3525 BlockChainStream_Destroy(bbTempChain);
3526 bigBlockChain = BlockChainStream_Construct(This,
3527 NULL,
3528 streamEntryRef);
3530 return bigBlockChain;
3533 /******************************************************************************
3534 * Storage32Impl_BigBlocksToSmallBlocks
3536 * This method will convert a big block chain to a small block chain.
3537 * The big block chain will be destroyed on success.
3539 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3540 StorageImpl* This,
3541 BlockChainStream** ppbbChain)
3543 ULARGE_INTEGER size, offset, cbTotalRead;
3544 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3545 DirRef streamEntryRef;
3546 HRESULT resWrite = S_OK, resRead;
3547 DirEntry streamEntry;
3548 BYTE* buffer;
3549 SmallBlockChainStream* sbTempChain;
3551 TRACE("%p %p\n", This, ppbbChain);
3553 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3554 DIRENTRY_NULL);
3556 if(!sbTempChain)
3557 return NULL;
3559 size = BlockChainStream_GetSize(*ppbbChain);
3560 SmallBlockChainStream_SetSize(sbTempChain, size);
3562 offset.u.HighPart = 0;
3563 offset.u.LowPart = 0;
3564 cbTotalRead.QuadPart = 0;
3565 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3568 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3569 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3570 buffer, &cbRead);
3572 if(FAILED(resRead))
3573 break;
3575 if(cbRead > 0)
3577 cbTotalRead.QuadPart += cbRead;
3579 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3580 cbRead, buffer, &cbWritten);
3582 if(FAILED(resWrite))
3583 break;
3585 offset.u.LowPart += cbRead;
3587 }while(cbTotalRead.QuadPart < size.QuadPart);
3588 HeapFree(GetProcessHeap(), 0, buffer);
3590 size.u.HighPart = 0;
3591 size.u.LowPart = 0;
3593 if(FAILED(resRead) || FAILED(resWrite))
3595 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3596 SmallBlockChainStream_SetSize(sbTempChain, size);
3597 SmallBlockChainStream_Destroy(sbTempChain);
3598 return NULL;
3601 /* destroy the original big block chain */
3602 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3603 BlockChainStream_SetSize(*ppbbChain, size);
3604 BlockChainStream_Destroy(*ppbbChain);
3605 *ppbbChain = NULL;
3607 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3608 streamEntry.startingBlock = sbHeadOfChain;
3609 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3611 SmallBlockChainStream_Destroy(sbTempChain);
3612 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3615 static void StorageInternalImpl_Invalidate( StorageInternalImpl *This )
3617 if (This->base.ancestorStorage)
3619 TRACE("Storage invalidated (stg=%p)\n", This);
3621 This->base.ancestorStorage = NULL;
3623 StorageBaseImpl_DeleteAll(&This->base);
3625 list_remove(&This->ParentListEntry);
3629 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3631 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3633 StorageInternalImpl_Invalidate(This);
3635 HeapFree(GetProcessHeap(), 0, This);
3638 static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
3639 const DirEntry *newData, DirRef *index)
3641 return StorageBaseImpl_CreateDirEntry(&base->ancestorStorage->base,
3642 newData, index);
3645 /******************************************************************************
3647 ** Storage32InternalImpl_Commit
3650 static HRESULT WINAPI StorageInternalImpl_Commit(
3651 IStorage* iface,
3652 DWORD grfCommitFlags) /* [in] */
3654 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3655 return S_OK;
3658 /******************************************************************************
3660 ** Storage32InternalImpl_Revert
3663 static HRESULT WINAPI StorageInternalImpl_Revert(
3664 IStorage* iface)
3666 FIXME("(%p): stub\n", iface);
3667 return S_OK;
3670 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3672 IStorage_Release((IStorage*)This->parentStorage);
3673 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3674 HeapFree(GetProcessHeap(), 0, This);
3677 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3678 IEnumSTATSTG* iface,
3679 REFIID riid,
3680 void** ppvObject)
3682 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3684 if (ppvObject==0)
3685 return E_INVALIDARG;
3687 *ppvObject = 0;
3689 if (IsEqualGUID(&IID_IUnknown, riid) ||
3690 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3692 *ppvObject = This;
3693 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3694 return S_OK;
3697 return E_NOINTERFACE;
3700 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3701 IEnumSTATSTG* iface)
3703 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3704 return InterlockedIncrement(&This->ref);
3707 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3708 IEnumSTATSTG* iface)
3710 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3712 ULONG newRef;
3714 newRef = InterlockedDecrement(&This->ref);
3716 if (newRef==0)
3718 IEnumSTATSTGImpl_Destroy(This);
3721 return newRef;
3724 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3725 IEnumSTATSTG* iface,
3726 ULONG celt,
3727 STATSTG* rgelt,
3728 ULONG* pceltFetched)
3730 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3732 DirEntry currentEntry;
3733 STATSTG* currentReturnStruct = rgelt;
3734 ULONG objectFetched = 0;
3735 DirRef currentSearchNode;
3737 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3738 return E_INVALIDARG;
3741 * To avoid the special case, get another pointer to a ULONG value if
3742 * the caller didn't supply one.
3744 if (pceltFetched==0)
3745 pceltFetched = &objectFetched;
3748 * Start the iteration, we will iterate until we hit the end of the
3749 * linked list or until we hit the number of items to iterate through
3751 *pceltFetched = 0;
3754 * Start with the node at the top of the stack.
3756 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3758 while ( ( *pceltFetched < celt) &&
3759 ( currentSearchNode!=DIRENTRY_NULL) )
3762 * Remove the top node from the stack
3764 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3767 * Read the entry from the storage.
3769 StorageImpl_ReadDirEntry(This->parentStorage,
3770 currentSearchNode,
3771 &currentEntry);
3774 * Copy the information to the return buffer.
3776 StorageUtl_CopyDirEntryToSTATSTG(&This->parentStorage->base,
3777 currentReturnStruct,
3778 &currentEntry,
3779 STATFLAG_DEFAULT);
3782 * Step to the next item in the iteration
3784 (*pceltFetched)++;
3785 currentReturnStruct++;
3788 * Push the next search node in the search stack.
3790 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
3793 * continue the iteration.
3795 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3798 if (*pceltFetched == celt)
3799 return S_OK;
3801 return S_FALSE;
3805 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3806 IEnumSTATSTG* iface,
3807 ULONG celt)
3809 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3811 DirEntry currentEntry;
3812 ULONG objectFetched = 0;
3813 DirRef currentSearchNode;
3816 * Start with the node at the top of the stack.
3818 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3820 while ( (objectFetched < celt) &&
3821 (currentSearchNode!=DIRENTRY_NULL) )
3824 * Remove the top node from the stack
3826 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3829 * Read the entry from the storage.
3831 StorageImpl_ReadDirEntry(This->parentStorage,
3832 currentSearchNode,
3833 &currentEntry);
3836 * Step to the next item in the iteration
3838 objectFetched++;
3841 * Push the next search node in the search stack.
3843 IEnumSTATSTGImpl_PushSearchNode(This, currentEntry.rightChild);
3846 * continue the iteration.
3848 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3851 if (objectFetched == celt)
3852 return S_OK;
3854 return S_FALSE;
3857 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3858 IEnumSTATSTG* iface)
3860 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3862 DirEntry storageEntry;
3863 BOOL readSuccessful;
3866 * Re-initialize the search stack to an empty stack
3868 This->stackSize = 0;
3871 * Read the storage entry from the top-level storage.
3873 readSuccessful = StorageImpl_ReadDirEntry(
3874 This->parentStorage,
3875 This->storageDirEntry,
3876 &storageEntry);
3878 if (readSuccessful)
3880 assert(storageEntry.sizeOfNameString!=0);
3883 * Push the search node in the search stack.
3885 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.dirRootEntry);
3888 return S_OK;
3891 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3892 IEnumSTATSTG* iface,
3893 IEnumSTATSTG** ppenum)
3895 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3897 IEnumSTATSTGImpl* newClone;
3900 * Perform a sanity check on the parameters.
3902 if (ppenum==0)
3903 return E_INVALIDARG;
3905 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3906 This->storageDirEntry);
3910 * The new clone enumeration must point to the same current node as
3911 * the ole one.
3913 newClone->stackSize = This->stackSize ;
3914 newClone->stackMaxSize = This->stackMaxSize ;
3915 newClone->stackToVisit =
3916 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3918 memcpy(
3919 newClone->stackToVisit,
3920 This->stackToVisit,
3921 sizeof(DirRef) * newClone->stackSize);
3923 *ppenum = (IEnumSTATSTG*)newClone;
3926 * Don't forget to nail down a reference to the clone before
3927 * returning it.
3929 IEnumSTATSTGImpl_AddRef(*ppenum);
3931 return S_OK;
3934 static void IEnumSTATSTGImpl_PushSearchNode(
3935 IEnumSTATSTGImpl* This,
3936 DirRef nodeToPush)
3938 DirEntry storageEntry;
3939 BOOL readSuccessful;
3942 * First, make sure we're not trying to push an unexisting node.
3944 if (nodeToPush==DIRENTRY_NULL)
3945 return;
3948 * First push the node to the stack
3950 if (This->stackSize == This->stackMaxSize)
3952 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3954 This->stackToVisit = HeapReAlloc(
3955 GetProcessHeap(),
3957 This->stackToVisit,
3958 sizeof(DirRef) * This->stackMaxSize);
3961 This->stackToVisit[This->stackSize] = nodeToPush;
3962 This->stackSize++;
3965 * Read the storage entry from the top-level storage.
3967 readSuccessful = StorageImpl_ReadDirEntry(
3968 This->parentStorage,
3969 nodeToPush,
3970 &storageEntry);
3972 if (readSuccessful)
3974 assert(storageEntry.sizeOfNameString!=0);
3977 * Push the previous search node in the search stack.
3979 IEnumSTATSTGImpl_PushSearchNode(This, storageEntry.leftChild);
3983 static DirRef IEnumSTATSTGImpl_PopSearchNode(
3984 IEnumSTATSTGImpl* This,
3985 BOOL remove)
3987 DirRef topNode;
3989 if (This->stackSize == 0)
3990 return DIRENTRY_NULL;
3992 topNode = This->stackToVisit[This->stackSize-1];
3994 if (remove)
3995 This->stackSize--;
3997 return topNode;
4001 * Virtual function table for the IEnumSTATSTGImpl class.
4003 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4005 IEnumSTATSTGImpl_QueryInterface,
4006 IEnumSTATSTGImpl_AddRef,
4007 IEnumSTATSTGImpl_Release,
4008 IEnumSTATSTGImpl_Next,
4009 IEnumSTATSTGImpl_Skip,
4010 IEnumSTATSTGImpl_Reset,
4011 IEnumSTATSTGImpl_Clone
4014 /******************************************************************************
4015 ** IEnumSTATSTGImpl implementation
4018 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4019 StorageImpl* parentStorage,
4020 DirRef storageDirEntry)
4022 IEnumSTATSTGImpl* newEnumeration;
4024 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4026 if (newEnumeration!=0)
4029 * Set-up the virtual function table and reference count.
4031 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4032 newEnumeration->ref = 0;
4035 * We want to nail-down the reference to the storage in case the
4036 * enumeration out-lives the storage in the client application.
4038 newEnumeration->parentStorage = parentStorage;
4039 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4041 newEnumeration->storageDirEntry = storageDirEntry;
4044 * Initialize the search stack
4046 newEnumeration->stackSize = 0;
4047 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4048 newEnumeration->stackToVisit =
4049 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef)*ENUMSTATSGT_SIZE_INCREMENT);
4052 * Make sure the current node of the iterator is the first one.
4054 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4057 return newEnumeration;
4061 * Virtual function table for the Storage32InternalImpl class.
4063 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4065 StorageBaseImpl_QueryInterface,
4066 StorageBaseImpl_AddRef,
4067 StorageBaseImpl_Release,
4068 StorageBaseImpl_CreateStream,
4069 StorageBaseImpl_OpenStream,
4070 StorageBaseImpl_CreateStorage,
4071 StorageBaseImpl_OpenStorage,
4072 StorageBaseImpl_CopyTo,
4073 StorageBaseImpl_MoveElementTo,
4074 StorageInternalImpl_Commit,
4075 StorageInternalImpl_Revert,
4076 StorageBaseImpl_EnumElements,
4077 StorageBaseImpl_DestroyElement,
4078 StorageBaseImpl_RenameElement,
4079 StorageBaseImpl_SetElementTimes,
4080 StorageBaseImpl_SetClass,
4081 StorageBaseImpl_SetStateBits,
4082 StorageBaseImpl_Stat
4085 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
4087 StorageInternalImpl_Destroy,
4088 StorageInternalImpl_CreateDirEntry
4091 /******************************************************************************
4092 ** Storage32InternalImpl implementation
4095 static StorageInternalImpl* StorageInternalImpl_Construct(
4096 StorageImpl* ancestorStorage,
4097 DWORD openFlags,
4098 DirRef storageDirEntry)
4100 StorageInternalImpl* newStorage;
4102 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4104 if (newStorage!=0)
4106 list_init(&newStorage->base.strmHead);
4108 list_init(&newStorage->base.storageHead);
4111 * Initialize the virtual function table.
4113 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4114 newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
4115 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4118 * Keep the ancestor storage pointer but do not nail a reference to it.
4120 newStorage->base.ancestorStorage = ancestorStorage;
4123 * Keep a reference to the directory entry of this storage
4125 newStorage->base.storageDirEntry = storageDirEntry;
4127 newStorage->base.create = 0;
4129 return newStorage;
4132 return 0;
4135 /******************************************************************************
4136 ** StorageUtl implementation
4139 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4141 WORD tmp;
4143 memcpy(&tmp, buffer+offset, sizeof(WORD));
4144 *value = lendian16toh(tmp);
4147 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4149 value = htole16(value);
4150 memcpy(buffer+offset, &value, sizeof(WORD));
4153 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4155 DWORD tmp;
4157 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4158 *value = lendian32toh(tmp);
4161 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4163 value = htole32(value);
4164 memcpy(buffer+offset, &value, sizeof(DWORD));
4167 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4168 ULARGE_INTEGER* value)
4170 #ifdef WORDS_BIGENDIAN
4171 ULARGE_INTEGER tmp;
4173 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4174 value->u.LowPart = htole32(tmp.u.HighPart);
4175 value->u.HighPart = htole32(tmp.u.LowPart);
4176 #else
4177 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4178 #endif
4181 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4182 const ULARGE_INTEGER *value)
4184 #ifdef WORDS_BIGENDIAN
4185 ULARGE_INTEGER tmp;
4187 tmp.u.LowPart = htole32(value->u.HighPart);
4188 tmp.u.HighPart = htole32(value->u.LowPart);
4189 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4190 #else
4191 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4192 #endif
4195 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4197 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4198 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4199 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4201 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4204 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4206 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4207 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4208 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4210 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4213 void StorageUtl_CopyDirEntryToSTATSTG(
4214 StorageBaseImpl* storage,
4215 STATSTG* destination,
4216 const DirEntry* source,
4217 int statFlags)
4219 LPCWSTR entryName;
4221 if (source->stgType == STGTY_ROOT)
4223 /* replace the name of root entry (often "Root Entry") by the file name */
4224 entryName = storage->filename;
4226 else
4228 entryName = source->name;
4232 * The copy of the string occurs only when the flag is not set
4234 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4235 (entryName == NULL) ||
4236 (entryName[0] == 0) )
4238 destination->pwcsName = 0;
4240 else
4242 destination->pwcsName =
4243 CoTaskMemAlloc((lstrlenW(entryName)+1)*sizeof(WCHAR));
4245 strcpyW(destination->pwcsName, entryName);
4248 switch (source->stgType)
4250 case STGTY_STORAGE:
4251 case STGTY_ROOT:
4252 destination->type = STGTY_STORAGE;
4253 break;
4254 case STGTY_STREAM:
4255 destination->type = STGTY_STREAM;
4256 break;
4257 default:
4258 destination->type = STGTY_STREAM;
4259 break;
4262 destination->cbSize = source->size;
4264 currentReturnStruct->mtime = {0}; TODO
4265 currentReturnStruct->ctime = {0};
4266 currentReturnStruct->atime = {0};
4268 destination->grfMode = 0;
4269 destination->grfLocksSupported = 0;
4270 destination->clsid = source->clsid;
4271 destination->grfStateBits = 0;
4272 destination->reserved = 0;
4275 /******************************************************************************
4276 ** BlockChainStream implementation
4279 BlockChainStream* BlockChainStream_Construct(
4280 StorageImpl* parentStorage,
4281 ULONG* headOfStreamPlaceHolder,
4282 DirRef dirEntry)
4284 BlockChainStream* newStream;
4285 ULONG blockIndex;
4287 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4289 newStream->parentStorage = parentStorage;
4290 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4291 newStream->ownerDirEntry = dirEntry;
4292 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4293 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4294 newStream->numBlocks = 0;
4296 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4298 while (blockIndex != BLOCK_END_OF_CHAIN)
4300 newStream->numBlocks++;
4301 newStream->tailIndex = blockIndex;
4303 if(FAILED(StorageImpl_GetNextBlockInChain(
4304 parentStorage,
4305 blockIndex,
4306 &blockIndex)))
4308 HeapFree(GetProcessHeap(), 0, newStream);
4309 return NULL;
4313 return newStream;
4316 void BlockChainStream_Destroy(BlockChainStream* This)
4318 HeapFree(GetProcessHeap(), 0, This);
4321 /******************************************************************************
4322 * BlockChainStream_GetHeadOfChain
4324 * Returns the head of this stream chain.
4325 * Some special chains don't have directory entries, their heads are kept in
4326 * This->headOfStreamPlaceHolder.
4329 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4331 DirEntry chainEntry;
4332 BOOL readSuccessful;
4334 if (This->headOfStreamPlaceHolder != 0)
4335 return *(This->headOfStreamPlaceHolder);
4337 if (This->ownerDirEntry != DIRENTRY_NULL)
4339 readSuccessful = StorageImpl_ReadDirEntry(
4340 This->parentStorage,
4341 This->ownerDirEntry,
4342 &chainEntry);
4344 if (readSuccessful)
4346 return chainEntry.startingBlock;
4350 return BLOCK_END_OF_CHAIN;
4353 /******************************************************************************
4354 * BlockChainStream_GetCount
4356 * Returns the number of blocks that comprises this chain.
4357 * This is not the size of the stream as the last block may not be full!
4360 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4362 ULONG blockIndex;
4363 ULONG count = 0;
4365 blockIndex = BlockChainStream_GetHeadOfChain(This);
4367 while (blockIndex != BLOCK_END_OF_CHAIN)
4369 count++;
4371 if(FAILED(StorageImpl_GetNextBlockInChain(
4372 This->parentStorage,
4373 blockIndex,
4374 &blockIndex)))
4375 return 0;
4378 return count;
4381 /******************************************************************************
4382 * BlockChainStream_ReadAt
4384 * Reads a specified number of bytes from this chain at the specified offset.
4385 * bytesRead may be NULL.
4386 * Failure will be returned if the specified number of bytes has not been read.
4388 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4389 ULARGE_INTEGER offset,
4390 ULONG size,
4391 void* buffer,
4392 ULONG* bytesRead)
4394 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4395 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4396 ULONG bytesToReadInBuffer;
4397 ULONG blockIndex;
4398 BYTE* bufferWalker;
4400 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4403 * Find the first block in the stream that contains part of the buffer.
4405 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4406 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4407 (blockNoInSequence < This->lastBlockNoInSequence) )
4409 blockIndex = BlockChainStream_GetHeadOfChain(This);
4410 This->lastBlockNoInSequence = blockNoInSequence;
4412 else
4414 ULONG temp = blockNoInSequence;
4416 blockIndex = This->lastBlockNoInSequenceIndex;
4417 blockNoInSequence -= This->lastBlockNoInSequence;
4418 This->lastBlockNoInSequence = temp;
4421 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4423 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4424 return STG_E_DOCFILECORRUPT;
4425 blockNoInSequence--;
4428 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4429 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4431 This->lastBlockNoInSequenceIndex = blockIndex;
4434 * Start reading the buffer.
4436 *bytesRead = 0;
4437 bufferWalker = buffer;
4439 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4441 ULARGE_INTEGER ulOffset;
4442 DWORD bytesReadAt;
4444 * Calculate how many bytes we can copy from this big block.
4446 bytesToReadInBuffer =
4447 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4449 TRACE("block %i\n",blockIndex);
4450 ulOffset.u.HighPart = 0;
4451 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4452 offsetInBlock;
4454 StorageImpl_ReadAt(This->parentStorage,
4455 ulOffset,
4456 bufferWalker,
4457 bytesToReadInBuffer,
4458 &bytesReadAt);
4460 * Step to the next big block.
4462 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4463 return STG_E_DOCFILECORRUPT;
4465 bufferWalker += bytesReadAt;
4466 size -= bytesReadAt;
4467 *bytesRead += bytesReadAt;
4468 offsetInBlock = 0; /* There is no offset on the next block */
4470 if (bytesToReadInBuffer != bytesReadAt)
4471 break;
4474 return (size == 0) ? S_OK : STG_E_READFAULT;
4477 /******************************************************************************
4478 * BlockChainStream_WriteAt
4480 * Writes the specified number of bytes to this chain at the specified offset.
4481 * Will fail if not all specified number of bytes have been written.
4483 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4484 ULARGE_INTEGER offset,
4485 ULONG size,
4486 const void* buffer,
4487 ULONG* bytesWritten)
4489 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4490 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4491 ULONG bytesToWrite;
4492 ULONG blockIndex;
4493 const BYTE* bufferWalker;
4496 * Find the first block in the stream that contains part of the buffer.
4498 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4499 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4500 (blockNoInSequence < This->lastBlockNoInSequence) )
4502 blockIndex = BlockChainStream_GetHeadOfChain(This);
4503 This->lastBlockNoInSequence = blockNoInSequence;
4505 else
4507 ULONG temp = blockNoInSequence;
4509 blockIndex = This->lastBlockNoInSequenceIndex;
4510 blockNoInSequence -= This->lastBlockNoInSequence;
4511 This->lastBlockNoInSequence = temp;
4514 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4516 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4517 &blockIndex)))
4518 return STG_E_DOCFILECORRUPT;
4519 blockNoInSequence--;
4522 This->lastBlockNoInSequenceIndex = blockIndex;
4524 /* BlockChainStream_SetSize should have already been called to ensure we have
4525 * enough blocks in the chain to write into */
4526 if (blockIndex == BLOCK_END_OF_CHAIN)
4528 ERR("not enough blocks in chain to write data\n");
4529 return STG_E_DOCFILECORRUPT;
4532 *bytesWritten = 0;
4533 bufferWalker = buffer;
4535 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4537 ULARGE_INTEGER ulOffset;
4538 DWORD bytesWrittenAt;
4540 * Calculate how many bytes we can copy from this big block.
4542 bytesToWrite =
4543 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4545 TRACE("block %i\n",blockIndex);
4546 ulOffset.u.HighPart = 0;
4547 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4548 offsetInBlock;
4550 StorageImpl_WriteAt(This->parentStorage,
4551 ulOffset,
4552 bufferWalker,
4553 bytesToWrite,
4554 &bytesWrittenAt);
4557 * Step to the next big block.
4559 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4560 &blockIndex)))
4561 return STG_E_DOCFILECORRUPT;
4563 bufferWalker += bytesWrittenAt;
4564 size -= bytesWrittenAt;
4565 *bytesWritten += bytesWrittenAt;
4566 offsetInBlock = 0; /* There is no offset on the next block */
4568 if (bytesWrittenAt != bytesToWrite)
4569 break;
4572 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4575 /******************************************************************************
4576 * BlockChainStream_Shrink
4578 * Shrinks this chain in the big block depot.
4580 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4581 ULARGE_INTEGER newSize)
4583 ULONG blockIndex, extraBlock;
4584 ULONG numBlocks;
4585 ULONG count = 1;
4588 * Reset the last accessed block cache.
4590 This->lastBlockNoInSequence = 0xFFFFFFFF;
4591 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4594 * Figure out how many blocks are needed to contain the new size
4596 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4598 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4599 numBlocks++;
4601 blockIndex = BlockChainStream_GetHeadOfChain(This);
4604 * Go to the new end of chain
4606 while (count < numBlocks)
4608 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4609 &blockIndex)))
4610 return FALSE;
4611 count++;
4614 /* Get the next block before marking the new end */
4615 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4616 &extraBlock)))
4617 return FALSE;
4619 /* Mark the new end of chain */
4620 StorageImpl_SetNextBlockInChain(
4621 This->parentStorage,
4622 blockIndex,
4623 BLOCK_END_OF_CHAIN);
4625 This->tailIndex = blockIndex;
4626 This->numBlocks = numBlocks;
4629 * Mark the extra blocks as free
4631 while (extraBlock != BLOCK_END_OF_CHAIN)
4633 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4634 &blockIndex)))
4635 return FALSE;
4636 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4637 extraBlock = blockIndex;
4640 return TRUE;
4643 /******************************************************************************
4644 * BlockChainStream_Enlarge
4646 * Grows this chain in the big block depot.
4648 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4649 ULARGE_INTEGER newSize)
4651 ULONG blockIndex, currentBlock;
4652 ULONG newNumBlocks;
4653 ULONG oldNumBlocks = 0;
4655 blockIndex = BlockChainStream_GetHeadOfChain(This);
4658 * Empty chain. Create the head.
4660 if (blockIndex == BLOCK_END_OF_CHAIN)
4662 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4663 StorageImpl_SetNextBlockInChain(This->parentStorage,
4664 blockIndex,
4665 BLOCK_END_OF_CHAIN);
4667 if (This->headOfStreamPlaceHolder != 0)
4669 *(This->headOfStreamPlaceHolder) = blockIndex;
4671 else
4673 DirEntry chainEntry;
4674 assert(This->ownerDirEntry != DIRENTRY_NULL);
4676 StorageImpl_ReadDirEntry(
4677 This->parentStorage,
4678 This->ownerDirEntry,
4679 &chainEntry);
4681 chainEntry.startingBlock = blockIndex;
4683 StorageImpl_WriteDirEntry(
4684 This->parentStorage,
4685 This->ownerDirEntry,
4686 &chainEntry);
4689 This->tailIndex = blockIndex;
4690 This->numBlocks = 1;
4694 * Figure out how many blocks are needed to contain this stream
4696 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4698 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4699 newNumBlocks++;
4702 * Go to the current end of chain
4704 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4706 currentBlock = blockIndex;
4708 while (blockIndex != BLOCK_END_OF_CHAIN)
4710 This->numBlocks++;
4711 currentBlock = blockIndex;
4713 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4714 &blockIndex)))
4715 return FALSE;
4718 This->tailIndex = currentBlock;
4721 currentBlock = This->tailIndex;
4722 oldNumBlocks = This->numBlocks;
4725 * Add new blocks to the chain
4727 if (oldNumBlocks < newNumBlocks)
4729 while (oldNumBlocks < newNumBlocks)
4731 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4733 StorageImpl_SetNextBlockInChain(
4734 This->parentStorage,
4735 currentBlock,
4736 blockIndex);
4738 StorageImpl_SetNextBlockInChain(
4739 This->parentStorage,
4740 blockIndex,
4741 BLOCK_END_OF_CHAIN);
4743 currentBlock = blockIndex;
4744 oldNumBlocks++;
4747 This->tailIndex = blockIndex;
4748 This->numBlocks = newNumBlocks;
4751 return TRUE;
4754 /******************************************************************************
4755 * BlockChainStream_SetSize
4757 * Sets the size of this stream. The big block depot will be updated.
4758 * The file will grow if we grow the chain.
4760 * TODO: Free the actual blocks in the file when we shrink the chain.
4761 * Currently, the blocks are still in the file. So the file size
4762 * doesn't shrink even if we shrink streams.
4764 BOOL BlockChainStream_SetSize(
4765 BlockChainStream* This,
4766 ULARGE_INTEGER newSize)
4768 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4770 if (newSize.u.LowPart == size.u.LowPart)
4771 return TRUE;
4773 if (newSize.u.LowPart < size.u.LowPart)
4775 BlockChainStream_Shrink(This, newSize);
4777 else
4779 BlockChainStream_Enlarge(This, newSize);
4782 return TRUE;
4785 /******************************************************************************
4786 * BlockChainStream_GetSize
4788 * Returns the size of this chain.
4789 * Will return the block count if this chain doesn't have a directory entry.
4791 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4793 DirEntry chainEntry;
4795 if(This->headOfStreamPlaceHolder == NULL)
4798 * This chain has a directory entry so use the size value from there.
4800 StorageImpl_ReadDirEntry(
4801 This->parentStorage,
4802 This->ownerDirEntry,
4803 &chainEntry);
4805 return chainEntry.size;
4807 else
4810 * this chain is a chain that does not have a directory entry, figure out the
4811 * size by making the product number of used blocks times the
4812 * size of them
4814 ULARGE_INTEGER result;
4815 result.u.HighPart = 0;
4817 result.u.LowPart =
4818 BlockChainStream_GetCount(This) *
4819 This->parentStorage->bigBlockSize;
4821 return result;
4825 /******************************************************************************
4826 ** SmallBlockChainStream implementation
4829 SmallBlockChainStream* SmallBlockChainStream_Construct(
4830 StorageImpl* parentStorage,
4831 ULONG* headOfStreamPlaceHolder,
4832 DirRef dirEntry)
4834 SmallBlockChainStream* newStream;
4836 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4838 newStream->parentStorage = parentStorage;
4839 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4840 newStream->ownerDirEntry = dirEntry;
4842 return newStream;
4845 void SmallBlockChainStream_Destroy(
4846 SmallBlockChainStream* This)
4848 HeapFree(GetProcessHeap(), 0, This);
4851 /******************************************************************************
4852 * SmallBlockChainStream_GetHeadOfChain
4854 * Returns the head of this chain of small blocks.
4856 static ULONG SmallBlockChainStream_GetHeadOfChain(
4857 SmallBlockChainStream* This)
4859 DirEntry chainEntry;
4860 BOOL readSuccessful;
4862 if (This->headOfStreamPlaceHolder != NULL)
4863 return *(This->headOfStreamPlaceHolder);
4865 if (This->ownerDirEntry)
4867 readSuccessful = StorageImpl_ReadDirEntry(
4868 This->parentStorage,
4869 This->ownerDirEntry,
4870 &chainEntry);
4872 if (readSuccessful)
4874 return chainEntry.startingBlock;
4879 return BLOCK_END_OF_CHAIN;
4882 /******************************************************************************
4883 * SmallBlockChainStream_GetNextBlockInChain
4885 * Returns the index of the next small block in this chain.
4887 * Return Values:
4888 * - BLOCK_END_OF_CHAIN: end of this chain
4889 * - BLOCK_UNUSED: small block 'blockIndex' is free
4891 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4892 SmallBlockChainStream* This,
4893 ULONG blockIndex,
4894 ULONG* nextBlockInChain)
4896 ULARGE_INTEGER offsetOfBlockInDepot;
4897 DWORD buffer;
4898 ULONG bytesRead;
4899 HRESULT res;
4901 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4903 offsetOfBlockInDepot.u.HighPart = 0;
4904 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4907 * Read those bytes in the buffer from the small block file.
4909 res = BlockChainStream_ReadAt(
4910 This->parentStorage->smallBlockDepotChain,
4911 offsetOfBlockInDepot,
4912 sizeof(DWORD),
4913 &buffer,
4914 &bytesRead);
4916 if (SUCCEEDED(res))
4918 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4919 return S_OK;
4922 return res;
4925 /******************************************************************************
4926 * SmallBlockChainStream_SetNextBlockInChain
4928 * Writes the index of the next block of the specified block in the small
4929 * block depot.
4930 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4931 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4933 static void SmallBlockChainStream_SetNextBlockInChain(
4934 SmallBlockChainStream* This,
4935 ULONG blockIndex,
4936 ULONG nextBlock)
4938 ULARGE_INTEGER offsetOfBlockInDepot;
4939 DWORD buffer;
4940 ULONG bytesWritten;
4942 offsetOfBlockInDepot.u.HighPart = 0;
4943 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4945 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4948 * Read those bytes in the buffer from the small block file.
4950 BlockChainStream_WriteAt(
4951 This->parentStorage->smallBlockDepotChain,
4952 offsetOfBlockInDepot,
4953 sizeof(DWORD),
4954 &buffer,
4955 &bytesWritten);
4958 /******************************************************************************
4959 * SmallBlockChainStream_FreeBlock
4961 * Flag small block 'blockIndex' as free in the small block depot.
4963 static void SmallBlockChainStream_FreeBlock(
4964 SmallBlockChainStream* This,
4965 ULONG blockIndex)
4967 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4970 /******************************************************************************
4971 * SmallBlockChainStream_GetNextFreeBlock
4973 * Returns the index of a free small block. The small block depot will be
4974 * enlarged if necessary. The small block chain will also be enlarged if
4975 * necessary.
4977 static ULONG SmallBlockChainStream_GetNextFreeBlock(
4978 SmallBlockChainStream* This)
4980 ULARGE_INTEGER offsetOfBlockInDepot;
4981 DWORD buffer;
4982 ULONG bytesRead;
4983 ULONG blockIndex = 0;
4984 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4985 HRESULT res = S_OK;
4986 ULONG smallBlocksPerBigBlock;
4988 offsetOfBlockInDepot.u.HighPart = 0;
4991 * Scan the small block depot for a free block
4993 while (nextBlockIndex != BLOCK_UNUSED)
4995 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4997 res = BlockChainStream_ReadAt(
4998 This->parentStorage->smallBlockDepotChain,
4999 offsetOfBlockInDepot,
5000 sizeof(DWORD),
5001 &buffer,
5002 &bytesRead);
5005 * If we run out of space for the small block depot, enlarge it
5007 if (SUCCEEDED(res))
5009 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5011 if (nextBlockIndex != BLOCK_UNUSED)
5012 blockIndex++;
5014 else
5016 ULONG count =
5017 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5019 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5020 ULONG nextBlock, newsbdIndex;
5021 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5023 nextBlock = sbdIndex;
5024 while (nextBlock != BLOCK_END_OF_CHAIN)
5026 sbdIndex = nextBlock;
5027 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5030 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5031 if (sbdIndex != BLOCK_END_OF_CHAIN)
5032 StorageImpl_SetNextBlockInChain(
5033 This->parentStorage,
5034 sbdIndex,
5035 newsbdIndex);
5037 StorageImpl_SetNextBlockInChain(
5038 This->parentStorage,
5039 newsbdIndex,
5040 BLOCK_END_OF_CHAIN);
5043 * Initialize all the small blocks to free
5045 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5046 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5048 if (count == 0)
5051 * We have just created the small block depot.
5053 DirEntry rootEntry;
5054 ULONG sbStartIndex;
5057 * Save it in the header
5059 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5060 StorageImpl_SaveFileHeader(This->parentStorage);
5063 * And allocate the first big block that will contain small blocks
5065 sbStartIndex =
5066 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5068 StorageImpl_SetNextBlockInChain(
5069 This->parentStorage,
5070 sbStartIndex,
5071 BLOCK_END_OF_CHAIN);
5073 StorageImpl_ReadDirEntry(
5074 This->parentStorage,
5075 This->parentStorage->base.storageDirEntry,
5076 &rootEntry);
5078 rootEntry.startingBlock = sbStartIndex;
5079 rootEntry.size.u.HighPart = 0;
5080 rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
5082 StorageImpl_WriteDirEntry(
5083 This->parentStorage,
5084 This->parentStorage->base.storageDirEntry,
5085 &rootEntry);
5087 else
5088 StorageImpl_SaveFileHeader(This->parentStorage);
5092 smallBlocksPerBigBlock =
5093 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5096 * Verify if we have to allocate big blocks to contain small blocks
5098 if (blockIndex % smallBlocksPerBigBlock == 0)
5100 DirEntry rootEntry;
5101 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5103 StorageImpl_ReadDirEntry(
5104 This->parentStorage,
5105 This->parentStorage->base.storageDirEntry,
5106 &rootEntry);
5108 if (rootEntry.size.u.LowPart <
5109 (blocksRequired * This->parentStorage->bigBlockSize))
5111 rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
5113 BlockChainStream_SetSize(
5114 This->parentStorage->smallBlockRootChain,
5115 rootEntry.size);
5117 StorageImpl_WriteDirEntry(
5118 This->parentStorage,
5119 This->parentStorage->base.storageDirEntry,
5120 &rootEntry);
5124 return blockIndex;
5127 /******************************************************************************
5128 * SmallBlockChainStream_ReadAt
5130 * Reads a specified number of bytes from this chain at the specified offset.
5131 * bytesRead may be NULL.
5132 * Failure will be returned if the specified number of bytes has not been read.
5134 HRESULT SmallBlockChainStream_ReadAt(
5135 SmallBlockChainStream* This,
5136 ULARGE_INTEGER offset,
5137 ULONG size,
5138 void* buffer,
5139 ULONG* bytesRead)
5141 HRESULT rc = S_OK;
5142 ULARGE_INTEGER offsetInBigBlockFile;
5143 ULONG blockNoInSequence =
5144 offset.u.LowPart / This->parentStorage->smallBlockSize;
5146 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5147 ULONG bytesToReadInBuffer;
5148 ULONG blockIndex;
5149 ULONG bytesReadFromBigBlockFile;
5150 BYTE* bufferWalker;
5153 * This should never happen on a small block file.
5155 assert(offset.u.HighPart==0);
5158 * Find the first block in the stream that contains part of the buffer.
5160 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5162 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5164 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5165 if(FAILED(rc))
5166 return rc;
5167 blockNoInSequence--;
5171 * Start reading the buffer.
5173 *bytesRead = 0;
5174 bufferWalker = buffer;
5176 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5179 * Calculate how many bytes we can copy from this small block.
5181 bytesToReadInBuffer =
5182 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5185 * Calculate the offset of the small block in the small block file.
5187 offsetInBigBlockFile.u.HighPart = 0;
5188 offsetInBigBlockFile.u.LowPart =
5189 blockIndex * This->parentStorage->smallBlockSize;
5191 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5194 * Read those bytes in the buffer from the small block file.
5195 * The small block has already been identified so it shouldn't fail
5196 * unless the file is corrupt.
5198 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5199 offsetInBigBlockFile,
5200 bytesToReadInBuffer,
5201 bufferWalker,
5202 &bytesReadFromBigBlockFile);
5204 if (FAILED(rc))
5205 return rc;
5208 * Step to the next big block.
5210 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5211 if(FAILED(rc))
5212 return STG_E_DOCFILECORRUPT;
5214 bufferWalker += bytesReadFromBigBlockFile;
5215 size -= bytesReadFromBigBlockFile;
5216 *bytesRead += bytesReadFromBigBlockFile;
5217 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5220 return (size == 0) ? S_OK : STG_E_READFAULT;
5223 /******************************************************************************
5224 * SmallBlockChainStream_WriteAt
5226 * Writes the specified number of bytes to this chain at the specified offset.
5227 * Will fail if not all specified number of bytes have been written.
5229 HRESULT SmallBlockChainStream_WriteAt(
5230 SmallBlockChainStream* This,
5231 ULARGE_INTEGER offset,
5232 ULONG size,
5233 const void* buffer,
5234 ULONG* bytesWritten)
5236 ULARGE_INTEGER offsetInBigBlockFile;
5237 ULONG blockNoInSequence =
5238 offset.u.LowPart / This->parentStorage->smallBlockSize;
5240 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5241 ULONG bytesToWriteInBuffer;
5242 ULONG blockIndex;
5243 ULONG bytesWrittenToBigBlockFile;
5244 const BYTE* bufferWalker;
5245 HRESULT res;
5248 * This should never happen on a small block file.
5250 assert(offset.u.HighPart==0);
5253 * Find the first block in the stream that contains part of the buffer.
5255 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5257 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5259 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5260 return STG_E_DOCFILECORRUPT;
5261 blockNoInSequence--;
5265 * Start writing the buffer.
5267 *bytesWritten = 0;
5268 bufferWalker = buffer;
5269 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5272 * Calculate how many bytes we can copy to this small block.
5274 bytesToWriteInBuffer =
5275 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5278 * Calculate the offset of the small block in the small block file.
5280 offsetInBigBlockFile.u.HighPart = 0;
5281 offsetInBigBlockFile.u.LowPart =
5282 blockIndex * This->parentStorage->smallBlockSize;
5284 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5287 * Write those bytes in the buffer to the small block file.
5289 res = BlockChainStream_WriteAt(
5290 This->parentStorage->smallBlockRootChain,
5291 offsetInBigBlockFile,
5292 bytesToWriteInBuffer,
5293 bufferWalker,
5294 &bytesWrittenToBigBlockFile);
5295 if (FAILED(res))
5296 return res;
5299 * Step to the next big block.
5301 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5302 &blockIndex)))
5303 return FALSE;
5304 bufferWalker += bytesWrittenToBigBlockFile;
5305 size -= bytesWrittenToBigBlockFile;
5306 *bytesWritten += bytesWrittenToBigBlockFile;
5307 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5310 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5313 /******************************************************************************
5314 * SmallBlockChainStream_Shrink
5316 * Shrinks this chain in the small block depot.
5318 static BOOL SmallBlockChainStream_Shrink(
5319 SmallBlockChainStream* This,
5320 ULARGE_INTEGER newSize)
5322 ULONG blockIndex, extraBlock;
5323 ULONG numBlocks;
5324 ULONG count = 0;
5326 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5328 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5329 numBlocks++;
5331 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5334 * Go to the new end of chain
5336 while (count < numBlocks)
5338 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5339 &blockIndex)))
5340 return FALSE;
5341 count++;
5345 * If the count is 0, we have a special case, the head of the chain was
5346 * just freed.
5348 if (count == 0)
5350 DirEntry chainEntry;
5352 StorageImpl_ReadDirEntry(This->parentStorage,
5353 This->ownerDirEntry,
5354 &chainEntry);
5356 chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
5358 StorageImpl_WriteDirEntry(This->parentStorage,
5359 This->ownerDirEntry,
5360 &chainEntry);
5363 * We start freeing the chain at the head block.
5365 extraBlock = blockIndex;
5367 else
5369 /* Get the next block before marking the new end */
5370 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5371 &extraBlock)))
5372 return FALSE;
5374 /* Mark the new end of chain */
5375 SmallBlockChainStream_SetNextBlockInChain(
5376 This,
5377 blockIndex,
5378 BLOCK_END_OF_CHAIN);
5382 * Mark the extra blocks as free
5384 while (extraBlock != BLOCK_END_OF_CHAIN)
5386 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5387 &blockIndex)))
5388 return FALSE;
5389 SmallBlockChainStream_FreeBlock(This, extraBlock);
5390 extraBlock = blockIndex;
5393 return TRUE;
5396 /******************************************************************************
5397 * SmallBlockChainStream_Enlarge
5399 * Grows this chain in the small block depot.
5401 static BOOL SmallBlockChainStream_Enlarge(
5402 SmallBlockChainStream* This,
5403 ULARGE_INTEGER newSize)
5405 ULONG blockIndex, currentBlock;
5406 ULONG newNumBlocks;
5407 ULONG oldNumBlocks = 0;
5409 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5412 * Empty chain. Create the head.
5414 if (blockIndex == BLOCK_END_OF_CHAIN)
5416 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5417 SmallBlockChainStream_SetNextBlockInChain(
5418 This,
5419 blockIndex,
5420 BLOCK_END_OF_CHAIN);
5422 if (This->headOfStreamPlaceHolder != NULL)
5424 *(This->headOfStreamPlaceHolder) = blockIndex;
5426 else
5428 DirEntry chainEntry;
5430 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerDirEntry,
5431 &chainEntry);
5433 chainEntry.startingBlock = blockIndex;
5435 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerDirEntry,
5436 &chainEntry);
5440 currentBlock = blockIndex;
5443 * Figure out how many blocks are needed to contain this stream
5445 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5447 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5448 newNumBlocks++;
5451 * Go to the current end of chain
5453 while (blockIndex != BLOCK_END_OF_CHAIN)
5455 oldNumBlocks++;
5456 currentBlock = blockIndex;
5457 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5458 return FALSE;
5462 * Add new blocks to the chain
5464 while (oldNumBlocks < newNumBlocks)
5466 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5467 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5469 SmallBlockChainStream_SetNextBlockInChain(
5470 This,
5471 blockIndex,
5472 BLOCK_END_OF_CHAIN);
5474 currentBlock = blockIndex;
5475 oldNumBlocks++;
5478 return TRUE;
5481 /******************************************************************************
5482 * SmallBlockChainStream_SetSize
5484 * Sets the size of this stream.
5485 * The file will grow if we grow the chain.
5487 * TODO: Free the actual blocks in the file when we shrink the chain.
5488 * Currently, the blocks are still in the file. So the file size
5489 * doesn't shrink even if we shrink streams.
5491 BOOL SmallBlockChainStream_SetSize(
5492 SmallBlockChainStream* This,
5493 ULARGE_INTEGER newSize)
5495 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5497 if (newSize.u.LowPart == size.u.LowPart)
5498 return TRUE;
5500 if (newSize.u.LowPart < size.u.LowPart)
5502 SmallBlockChainStream_Shrink(This, newSize);
5504 else
5506 SmallBlockChainStream_Enlarge(This, newSize);
5509 return TRUE;
5512 /******************************************************************************
5513 * SmallBlockChainStream_GetCount
5515 * Returns the number of small blocks that comprises this chain.
5516 * This is not the size of the stream as the last block may not be full!
5519 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5521 ULONG blockIndex;
5522 ULONG count = 0;
5524 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5526 while(blockIndex != BLOCK_END_OF_CHAIN)
5528 count++;
5530 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5531 blockIndex, &blockIndex)))
5532 return 0;
5535 return count;
5538 /******************************************************************************
5539 * SmallBlockChainStream_GetSize
5541 * Returns the size of this chain.
5543 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5545 DirEntry chainEntry;
5547 if(This->headOfStreamPlaceHolder != NULL)
5549 ULARGE_INTEGER result;
5550 result.u.HighPart = 0;
5552 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5553 This->parentStorage->smallBlockSize;
5555 return result;
5558 StorageImpl_ReadDirEntry(
5559 This->parentStorage,
5560 This->ownerDirEntry,
5561 &chainEntry);
5563 return chainEntry.size;
5566 /******************************************************************************
5567 * StgCreateDocfile [OLE32.@]
5568 * Creates a new compound file storage object
5570 * PARAMS
5571 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5572 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5573 * reserved [ ?] unused?, usually 0
5574 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5576 * RETURNS
5577 * S_OK if the file was successfully created
5578 * some STG_E_ value if error
5579 * NOTES
5580 * if pwcsName is NULL, create file with new unique name
5581 * the function can returns
5582 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5583 * (unrealized now)
5585 HRESULT WINAPI StgCreateDocfile(
5586 LPCOLESTR pwcsName,
5587 DWORD grfMode,
5588 DWORD reserved,
5589 IStorage **ppstgOpen)
5591 StorageImpl* newStorage = 0;
5592 HANDLE hFile = INVALID_HANDLE_VALUE;
5593 HRESULT hr = STG_E_INVALIDFLAG;
5594 DWORD shareMode;
5595 DWORD accessMode;
5596 DWORD creationMode;
5597 DWORD fileAttributes;
5598 WCHAR tempFileName[MAX_PATH];
5600 TRACE("(%s, %x, %d, %p)\n",
5601 debugstr_w(pwcsName), grfMode,
5602 reserved, ppstgOpen);
5604 if (ppstgOpen == 0)
5605 return STG_E_INVALIDPOINTER;
5606 if (reserved != 0)
5607 return STG_E_INVALIDPARAMETER;
5609 /* if no share mode given then DENY_NONE is the default */
5610 if (STGM_SHARE_MODE(grfMode) == 0)
5611 grfMode |= STGM_SHARE_DENY_NONE;
5613 if ( FAILED( validateSTGM(grfMode) ))
5614 goto end;
5616 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5617 switch(STGM_ACCESS_MODE(grfMode))
5619 case STGM_WRITE:
5620 case STGM_READWRITE:
5621 break;
5622 default:
5623 goto end;
5626 /* in direct mode, can only use SHARE_EXCLUSIVE */
5627 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5628 goto end;
5630 /* but in transacted mode, any share mode is valid */
5633 * Generate a unique name.
5635 if (pwcsName == 0)
5637 WCHAR tempPath[MAX_PATH];
5638 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5640 memset(tempPath, 0, sizeof(tempPath));
5641 memset(tempFileName, 0, sizeof(tempFileName));
5643 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5644 tempPath[0] = '.';
5646 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5647 pwcsName = tempFileName;
5648 else
5650 hr = STG_E_INSUFFICIENTMEMORY;
5651 goto end;
5654 creationMode = TRUNCATE_EXISTING;
5656 else
5658 creationMode = GetCreationModeFromSTGM(grfMode);
5662 * Interpret the STGM value grfMode
5664 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5665 accessMode = GetAccessModeFromSTGM(grfMode);
5667 if (grfMode & STGM_DELETEONRELEASE)
5668 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5669 else
5670 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5672 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5673 FIXME("Storage share mode not implemented.\n");
5675 if (grfMode & STGM_TRANSACTED)
5676 FIXME("Transacted mode not implemented.\n");
5678 *ppstgOpen = 0;
5680 hFile = CreateFileW(pwcsName,
5681 accessMode,
5682 shareMode,
5683 NULL,
5684 creationMode,
5685 fileAttributes,
5688 if (hFile == INVALID_HANDLE_VALUE)
5690 if(GetLastError() == ERROR_FILE_EXISTS)
5691 hr = STG_E_FILEALREADYEXISTS;
5692 else
5693 hr = E_FAIL;
5694 goto end;
5698 * Allocate and initialize the new IStorage32object.
5700 hr = StorageImpl_Construct(
5701 hFile,
5702 pwcsName,
5703 NULL,
5704 grfMode,
5705 TRUE,
5706 TRUE,
5707 &newStorage);
5709 if (FAILED(hr))
5711 goto end;
5715 * Get an "out" pointer for the caller.
5717 *ppstgOpen = (IStorage*)newStorage;
5719 end:
5720 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5722 return hr;
5725 /******************************************************************************
5726 * StgCreateStorageEx [OLE32.@]
5728 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5730 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5731 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5733 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5735 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5736 return STG_E_INVALIDPARAMETER;
5739 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5741 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5742 return STG_E_INVALIDPARAMETER;
5745 if (stgfmt == STGFMT_FILE)
5747 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5748 return STG_E_INVALIDPARAMETER;
5751 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5753 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5754 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5757 ERR("Invalid stgfmt argument\n");
5758 return STG_E_INVALIDPARAMETER;
5761 /******************************************************************************
5762 * StgCreatePropSetStg [OLE32.@]
5764 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5765 IPropertySetStorage **ppPropSetStg)
5767 HRESULT hr;
5769 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5770 if (reserved)
5771 hr = STG_E_INVALIDPARAMETER;
5772 else
5773 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5774 (void**)ppPropSetStg);
5775 return hr;
5778 /******************************************************************************
5779 * StgOpenStorageEx [OLE32.@]
5781 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5783 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5784 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5786 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5788 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5789 return STG_E_INVALIDPARAMETER;
5792 switch (stgfmt)
5794 case STGFMT_FILE:
5795 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5796 return STG_E_INVALIDPARAMETER;
5798 case STGFMT_STORAGE:
5799 break;
5801 case STGFMT_DOCFILE:
5802 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5804 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5805 return STG_E_INVALIDPARAMETER;
5807 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5808 break;
5810 case STGFMT_ANY:
5811 WARN("STGFMT_ANY assuming storage\n");
5812 break;
5814 default:
5815 return STG_E_INVALIDPARAMETER;
5818 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5822 /******************************************************************************
5823 * StgOpenStorage [OLE32.@]
5825 HRESULT WINAPI StgOpenStorage(
5826 const OLECHAR *pwcsName,
5827 IStorage *pstgPriority,
5828 DWORD grfMode,
5829 SNB snbExclude,
5830 DWORD reserved,
5831 IStorage **ppstgOpen)
5833 StorageImpl* newStorage = 0;
5834 HRESULT hr = S_OK;
5835 HANDLE hFile = 0;
5836 DWORD shareMode;
5837 DWORD accessMode;
5838 WCHAR fullname[MAX_PATH];
5840 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5841 debugstr_w(pwcsName), pstgPriority, grfMode,
5842 snbExclude, reserved, ppstgOpen);
5844 if (pwcsName == 0)
5846 hr = STG_E_INVALIDNAME;
5847 goto end;
5850 if (ppstgOpen == 0)
5852 hr = STG_E_INVALIDPOINTER;
5853 goto end;
5856 if (reserved)
5858 hr = STG_E_INVALIDPARAMETER;
5859 goto end;
5862 if (grfMode & STGM_PRIORITY)
5864 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5865 return STG_E_INVALIDFLAG;
5866 if (grfMode & STGM_DELETEONRELEASE)
5867 return STG_E_INVALIDFUNCTION;
5868 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5869 return STG_E_INVALIDFLAG;
5870 grfMode &= ~0xf0; /* remove the existing sharing mode */
5871 grfMode |= STGM_SHARE_DENY_NONE;
5873 /* STGM_PRIORITY stops other IStorage objects on the same file from
5874 * committing until the STGM_PRIORITY IStorage is closed. it also
5875 * stops non-transacted mode StgOpenStorage calls with write access from
5876 * succeeding. obviously, both of these cannot be achieved through just
5877 * file share flags */
5878 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5882 * Validate the sharing mode
5884 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5885 switch(STGM_SHARE_MODE(grfMode))
5887 case STGM_SHARE_EXCLUSIVE:
5888 case STGM_SHARE_DENY_WRITE:
5889 break;
5890 default:
5891 hr = STG_E_INVALIDFLAG;
5892 goto end;
5895 if ( FAILED( validateSTGM(grfMode) ) ||
5896 (grfMode&STGM_CREATE))
5898 hr = STG_E_INVALIDFLAG;
5899 goto end;
5902 /* shared reading requires transacted mode */
5903 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5904 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5905 !(grfMode&STGM_TRANSACTED) )
5907 hr = STG_E_INVALIDFLAG;
5908 goto end;
5912 * Interpret the STGM value grfMode
5914 shareMode = GetShareModeFromSTGM(grfMode);
5915 accessMode = GetAccessModeFromSTGM(grfMode);
5917 *ppstgOpen = 0;
5919 hFile = CreateFileW( pwcsName,
5920 accessMode,
5921 shareMode,
5922 NULL,
5923 OPEN_EXISTING,
5924 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5927 if (hFile==INVALID_HANDLE_VALUE)
5929 DWORD last_error = GetLastError();
5931 hr = E_FAIL;
5933 switch (last_error)
5935 case ERROR_FILE_NOT_FOUND:
5936 hr = STG_E_FILENOTFOUND;
5937 break;
5939 case ERROR_PATH_NOT_FOUND:
5940 hr = STG_E_PATHNOTFOUND;
5941 break;
5943 case ERROR_ACCESS_DENIED:
5944 case ERROR_WRITE_PROTECT:
5945 hr = STG_E_ACCESSDENIED;
5946 break;
5948 case ERROR_SHARING_VIOLATION:
5949 hr = STG_E_SHAREVIOLATION;
5950 break;
5952 default:
5953 hr = E_FAIL;
5956 goto end;
5960 * Refuse to open the file if it's too small to be a structured storage file
5961 * FIXME: verify the file when reading instead of here
5963 if (GetFileSize(hFile, NULL) < 0x100)
5965 CloseHandle(hFile);
5966 hr = STG_E_FILEALREADYEXISTS;
5967 goto end;
5971 * Allocate and initialize the new IStorage32object.
5973 hr = StorageImpl_Construct(
5974 hFile,
5975 pwcsName,
5976 NULL,
5977 grfMode,
5978 TRUE,
5979 FALSE,
5980 &newStorage);
5982 if (FAILED(hr))
5985 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5987 if(hr == STG_E_INVALIDHEADER)
5988 hr = STG_E_FILEALREADYEXISTS;
5989 goto end;
5992 /* prepare the file name string given in lieu of the root property name */
5993 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5994 memcpy(newStorage->base.filename, fullname, DIRENTRY_NAME_BUFFER_LEN);
5995 newStorage->base.filename[DIRENTRY_NAME_BUFFER_LEN-1] = '\0';
5998 * Get an "out" pointer for the caller.
6000 *ppstgOpen = (IStorage*)newStorage;
6002 end:
6003 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6004 return hr;
6007 /******************************************************************************
6008 * StgCreateDocfileOnILockBytes [OLE32.@]
6010 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6011 ILockBytes *plkbyt,
6012 DWORD grfMode,
6013 DWORD reserved,
6014 IStorage** ppstgOpen)
6016 StorageImpl* newStorage = 0;
6017 HRESULT hr = S_OK;
6019 if ((ppstgOpen == 0) || (plkbyt == 0))
6020 return STG_E_INVALIDPOINTER;
6023 * Allocate and initialize the new IStorage object.
6025 hr = StorageImpl_Construct(
6028 plkbyt,
6029 grfMode,
6030 FALSE,
6031 TRUE,
6032 &newStorage);
6034 if (FAILED(hr))
6036 return hr;
6040 * Get an "out" pointer for the caller.
6042 *ppstgOpen = (IStorage*)newStorage;
6044 return hr;
6047 /******************************************************************************
6048 * StgOpenStorageOnILockBytes [OLE32.@]
6050 HRESULT WINAPI StgOpenStorageOnILockBytes(
6051 ILockBytes *plkbyt,
6052 IStorage *pstgPriority,
6053 DWORD grfMode,
6054 SNB snbExclude,
6055 DWORD reserved,
6056 IStorage **ppstgOpen)
6058 StorageImpl* newStorage = 0;
6059 HRESULT hr = S_OK;
6061 if ((plkbyt == 0) || (ppstgOpen == 0))
6062 return STG_E_INVALIDPOINTER;
6064 if ( FAILED( validateSTGM(grfMode) ))
6065 return STG_E_INVALIDFLAG;
6067 *ppstgOpen = 0;
6070 * Allocate and initialize the new IStorage object.
6072 hr = StorageImpl_Construct(
6075 plkbyt,
6076 grfMode,
6077 FALSE,
6078 FALSE,
6079 &newStorage);
6081 if (FAILED(hr))
6083 return hr;
6087 * Get an "out" pointer for the caller.
6089 *ppstgOpen = (IStorage*)newStorage;
6091 return hr;
6094 /******************************************************************************
6095 * StgSetTimes [ole32.@]
6096 * StgSetTimes [OLE32.@]
6100 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6101 FILETIME const *patime, FILETIME const *pmtime)
6103 IStorage *stg = NULL;
6104 HRESULT r;
6106 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6108 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6109 0, 0, &stg);
6110 if( SUCCEEDED(r) )
6112 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6113 IStorage_Release(stg);
6116 return r;
6119 /******************************************************************************
6120 * StgIsStorageILockBytes [OLE32.@]
6122 * Determines if the ILockBytes contains a storage object.
6124 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6126 BYTE sig[8];
6127 ULARGE_INTEGER offset;
6129 offset.u.HighPart = 0;
6130 offset.u.LowPart = 0;
6132 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6134 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6135 return S_OK;
6137 return S_FALSE;
6140 /******************************************************************************
6141 * WriteClassStg [OLE32.@]
6143 * This method will store the specified CLSID in the specified storage object
6145 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6147 HRESULT hRes;
6149 if(!pStg)
6150 return E_INVALIDARG;
6152 if(!rclsid)
6153 return STG_E_INVALIDPOINTER;
6155 hRes = IStorage_SetClass(pStg, rclsid);
6157 return hRes;
6160 /***********************************************************************
6161 * ReadClassStg (OLE32.@)
6163 * This method reads the CLSID previously written to a storage object with
6164 * the WriteClassStg.
6166 * PARAMS
6167 * pstg [I] IStorage pointer
6168 * pclsid [O] Pointer to where the CLSID is written
6170 * RETURNS
6171 * Success: S_OK.
6172 * Failure: HRESULT code.
6174 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6176 STATSTG pstatstg;
6177 HRESULT hRes;
6179 TRACE("(%p, %p)\n", pstg, pclsid);
6181 if(!pstg || !pclsid)
6182 return E_INVALIDARG;
6185 * read a STATSTG structure (contains the clsid) from the storage
6187 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6189 if(SUCCEEDED(hRes))
6190 *pclsid=pstatstg.clsid;
6192 return hRes;
6195 /***********************************************************************
6196 * OleLoadFromStream (OLE32.@)
6198 * This function loads an object from stream
6200 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6202 CLSID clsid;
6203 HRESULT res;
6204 LPPERSISTSTREAM xstm;
6206 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6208 res=ReadClassStm(pStm,&clsid);
6209 if (FAILED(res))
6210 return res;
6211 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6212 if (FAILED(res))
6213 return res;
6214 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6215 if (FAILED(res)) {
6216 IUnknown_Release((IUnknown*)*ppvObj);
6217 return res;
6219 res=IPersistStream_Load(xstm,pStm);
6220 IPersistStream_Release(xstm);
6221 /* FIXME: all refcounts ok at this point? I think they should be:
6222 * pStm : unchanged
6223 * ppvObj : 1
6224 * xstm : 0 (released)
6226 return res;
6229 /***********************************************************************
6230 * OleSaveToStream (OLE32.@)
6232 * This function saves an object with the IPersistStream interface on it
6233 * to the specified stream.
6235 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6238 CLSID clsid;
6239 HRESULT res;
6241 TRACE("(%p,%p)\n",pPStm,pStm);
6243 res=IPersistStream_GetClassID(pPStm,&clsid);
6245 if (SUCCEEDED(res)){
6247 res=WriteClassStm(pStm,&clsid);
6249 if (SUCCEEDED(res))
6251 res=IPersistStream_Save(pPStm,pStm,TRUE);
6254 TRACE("Finished Save\n");
6255 return res;
6258 /****************************************************************************
6259 * This method validate a STGM parameter that can contain the values below
6261 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6262 * The stgm values contained in 0xffff0000 are bitmasks.
6264 * STGM_DIRECT 0x00000000
6265 * STGM_TRANSACTED 0x00010000
6266 * STGM_SIMPLE 0x08000000
6268 * STGM_READ 0x00000000
6269 * STGM_WRITE 0x00000001
6270 * STGM_READWRITE 0x00000002
6272 * STGM_SHARE_DENY_NONE 0x00000040
6273 * STGM_SHARE_DENY_READ 0x00000030
6274 * STGM_SHARE_DENY_WRITE 0x00000020
6275 * STGM_SHARE_EXCLUSIVE 0x00000010
6277 * STGM_PRIORITY 0x00040000
6278 * STGM_DELETEONRELEASE 0x04000000
6280 * STGM_CREATE 0x00001000
6281 * STGM_CONVERT 0x00020000
6282 * STGM_FAILIFTHERE 0x00000000
6284 * STGM_NOSCRATCH 0x00100000
6285 * STGM_NOSNAPSHOT 0x00200000
6287 static HRESULT validateSTGM(DWORD stgm)
6289 DWORD access = STGM_ACCESS_MODE(stgm);
6290 DWORD share = STGM_SHARE_MODE(stgm);
6291 DWORD create = STGM_CREATE_MODE(stgm);
6293 if (stgm&~STGM_KNOWN_FLAGS)
6295 ERR("unknown flags %08x\n", stgm);
6296 return E_FAIL;
6299 switch (access)
6301 case STGM_READ:
6302 case STGM_WRITE:
6303 case STGM_READWRITE:
6304 break;
6305 default:
6306 return E_FAIL;
6309 switch (share)
6311 case STGM_SHARE_DENY_NONE:
6312 case STGM_SHARE_DENY_READ:
6313 case STGM_SHARE_DENY_WRITE:
6314 case STGM_SHARE_EXCLUSIVE:
6315 break;
6316 default:
6317 return E_FAIL;
6320 switch (create)
6322 case STGM_CREATE:
6323 case STGM_FAILIFTHERE:
6324 break;
6325 default:
6326 return E_FAIL;
6330 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6332 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6333 return E_FAIL;
6336 * STGM_CREATE | STGM_CONVERT
6337 * if both are false, STGM_FAILIFTHERE is set to TRUE
6339 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6340 return E_FAIL;
6343 * STGM_NOSCRATCH requires STGM_TRANSACTED
6345 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6346 return E_FAIL;
6349 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6350 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6352 if ( (stgm & STGM_NOSNAPSHOT) &&
6353 (!(stgm & STGM_TRANSACTED) ||
6354 share == STGM_SHARE_EXCLUSIVE ||
6355 share == STGM_SHARE_DENY_WRITE) )
6356 return E_FAIL;
6358 return S_OK;
6361 /****************************************************************************
6362 * GetShareModeFromSTGM
6364 * This method will return a share mode flag from a STGM value.
6365 * The STGM value is assumed valid.
6367 static DWORD GetShareModeFromSTGM(DWORD stgm)
6369 switch (STGM_SHARE_MODE(stgm))
6371 case STGM_SHARE_DENY_NONE:
6372 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6373 case STGM_SHARE_DENY_READ:
6374 return FILE_SHARE_WRITE;
6375 case STGM_SHARE_DENY_WRITE:
6376 return FILE_SHARE_READ;
6377 case STGM_SHARE_EXCLUSIVE:
6378 return 0;
6380 ERR("Invalid share mode!\n");
6381 assert(0);
6382 return 0;
6385 /****************************************************************************
6386 * GetAccessModeFromSTGM
6388 * This method will return an access mode flag from a STGM value.
6389 * The STGM value is assumed valid.
6391 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6393 switch (STGM_ACCESS_MODE(stgm))
6395 case STGM_READ:
6396 return GENERIC_READ;
6397 case STGM_WRITE:
6398 case STGM_READWRITE:
6399 return GENERIC_READ | GENERIC_WRITE;
6401 ERR("Invalid access mode!\n");
6402 assert(0);
6403 return 0;
6406 /****************************************************************************
6407 * GetCreationModeFromSTGM
6409 * This method will return a creation mode flag from a STGM value.
6410 * The STGM value is assumed valid.
6412 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6414 switch(STGM_CREATE_MODE(stgm))
6416 case STGM_CREATE:
6417 return CREATE_ALWAYS;
6418 case STGM_CONVERT:
6419 FIXME("STGM_CONVERT not implemented!\n");
6420 return CREATE_NEW;
6421 case STGM_FAILIFTHERE:
6422 return CREATE_NEW;
6424 ERR("Invalid create mode!\n");
6425 assert(0);
6426 return 0;
6430 /*************************************************************************
6431 * OLECONVERT_LoadOLE10 [Internal]
6433 * Loads the OLE10 STREAM to memory
6435 * PARAMS
6436 * pOleStream [I] The OLESTREAM
6437 * pData [I] Data Structure for the OLESTREAM Data
6439 * RETURNS
6440 * Success: S_OK
6441 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6442 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6444 * NOTES
6445 * This function is used by OleConvertOLESTREAMToIStorage only.
6447 * Memory allocated for pData must be freed by the caller
6449 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6451 DWORD dwSize;
6452 HRESULT hRes = S_OK;
6453 int nTryCnt=0;
6454 int max_try = 6;
6456 pData->pData = NULL;
6457 pData->pstrOleObjFileName = NULL;
6459 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6461 /* Get the OleID */
6462 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6463 if(dwSize != sizeof(pData->dwOleID))
6465 hRes = CONVERT10_E_OLESTREAM_GET;
6467 else if(pData->dwOleID != OLESTREAM_ID)
6469 hRes = CONVERT10_E_OLESTREAM_FMT;
6471 else
6473 hRes = S_OK;
6474 break;
6478 if(hRes == S_OK)
6480 /* Get the TypeID... more info needed for this field */
6481 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6482 if(dwSize != sizeof(pData->dwTypeID))
6484 hRes = CONVERT10_E_OLESTREAM_GET;
6487 if(hRes == S_OK)
6489 if(pData->dwTypeID != 0)
6491 /* Get the length of the OleTypeName */
6492 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6493 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6495 hRes = CONVERT10_E_OLESTREAM_GET;
6498 if(hRes == S_OK)
6500 if(pData->dwOleTypeNameLength > 0)
6502 /* Get the OleTypeName */
6503 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6504 if(dwSize != pData->dwOleTypeNameLength)
6506 hRes = CONVERT10_E_OLESTREAM_GET;
6510 if(bStrem1)
6512 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6513 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6515 hRes = CONVERT10_E_OLESTREAM_GET;
6517 if(hRes == S_OK)
6519 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6520 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6521 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6522 if(pData->pstrOleObjFileName)
6524 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6525 if(dwSize != pData->dwOleObjFileNameLength)
6527 hRes = CONVERT10_E_OLESTREAM_GET;
6530 else
6531 hRes = CONVERT10_E_OLESTREAM_GET;
6534 else
6536 /* Get the Width of the Metafile */
6537 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6538 if(dwSize != sizeof(pData->dwMetaFileWidth))
6540 hRes = CONVERT10_E_OLESTREAM_GET;
6542 if(hRes == S_OK)
6544 /* Get the Height of the Metafile */
6545 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6546 if(dwSize != sizeof(pData->dwMetaFileHeight))
6548 hRes = CONVERT10_E_OLESTREAM_GET;
6552 if(hRes == S_OK)
6554 /* Get the Length of the Data */
6555 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6556 if(dwSize != sizeof(pData->dwDataLength))
6558 hRes = CONVERT10_E_OLESTREAM_GET;
6562 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6564 if(!bStrem1) /* if it is a second OLE stream data */
6566 pData->dwDataLength -= 8;
6567 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6568 if(dwSize != sizeof(pData->strUnknown))
6570 hRes = CONVERT10_E_OLESTREAM_GET;
6574 if(hRes == S_OK)
6576 if(pData->dwDataLength > 0)
6578 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6580 /* Get Data (ex. IStorage, Metafile, or BMP) */
6581 if(pData->pData)
6583 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6584 if(dwSize != pData->dwDataLength)
6586 hRes = CONVERT10_E_OLESTREAM_GET;
6589 else
6591 hRes = CONVERT10_E_OLESTREAM_GET;
6597 return hRes;
6600 /*************************************************************************
6601 * OLECONVERT_SaveOLE10 [Internal]
6603 * Saves the OLE10 STREAM From memory
6605 * PARAMS
6606 * pData [I] Data Structure for the OLESTREAM Data
6607 * pOleStream [I] The OLESTREAM to save
6609 * RETURNS
6610 * Success: S_OK
6611 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6613 * NOTES
6614 * This function is used by OleConvertIStorageToOLESTREAM only.
6617 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6619 DWORD dwSize;
6620 HRESULT hRes = S_OK;
6623 /* Set the OleID */
6624 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6625 if(dwSize != sizeof(pData->dwOleID))
6627 hRes = CONVERT10_E_OLESTREAM_PUT;
6630 if(hRes == S_OK)
6632 /* Set the TypeID */
6633 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6634 if(dwSize != sizeof(pData->dwTypeID))
6636 hRes = CONVERT10_E_OLESTREAM_PUT;
6640 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6642 /* Set the Length of the OleTypeName */
6643 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6644 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6646 hRes = CONVERT10_E_OLESTREAM_PUT;
6649 if(hRes == S_OK)
6651 if(pData->dwOleTypeNameLength > 0)
6653 /* Set the OleTypeName */
6654 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6655 if(dwSize != pData->dwOleTypeNameLength)
6657 hRes = CONVERT10_E_OLESTREAM_PUT;
6662 if(hRes == S_OK)
6664 /* Set the width of the Metafile */
6665 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6666 if(dwSize != sizeof(pData->dwMetaFileWidth))
6668 hRes = CONVERT10_E_OLESTREAM_PUT;
6672 if(hRes == S_OK)
6674 /* Set the height of the Metafile */
6675 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6676 if(dwSize != sizeof(pData->dwMetaFileHeight))
6678 hRes = CONVERT10_E_OLESTREAM_PUT;
6682 if(hRes == S_OK)
6684 /* Set the length of the Data */
6685 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6686 if(dwSize != sizeof(pData->dwDataLength))
6688 hRes = CONVERT10_E_OLESTREAM_PUT;
6692 if(hRes == S_OK)
6694 if(pData->dwDataLength > 0)
6696 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6697 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6698 if(dwSize != pData->dwDataLength)
6700 hRes = CONVERT10_E_OLESTREAM_PUT;
6705 return hRes;
6708 /*************************************************************************
6709 * OLECONVERT_GetOLE20FromOLE10[Internal]
6711 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6712 * opens it, and copies the content to the dest IStorage for
6713 * OleConvertOLESTREAMToIStorage
6716 * PARAMS
6717 * pDestStorage [I] The IStorage to copy the data to
6718 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6719 * nBufferLength [I] The size of the buffer
6721 * RETURNS
6722 * Nothing
6724 * NOTES
6728 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6730 HRESULT hRes;
6731 HANDLE hFile;
6732 IStorage *pTempStorage;
6733 DWORD dwNumOfBytesWritten;
6734 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6735 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6737 /* Create a temp File */
6738 GetTempPathW(MAX_PATH, wstrTempDir);
6739 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6740 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6742 if(hFile != INVALID_HANDLE_VALUE)
6744 /* Write IStorage Data to File */
6745 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6746 CloseHandle(hFile);
6748 /* Open and copy temp storage to the Dest Storage */
6749 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6750 if(hRes == S_OK)
6752 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6753 IStorage_Release(pTempStorage);
6755 DeleteFileW(wstrTempFile);
6760 /*************************************************************************
6761 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6763 * Saves the OLE10 STREAM From memory
6765 * PARAMS
6766 * pStorage [I] The Src IStorage to copy
6767 * pData [I] The Dest Memory to write to.
6769 * RETURNS
6770 * The size in bytes allocated for pData
6772 * NOTES
6773 * Memory allocated for pData must be freed by the caller
6775 * Used by OleConvertIStorageToOLESTREAM only.
6778 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6780 HANDLE hFile;
6781 HRESULT hRes;
6782 DWORD nDataLength = 0;
6783 IStorage *pTempStorage;
6784 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6785 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6787 *pData = NULL;
6789 /* Create temp Storage */
6790 GetTempPathW(MAX_PATH, wstrTempDir);
6791 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6792 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6794 if(hRes == S_OK)
6796 /* Copy Src Storage to the Temp Storage */
6797 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6798 IStorage_Release(pTempStorage);
6800 /* Open Temp Storage as a file and copy to memory */
6801 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6802 if(hFile != INVALID_HANDLE_VALUE)
6804 nDataLength = GetFileSize(hFile, NULL);
6805 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6806 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6807 CloseHandle(hFile);
6809 DeleteFileW(wstrTempFile);
6811 return nDataLength;
6814 /*************************************************************************
6815 * OLECONVERT_CreateOleStream [Internal]
6817 * Creates the "\001OLE" stream in the IStorage if necessary.
6819 * PARAMS
6820 * pStorage [I] Dest storage to create the stream in
6822 * RETURNS
6823 * Nothing
6825 * NOTES
6826 * This function is used by OleConvertOLESTREAMToIStorage only.
6828 * This stream is still unknown, MS Word seems to have extra data
6829 * but since the data is stored in the OLESTREAM there should be
6830 * no need to recreate the stream. If the stream is manually
6831 * deleted it will create it with this default data.
6834 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6836 HRESULT hRes;
6837 IStream *pStream;
6838 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6839 BYTE pOleStreamHeader [] =
6841 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6842 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6843 0x00, 0x00, 0x00, 0x00
6846 /* Create stream if not present */
6847 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6848 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6850 if(hRes == S_OK)
6852 /* Write default Data */
6853 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6854 IStream_Release(pStream);
6858 /* write a string to a stream, preceded by its length */
6859 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6861 HRESULT r;
6862 LPSTR str;
6863 DWORD len = 0;
6865 if( string )
6866 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6867 r = IStream_Write( stm, &len, sizeof(len), NULL);
6868 if( FAILED( r ) )
6869 return r;
6870 if(len == 0)
6871 return r;
6872 str = CoTaskMemAlloc( len );
6873 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6874 r = IStream_Write( stm, str, len, NULL);
6875 CoTaskMemFree( str );
6876 return r;
6879 /* read a string preceded by its length from a stream */
6880 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6882 HRESULT r;
6883 DWORD len, count = 0;
6884 LPSTR str;
6885 LPWSTR wstr;
6887 r = IStream_Read( stm, &len, sizeof(len), &count );
6888 if( FAILED( r ) )
6889 return r;
6890 if( count != sizeof(len) )
6891 return E_OUTOFMEMORY;
6893 TRACE("%d bytes\n",len);
6895 str = CoTaskMemAlloc( len );
6896 if( !str )
6897 return E_OUTOFMEMORY;
6898 count = 0;
6899 r = IStream_Read( stm, str, len, &count );
6900 if( FAILED( r ) )
6901 return r;
6902 if( count != len )
6904 CoTaskMemFree( str );
6905 return E_OUTOFMEMORY;
6908 TRACE("Read string %s\n",debugstr_an(str,len));
6910 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6911 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6912 if( wstr )
6913 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6914 CoTaskMemFree( str );
6916 *string = wstr;
6918 return r;
6922 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6923 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6925 IStream *pstm;
6926 HRESULT r = S_OK;
6927 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6929 static const BYTE unknown1[12] =
6930 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6931 0xFF, 0xFF, 0xFF, 0xFF};
6932 static const BYTE unknown2[16] =
6933 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6936 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6937 debugstr_w(lpszUserType), debugstr_w(szClipName),
6938 debugstr_w(szProgIDName));
6940 /* Create a CompObj stream */
6941 r = IStorage_CreateStream(pstg, szwStreamName,
6942 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6943 if( FAILED (r) )
6944 return r;
6946 /* Write CompObj Structure to stream */
6947 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6949 if( SUCCEEDED( r ) )
6950 r = WriteClassStm( pstm, clsid );
6952 if( SUCCEEDED( r ) )
6953 r = STREAM_WriteString( pstm, lpszUserType );
6954 if( SUCCEEDED( r ) )
6955 r = STREAM_WriteString( pstm, szClipName );
6956 if( SUCCEEDED( r ) )
6957 r = STREAM_WriteString( pstm, szProgIDName );
6958 if( SUCCEEDED( r ) )
6959 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6961 IStream_Release( pstm );
6963 return r;
6966 /***********************************************************************
6967 * WriteFmtUserTypeStg (OLE32.@)
6969 HRESULT WINAPI WriteFmtUserTypeStg(
6970 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6972 HRESULT r;
6973 WCHAR szwClipName[0x40];
6974 CLSID clsid = CLSID_NULL;
6975 LPWSTR wstrProgID = NULL;
6976 DWORD n;
6978 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6980 /* get the clipboard format name */
6981 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
6982 szwClipName[n]=0;
6984 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6986 /* FIXME: There's room to save a CLSID and its ProgID, but
6987 the CLSID is not looked up in the registry and in all the
6988 tests I wrote it was CLSID_NULL. Where does it come from?
6991 /* get the real program ID. This may fail, but that's fine */
6992 ProgIDFromCLSID(&clsid, &wstrProgID);
6994 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6996 r = STORAGE_WriteCompObj( pstg, &clsid,
6997 lpszUserType, szwClipName, wstrProgID );
6999 CoTaskMemFree(wstrProgID);
7001 return r;
7005 /******************************************************************************
7006 * ReadFmtUserTypeStg [OLE32.@]
7008 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7010 HRESULT r;
7011 IStream *stm = 0;
7012 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7013 unsigned char unknown1[12];
7014 unsigned char unknown2[16];
7015 DWORD count;
7016 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7017 CLSID clsid;
7019 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7021 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7022 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7023 if( FAILED ( r ) )
7025 WARN("Failed to open stream r = %08x\n", r);
7026 return r;
7029 /* read the various parts of the structure */
7030 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7031 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7032 goto end;
7033 r = ReadClassStm( stm, &clsid );
7034 if( FAILED( r ) )
7035 goto end;
7037 r = STREAM_ReadString( stm, &szCLSIDName );
7038 if( FAILED( r ) )
7039 goto end;
7041 r = STREAM_ReadString( stm, &szOleTypeName );
7042 if( FAILED( r ) )
7043 goto end;
7045 r = STREAM_ReadString( stm, &szProgIDName );
7046 if( FAILED( r ) )
7047 goto end;
7049 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7050 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7051 goto end;
7053 /* ok, success... now we just need to store what we found */
7054 if( pcf )
7055 *pcf = RegisterClipboardFormatW( szOleTypeName );
7056 CoTaskMemFree( szOleTypeName );
7058 if( lplpszUserType )
7059 *lplpszUserType = szCLSIDName;
7060 CoTaskMemFree( szProgIDName );
7062 end:
7063 IStream_Release( stm );
7065 return r;
7069 /*************************************************************************
7070 * OLECONVERT_CreateCompObjStream [Internal]
7072 * Creates a "\001CompObj" is the destination IStorage if necessary.
7074 * PARAMS
7075 * pStorage [I] The dest IStorage to create the CompObj Stream
7076 * if necessary.
7077 * strOleTypeName [I] The ProgID
7079 * RETURNS
7080 * Success: S_OK
7081 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7083 * NOTES
7084 * This function is used by OleConvertOLESTREAMToIStorage only.
7086 * The stream data is stored in the OLESTREAM and there should be
7087 * no need to recreate the stream. If the stream is manually
7088 * deleted it will attempt to create it by querying the registry.
7092 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7094 IStream *pStream;
7095 HRESULT hStorageRes, hRes = S_OK;
7096 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7097 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7098 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7100 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7101 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7103 /* Initialize the CompObj structure */
7104 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7105 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7106 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7109 /* Create a CompObj stream if it doesn't exist */
7110 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7111 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7112 if(hStorageRes == S_OK)
7114 /* copy the OleTypeName to the compobj struct */
7115 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7116 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7118 /* copy the OleTypeName to the compobj struct */
7119 /* Note: in the test made, these were Identical */
7120 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7121 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7123 /* Get the CLSID */
7124 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7125 bufferW, OLESTREAM_MAX_STR_LEN );
7126 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7128 if(hRes == S_OK)
7130 HKEY hKey;
7131 LONG hErr;
7132 /* Get the CLSID Default Name from the Registry */
7133 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7134 if(hErr == ERROR_SUCCESS)
7136 char strTemp[OLESTREAM_MAX_STR_LEN];
7137 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7138 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7139 if(hErr == ERROR_SUCCESS)
7141 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7143 RegCloseKey(hKey);
7147 /* Write CompObj Structure to stream */
7148 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7150 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7152 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7153 if(IStorageCompObj.dwCLSIDNameLength > 0)
7155 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7157 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7158 if(IStorageCompObj.dwOleTypeNameLength > 0)
7160 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7162 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7163 if(IStorageCompObj.dwProgIDNameLength > 0)
7165 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7167 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7168 IStream_Release(pStream);
7170 return hRes;
7174 /*************************************************************************
7175 * OLECONVERT_CreateOlePresStream[Internal]
7177 * Creates the "\002OlePres000" Stream with the Metafile data
7179 * PARAMS
7180 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7181 * dwExtentX [I] Width of the Metafile
7182 * dwExtentY [I] Height of the Metafile
7183 * pData [I] Metafile data
7184 * dwDataLength [I] Size of the Metafile data
7186 * RETURNS
7187 * Success: S_OK
7188 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7190 * NOTES
7191 * This function is used by OleConvertOLESTREAMToIStorage only.
7194 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7196 HRESULT hRes;
7197 IStream *pStream;
7198 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7199 BYTE pOlePresStreamHeader [] =
7201 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7202 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7203 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7204 0x00, 0x00, 0x00, 0x00
7207 BYTE pOlePresStreamHeaderEmpty [] =
7209 0x00, 0x00, 0x00, 0x00,
7210 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7211 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7212 0x00, 0x00, 0x00, 0x00
7215 /* Create the OlePres000 Stream */
7216 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7217 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7219 if(hRes == S_OK)
7221 DWORD nHeaderSize;
7222 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7224 memset(&OlePres, 0, sizeof(OlePres));
7225 /* Do we have any metafile data to save */
7226 if(dwDataLength > 0)
7228 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7229 nHeaderSize = sizeof(pOlePresStreamHeader);
7231 else
7233 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7234 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7236 /* Set width and height of the metafile */
7237 OlePres.dwExtentX = dwExtentX;
7238 OlePres.dwExtentY = -dwExtentY;
7240 /* Set Data and Length */
7241 if(dwDataLength > sizeof(METAFILEPICT16))
7243 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7244 OlePres.pData = &(pData[8]);
7246 /* Save OlePres000 Data to Stream */
7247 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7248 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7249 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7250 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7251 if(OlePres.dwSize > 0)
7253 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7255 IStream_Release(pStream);
7259 /*************************************************************************
7260 * OLECONVERT_CreateOle10NativeStream [Internal]
7262 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7264 * PARAMS
7265 * pStorage [I] Dest storage to create the stream in
7266 * pData [I] Ole10 Native Data (ex. bmp)
7267 * dwDataLength [I] Size of the Ole10 Native Data
7269 * RETURNS
7270 * Nothing
7272 * NOTES
7273 * This function is used by OleConvertOLESTREAMToIStorage only.
7275 * Might need to verify the data and return appropriate error message
7278 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7280 HRESULT hRes;
7281 IStream *pStream;
7282 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7284 /* Create the Ole10Native Stream */
7285 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7286 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7288 if(hRes == S_OK)
7290 /* Write info to stream */
7291 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7292 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7293 IStream_Release(pStream);
7298 /*************************************************************************
7299 * OLECONVERT_GetOLE10ProgID [Internal]
7301 * Finds the ProgID (or OleTypeID) from the IStorage
7303 * PARAMS
7304 * pStorage [I] The Src IStorage to get the ProgID
7305 * strProgID [I] the ProgID string to get
7306 * dwSize [I] the size of the string
7308 * RETURNS
7309 * Success: S_OK
7310 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7312 * NOTES
7313 * This function is used by OleConvertIStorageToOLESTREAM only.
7317 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7319 HRESULT hRes;
7320 IStream *pStream;
7321 LARGE_INTEGER iSeekPos;
7322 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7323 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7325 /* Open the CompObj Stream */
7326 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7327 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7328 if(hRes == S_OK)
7331 /*Get the OleType from the CompObj Stream */
7332 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7333 iSeekPos.u.HighPart = 0;
7335 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7336 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7337 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7338 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7339 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7340 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7341 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7343 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7344 if(*dwSize > 0)
7346 IStream_Read(pStream, strProgID, *dwSize, NULL);
7348 IStream_Release(pStream);
7350 else
7352 STATSTG stat;
7353 LPOLESTR wstrProgID;
7355 /* Get the OleType from the registry */
7356 REFCLSID clsid = &(stat.clsid);
7357 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7358 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7359 if(hRes == S_OK)
7361 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7365 return hRes;
7368 /*************************************************************************
7369 * OLECONVERT_GetOle10PresData [Internal]
7371 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7373 * PARAMS
7374 * pStorage [I] Src IStroage
7375 * pOleStream [I] Dest OleStream Mem Struct
7377 * RETURNS
7378 * Nothing
7380 * NOTES
7381 * This function is used by OleConvertIStorageToOLESTREAM only.
7383 * Memory allocated for pData must be freed by the caller
7387 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7390 HRESULT hRes;
7391 IStream *pStream;
7392 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7394 /* Initialize Default data for OLESTREAM */
7395 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7396 pOleStreamData[0].dwTypeID = 2;
7397 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7398 pOleStreamData[1].dwTypeID = 0;
7399 pOleStreamData[0].dwMetaFileWidth = 0;
7400 pOleStreamData[0].dwMetaFileHeight = 0;
7401 pOleStreamData[0].pData = NULL;
7402 pOleStreamData[1].pData = NULL;
7404 /* Open Ole10Native Stream */
7405 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7406 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7407 if(hRes == S_OK)
7410 /* Read Size and Data */
7411 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7412 if(pOleStreamData->dwDataLength > 0)
7414 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7415 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7417 IStream_Release(pStream);
7423 /*************************************************************************
7424 * OLECONVERT_GetOle20PresData[Internal]
7426 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7428 * PARAMS
7429 * pStorage [I] Src IStroage
7430 * pOleStreamData [I] Dest OleStream Mem Struct
7432 * RETURNS
7433 * Nothing
7435 * NOTES
7436 * This function is used by OleConvertIStorageToOLESTREAM only.
7438 * Memory allocated for pData must be freed by the caller
7440 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7442 HRESULT hRes;
7443 IStream *pStream;
7444 OLECONVERT_ISTORAGE_OLEPRES olePress;
7445 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7447 /* Initialize Default data for OLESTREAM */
7448 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7449 pOleStreamData[0].dwTypeID = 2;
7450 pOleStreamData[0].dwMetaFileWidth = 0;
7451 pOleStreamData[0].dwMetaFileHeight = 0;
7452 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7453 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7454 pOleStreamData[1].dwTypeID = 0;
7455 pOleStreamData[1].dwOleTypeNameLength = 0;
7456 pOleStreamData[1].strOleTypeName[0] = 0;
7457 pOleStreamData[1].dwMetaFileWidth = 0;
7458 pOleStreamData[1].dwMetaFileHeight = 0;
7459 pOleStreamData[1].pData = NULL;
7460 pOleStreamData[1].dwDataLength = 0;
7463 /* Open OlePress000 stream */
7464 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7465 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7466 if(hRes == S_OK)
7468 LARGE_INTEGER iSeekPos;
7469 METAFILEPICT16 MetaFilePict;
7470 static const char strMetafilePictName[] = "METAFILEPICT";
7472 /* Set the TypeID for a Metafile */
7473 pOleStreamData[1].dwTypeID = 5;
7475 /* Set the OleTypeName to Metafile */
7476 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7477 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7479 iSeekPos.u.HighPart = 0;
7480 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7482 /* Get Presentation Data */
7483 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7484 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7485 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7486 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7488 /*Set width and Height */
7489 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7490 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7491 if(olePress.dwSize > 0)
7493 /* Set Length */
7494 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7496 /* Set MetaFilePict struct */
7497 MetaFilePict.mm = 8;
7498 MetaFilePict.xExt = olePress.dwExtentX;
7499 MetaFilePict.yExt = olePress.dwExtentY;
7500 MetaFilePict.hMF = 0;
7502 /* Get Metafile Data */
7503 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7504 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7505 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7507 IStream_Release(pStream);
7511 /*************************************************************************
7512 * OleConvertOLESTREAMToIStorage [OLE32.@]
7514 * Read info on MSDN
7516 * TODO
7517 * DVTARGETDEVICE parameter is not handled
7518 * Still unsure of some mem fields for OLE 10 Stream
7519 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7520 * and "\001OLE" streams
7523 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7524 LPOLESTREAM pOleStream,
7525 LPSTORAGE pstg,
7526 const DVTARGETDEVICE* ptd)
7528 int i;
7529 HRESULT hRes=S_OK;
7530 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7532 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7534 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7536 if(ptd != NULL)
7538 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7541 if(pstg == NULL || pOleStream == NULL)
7543 hRes = E_INVALIDARG;
7546 if(hRes == S_OK)
7548 /* Load the OLESTREAM to Memory */
7549 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7552 if(hRes == S_OK)
7554 /* Load the OLESTREAM to Memory (part 2)*/
7555 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7558 if(hRes == S_OK)
7561 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7563 /* Do we have the IStorage Data in the OLESTREAM */
7564 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7566 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7567 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7569 else
7571 /* It must be an original OLE 1.0 source */
7572 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7575 else
7577 /* It must be an original OLE 1.0 source */
7578 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7581 /* Create CompObj Stream if necessary */
7582 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7583 if(hRes == S_OK)
7585 /*Create the Ole Stream if necessary */
7586 OLECONVERT_CreateOleStream(pstg);
7591 /* Free allocated memory */
7592 for(i=0; i < 2; i++)
7594 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7595 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7596 pOleStreamData[i].pstrOleObjFileName = NULL;
7598 return hRes;
7601 /*************************************************************************
7602 * OleConvertIStorageToOLESTREAM [OLE32.@]
7604 * Read info on MSDN
7606 * Read info on MSDN
7608 * TODO
7609 * Still unsure of some mem fields for OLE 10 Stream
7610 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7611 * and "\001OLE" streams.
7614 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7615 LPSTORAGE pstg,
7616 LPOLESTREAM pOleStream)
7618 int i;
7619 HRESULT hRes = S_OK;
7620 IStream *pStream;
7621 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7622 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7624 TRACE("%p %p\n", pstg, pOleStream);
7626 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7628 if(pstg == NULL || pOleStream == NULL)
7630 hRes = E_INVALIDARG;
7632 if(hRes == S_OK)
7634 /* Get the ProgID */
7635 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7636 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7638 if(hRes == S_OK)
7640 /* Was it originally Ole10 */
7641 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7642 if(hRes == S_OK)
7644 IStream_Release(pStream);
7645 /* Get Presentation Data for Ole10Native */
7646 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7648 else
7650 /* Get Presentation Data (OLE20) */
7651 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7654 /* Save OLESTREAM */
7655 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7656 if(hRes == S_OK)
7658 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7663 /* Free allocated memory */
7664 for(i=0; i < 2; i++)
7666 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7669 return hRes;
7672 /***********************************************************************
7673 * GetConvertStg (OLE32.@)
7675 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7676 FIXME("unimplemented stub!\n");
7677 return E_FAIL;
7680 /******************************************************************************
7681 * StgIsStorageFile [OLE32.@]
7682 * Verify if the file contains a storage object
7684 * PARAMS
7685 * fn [ I] Filename
7687 * RETURNS
7688 * S_OK if file has magic bytes as a storage object
7689 * S_FALSE if file is not storage
7691 HRESULT WINAPI
7692 StgIsStorageFile(LPCOLESTR fn)
7694 HANDLE hf;
7695 BYTE magic[8];
7696 DWORD bytes_read;
7698 TRACE("%s\n", debugstr_w(fn));
7699 hf = CreateFileW(fn, GENERIC_READ,
7700 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7701 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7703 if (hf == INVALID_HANDLE_VALUE)
7704 return STG_E_FILENOTFOUND;
7706 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7708 WARN(" unable to read file\n");
7709 CloseHandle(hf);
7710 return S_FALSE;
7713 CloseHandle(hf);
7715 if (bytes_read != 8) {
7716 WARN(" too short\n");
7717 return S_FALSE;
7720 if (!memcmp(magic,STORAGE_magic,8)) {
7721 WARN(" -> YES\n");
7722 return S_OK;
7725 WARN(" -> Invalid header.\n");
7726 return S_FALSE;
7729 /***********************************************************************
7730 * WriteClassStm (OLE32.@)
7732 * Writes a CLSID to a stream.
7734 * PARAMS
7735 * pStm [I] Stream to write to.
7736 * rclsid [I] CLSID to write.
7738 * RETURNS
7739 * Success: S_OK.
7740 * Failure: HRESULT code.
7742 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7744 TRACE("(%p,%p)\n",pStm,rclsid);
7746 if (!pStm || !rclsid)
7747 return E_INVALIDARG;
7749 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7752 /***********************************************************************
7753 * ReadClassStm (OLE32.@)
7755 * Reads a CLSID from a stream.
7757 * PARAMS
7758 * pStm [I] Stream to read from.
7759 * rclsid [O] CLSID to read.
7761 * RETURNS
7762 * Success: S_OK.
7763 * Failure: HRESULT code.
7765 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7767 ULONG nbByte;
7768 HRESULT res;
7770 TRACE("(%p,%p)\n",pStm,pclsid);
7772 if (!pStm || !pclsid)
7773 return E_INVALIDARG;
7775 /* clear the output args */
7776 *pclsid = CLSID_NULL;
7778 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7780 if (FAILED(res))
7781 return res;
7783 if (nbByte != sizeof(CLSID))
7784 return STG_E_READFAULT;
7785 else
7786 return S_OK;