Don't import from ntdll.
[wine] / dlls / ole32 / storage32.c
1 /*
2  * Compound Storage (32 bit version)
3  * Storage implementation
4  *
5  * This file contains the compound file implementation
6  * of the storage interface.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Sylvain St-Germain
10  * Copyright 1999 Thuy Nguyen
11  * Copyright 2005 Mike McCormack
12  *
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.
17  *
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.
22  *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27
28 #include <assert.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #define COBJMACROS
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
37
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winnls.h"
41 #include "winuser.h"
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
44
45 #include "storage32.h"
46 #include "ole2.h"      /* For Write/ReadClassStm */
47
48 #include "winreg.h"
49 #include "wine/wingdi16.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(storage);
52
53 #define FILE_BEGIN 0
54
55 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
56 #define OLESTREAM_ID 0x501
57 #define OLESTREAM_MAX_STR_LEN 255
58
59 static const char rootPropertyName[] = "Root Entry";
60
61
62 /* OLESTREAM memory structure to use for Get and Put Routines */
63 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
64 typedef struct
65 {
66     DWORD dwOleID;
67     DWORD dwTypeID;
68     DWORD dwOleTypeNameLength;
69     CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
70     CHAR  *pstrOleObjFileName;
71     DWORD dwOleObjFileNameLength;
72     DWORD dwMetaFileWidth;
73     DWORD dwMetaFileHeight;
74     CHAR  strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
75     DWORD dwDataLength;
76     BYTE *pData;
77 }OLECONVERT_OLESTREAM_DATA;
78
79 /* CompObj Stream structure */
80 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
81 typedef struct
82 {
83     BYTE byUnknown1[12];
84     CLSID clsid;
85     DWORD dwCLSIDNameLength;
86     CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
87     DWORD dwOleTypeNameLength;
88     CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
89     DWORD dwProgIDNameLength;
90     CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
91     BYTE byUnknown2[16];
92 }OLECONVERT_ISTORAGE_COMPOBJ;
93
94
95 /* Ole Presention Stream structure */
96 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
97 typedef struct
98 {
99     BYTE byUnknown1[28];
100     DWORD dwExtentX;
101     DWORD dwExtentY;
102     DWORD dwSize;
103     BYTE *pData;
104 }OLECONVERT_ISTORAGE_OLEPRES;
105
106
107
108 /***********************************************************************
109  * Forward declaration of internal functions used by the method DestroyElement
110  */
111 static HRESULT deleteStorageProperty(
112   StorageImpl *parentStorage,
113   ULONG        foundPropertyIndexToDelete,
114   StgProperty  propertyToDelete);
115
116 static HRESULT deleteStreamProperty(
117   StorageImpl *parentStorage,
118   ULONG         foundPropertyIndexToDelete,
119   StgProperty   propertyToDelete);
120
121 static HRESULT findPlaceholder(
122   StorageImpl *storage,
123   ULONG         propertyIndexToStore,
124   ULONG         storagePropertyIndex,
125   INT         typeOfRelation);
126
127 static HRESULT adjustPropertyChain(
128   StorageImpl *This,
129   StgProperty   propertyToDelete,
130   StgProperty   parentProperty,
131   ULONG         parentPropertyId,
132   INT         typeOfRelation);
133
134 /***********************************************************************
135  * Declaration of the functions used to manipulate StgProperty
136  */
137
138 static ULONG getFreeProperty(
139   StorageImpl *storage);
140
141 static void updatePropertyChain(
142   StorageImpl *storage,
143   ULONG       newPropertyIndex,
144   StgProperty newProperty);
145
146 static LONG propertyNameCmp(
147     const OLECHAR *newProperty,
148     const OLECHAR *currentProperty);
149
150
151 /***********************************************************************
152  * Declaration of miscellaneous functions...
153  */
154 static HRESULT validateSTGM(DWORD stgmValue);
155
156 static DWORD GetShareModeFromSTGM(DWORD stgm);
157 static DWORD GetAccessModeFromSTGM(DWORD stgm);
158 static DWORD GetCreationModeFromSTGM(DWORD stgm);
159
160 extern IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
161
162
163
164 /************************************************************************
165 ** Storage32BaseImpl implementatiion
166 */
167
168 /************************************************************************
169  * Storage32BaseImpl_QueryInterface (IUnknown)
170  *
171  * This method implements the common QueryInterface for all IStorage32
172  * implementations contained in this file.
173  *
174  * See Windows documentation for more details on IUnknown methods.
175  */
176 HRESULT WINAPI StorageBaseImpl_QueryInterface(
177   IStorage*        iface,
178   REFIID             riid,
179   void**             ppvObject)
180 {
181   StorageBaseImpl *This = (StorageBaseImpl *)iface;
182   /*
183    * Perform a sanity check on the parameters.
184    */
185   if ( (This==0) || (ppvObject==0) )
186     return E_INVALIDARG;
187
188   /*
189    * Initialize the return parameter.
190    */
191   *ppvObject = 0;
192
193   /*
194    * Compare the riid with the interface IDs implemented by this object.
195    */
196   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
197   {
198     *ppvObject = (IStorage*)This;
199   }
200   else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
201   {
202     *ppvObject = (IStorage*)This;
203   }
204   else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
205   {
206     *ppvObject = (IStorage*)&This->pssVtbl;
207   }
208
209   /*
210    * Check that we obtained an interface.
211    */
212   if ((*ppvObject)==0)
213     return E_NOINTERFACE;
214
215   /*
216    * Query Interface always increases the reference count by one when it is
217    * successful
218    */
219   IStorage_AddRef(iface);
220
221   return S_OK;
222 }
223
224 /************************************************************************
225  * Storage32BaseImpl_AddRef (IUnknown)
226  *
227  * This method implements the common AddRef for all IStorage32
228  * implementations contained in this file.
229  *
230  * See Windows documentation for more details on IUnknown methods.
231  */
232 ULONG WINAPI StorageBaseImpl_AddRef(
233             IStorage* iface)
234 {
235   StorageBaseImpl *This = (StorageBaseImpl *)iface;
236   ULONG ref = InterlockedIncrement(&This->ref);
237
238   TRACE("(%p) AddRef to %ld\n", This, ref);
239
240   return ref;
241 }
242
243 /************************************************************************
244  * Storage32BaseImpl_Release (IUnknown)
245  *
246  * This method implements the common Release for all IStorage32
247  * implementations contained in this file.
248  *
249  * See Windows documentation for more details on IUnknown methods.
250  */
251 ULONG WINAPI StorageBaseImpl_Release(
252       IStorage* iface)
253 {
254   StorageBaseImpl *This = (StorageBaseImpl *)iface;
255   /*
256    * Decrease the reference count on this object.
257    */
258   ULONG ref = InterlockedDecrement(&This->ref);
259
260   TRACE("(%p) ReleaseRef to %ld\n", This, ref);
261
262   /*
263    * If the reference count goes down to 0, perform suicide.
264    */
265   if (ref == 0)
266   {
267     /*
268      * Since we are using a system of base-classes, we want to call the
269      * destructor of the appropriate derived class. To do this, we are
270      * using virtual functions to implement the destructor.
271      */
272     This->v_destructor(This);
273   }
274
275   return ref;
276 }
277
278 /************************************************************************
279  * Storage32BaseImpl_OpenStream (IStorage)
280  *
281  * This method will open the specified stream object from the current storage.
282  *
283  * See Windows documentation for more details on IStorage methods.
284  */
285 HRESULT WINAPI StorageBaseImpl_OpenStream(
286   IStorage*        iface,
287   const OLECHAR*   pwcsName,  /* [string][in] */
288   void*            reserved1, /* [unique][in] */
289   DWORD            grfMode,   /* [in]  */
290   DWORD            reserved2, /* [in]  */
291   IStream**        ppstm)     /* [out] */
292 {
293   StorageBaseImpl *This = (StorageBaseImpl *)iface;
294   IEnumSTATSTGImpl* propertyEnumeration;
295   StgStreamImpl*    newStream;
296   StgProperty       currentProperty;
297   ULONG             foundPropertyIndex;
298   HRESULT           res = STG_E_UNKNOWN;
299   DWORD             parent_grfMode;
300
301   TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
302         iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
303
304   /*
305    * Perform a sanity check on the parameters.
306    */
307   if ( (pwcsName==NULL) || (ppstm==0) )
308   {
309     res = E_INVALIDARG;
310     goto end;
311   }
312
313   /*
314    * Initialize the out parameter
315    */
316   *ppstm = NULL;
317
318   /*
319    * Validate the STGM flags
320    */
321   if ( FAILED( validateSTGM(grfMode) ))
322   {
323     res = STG_E_INVALIDFLAG;
324     goto end;
325   }
326
327   /*
328    * As documented.
329    */
330   if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
331         (grfMode & STGM_DELETEONRELEASE) ||
332         (grfMode & STGM_TRANSACTED) )
333   {
334     res = STG_E_INVALIDFUNCTION;
335     goto end;
336   }
337
338   /*
339    * Check that we're compatible with the parent's storage mode
340    */
341   parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
342   if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
343   {
344     res = STG_E_ACCESSDENIED;
345     goto end;
346   }
347
348   /*
349    * Create a property enumeration to search the properties
350    */
351   propertyEnumeration = IEnumSTATSTGImpl_Construct(
352     This->ancestorStorage,
353     This->rootPropertySetIndex);
354
355   /*
356    * Search the enumeration for the property with the given name
357    */
358   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
359     propertyEnumeration,
360     pwcsName,
361     &currentProperty);
362
363   /*
364    * Delete the property enumeration since we don't need it anymore
365    */
366   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
367
368   /*
369    * If it was found, construct the stream object and return a pointer to it.
370    */
371   if ( (foundPropertyIndex!=PROPERTY_NULL) &&
372        (currentProperty.propertyType==PROPTYPE_STREAM) )
373   {
374     newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
375
376     if (newStream!=0)
377     {
378       newStream->grfMode = grfMode;
379       *ppstm = (IStream*)newStream;
380
381       /*
382        * Since we are returning a pointer to the interface, we have to
383        * nail down the reference.
384        */
385       IStream_AddRef(*ppstm);
386
387       res = S_OK;
388       goto end;
389     }
390
391     res = E_OUTOFMEMORY;
392     goto end;
393   }
394
395   res = STG_E_FILENOTFOUND;
396
397 end:
398   if (res == S_OK)
399     TRACE("<-- IStream %p\n", *ppstm);
400   TRACE("<-- %08lx\n", res);
401   return res;
402 }
403
404 /************************************************************************
405  * Storage32BaseImpl_OpenStorage (IStorage)
406  *
407  * This method will open a new storage object from the current storage.
408  *
409  * See Windows documentation for more details on IStorage methods.
410  */
411 HRESULT WINAPI StorageBaseImpl_OpenStorage(
412   IStorage*        iface,
413   const OLECHAR*   pwcsName,      /* [string][unique][in] */
414   IStorage*        pstgPriority,  /* [unique][in] */
415   DWORD            grfMode,       /* [in] */
416   SNB              snbExclude,    /* [unique][in] */
417   DWORD            reserved,      /* [in] */
418   IStorage**       ppstg)         /* [out] */
419 {
420   StorageBaseImpl *This = (StorageBaseImpl *)iface;
421   StorageInternalImpl* newStorage;
422   IEnumSTATSTGImpl*      propertyEnumeration;
423   StgProperty            currentProperty;
424   ULONG                  foundPropertyIndex;
425   HRESULT                res = STG_E_UNKNOWN;
426   DWORD                  parent_grfMode;
427
428   TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
429         iface, debugstr_w(pwcsName), pstgPriority,
430         grfMode, snbExclude, reserved, ppstg);
431
432   /*
433    * Perform a sanity check on the parameters.
434    */
435   if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
436   {
437     res = E_INVALIDARG;
438     goto end;
439   }
440
441   /* as documented */
442   if (snbExclude != NULL)
443   {
444     res = STG_E_INVALIDPARAMETER;
445     goto end;
446   }
447
448   /*
449    * Validate the STGM flags
450    */
451   if ( FAILED( validateSTGM(grfMode) ))
452   {
453     res = STG_E_INVALIDFLAG;
454     goto end;
455   }
456
457   /*
458    * As documented.
459    */
460   if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
461         (grfMode & STGM_DELETEONRELEASE) ||
462         (grfMode & STGM_PRIORITY) )
463   {
464     res = STG_E_INVALIDFUNCTION;
465     goto end;
466   }
467
468   /*
469    * Check that we're compatible with the parent's storage mode
470    */
471   parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
472   if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
473   {
474     res = STG_E_ACCESSDENIED;
475     goto end;
476   }
477
478   /*
479    * Initialize the out parameter
480    */
481   *ppstg = NULL;
482
483   /*
484    * Create a property enumeration to search the properties
485    */
486   propertyEnumeration = IEnumSTATSTGImpl_Construct(
487                           This->ancestorStorage,
488                           This->rootPropertySetIndex);
489
490   /*
491    * Search the enumeration for the property with the given name
492    */
493   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
494                          propertyEnumeration,
495                          pwcsName,
496                          &currentProperty);
497
498   /*
499    * Delete the property enumeration since we don't need it anymore
500    */
501   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
502
503   /*
504    * If it was found, construct the stream object and return a pointer to it.
505    */
506   if ( (foundPropertyIndex!=PROPERTY_NULL) &&
507        (currentProperty.propertyType==PROPTYPE_STORAGE) )
508   {
509     /*
510      * Construct a new Storage object
511      */
512     newStorage = StorageInternalImpl_Construct(
513                    This->ancestorStorage,
514                    grfMode,
515                    foundPropertyIndex);
516
517     if (newStorage != 0)
518     {
519       *ppstg = (IStorage*)newStorage;
520
521       /*
522        * Since we are returning a pointer to the interface,
523        * we have to nail down the reference.
524        */
525       StorageBaseImpl_AddRef(*ppstg);
526
527       res = S_OK;
528       goto end;
529     }
530
531     res = STG_E_INSUFFICIENTMEMORY;
532     goto end;
533   }
534
535   res = STG_E_FILENOTFOUND;
536
537 end:
538   TRACE("<-- %08lx\n", res);
539   return res;
540 }
541
542 /************************************************************************
543  * Storage32BaseImpl_EnumElements (IStorage)
544  *
545  * This method will create an enumerator object that can be used to
546  * retrieve informatino about all the properties in the storage object.
547  *
548  * See Windows documentation for more details on IStorage methods.
549  */
550 HRESULT WINAPI StorageBaseImpl_EnumElements(
551   IStorage*       iface,
552   DWORD           reserved1, /* [in] */
553   void*           reserved2, /* [size_is][unique][in] */
554   DWORD           reserved3, /* [in] */
555   IEnumSTATSTG**  ppenum)    /* [out] */
556 {
557   StorageBaseImpl *This = (StorageBaseImpl *)iface;
558   IEnumSTATSTGImpl* newEnum;
559
560   TRACE("(%p, %ld, %p, %ld, %p)\n",
561         iface, reserved1, reserved2, reserved3, ppenum);
562
563   /*
564    * Perform a sanity check on the parameters.
565    */
566   if ( (This==0) || (ppenum==0))
567     return E_INVALIDARG;
568
569   /*
570    * Construct the enumerator.
571    */
572   newEnum = IEnumSTATSTGImpl_Construct(
573               This->ancestorStorage,
574               This->rootPropertySetIndex);
575
576   if (newEnum!=0)
577   {
578     *ppenum = (IEnumSTATSTG*)newEnum;
579
580     /*
581      * Don't forget to nail down a reference to the new object before
582      * returning it.
583      */
584     IEnumSTATSTG_AddRef(*ppenum);
585
586     return S_OK;
587   }
588
589   return E_OUTOFMEMORY;
590 }
591
592 /************************************************************************
593  * Storage32BaseImpl_Stat (IStorage)
594  *
595  * This method will retrieve information about this storage object.
596  *
597  * See Windows documentation for more details on IStorage methods.
598  */
599 HRESULT WINAPI StorageBaseImpl_Stat(
600   IStorage*        iface,
601   STATSTG*         pstatstg,     /* [out] */
602   DWORD            grfStatFlag)  /* [in] */
603 {
604   StorageBaseImpl *This = (StorageBaseImpl *)iface;
605   StgProperty    curProperty;
606   BOOL           readSuccessful;
607   HRESULT        res = STG_E_UNKNOWN;
608
609   TRACE("(%p, %p, %lx)\n",
610         iface, pstatstg, grfStatFlag);
611
612   /*
613    * Perform a sanity check on the parameters.
614    */
615   if ( (This==0) || (pstatstg==0))
616   {
617     res = E_INVALIDARG;
618     goto end;
619   }
620
621   /*
622    * Read the information from the property.
623    */
624   readSuccessful = StorageImpl_ReadProperty(
625                     This->ancestorStorage,
626                     This->rootPropertySetIndex,
627                     &curProperty);
628
629   if (readSuccessful)
630   {
631     StorageUtl_CopyPropertyToSTATSTG(
632       pstatstg,
633       &curProperty,
634       grfStatFlag);
635
636     res = S_OK;
637     goto end;
638   }
639
640   res = E_FAIL;
641
642 end:
643   if (res == S_OK)
644   {
645     TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
646   }
647   TRACE("<-- %08lx\n", res);
648   return res;
649 }
650
651 /************************************************************************
652  * Storage32BaseImpl_RenameElement (IStorage)
653  *
654  * This method will rename the specified element.
655  *
656  * See Windows documentation for more details on IStorage methods.
657  *
658  * Implementation notes: The method used to rename consists of creating a clone
659  *    of the deleted StgProperty object setting it with the new name and to
660  *    perform a DestroyElement of the old StgProperty.
661  */
662 HRESULT WINAPI StorageBaseImpl_RenameElement(
663             IStorage*        iface,
664             const OLECHAR*   pwcsOldName,  /* [in] */
665             const OLECHAR*   pwcsNewName)  /* [in] */
666 {
667   StorageBaseImpl *This = (StorageBaseImpl *)iface;
668   IEnumSTATSTGImpl* propertyEnumeration;
669   StgProperty       currentProperty;
670   ULONG             foundPropertyIndex;
671
672   TRACE("(%p, %s, %s)\n",
673         iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
674
675   /*
676    * Create a property enumeration to search the properties
677    */
678   propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
679                                                    This->rootPropertySetIndex);
680
681   /*
682    * Search the enumeration for the new property name
683    */
684   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
685                                                      pwcsNewName,
686                                                      &currentProperty);
687
688   if (foundPropertyIndex != PROPERTY_NULL)
689   {
690     /*
691      * There is already a property with the new name
692      */
693     IEnumSTATSTGImpl_Destroy(propertyEnumeration);
694     return STG_E_FILEALREADYEXISTS;
695   }
696
697   IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
698
699   /*
700    * Search the enumeration for the old property name
701    */
702   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
703                                                      pwcsOldName,
704                                                      &currentProperty);
705
706   /*
707    * Delete the property enumeration since we don't need it anymore
708    */
709   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
710
711   if (foundPropertyIndex != PROPERTY_NULL)
712   {
713     StgProperty renamedProperty;
714     ULONG       renamedPropertyIndex;
715
716     /*
717      * Setup a new property for the renamed property
718      */
719     renamedProperty.sizeOfNameString =
720       ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
721
722     if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
723       return STG_E_INVALIDNAME;
724
725     strcpyW(renamedProperty.name, pwcsNewName);
726
727     renamedProperty.propertyType  = currentProperty.propertyType;
728     renamedProperty.startingBlock = currentProperty.startingBlock;
729     renamedProperty.size.u.LowPart  = currentProperty.size.u.LowPart;
730     renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
731
732     renamedProperty.previousProperty = PROPERTY_NULL;
733     renamedProperty.nextProperty     = PROPERTY_NULL;
734
735     /*
736      * Bring the dirProperty link in case it is a storage and in which
737      * case the renamed storage elements don't require to be reorganized.
738      */
739     renamedProperty.dirProperty = currentProperty.dirProperty;
740
741     /* call CoFileTime to get the current time
742     renamedProperty.timeStampS1
743     renamedProperty.timeStampD1
744     renamedProperty.timeStampS2
745     renamedProperty.timeStampD2
746     renamedProperty.propertyUniqueID
747     */
748
749     /*
750      * Obtain a free property in the property chain
751      */
752     renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
753
754     /*
755      * Save the new property into the new property spot
756      */
757     StorageImpl_WriteProperty(
758       This->ancestorStorage,
759       renamedPropertyIndex,
760       &renamedProperty);
761
762     /*
763      * Find a spot in the property chain for our newly created property.
764      */
765     updatePropertyChain(
766       (StorageImpl*)This,
767       renamedPropertyIndex,
768       renamedProperty);
769
770     /*
771      * At this point the renamed property has been inserted in the tree,
772      * now, before to Destroy the old property we must zeroed it's dirProperty
773      * otherwise the DestroyProperty below will zap it all and we do not want
774      * this to happen.
775      * Also, we fake that the old property is a storage so the DestroyProperty
776      * will not do a SetSize(0) on the stream data.
777      *
778      * This means that we need to tweek the StgProperty if it is a stream or a
779      * non empty storage.
780      */
781     StorageImpl_ReadProperty(This->ancestorStorage,
782                              foundPropertyIndex,
783                              &currentProperty);
784
785     currentProperty.dirProperty  = PROPERTY_NULL;
786     currentProperty.propertyType = PROPTYPE_STORAGE;
787     StorageImpl_WriteProperty(
788       This->ancestorStorage,
789       foundPropertyIndex,
790       &currentProperty);
791
792     /*
793      * Invoke Destroy to get rid of the ole property and automatically redo
794      * the linking of it's previous and next members...
795      */
796     IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
797
798   }
799   else
800   {
801     /*
802      * There is no property with the old name
803      */
804     return STG_E_FILENOTFOUND;
805   }
806
807   return S_OK;
808 }
809
810 /************************************************************************
811  * Storage32BaseImpl_CreateStream (IStorage)
812  *
813  * This method will create a stream object within this storage
814  *
815  * See Windows documentation for more details on IStorage methods.
816  */
817 HRESULT WINAPI StorageBaseImpl_CreateStream(
818             IStorage*        iface,
819             const OLECHAR*   pwcsName,  /* [string][in] */
820             DWORD            grfMode,   /* [in] */
821             DWORD            reserved1, /* [in] */
822             DWORD            reserved2, /* [in] */
823             IStream**        ppstm)     /* [out] */
824 {
825   StorageBaseImpl *This = (StorageBaseImpl *)iface;
826   IEnumSTATSTGImpl* propertyEnumeration;
827   StgStreamImpl*    newStream;
828   StgProperty       currentProperty, newStreamProperty;
829   ULONG             foundPropertyIndex, newPropertyIndex;
830   DWORD             parent_grfMode;
831
832   TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
833         iface, debugstr_w(pwcsName), grfMode,
834         reserved1, reserved2, ppstm);
835
836   /*
837    * Validate parameters
838    */
839   if (ppstm == 0)
840     return STG_E_INVALIDPOINTER;
841
842   if (pwcsName == 0)
843     return STG_E_INVALIDNAME;
844
845   if (reserved1 || reserved2)
846     return STG_E_INVALIDPARAMETER;
847
848   /*
849    * Validate the STGM flags
850    */
851   if ( FAILED( validateSTGM(grfMode) ))
852     return STG_E_INVALIDFLAG;
853
854   if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) 
855     return STG_E_INVALIDFLAG;
856
857   /*
858    * As documented.
859    */
860   if ((grfMode & STGM_DELETEONRELEASE) ||
861       (grfMode & STGM_TRANSACTED))
862     return STG_E_INVALIDFUNCTION;
863
864   /*
865    * Check that we're compatible with the parent's storage mode
866    */
867   parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
868   if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
869     return STG_E_ACCESSDENIED;
870
871   /*
872    * Initialize the out parameter
873    */
874   *ppstm = 0;
875
876   /*
877    * Create a property enumeration to search the properties
878    */
879   propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
880                                                    This->rootPropertySetIndex);
881
882   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
883                                                      pwcsName,
884                                                      &currentProperty);
885
886   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
887
888   if (foundPropertyIndex != PROPERTY_NULL)
889   {
890     /*
891      * An element with this name already exists
892      */
893     if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
894     {
895       IStorage_DestroyElement(iface, pwcsName);
896     }
897     else
898       return STG_E_FILEALREADYEXISTS;
899   }
900
901   /*
902    * memset the empty property
903    */
904   memset(&newStreamProperty, 0, sizeof(StgProperty));
905
906   newStreamProperty.sizeOfNameString =
907       ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
908
909   if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
910     return STG_E_INVALIDNAME;
911
912   strcpyW(newStreamProperty.name, pwcsName);
913
914   newStreamProperty.propertyType  = PROPTYPE_STREAM;
915   newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
916   newStreamProperty.size.u.LowPart  = 0;
917   newStreamProperty.size.u.HighPart = 0;
918
919   newStreamProperty.previousProperty = PROPERTY_NULL;
920   newStreamProperty.nextProperty     = PROPERTY_NULL;
921   newStreamProperty.dirProperty      = PROPERTY_NULL;
922
923   /* call CoFileTime to get the current time
924   newStreamProperty.timeStampS1
925   newStreamProperty.timeStampD1
926   newStreamProperty.timeStampS2
927   newStreamProperty.timeStampD2
928   */
929
930   /*  newStreamProperty.propertyUniqueID */
931
932   /*
933    * Get a free property or create a new one
934    */
935   newPropertyIndex = getFreeProperty(This->ancestorStorage);
936
937   /*
938    * Save the new property into the new property spot
939    */
940   StorageImpl_WriteProperty(
941     This->ancestorStorage,
942     newPropertyIndex,
943     &newStreamProperty);
944
945   /*
946    * Find a spot in the property chain for our newly created property.
947    */
948   updatePropertyChain(
949     (StorageImpl*)This,
950     newPropertyIndex,
951     newStreamProperty);
952
953   /*
954    * Open the stream to return it.
955    */
956   newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
957
958   if (newStream != 0)
959   {
960     *ppstm = (IStream*)newStream;
961
962     /*
963      * Since we are returning a pointer to the interface, we have to nail down
964      * the reference.
965      */
966     IStream_AddRef(*ppstm);
967   }
968   else
969   {
970     return STG_E_INSUFFICIENTMEMORY;
971   }
972
973   return S_OK;
974 }
975
976 /************************************************************************
977  * Storage32BaseImpl_SetClass (IStorage)
978  *
979  * This method will write the specified CLSID in the property of this
980  * storage.
981  *
982  * See Windows documentation for more details on IStorage methods.
983  */
984 HRESULT WINAPI StorageBaseImpl_SetClass(
985   IStorage*        iface,
986   REFCLSID         clsid) /* [in] */
987 {
988   StorageBaseImpl *This = (StorageBaseImpl *)iface;
989   HRESULT hRes = E_FAIL;
990   StgProperty curProperty;
991   BOOL success;
992
993   TRACE("(%p, %p)\n", iface, clsid);
994
995   success = StorageImpl_ReadProperty(This->ancestorStorage,
996                                        This->rootPropertySetIndex,
997                                        &curProperty);
998   if (success)
999   {
1000     curProperty.propertyUniqueID = *clsid;
1001
1002     success =  StorageImpl_WriteProperty(This->ancestorStorage,
1003                                            This->rootPropertySetIndex,
1004                                            &curProperty);
1005     if (success)
1006       hRes = S_OK;
1007   }
1008
1009   return hRes;
1010 }
1011
1012 /************************************************************************
1013 ** Storage32Impl implementation
1014 */
1015
1016 /************************************************************************
1017  * Storage32Impl_CreateStorage (IStorage)
1018  *
1019  * This method will create the storage object within the provided storage.
1020  *
1021  * See Windows documentation for more details on IStorage methods.
1022  */
1023 HRESULT WINAPI StorageImpl_CreateStorage(
1024   IStorage*      iface,
1025   const OLECHAR  *pwcsName, /* [string][in] */
1026   DWORD            grfMode,   /* [in] */
1027   DWORD            reserved1, /* [in] */
1028   DWORD            reserved2, /* [in] */
1029   IStorage       **ppstg)   /* [out] */
1030 {
1031   StorageImpl* const This=(StorageImpl*)iface;
1032
1033   IEnumSTATSTGImpl *propertyEnumeration;
1034   StgProperty      currentProperty;
1035   StgProperty      newProperty;
1036   ULONG            foundPropertyIndex;
1037   ULONG            newPropertyIndex;
1038   HRESULT          hr;
1039   DWORD            parent_grfMode;
1040
1041   TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1042         iface, debugstr_w(pwcsName), grfMode,
1043         reserved1, reserved2, ppstg);
1044
1045   /*
1046    * Validate parameters
1047    */
1048   if (ppstg == 0)
1049     return STG_E_INVALIDPOINTER;
1050
1051   if (pwcsName == 0)
1052     return STG_E_INVALIDNAME;
1053
1054   /*
1055    * Validate the STGM flags
1056    */
1057   if ( FAILED( validateSTGM(grfMode) ) ||
1058        (grfMode & STGM_DELETEONRELEASE) )
1059     return STG_E_INVALIDFLAG;
1060
1061   /*
1062    * Check that we're compatible with the parent's storage mode
1063    */
1064   parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
1065   if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
1066     return STG_E_ACCESSDENIED;
1067
1068   /*
1069    * Initialize the out parameter
1070    */
1071   *ppstg = 0;
1072
1073   /*
1074    * Create a property enumeration and search the properties
1075    */
1076   propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1077                                                     This->base.rootPropertySetIndex);
1078
1079   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1080                                                      pwcsName,
1081                                                      &currentProperty);
1082   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1083
1084   if (foundPropertyIndex != PROPERTY_NULL)
1085   {
1086     /*
1087      * An element with this name already exists
1088      */
1089     if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1090       IStorage_DestroyElement(iface, pwcsName);
1091     else
1092       return STG_E_FILEALREADYEXISTS;
1093   }
1094
1095   /*
1096    * memset the empty property
1097    */
1098   memset(&newProperty, 0, sizeof(StgProperty));
1099
1100   newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1101
1102   if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1103     return STG_E_INVALIDNAME;
1104
1105   strcpyW(newProperty.name, pwcsName);
1106
1107   newProperty.propertyType  = PROPTYPE_STORAGE;
1108   newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1109   newProperty.size.u.LowPart  = 0;
1110   newProperty.size.u.HighPart = 0;
1111
1112   newProperty.previousProperty = PROPERTY_NULL;
1113   newProperty.nextProperty     = PROPERTY_NULL;
1114   newProperty.dirProperty      = PROPERTY_NULL;
1115
1116   /* call CoFileTime to get the current time
1117   newProperty.timeStampS1
1118   newProperty.timeStampD1
1119   newProperty.timeStampS2
1120   newProperty.timeStampD2
1121   */
1122
1123   /*  newStorageProperty.propertyUniqueID */
1124
1125   /*
1126    * Obtain a free property in the property chain
1127    */
1128   newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1129
1130   /*
1131    * Save the new property into the new property spot
1132    */
1133   StorageImpl_WriteProperty(
1134     This->base.ancestorStorage,
1135     newPropertyIndex,
1136     &newProperty);
1137
1138   /*
1139    * Find a spot in the property chain for our newly created property.
1140    */
1141   updatePropertyChain(
1142     This,
1143     newPropertyIndex,
1144     newProperty);
1145
1146   /*
1147    * Open it to get a pointer to return.
1148    */
1149   hr = IStorage_OpenStorage(
1150          iface,
1151          (const OLECHAR*)pwcsName,
1152          0,
1153          grfMode,
1154          0,
1155          0,
1156          ppstg);
1157
1158   if( (hr != S_OK) || (*ppstg == NULL))
1159   {
1160     return hr;
1161   }
1162
1163
1164   return S_OK;
1165 }
1166
1167
1168 /***************************************************************************
1169  *
1170  * Internal Method
1171  *
1172  * Get a free property or create a new one.
1173  */
1174 static ULONG getFreeProperty(
1175   StorageImpl *storage)
1176 {
1177   ULONG       currentPropertyIndex = 0;
1178   ULONG       newPropertyIndex     = PROPERTY_NULL;
1179   BOOL      readSuccessful        = TRUE;
1180   StgProperty currentProperty;
1181
1182   do
1183   {
1184     /*
1185      * Start by reading the root property
1186      */
1187     readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1188                                                currentPropertyIndex,
1189                                                &currentProperty);
1190     if (readSuccessful)
1191     {
1192       if (currentProperty.sizeOfNameString == 0)
1193       {
1194         /*
1195          * The property existis and is available, we found it.
1196          */
1197         newPropertyIndex = currentPropertyIndex;
1198       }
1199     }
1200     else
1201     {
1202       /*
1203        * We exhausted the property list, we will create more space below
1204        */
1205       newPropertyIndex = currentPropertyIndex;
1206     }
1207     currentPropertyIndex++;
1208
1209   } while (newPropertyIndex == PROPERTY_NULL);
1210
1211   /*
1212    * grow the property chain
1213    */
1214   if (! readSuccessful)
1215   {
1216     StgProperty    emptyProperty;
1217     ULARGE_INTEGER newSize;
1218     ULONG          propertyIndex;
1219     ULONG          lastProperty  = 0;
1220     ULONG          blockCount    = 0;
1221
1222     /*
1223      * obtain the new count of property blocks
1224      */
1225     blockCount = BlockChainStream_GetCount(
1226                    storage->base.ancestorStorage->rootBlockChain)+1;
1227
1228     /*
1229      * initialize the size used by the property stream
1230      */
1231     newSize.u.HighPart = 0;
1232     newSize.u.LowPart  = storage->bigBlockSize * blockCount;
1233
1234     /*
1235      * add a property block to the property chain
1236      */
1237     BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1238
1239     /*
1240      * memset the empty property in order to initialize the unused newly
1241      * created property
1242      */
1243     memset(&emptyProperty, 0, sizeof(StgProperty));
1244
1245     /*
1246      * initialize them
1247      */
1248     lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1249
1250     for(
1251       propertyIndex = newPropertyIndex;
1252       propertyIndex < lastProperty;
1253       propertyIndex++)
1254     {
1255       StorageImpl_WriteProperty(
1256         storage->base.ancestorStorage,
1257         propertyIndex,
1258         &emptyProperty);
1259     }
1260   }
1261
1262   return newPropertyIndex;
1263 }
1264
1265 /****************************************************************************
1266  *
1267  * Internal Method
1268  *
1269  * Case insensitive comparaison of StgProperty.name by first considering
1270  * their size.
1271  *
1272  * Returns <0 when newPrpoerty < currentProperty
1273  *         >0 when newPrpoerty > currentProperty
1274  *          0 when newPrpoerty == currentProperty
1275  */
1276 static LONG propertyNameCmp(
1277     const OLECHAR *newProperty,
1278     const OLECHAR *currentProperty)
1279 {
1280   LONG diff      = lstrlenW(newProperty) - lstrlenW(currentProperty);
1281
1282   if (diff == 0)
1283   {
1284     /*
1285      * We compare the string themselves only when they are of the same length
1286      */
1287     diff = lstrcmpiW( newProperty, currentProperty);
1288   }
1289
1290   return diff;
1291 }
1292
1293 /****************************************************************************
1294  *
1295  * Internal Method
1296  *
1297  * Properly link this new element in the property chain.
1298  */
1299 static void updatePropertyChain(
1300   StorageImpl *storage,
1301   ULONG         newPropertyIndex,
1302   StgProperty   newProperty)
1303 {
1304   StgProperty currentProperty;
1305
1306   /*
1307    * Read the root property
1308    */
1309   StorageImpl_ReadProperty(storage->base.ancestorStorage,
1310                              storage->base.rootPropertySetIndex,
1311                              &currentProperty);
1312
1313   if (currentProperty.dirProperty != PROPERTY_NULL)
1314   {
1315     /*
1316      * The root storage contains some element, therefore, start the research
1317      * for the appropriate location.
1318      */
1319     BOOL found = 0;
1320     ULONG  current, next, previous, currentPropertyId;
1321
1322     /*
1323      * Keep the StgProperty sequence number of the storage first property
1324      */
1325     currentPropertyId = currentProperty.dirProperty;
1326
1327     /*
1328      * Read
1329      */
1330     StorageImpl_ReadProperty(storage->base.ancestorStorage,
1331                                currentProperty.dirProperty,
1332                                &currentProperty);
1333
1334     previous = currentProperty.previousProperty;
1335     next     = currentProperty.nextProperty;
1336     current  = currentPropertyId;
1337
1338     while (found == 0)
1339     {
1340       LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1341
1342       if (diff < 0)
1343       {
1344         if (previous != PROPERTY_NULL)
1345         {
1346           StorageImpl_ReadProperty(storage->base.ancestorStorage,
1347                                      previous,
1348                                      &currentProperty);
1349           current = previous;
1350         }
1351         else
1352         {
1353           currentProperty.previousProperty = newPropertyIndex;
1354           StorageImpl_WriteProperty(storage->base.ancestorStorage,
1355                                       current,
1356                                       &currentProperty);
1357           found = 1;
1358         }
1359       }
1360       else if (diff > 0)
1361       {
1362         if (next != PROPERTY_NULL)
1363         {
1364           StorageImpl_ReadProperty(storage->base.ancestorStorage,
1365                                      next,
1366                                      &currentProperty);
1367           current = next;
1368         }
1369         else
1370         {
1371           currentProperty.nextProperty = newPropertyIndex;
1372           StorageImpl_WriteProperty(storage->base.ancestorStorage,
1373                                       current,
1374                                       &currentProperty);
1375           found = 1;
1376         }
1377       }
1378       else
1379       {
1380         /*
1381          * Trying to insert an item with the same name in the
1382          * subtree structure.
1383          */
1384         assert(FALSE);
1385       }
1386
1387       previous = currentProperty.previousProperty;
1388       next     = currentProperty.nextProperty;
1389     }
1390   }
1391   else
1392   {
1393     /*
1394      * The root storage is empty, link the new property to it's dir property
1395      */
1396     currentProperty.dirProperty = newPropertyIndex;
1397     StorageImpl_WriteProperty(storage->base.ancestorStorage,
1398                                 storage->base.rootPropertySetIndex,
1399                                 &currentProperty);
1400   }
1401 }
1402
1403
1404 /*************************************************************************
1405  * CopyTo (IStorage)
1406  */
1407 HRESULT WINAPI StorageImpl_CopyTo(
1408   IStorage*   iface,
1409   DWORD       ciidExclude,  /* [in] */
1410   const IID*  rgiidExclude, /* [size_is][unique][in] */
1411   SNB         snbExclude,   /* [unique][in] */
1412   IStorage*   pstgDest)     /* [unique][in] */
1413 {
1414   IEnumSTATSTG *elements     = 0;
1415   STATSTG      curElement, strStat;
1416   HRESULT      hr;
1417   IStorage     *pstgTmp, *pstgChild;
1418   IStream      *pstrTmp, *pstrChild;
1419
1420   if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1421     FIXME("Exclude option not implemented\n");
1422
1423   TRACE("(%p, %ld, %p, %p, %p)\n",
1424         iface, ciidExclude, rgiidExclude,
1425         snbExclude, pstgDest);
1426
1427   /*
1428    * Perform a sanity check
1429    */
1430   if ( pstgDest == 0 )
1431     return STG_E_INVALIDPOINTER;
1432
1433   /*
1434    * Enumerate the elements
1435    */
1436   hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1437
1438   if ( hr != S_OK )
1439     return hr;
1440
1441   /*
1442    * set the class ID
1443    */
1444   IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1445   IStorage_SetClass( pstgDest, &curElement.clsid );
1446
1447   do
1448   {
1449     /*
1450      * Obtain the next element
1451      */
1452     hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1453
1454     if ( hr == S_FALSE )
1455     {
1456       hr = S_OK;   /* done, every element has been copied */
1457       break;
1458     }
1459
1460     if (curElement.type == STGTY_STORAGE)
1461     {
1462       /*
1463        * open child source storage
1464        */
1465       hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1466                                  STGM_READ|STGM_SHARE_EXCLUSIVE,
1467                                  NULL, 0, &pstgChild );
1468
1469       if (hr != S_OK)
1470         break;
1471
1472       /*
1473        * Check if destination storage is not a child of the source
1474        * storage, which will cause an infinite loop
1475        */
1476       if (pstgChild == pstgDest)
1477       {
1478         IEnumSTATSTG_Release(elements);
1479
1480         return STG_E_ACCESSDENIED;
1481       }
1482
1483       /*
1484        * create a new storage in destination storage
1485        */
1486       hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1487                                    STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1488                                    0, 0,
1489                                    &pstgTmp );
1490       /*
1491        * if it already exist, don't create a new one use this one
1492        */
1493       if (hr == STG_E_FILEALREADYEXISTS)
1494       {
1495         hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1496                                    STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1497                                    NULL, 0, &pstgTmp );
1498       }
1499
1500       if (hr != S_OK)
1501         break;
1502
1503
1504       /*
1505        * do the copy recursively
1506        */
1507       hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1508                                snbExclude, pstgTmp );
1509
1510       IStorage_Release( pstgTmp );
1511       IStorage_Release( pstgChild );
1512     }
1513     else if (curElement.type == STGTY_STREAM)
1514     {
1515       /*
1516        * create a new stream in destination storage. If the stream already
1517        * exist, it will be deleted and a new one will be created.
1518        */
1519       hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1520                                   STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1521                                   0, 0, &pstrTmp );
1522
1523       if (hr != S_OK)
1524         break;
1525
1526       /*
1527        * open child stream storage
1528        */
1529       hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1530                                 STGM_READ|STGM_SHARE_EXCLUSIVE,
1531                                 0, &pstrChild );
1532
1533       if (hr != S_OK)
1534         break;
1535
1536       /*
1537        * Get the size of the source stream
1538        */
1539       IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1540
1541       /*
1542        * Set the size of the destination stream.
1543        */
1544       IStream_SetSize(pstrTmp, strStat.cbSize);
1545
1546       /*
1547        * do the copy
1548        */
1549       hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1550                            NULL, NULL );
1551
1552       IStream_Release( pstrTmp );
1553       IStream_Release( pstrChild );
1554     }
1555     else
1556     {
1557       WARN("unknown element type: %ld\n", curElement.type);
1558     }
1559
1560   } while (hr == S_OK);
1561
1562   /*
1563    * Clean-up
1564    */
1565   IEnumSTATSTG_Release(elements);
1566
1567   return hr;
1568 }
1569
1570 /*************************************************************************
1571  * MoveElementTo (IStorage)
1572  */
1573 HRESULT WINAPI StorageImpl_MoveElementTo(
1574   IStorage*     iface,
1575   const OLECHAR *pwcsName,   /* [string][in] */
1576   IStorage      *pstgDest,   /* [unique][in] */
1577   const OLECHAR *pwcsNewName,/* [string][in] */
1578   DWORD           grfFlags)    /* [in] */
1579 {
1580   FIXME("not implemented!\n");
1581   return E_NOTIMPL;
1582 }
1583
1584 /*************************************************************************
1585  * Commit (IStorage)
1586  */
1587 HRESULT WINAPI StorageImpl_Commit(
1588   IStorage*   iface,
1589   DWORD         grfCommitFlags)/* [in] */
1590 {
1591   FIXME("(%ld): stub!\n", grfCommitFlags);
1592   return S_OK;
1593 }
1594
1595 /*************************************************************************
1596  * Revert (IStorage)
1597  */
1598 HRESULT WINAPI StorageImpl_Revert(
1599   IStorage* iface)
1600 {
1601   FIXME("not implemented!\n");
1602   return E_NOTIMPL;
1603 }
1604
1605 /*************************************************************************
1606  * DestroyElement (IStorage)
1607  *
1608  * Stategy: This implementation is build this way for simplicity not for speed.
1609  *          I always delete the top most element of the enumeration and adjust
1610  *          the deleted element pointer all the time.  This takes longer to
1611  *          do but allow to reinvoke DestroyElement whenever we encounter a
1612  *          storage object.  The optimisation reside in the usage of another
1613  *          enumeration stategy that would give all the leaves of a storage
1614  *          first. (postfix order)
1615  */
1616 HRESULT WINAPI StorageImpl_DestroyElement(
1617   IStorage*     iface,
1618   const OLECHAR *pwcsName)/* [string][in] */
1619 {
1620   StorageImpl* const This=(StorageImpl*)iface;
1621
1622   IEnumSTATSTGImpl* propertyEnumeration;
1623   HRESULT           hr = S_OK;
1624   BOOL            res;
1625   StgProperty       propertyToDelete;
1626   StgProperty       parentProperty;
1627   ULONG             foundPropertyIndexToDelete;
1628   ULONG             typeOfRelation;
1629   ULONG             parentPropertyId;
1630
1631   TRACE("(%p, %s)\n",
1632         iface, debugstr_w(pwcsName));
1633
1634   /*
1635    * Perform a sanity check on the parameters.
1636    */
1637   if (pwcsName==NULL)
1638     return STG_E_INVALIDPOINTER;
1639
1640   /*
1641    * Create a property enumeration to search the property with the given name
1642    */
1643   propertyEnumeration = IEnumSTATSTGImpl_Construct(
1644     This->base.ancestorStorage,
1645     This->base.rootPropertySetIndex);
1646
1647   foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1648     propertyEnumeration,
1649     pwcsName,
1650     &propertyToDelete);
1651
1652   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1653
1654   if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1655   {
1656     return STG_E_FILENOTFOUND;
1657   }
1658
1659   /*
1660    * Find the parent property of the property to delete (the one that
1661    * link to it).  If This->dirProperty == foundPropertyIndexToDelete,
1662    * the parent is This. Otherwise, the parent is one of it's sibling...
1663    */
1664
1665   /*
1666    * First, read This's StgProperty..
1667    */
1668   res = StorageImpl_ReadProperty(
1669           This->base.ancestorStorage,
1670           This->base.rootPropertySetIndex,
1671           &parentProperty);
1672
1673   assert(res);
1674
1675   /*
1676    * Second, check to see if by any chance the actual storage (This) is not
1677    * the parent of the property to delete... We never know...
1678    */
1679   if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1680   {
1681     /*
1682      * Set data as it would have been done in the else part...
1683      */
1684     typeOfRelation   = PROPERTY_RELATION_DIR;
1685     parentPropertyId = This->base.rootPropertySetIndex;
1686   }
1687   else
1688   {
1689     /*
1690      * Create a property enumeration to search the parent properties, and
1691      * delete it once done.
1692      */
1693     IEnumSTATSTGImpl* propertyEnumeration2;
1694
1695     propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1696       This->base.ancestorStorage,
1697       This->base.rootPropertySetIndex);
1698
1699     typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1700       propertyEnumeration2,
1701       foundPropertyIndexToDelete,
1702       &parentProperty,
1703       &parentPropertyId);
1704
1705     IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1706   }
1707
1708   if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1709   {
1710     hr = deleteStorageProperty(
1711            This,
1712            foundPropertyIndexToDelete,
1713            propertyToDelete);
1714   }
1715   else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1716   {
1717     hr = deleteStreamProperty(
1718            This,
1719            foundPropertyIndexToDelete,
1720            propertyToDelete);
1721   }
1722
1723   if (hr!=S_OK)
1724     return hr;
1725
1726   /*
1727    * Adjust the property chain
1728    */
1729   hr = adjustPropertyChain(
1730         This,
1731         propertyToDelete,
1732         parentProperty,
1733         parentPropertyId,
1734         typeOfRelation);
1735
1736   return hr;
1737 }
1738
1739
1740 /************************************************************************
1741  * StorageImpl_Stat (IStorage)
1742  *
1743  * This method will retrieve information about this storage object.
1744  *
1745  * See Windows documentation for more details on IStorage methods.
1746  */
1747 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1748                                  STATSTG*  pstatstg,     /* [out] */
1749                                  DWORD     grfStatFlag)  /* [in] */
1750 {
1751   StorageImpl* const This = (StorageImpl*)iface;
1752   HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1753
1754   if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1755   {
1756       CoTaskMemFree(pstatstg->pwcsName);
1757       pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1758       strcpyW(pstatstg->pwcsName, This->pwcsName);
1759   }
1760
1761   return result;
1762 }
1763
1764
1765
1766 /*********************************************************************
1767  *
1768  * Internal Method
1769  *
1770  * Perform the deletion of a complete storage node
1771  *
1772  */
1773 static HRESULT deleteStorageProperty(
1774   StorageImpl *parentStorage,
1775   ULONG        indexOfPropertyToDelete,
1776   StgProperty  propertyToDelete)
1777 {
1778   IEnumSTATSTG *elements     = 0;
1779   IStorage   *childStorage = 0;
1780   STATSTG      currentElement;
1781   HRESULT      hr;
1782   HRESULT      destroyHr = S_OK;
1783
1784   /*
1785    * Open the storage and enumerate it
1786    */
1787   hr = StorageBaseImpl_OpenStorage(
1788         (IStorage*)parentStorage,
1789         propertyToDelete.name,
1790         0,
1791         STGM_SHARE_EXCLUSIVE,
1792         0,
1793         0,
1794         &childStorage);
1795
1796   if (hr != S_OK)
1797   {
1798     return hr;
1799   }
1800
1801   /*
1802    * Enumerate the elements
1803    */
1804   IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1805
1806   do
1807   {
1808     /*
1809      * Obtain the next element
1810      */
1811     hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1812     if (hr==S_OK)
1813     {
1814       destroyHr = StorageImpl_DestroyElement(
1815                     (IStorage*)childStorage,
1816                     (OLECHAR*)currentElement.pwcsName);
1817
1818       CoTaskMemFree(currentElement.pwcsName);
1819     }
1820
1821     /*
1822      * We need to Reset the enumeration every time because we delete elements
1823      * and the enumeration could be invalid
1824      */
1825     IEnumSTATSTG_Reset(elements);
1826
1827   } while ((hr == S_OK) && (destroyHr == S_OK));
1828
1829   /*
1830    * Invalidate the property by zeroing it's name member.
1831    */
1832   propertyToDelete.sizeOfNameString = 0;
1833
1834   StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1835                             indexOfPropertyToDelete,
1836                             &propertyToDelete);
1837
1838   IStorage_Release(childStorage);
1839   IEnumSTATSTG_Release(elements);
1840
1841   return destroyHr;
1842 }
1843
1844 /*********************************************************************
1845  *
1846  * Internal Method
1847  *
1848  * Perform the deletion of a stream node
1849  *
1850  */
1851 static HRESULT deleteStreamProperty(
1852   StorageImpl *parentStorage,
1853   ULONG         indexOfPropertyToDelete,
1854   StgProperty   propertyToDelete)
1855 {
1856   IStream      *pis;
1857   HRESULT        hr;
1858   ULARGE_INTEGER size;
1859
1860   size.u.HighPart = 0;
1861   size.u.LowPart = 0;
1862
1863   hr = StorageBaseImpl_OpenStream(
1864          (IStorage*)parentStorage,
1865          (OLECHAR*)propertyToDelete.name,
1866          NULL,
1867          STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1868          0,
1869          &pis);
1870
1871   if (hr!=S_OK)
1872   {
1873     return(hr);
1874   }
1875
1876   /*
1877    * Zap the stream
1878    */
1879   hr = IStream_SetSize(pis, size);
1880
1881   if(hr != S_OK)
1882   {
1883     return hr;
1884   }
1885
1886   /*
1887    * Release the stream object.
1888    */
1889   IStream_Release(pis);
1890
1891   /*
1892    * Invalidate the property by zeroing it's name member.
1893    */
1894   propertyToDelete.sizeOfNameString = 0;
1895
1896   /*
1897    * Here we should re-read the property so we get the updated pointer
1898    * but since we are here to zap it, I don't do it...
1899    */
1900   StorageImpl_WriteProperty(
1901     parentStorage->base.ancestorStorage,
1902     indexOfPropertyToDelete,
1903     &propertyToDelete);
1904
1905   return S_OK;
1906 }
1907
1908 /*********************************************************************
1909  *
1910  * Internal Method
1911  *
1912  * Finds a placeholder for the StgProperty within the Storage
1913  *
1914  */
1915 static HRESULT findPlaceholder(
1916   StorageImpl *storage,
1917   ULONG         propertyIndexToStore,
1918   ULONG         storePropertyIndex,
1919   INT         typeOfRelation)
1920 {
1921   StgProperty storeProperty;
1922   HRESULT     hr = S_OK;
1923   BOOL      res = TRUE;
1924
1925   /*
1926    * Read the storage property
1927    */
1928   res = StorageImpl_ReadProperty(
1929           storage->base.ancestorStorage,
1930           storePropertyIndex,
1931           &storeProperty);
1932
1933   if(! res)
1934   {
1935     return E_FAIL;
1936   }
1937
1938   if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1939   {
1940     if (storeProperty.previousProperty != PROPERTY_NULL)
1941     {
1942       return findPlaceholder(
1943                storage,
1944                propertyIndexToStore,
1945                storeProperty.previousProperty,
1946                typeOfRelation);
1947     }
1948     else
1949     {
1950       storeProperty.previousProperty = propertyIndexToStore;
1951     }
1952   }
1953   else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1954   {
1955     if (storeProperty.nextProperty != PROPERTY_NULL)
1956     {
1957       return findPlaceholder(
1958                storage,
1959                propertyIndexToStore,
1960                storeProperty.nextProperty,
1961                typeOfRelation);
1962     }
1963     else
1964     {
1965       storeProperty.nextProperty = propertyIndexToStore;
1966     }
1967   }
1968   else if (typeOfRelation == PROPERTY_RELATION_DIR)
1969   {
1970     if (storeProperty.dirProperty != PROPERTY_NULL)
1971     {
1972       return findPlaceholder(
1973                storage,
1974                propertyIndexToStore,
1975                storeProperty.dirProperty,
1976                typeOfRelation);
1977     }
1978     else
1979     {
1980       storeProperty.dirProperty = propertyIndexToStore;
1981     }
1982   }
1983
1984   hr = StorageImpl_WriteProperty(
1985          storage->base.ancestorStorage,
1986          storePropertyIndex,
1987          &storeProperty);
1988
1989   if(! hr)
1990   {
1991     return E_FAIL;
1992   }
1993
1994   return S_OK;
1995 }
1996
1997 /*************************************************************************
1998  *
1999  * Internal Method
2000  *
2001  * This method takes the previous and the next property link of a property
2002  * to be deleted and find them a place in the Storage.
2003  */
2004 static HRESULT adjustPropertyChain(
2005   StorageImpl *This,
2006   StgProperty   propertyToDelete,
2007   StgProperty   parentProperty,
2008   ULONG         parentPropertyId,
2009   INT         typeOfRelation)
2010 {
2011   ULONG   newLinkProperty        = PROPERTY_NULL;
2012   BOOL  needToFindAPlaceholder = FALSE;
2013   ULONG   storeNode              = PROPERTY_NULL;
2014   ULONG   toStoreNode            = PROPERTY_NULL;
2015   INT   relationType           = 0;
2016   HRESULT hr                     = S_OK;
2017   BOOL  res                    = TRUE;
2018
2019   if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2020   {
2021     if (propertyToDelete.previousProperty != PROPERTY_NULL)
2022     {
2023       /*
2024        * Set the parent previous to the property to delete previous
2025        */
2026       newLinkProperty = propertyToDelete.previousProperty;
2027
2028       if (propertyToDelete.nextProperty != PROPERTY_NULL)
2029       {
2030         /*
2031          * We also need to find a storage for the other link, setup variables
2032          * to do this at the end...
2033          */
2034         needToFindAPlaceholder = TRUE;
2035         storeNode              = propertyToDelete.previousProperty;
2036         toStoreNode            = propertyToDelete.nextProperty;
2037         relationType           = PROPERTY_RELATION_NEXT;
2038       }
2039     }
2040     else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2041     {
2042       /*
2043        * Set the parent previous to the property to delete next
2044        */
2045       newLinkProperty = propertyToDelete.nextProperty;
2046     }
2047
2048     /*
2049      * Link it for real...
2050      */
2051     parentProperty.previousProperty = newLinkProperty;
2052
2053   }
2054   else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2055   {
2056     if (propertyToDelete.previousProperty != PROPERTY_NULL)
2057     {
2058       /*
2059        * Set the parent next to the property to delete next previous
2060        */
2061       newLinkProperty = propertyToDelete.previousProperty;
2062
2063       if (propertyToDelete.nextProperty != PROPERTY_NULL)
2064       {
2065         /*
2066          * We also need to find a storage for the other link, setup variables
2067          * to do this at the end...
2068          */
2069         needToFindAPlaceholder = TRUE;
2070         storeNode              = propertyToDelete.previousProperty;
2071         toStoreNode            = propertyToDelete.nextProperty;
2072         relationType           = PROPERTY_RELATION_NEXT;
2073       }
2074     }
2075     else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2076     {
2077       /*
2078        * Set the parent next to the property to delete next
2079        */
2080       newLinkProperty = propertyToDelete.nextProperty;
2081     }
2082
2083     /*
2084      * Link it for real...
2085      */
2086     parentProperty.nextProperty = newLinkProperty;
2087   }
2088   else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2089   {
2090     if (propertyToDelete.previousProperty != PROPERTY_NULL)
2091     {
2092       /*
2093        * Set the parent dir to the property to delete previous
2094        */
2095       newLinkProperty = propertyToDelete.previousProperty;
2096
2097       if (propertyToDelete.nextProperty != PROPERTY_NULL)
2098       {
2099         /*
2100          * We also need to find a storage for the other link, setup variables
2101          * to do this at the end...
2102          */
2103         needToFindAPlaceholder = TRUE;
2104         storeNode              = propertyToDelete.previousProperty;
2105         toStoreNode            = propertyToDelete.nextProperty;
2106         relationType           = PROPERTY_RELATION_NEXT;
2107       }
2108     }
2109     else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2110     {
2111       /*
2112        * Set the parent dir to the property to delete next
2113        */
2114       newLinkProperty = propertyToDelete.nextProperty;
2115     }
2116
2117     /*
2118      * Link it for real...
2119      */
2120     parentProperty.dirProperty = newLinkProperty;
2121   }
2122
2123   /*
2124    * Write back the parent property
2125    */
2126   res = StorageImpl_WriteProperty(
2127           This->base.ancestorStorage,
2128           parentPropertyId,
2129           &parentProperty);
2130   if(! res)
2131   {
2132     return E_FAIL;
2133   }
2134
2135   /*
2136    * If a placeholder is required for the other link, then, find one and
2137    * get out of here...
2138    */
2139   if (needToFindAPlaceholder)
2140   {
2141     hr = findPlaceholder(
2142            This,
2143            toStoreNode,
2144            storeNode,
2145            relationType);
2146   }
2147
2148   return hr;
2149 }
2150
2151
2152 /******************************************************************************
2153  * SetElementTimes (IStorage)
2154  */
2155 HRESULT WINAPI StorageImpl_SetElementTimes(
2156   IStorage*     iface,
2157   const OLECHAR *pwcsName,/* [string][in] */
2158   const FILETIME  *pctime,  /* [in] */
2159   const FILETIME  *patime,  /* [in] */
2160   const FILETIME  *pmtime)  /* [in] */
2161 {
2162   FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2163   return S_OK;
2164 }
2165
2166 /******************************************************************************
2167  * SetStateBits (IStorage)
2168  */
2169 HRESULT WINAPI StorageImpl_SetStateBits(
2170   IStorage*   iface,
2171   DWORD         grfStateBits,/* [in] */
2172   DWORD         grfMask)     /* [in] */
2173 {
2174   FIXME("not implemented!\n");
2175   return E_NOTIMPL;
2176 }
2177
2178 /*
2179  * Virtual function table for the IStorage32Impl class.
2180  */
2181 static IStorageVtbl Storage32Impl_Vtbl =
2182 {
2183     StorageBaseImpl_QueryInterface,
2184     StorageBaseImpl_AddRef,
2185     StorageBaseImpl_Release,
2186     StorageBaseImpl_CreateStream,
2187     StorageBaseImpl_OpenStream,
2188     StorageImpl_CreateStorage,
2189     StorageBaseImpl_OpenStorage,
2190     StorageImpl_CopyTo,
2191     StorageImpl_MoveElementTo,
2192     StorageImpl_Commit,
2193     StorageImpl_Revert,
2194     StorageBaseImpl_EnumElements,
2195     StorageImpl_DestroyElement,
2196     StorageBaseImpl_RenameElement,
2197     StorageImpl_SetElementTimes,
2198     StorageBaseImpl_SetClass,
2199     StorageImpl_SetStateBits,
2200     StorageImpl_Stat
2201 };
2202
2203 HRESULT StorageImpl_Construct(
2204   StorageImpl* This,
2205   HANDLE       hFile,
2206   LPCOLESTR    pwcsName,
2207   ILockBytes*  pLkbyt,
2208   DWORD        openFlags,
2209   BOOL         fileBased,
2210   BOOL         fileCreate)
2211 {
2212   HRESULT     hr = S_OK;
2213   StgProperty currentProperty;
2214   BOOL      readSuccessful;
2215   ULONG       currentPropertyIndex;
2216
2217   if ( FAILED( validateSTGM(openFlags) ))
2218     return STG_E_INVALIDFLAG;
2219
2220   memset(This, 0, sizeof(StorageImpl));
2221
2222   /*
2223    * Initialize the virtual function table.
2224    */
2225   This->base.lpVtbl = &Storage32Impl_Vtbl;
2226   This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2227   This->base.v_destructor = &StorageImpl_Destroy;
2228   This->base.openFlags = openFlags;
2229
2230   /*
2231    * This is the top-level storage so initialize the ancestor pointer
2232    * to this.
2233    */
2234   This->base.ancestorStorage = This;
2235
2236   /*
2237    * Initialize the physical support of the storage.
2238    */
2239   This->hFile = hFile;
2240
2241   /*
2242    * Store copy of file path.
2243    */
2244   if(pwcsName) {
2245       This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2246                                 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2247       if (!This->pwcsName)
2248          return STG_E_INSUFFICIENTMEMORY;
2249       strcpyW(This->pwcsName, pwcsName);
2250   }
2251
2252   /*
2253    * Initialize the big block cache.
2254    */
2255   This->bigBlockSize   = DEF_BIG_BLOCK_SIZE;
2256   This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2257   This->bigBlockFile   = BIGBLOCKFILE_Construct(hFile,
2258                                                 pLkbyt,
2259                                                 openFlags,
2260                                                 This->bigBlockSize,
2261                                                 fileBased);
2262
2263   if (This->bigBlockFile == 0)
2264     return E_FAIL;
2265
2266   if (fileCreate)
2267   {
2268     ULARGE_INTEGER size;
2269     BYTE* bigBlockBuffer;
2270
2271     /*
2272      * Initialize all header variables:
2273      * - The big block depot consists of one block and it is at block 0
2274      * - The properties start at block 1
2275      * - There is no small block depot
2276      */
2277     memset( This->bigBlockDepotStart,
2278             BLOCK_UNUSED,
2279             sizeof(This->bigBlockDepotStart));
2280
2281     This->bigBlockDepotCount    = 1;
2282     This->bigBlockDepotStart[0] = 0;
2283     This->rootStartBlock        = 1;
2284     This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
2285     This->bigBlockSizeBits      = DEF_BIG_BLOCK_SIZE_BITS;
2286     This->smallBlockSizeBits    = DEF_SMALL_BLOCK_SIZE_BITS;
2287     This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2288     This->extBigBlockDepotCount = 0;
2289
2290     StorageImpl_SaveFileHeader(This);
2291
2292     /*
2293      * Add one block for the big block depot and one block for the properties
2294      */
2295     size.u.HighPart = 0;
2296     size.u.LowPart  = This->bigBlockSize * 3;
2297     BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2298
2299     /*
2300      * Initialize the big block depot
2301      */
2302     bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2303     memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2304     StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2305     StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2306     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2307   }
2308   else
2309   {
2310     /*
2311      * Load the header for the file.
2312      */
2313     hr = StorageImpl_LoadFileHeader(This);
2314
2315     if (FAILED(hr))
2316     {
2317       BIGBLOCKFILE_Destructor(This->bigBlockFile);
2318
2319       return hr;
2320     }
2321   }
2322
2323   /*
2324    * There is no block depot cached yet.
2325    */
2326   This->indexBlockDepotCached = 0xFFFFFFFF;
2327
2328   /*
2329    * Start searching for free blocks with block 0.
2330    */
2331   This->prevFreeBlock = 0;
2332
2333   /*
2334    * Create the block chain abstractions.
2335    */
2336   if(!(This->rootBlockChain =
2337        BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2338     return STG_E_READFAULT;
2339
2340   if(!(This->smallBlockDepotChain =
2341        BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2342                                   PROPERTY_NULL)))
2343     return STG_E_READFAULT;
2344
2345   /*
2346    * Write the root property
2347    */
2348   if (fileCreate)
2349   {
2350     StgProperty rootProp;
2351     /*
2352      * Initialize the property chain
2353      */
2354     memset(&rootProp, 0, sizeof(rootProp));
2355     MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2356                          sizeof(rootProp.name)/sizeof(WCHAR) );
2357     rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2358     rootProp.propertyType     = PROPTYPE_ROOT;
2359     rootProp.previousProperty = PROPERTY_NULL;
2360     rootProp.nextProperty     = PROPERTY_NULL;
2361     rootProp.dirProperty      = PROPERTY_NULL;
2362     rootProp.startingBlock    = BLOCK_END_OF_CHAIN;
2363     rootProp.size.u.HighPart    = 0;
2364     rootProp.size.u.LowPart     = 0;
2365
2366     StorageImpl_WriteProperty(This, 0, &rootProp);
2367   }
2368
2369   /*
2370    * Find the ID of the root in the property sets.
2371    */
2372   currentPropertyIndex = 0;
2373
2374   do
2375   {
2376     readSuccessful = StorageImpl_ReadProperty(
2377                       This,
2378                       currentPropertyIndex,
2379                       &currentProperty);
2380
2381     if (readSuccessful)
2382     {
2383       if ( (currentProperty.sizeOfNameString != 0 ) &&
2384            (currentProperty.propertyType     == PROPTYPE_ROOT) )
2385       {
2386         This->base.rootPropertySetIndex = currentPropertyIndex;
2387       }
2388     }
2389
2390     currentPropertyIndex++;
2391
2392   } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2393
2394   if (!readSuccessful)
2395   {
2396     /* TODO CLEANUP */
2397     return STG_E_READFAULT;
2398   }
2399
2400   /*
2401    * Create the block chain abstraction for the small block root chain.
2402    */
2403   if(!(This->smallBlockRootChain =
2404        BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2405     return STG_E_READFAULT;
2406
2407   return hr;
2408 }
2409
2410 void StorageImpl_Destroy(StorageBaseImpl* iface)
2411 {
2412   StorageImpl *This = (StorageImpl*) iface;
2413   TRACE("(%p)\n", This);
2414
2415   HeapFree(GetProcessHeap(), 0, This->pwcsName);
2416
2417   BlockChainStream_Destroy(This->smallBlockRootChain);
2418   BlockChainStream_Destroy(This->rootBlockChain);
2419   BlockChainStream_Destroy(This->smallBlockDepotChain);
2420
2421   BIGBLOCKFILE_Destructor(This->bigBlockFile);
2422   HeapFree(GetProcessHeap(), 0, This);
2423 }
2424
2425 /******************************************************************************
2426  *      Storage32Impl_GetNextFreeBigBlock
2427  *
2428  * Returns the index of the next free big block.
2429  * If the big block depot is filled, this method will enlarge it.
2430  *
2431  */
2432 ULONG StorageImpl_GetNextFreeBigBlock(
2433   StorageImpl* This)
2434 {
2435   ULONG depotBlockIndexPos;
2436   void  *depotBuffer;
2437   ULONG depotBlockOffset;
2438   ULONG blocksPerDepot    = This->bigBlockSize / sizeof(ULONG);
2439   ULONG nextBlockIndex    = BLOCK_SPECIAL;
2440   int   depotIndex        = 0;
2441   ULONG freeBlock         = BLOCK_UNUSED;
2442
2443   depotIndex = This->prevFreeBlock / blocksPerDepot;
2444   depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2445
2446   /*
2447    * Scan the entire big block depot until we find a block marked free
2448    */
2449   while (nextBlockIndex != BLOCK_UNUSED)
2450   {
2451     if (depotIndex < COUNT_BBDEPOTINHEADER)
2452     {
2453       depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2454
2455       /*
2456        * Grow the primary depot.
2457        */
2458       if (depotBlockIndexPos == BLOCK_UNUSED)
2459       {
2460         depotBlockIndexPos = depotIndex*blocksPerDepot;
2461
2462         /*
2463          * Add a block depot.
2464          */
2465         Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2466         This->bigBlockDepotCount++;
2467         This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2468
2469         /*
2470          * Flag it as a block depot.
2471          */
2472         StorageImpl_SetNextBlockInChain(This,
2473                                           depotBlockIndexPos,
2474                                           BLOCK_SPECIAL);
2475
2476         /* Save new header information.
2477          */
2478         StorageImpl_SaveFileHeader(This);
2479       }
2480     }
2481     else
2482     {
2483       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2484
2485       if (depotBlockIndexPos == BLOCK_UNUSED)
2486       {
2487         /*
2488          * Grow the extended depot.
2489          */
2490         ULONG extIndex       = BLOCK_UNUSED;
2491         ULONG numExtBlocks   = depotIndex - COUNT_BBDEPOTINHEADER;
2492         ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2493
2494         if (extBlockOffset == 0)
2495         {
2496           /* We need an extended block.
2497            */
2498           extIndex = Storage32Impl_AddExtBlockDepot(This);
2499           This->extBigBlockDepotCount++;
2500           depotBlockIndexPos = extIndex + 1;
2501         }
2502         else
2503           depotBlockIndexPos = depotIndex * blocksPerDepot;
2504
2505         /*
2506          * Add a block depot and mark it in the extended block.
2507          */
2508         Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2509         This->bigBlockDepotCount++;
2510         Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2511
2512         /* Flag the block depot.
2513          */
2514         StorageImpl_SetNextBlockInChain(This,
2515                                           depotBlockIndexPos,
2516                                           BLOCK_SPECIAL);
2517
2518         /* If necessary, flag the extended depot block.
2519          */
2520         if (extIndex != BLOCK_UNUSED)
2521           StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2522
2523         /* Save header information.
2524          */
2525         StorageImpl_SaveFileHeader(This);
2526       }
2527     }
2528
2529     depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2530
2531     if (depotBuffer != 0)
2532     {
2533       while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2534               ( nextBlockIndex != BLOCK_UNUSED))
2535       {
2536         StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2537
2538         if (nextBlockIndex == BLOCK_UNUSED)
2539         {
2540           freeBlock = (depotIndex * blocksPerDepot) +
2541                       (depotBlockOffset/sizeof(ULONG));
2542         }
2543
2544         depotBlockOffset += sizeof(ULONG);
2545       }
2546
2547       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2548     }
2549
2550     depotIndex++;
2551     depotBlockOffset = 0;
2552   }
2553
2554   This->prevFreeBlock = freeBlock;
2555
2556   return freeBlock;
2557 }
2558
2559 /******************************************************************************
2560  *      Storage32Impl_AddBlockDepot
2561  *
2562  * This will create a depot block, essentially it is a block initialized
2563  * to BLOCK_UNUSEDs.
2564  */
2565 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2566 {
2567   BYTE* blockBuffer;
2568
2569   blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2570
2571   /*
2572    * Initialize blocks as free
2573    */
2574   memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2575
2576   StorageImpl_ReleaseBigBlock(This, blockBuffer);
2577 }
2578
2579 /******************************************************************************
2580  *      Storage32Impl_GetExtDepotBlock
2581  *
2582  * Returns the index of the block that corresponds to the specified depot
2583  * index. This method is only for depot indexes equal or greater than
2584  * COUNT_BBDEPOTINHEADER.
2585  */
2586 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2587 {
2588   ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2589   ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
2590   ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
2591   ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
2592   ULONG blockIndex             = BLOCK_UNUSED;
2593   ULONG extBlockIndex          = This->extBigBlockDepotStart;
2594
2595   assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2596
2597   if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2598     return BLOCK_UNUSED;
2599
2600   while (extBlockCount > 0)
2601   {
2602     extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2603     extBlockCount--;
2604   }
2605
2606   if (extBlockIndex != BLOCK_UNUSED)
2607   {
2608     BYTE* depotBuffer;
2609
2610     depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2611
2612     if (depotBuffer != 0)
2613     {
2614       StorageUtl_ReadDWord(depotBuffer,
2615                            extBlockOffset * sizeof(ULONG),
2616                            &blockIndex);
2617
2618       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2619     }
2620   }
2621
2622   return blockIndex;
2623 }
2624
2625 /******************************************************************************
2626  *      Storage32Impl_SetExtDepotBlock
2627  *
2628  * Associates the specified block index to the specified depot index.
2629  * This method is only for depot indexes equal or greater than
2630  * COUNT_BBDEPOTINHEADER.
2631  */
2632 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2633                                     ULONG depotIndex,
2634                                     ULONG blockIndex)
2635 {
2636   ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2637   ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
2638   ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
2639   ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
2640   ULONG extBlockIndex          = This->extBigBlockDepotStart;
2641
2642   assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2643
2644   while (extBlockCount > 0)
2645   {
2646     extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2647     extBlockCount--;
2648   }
2649
2650   if (extBlockIndex != BLOCK_UNUSED)
2651   {
2652     BYTE* depotBuffer;
2653
2654     depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2655
2656     if (depotBuffer != 0)
2657     {
2658       StorageUtl_WriteDWord(depotBuffer,
2659                             extBlockOffset * sizeof(ULONG),
2660                             blockIndex);
2661
2662       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2663     }
2664   }
2665 }
2666
2667 /******************************************************************************
2668  *      Storage32Impl_AddExtBlockDepot
2669  *
2670  * Creates an extended depot block.
2671  */
2672 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2673 {
2674   ULONG numExtBlocks           = This->extBigBlockDepotCount;
2675   ULONG nextExtBlock           = This->extBigBlockDepotStart;
2676   BYTE* depotBuffer            = NULL;
2677   ULONG index                  = BLOCK_UNUSED;
2678   ULONG nextBlockOffset        = This->bigBlockSize - sizeof(ULONG);
2679   ULONG blocksPerDepotBlock    = This->bigBlockSize / sizeof(ULONG);
2680   ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2681
2682   index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2683           blocksPerDepotBlock;
2684
2685   if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2686   {
2687     /*
2688      * The first extended block.
2689      */
2690     This->extBigBlockDepotStart = index;
2691   }
2692   else
2693   {
2694     unsigned int i;
2695     /*
2696      * Follow the chain to the last one.
2697      */
2698     for (i = 0; i < (numExtBlocks - 1); i++)
2699     {
2700       nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2701     }
2702
2703     /*
2704      * Add the new extended block to the chain.
2705      */
2706     depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2707     StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2708     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2709   }
2710
2711   /*
2712    * Initialize this block.
2713    */
2714   depotBuffer = StorageImpl_GetBigBlock(This, index);
2715   memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2716   StorageImpl_ReleaseBigBlock(This, depotBuffer);
2717
2718   return index;
2719 }
2720
2721 /******************************************************************************
2722  *      Storage32Impl_FreeBigBlock
2723  *
2724  * This method will flag the specified block as free in the big block depot.
2725  */
2726 void  StorageImpl_FreeBigBlock(
2727   StorageImpl* This,
2728   ULONG          blockIndex)
2729 {
2730   StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2731
2732   if (blockIndex < This->prevFreeBlock)
2733     This->prevFreeBlock = blockIndex;
2734 }
2735
2736 /************************************************************************
2737  * Storage32Impl_GetNextBlockInChain
2738  *
2739  * This method will retrieve the block index of the next big block in
2740  * in the chain.
2741  *
2742  * Params:  This       - Pointer to the Storage object.
2743  *          blockIndex - Index of the block to retrieve the chain
2744  *                       for.
2745  *          nextBlockIndex - receives the return value.
2746  *
2747  * Returns: This method returns the index of the next block in the chain.
2748  *          It will return the constants:
2749  *              BLOCK_SPECIAL - If the block given was not part of a
2750  *                              chain.
2751  *              BLOCK_END_OF_CHAIN - If the block given was the last in
2752  *                                   a chain.
2753  *              BLOCK_UNUSED - If the block given was not past of a chain
2754  *                             and is available.
2755  *              BLOCK_EXTBBDEPOT - This block is part of the extended
2756  *                                 big block depot.
2757  *
2758  * See Windows documentation for more details on IStorage methods.
2759  */
2760 HRESULT StorageImpl_GetNextBlockInChain(
2761   StorageImpl* This,
2762   ULONG        blockIndex,
2763   ULONG*       nextBlockIndex)
2764 {
2765   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
2766   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
2767   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2768   void* depotBuffer;
2769   ULONG depotBlockIndexPos;
2770   int index;
2771
2772   *nextBlockIndex   = BLOCK_SPECIAL;
2773
2774   if(depotBlockCount >= This->bigBlockDepotCount)
2775   {
2776     WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2777          This->bigBlockDepotCount);
2778     return STG_E_READFAULT;
2779   }
2780
2781   /*
2782    * Cache the currently accessed depot block.
2783    */
2784   if (depotBlockCount != This->indexBlockDepotCached)
2785   {
2786     This->indexBlockDepotCached = depotBlockCount;
2787
2788     if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2789     {
2790       depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2791     }
2792     else
2793     {
2794       /*
2795        * We have to look in the extended depot.
2796        */
2797       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2798     }
2799
2800     depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2801
2802     if (!depotBuffer)
2803       return STG_E_READFAULT;
2804
2805     for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2806     {
2807       StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2808       This->blockDepotCached[index] = *nextBlockIndex;
2809     }
2810     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2811   }
2812
2813   *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2814
2815   return S_OK;
2816 }
2817
2818 /******************************************************************************
2819  *      Storage32Impl_GetNextExtendedBlock
2820  *
2821  * Given an extended block this method will return the next extended block.
2822  *
2823  * NOTES:
2824  * The last ULONG of an extended block is the block index of the next
2825  * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2826  * depot.
2827  *
2828  * Return values:
2829  *    - The index of the next extended block
2830  *    - BLOCK_UNUSED: there is no next extended block.
2831  *    - Any other return values denotes failure.
2832  */
2833 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2834 {
2835   ULONG nextBlockIndex   = BLOCK_SPECIAL;
2836   ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2837   void* depotBuffer;
2838
2839   depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2840
2841   if (depotBuffer!=0)
2842   {
2843     StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2844
2845     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2846   }
2847
2848   return nextBlockIndex;
2849 }
2850
2851 /******************************************************************************
2852  *      Storage32Impl_SetNextBlockInChain
2853  *
2854  * This method will write the index of the specified block's next block
2855  * in the big block depot.
2856  *
2857  * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2858  *              do the following
2859  *
2860  * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2861  * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2862  * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2863  *
2864  */
2865 void  StorageImpl_SetNextBlockInChain(
2866           StorageImpl* This,
2867           ULONG          blockIndex,
2868           ULONG          nextBlock)
2869 {
2870   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
2871   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
2872   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2873   ULONG depotBlockIndexPos;
2874   void* depotBuffer;
2875
2876   assert(depotBlockCount < This->bigBlockDepotCount);
2877   assert(blockIndex != nextBlock);
2878
2879   if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2880   {
2881     depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2882   }
2883   else
2884   {
2885     /*
2886      * We have to look in the extended depot.
2887      */
2888     depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2889   }
2890
2891   depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2892
2893   if (depotBuffer!=0)
2894   {
2895     StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2896     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2897   }
2898
2899   /*
2900    * Update the cached block depot, if necessary.
2901    */
2902   if (depotBlockCount == This->indexBlockDepotCached)
2903   {
2904     This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2905   }
2906 }
2907
2908 /******************************************************************************
2909  *      Storage32Impl_LoadFileHeader
2910  *
2911  * This method will read in the file header, i.e. big block index -1.
2912  */
2913 HRESULT StorageImpl_LoadFileHeader(
2914           StorageImpl* This)
2915 {
2916   HRESULT hr = STG_E_FILENOTFOUND;
2917   void*   headerBigBlock = NULL;
2918   int     index;
2919
2920   /*
2921    * Get a pointer to the big block of data containing the header.
2922    */
2923   headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2924
2925   /*
2926    * Extract the information from the header.
2927    */
2928   if (headerBigBlock!=0)
2929   {
2930     /*
2931      * Check for the "magic number" signature and return an error if it is not
2932      * found.
2933      */
2934     if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2935     {
2936       StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2937       return STG_E_OLDFORMAT;
2938     }
2939
2940     if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2941     {
2942       StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2943       return STG_E_INVALIDHEADER;
2944     }
2945
2946     StorageUtl_ReadWord(
2947       headerBigBlock,
2948       OFFSET_BIGBLOCKSIZEBITS,
2949       &This->bigBlockSizeBits);
2950
2951     StorageUtl_ReadWord(
2952       headerBigBlock,
2953       OFFSET_SMALLBLOCKSIZEBITS,
2954       &This->smallBlockSizeBits);
2955
2956     StorageUtl_ReadDWord(
2957       headerBigBlock,
2958       OFFSET_BBDEPOTCOUNT,
2959       &This->bigBlockDepotCount);
2960
2961     StorageUtl_ReadDWord(
2962       headerBigBlock,
2963       OFFSET_ROOTSTARTBLOCK,
2964       &This->rootStartBlock);
2965
2966     StorageUtl_ReadDWord(
2967       headerBigBlock,
2968       OFFSET_SBDEPOTSTART,
2969       &This->smallBlockDepotStart);
2970
2971     StorageUtl_ReadDWord(
2972       headerBigBlock,
2973       OFFSET_EXTBBDEPOTSTART,
2974       &This->extBigBlockDepotStart);
2975
2976     StorageUtl_ReadDWord(
2977       headerBigBlock,
2978       OFFSET_EXTBBDEPOTCOUNT,
2979       &This->extBigBlockDepotCount);
2980
2981     for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2982     {
2983       StorageUtl_ReadDWord(
2984         headerBigBlock,
2985         OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2986         &(This->bigBlockDepotStart[index]));
2987     }
2988
2989     /*
2990      * Make the bitwise arithmetic to get the size of the blocks in bytes.
2991      */
2992     if ((1 << 2) == 4)
2993     {
2994       This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2995       This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2996     }
2997     else
2998     {
2999       This->bigBlockSize   = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3000       This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3001     }
3002
3003     /*
3004      * Right now, the code is making some assumptions about the size of the
3005      * blocks, just make sure they are what we're expecting.
3006      */
3007     if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3008         This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3009     {
3010         WARN("Broken OLE storage file\n");
3011         hr = STG_E_INVALIDHEADER;
3012     }
3013     else
3014         hr = S_OK;
3015
3016     /*
3017      * Release the block.
3018      */
3019     StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3020   }
3021
3022   return hr;
3023 }
3024
3025 /******************************************************************************
3026  *      Storage32Impl_SaveFileHeader
3027  *
3028  * This method will save to the file the header, i.e. big block -1.
3029  */
3030 void StorageImpl_SaveFileHeader(
3031           StorageImpl* This)
3032 {
3033   BYTE   headerBigBlock[BIG_BLOCK_SIZE];
3034   int    index;
3035   BOOL success;
3036
3037   /*
3038    * Get a pointer to the big block of data containing the header.
3039    */
3040   success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3041
3042   /*
3043    * If the block read failed, the file is probably new.
3044    */
3045   if (!success)
3046   {
3047     /*
3048      * Initialize for all unknown fields.
3049      */
3050     memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3051
3052     /*
3053      * Initialize the magic number.
3054      */
3055     memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3056
3057     /*
3058      * And a bunch of things we don't know what they mean
3059      */
3060     StorageUtl_WriteWord(headerBigBlock,  0x18, 0x3b);
3061     StorageUtl_WriteWord(headerBigBlock,  0x1a, 0x3);
3062     StorageUtl_WriteWord(headerBigBlock,  0x1c, (WORD)-2);
3063     StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3064   }
3065
3066   /*
3067    * Write the information to the header.
3068    */
3069   StorageUtl_WriteWord(
3070     headerBigBlock,
3071     OFFSET_BIGBLOCKSIZEBITS,
3072     This->bigBlockSizeBits);
3073
3074   StorageUtl_WriteWord(
3075     headerBigBlock,
3076     OFFSET_SMALLBLOCKSIZEBITS,
3077     This->smallBlockSizeBits);
3078
3079   StorageUtl_WriteDWord(
3080     headerBigBlock,
3081     OFFSET_BBDEPOTCOUNT,
3082     This->bigBlockDepotCount);
3083
3084   StorageUtl_WriteDWord(
3085     headerBigBlock,
3086     OFFSET_ROOTSTARTBLOCK,
3087     This->rootStartBlock);
3088
3089   StorageUtl_WriteDWord(
3090     headerBigBlock,
3091     OFFSET_SBDEPOTSTART,
3092     This->smallBlockDepotStart);
3093
3094   StorageUtl_WriteDWord(
3095     headerBigBlock,
3096     OFFSET_SBDEPOTCOUNT,
3097     This->smallBlockDepotChain ?
3098      BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3099
3100   StorageUtl_WriteDWord(
3101     headerBigBlock,
3102     OFFSET_EXTBBDEPOTSTART,
3103     This->extBigBlockDepotStart);
3104
3105   StorageUtl_WriteDWord(
3106     headerBigBlock,
3107     OFFSET_EXTBBDEPOTCOUNT,
3108     This->extBigBlockDepotCount);
3109
3110   for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3111   {
3112     StorageUtl_WriteDWord(
3113       headerBigBlock,
3114       OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3115       (This->bigBlockDepotStart[index]));
3116   }
3117
3118   /*
3119    * Write the big block back to the file.
3120    */
3121   StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3122 }
3123
3124 /******************************************************************************
3125  *      Storage32Impl_ReadProperty
3126  *
3127  * This method will read the specified property from the property chain.
3128  */
3129 BOOL StorageImpl_ReadProperty(
3130   StorageImpl* This,
3131   ULONG          index,
3132   StgProperty*   buffer)
3133 {
3134   BYTE           currentProperty[PROPSET_BLOCK_SIZE];
3135   ULARGE_INTEGER offsetInPropSet;
3136   BOOL         readSuccessful;
3137   ULONG          bytesRead;
3138
3139   offsetInPropSet.u.HighPart = 0;
3140   offsetInPropSet.u.LowPart  = index * PROPSET_BLOCK_SIZE;
3141
3142   readSuccessful = BlockChainStream_ReadAt(
3143                     This->rootBlockChain,
3144                     offsetInPropSet,
3145                     PROPSET_BLOCK_SIZE,
3146                     currentProperty,
3147                     &bytesRead);
3148
3149   if (readSuccessful)
3150   {
3151     /* replace the name of root entry (often "Root Entry") by the file name */
3152     WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3153                         This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3154
3155     memset(buffer->name, 0, sizeof(buffer->name));
3156     memcpy(
3157       buffer->name,
3158       propName,
3159       PROPERTY_NAME_BUFFER_LEN );
3160     TRACE("storage name: %s\n", debugstr_w(buffer->name));
3161
3162     memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3163
3164     StorageUtl_ReadWord(
3165       currentProperty,
3166       OFFSET_PS_NAMELENGTH,
3167       &buffer->sizeOfNameString);
3168
3169     StorageUtl_ReadDWord(
3170       currentProperty,
3171       OFFSET_PS_PREVIOUSPROP,
3172       &buffer->previousProperty);
3173
3174     StorageUtl_ReadDWord(
3175       currentProperty,
3176       OFFSET_PS_NEXTPROP,
3177       &buffer->nextProperty);
3178
3179     StorageUtl_ReadDWord(
3180       currentProperty,
3181       OFFSET_PS_DIRPROP,
3182       &buffer->dirProperty);
3183
3184     StorageUtl_ReadGUID(
3185       currentProperty,
3186       OFFSET_PS_GUID,
3187       &buffer->propertyUniqueID);
3188
3189     StorageUtl_ReadDWord(
3190       currentProperty,
3191       OFFSET_PS_TSS1,
3192       &buffer->timeStampS1);
3193
3194     StorageUtl_ReadDWord(
3195       currentProperty,
3196       OFFSET_PS_TSD1,
3197       &buffer->timeStampD1);
3198
3199     StorageUtl_ReadDWord(
3200       currentProperty,
3201       OFFSET_PS_TSS2,
3202       &buffer->timeStampS2);
3203
3204     StorageUtl_ReadDWord(
3205       currentProperty,
3206       OFFSET_PS_TSD2,
3207       &buffer->timeStampD2);
3208
3209     StorageUtl_ReadDWord(
3210       currentProperty,
3211       OFFSET_PS_STARTBLOCK,
3212       &buffer->startingBlock);
3213
3214     StorageUtl_ReadDWord(
3215       currentProperty,
3216       OFFSET_PS_SIZE,
3217       &buffer->size.u.LowPart);
3218
3219     buffer->size.u.HighPart = 0;
3220   }
3221
3222   return readSuccessful;
3223 }
3224
3225 /*********************************************************************
3226  * Write the specified property into the property chain
3227  */
3228 BOOL StorageImpl_WriteProperty(
3229   StorageImpl* This,
3230   ULONG          index,
3231   StgProperty*   buffer)
3232 {
3233   BYTE           currentProperty[PROPSET_BLOCK_SIZE];
3234   ULARGE_INTEGER offsetInPropSet;
3235   BOOL         writeSuccessful;
3236   ULONG          bytesWritten;
3237
3238   offsetInPropSet.u.HighPart = 0;
3239   offsetInPropSet.u.LowPart  = index * PROPSET_BLOCK_SIZE;
3240
3241   memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3242
3243   memcpy(
3244     currentProperty + OFFSET_PS_NAME,
3245     buffer->name,
3246     PROPERTY_NAME_BUFFER_LEN );
3247
3248   memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3249
3250   StorageUtl_WriteWord(
3251     currentProperty,
3252       OFFSET_PS_NAMELENGTH,
3253       buffer->sizeOfNameString);
3254
3255   StorageUtl_WriteDWord(
3256     currentProperty,
3257       OFFSET_PS_PREVIOUSPROP,
3258       buffer->previousProperty);
3259
3260   StorageUtl_WriteDWord(
3261     currentProperty,
3262       OFFSET_PS_NEXTPROP,
3263       buffer->nextProperty);
3264
3265   StorageUtl_WriteDWord(
3266     currentProperty,
3267       OFFSET_PS_DIRPROP,
3268       buffer->dirProperty);
3269
3270   StorageUtl_WriteGUID(
3271     currentProperty,
3272       OFFSET_PS_GUID,
3273       &buffer->propertyUniqueID);
3274
3275   StorageUtl_WriteDWord(
3276     currentProperty,
3277       OFFSET_PS_TSS1,
3278       buffer->timeStampS1);
3279
3280   StorageUtl_WriteDWord(
3281     currentProperty,
3282       OFFSET_PS_TSD1,
3283       buffer->timeStampD1);
3284
3285   StorageUtl_WriteDWord(
3286     currentProperty,
3287       OFFSET_PS_TSS2,
3288       buffer->timeStampS2);
3289
3290   StorageUtl_WriteDWord(
3291     currentProperty,
3292       OFFSET_PS_TSD2,
3293       buffer->timeStampD2);
3294
3295   StorageUtl_WriteDWord(
3296     currentProperty,
3297       OFFSET_PS_STARTBLOCK,
3298       buffer->startingBlock);
3299
3300   StorageUtl_WriteDWord(
3301     currentProperty,
3302       OFFSET_PS_SIZE,
3303       buffer->size.u.LowPart);
3304
3305   writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3306                                             offsetInPropSet,
3307                                             PROPSET_BLOCK_SIZE,
3308                                             currentProperty,
3309                                             &bytesWritten);
3310   return writeSuccessful;
3311 }
3312
3313 BOOL StorageImpl_ReadBigBlock(
3314   StorageImpl* This,
3315   ULONG          blockIndex,
3316   void*          buffer)
3317 {
3318   void* bigBlockBuffer;
3319
3320   bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3321
3322   if (bigBlockBuffer!=0)
3323   {
3324     memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3325
3326     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3327
3328     return TRUE;
3329   }
3330
3331   return FALSE;
3332 }
3333
3334 BOOL StorageImpl_WriteBigBlock(
3335   StorageImpl* This,
3336   ULONG          blockIndex,
3337   void*          buffer)
3338 {
3339   void* bigBlockBuffer;
3340
3341   bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3342
3343   if (bigBlockBuffer!=0)
3344   {
3345     memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3346
3347     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3348
3349     return TRUE;
3350   }
3351
3352   return FALSE;
3353 }
3354
3355 void* StorageImpl_GetROBigBlock(
3356   StorageImpl* This,
3357   ULONG          blockIndex)
3358 {
3359   return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3360 }
3361
3362 void* StorageImpl_GetBigBlock(
3363   StorageImpl* This,
3364   ULONG          blockIndex)
3365 {
3366   return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3367 }
3368
3369 void StorageImpl_ReleaseBigBlock(
3370   StorageImpl* This,
3371   void*          pBigBlock)
3372 {
3373   BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3374 }
3375
3376 /******************************************************************************
3377  *              Storage32Impl_SmallBlocksToBigBlocks
3378  *
3379  * This method will convert a small block chain to a big block chain.
3380  * The small block chain will be destroyed.
3381  */
3382 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3383                       StorageImpl* This,
3384                       SmallBlockChainStream** ppsbChain)
3385 {
3386   ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3387   ULARGE_INTEGER size, offset;
3388   ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3389   ULONG propertyIndex;
3390   BOOL successRead, successWrite;
3391   StgProperty chainProperty;
3392   BYTE *buffer;
3393   BlockChainStream *bbTempChain = NULL;
3394   BlockChainStream *bigBlockChain = NULL;
3395
3396   /*
3397    * Create a temporary big block chain that doesn't have
3398    * an associated property. This temporary chain will be
3399    * used to copy data from small blocks to big blocks.
3400    */
3401   bbTempChain = BlockChainStream_Construct(This,
3402                                            &bbHeadOfChain,
3403                                            PROPERTY_NULL);
3404   if(!bbTempChain) return NULL;
3405   /*
3406    * Grow the big block chain.
3407    */
3408   size = SmallBlockChainStream_GetSize(*ppsbChain);
3409   BlockChainStream_SetSize(bbTempChain, size);
3410
3411   /*
3412    * Copy the contents of the small block chain to the big block chain
3413    * by small block size increments.
3414    */
3415   offset.u.LowPart = 0;
3416   offset.u.HighPart = 0;
3417   cbTotalRead = 0;
3418   cbTotalWritten = 0;
3419
3420   buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3421   do
3422   {
3423     successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3424                                                offset,
3425                                                DEF_SMALL_BLOCK_SIZE,
3426                                                buffer,
3427                                                &cbRead);
3428     cbTotalRead += cbRead;
3429
3430     successWrite = BlockChainStream_WriteAt(bbTempChain,
3431                                             offset,
3432                                             cbRead,
3433                                             buffer,
3434                                             &cbWritten);
3435     cbTotalWritten += cbWritten;
3436
3437     offset.u.LowPart += This->smallBlockSize;
3438
3439   } while (successRead && successWrite);
3440   HeapFree(GetProcessHeap(),0,buffer);
3441
3442   assert(cbTotalRead == cbTotalWritten);
3443
3444   /*
3445    * Destroy the small block chain.
3446    */
3447   propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3448   size.u.HighPart = 0;
3449   size.u.LowPart  = 0;
3450   SmallBlockChainStream_SetSize(*ppsbChain, size);
3451   SmallBlockChainStream_Destroy(*ppsbChain);
3452   *ppsbChain = 0;
3453
3454   /*
3455    * Change the property information. This chain is now a big block chain
3456    * and it doesn't reside in the small blocks chain anymore.
3457    */
3458   StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3459
3460   chainProperty.startingBlock = bbHeadOfChain;
3461
3462   StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3463
3464   /*
3465    * Destroy the temporary propertyless big block chain.
3466    * Create a new big block chain associated with this property.
3467    */
3468   BlockChainStream_Destroy(bbTempChain);
3469   bigBlockChain = BlockChainStream_Construct(This,
3470                                              NULL,
3471                                              propertyIndex);
3472
3473   return bigBlockChain;
3474 }
3475
3476 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3477 {
3478   StorageInternalImpl* This = (StorageInternalImpl*) iface;
3479
3480   StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3481   HeapFree(GetProcessHeap(), 0, This);
3482 }
3483
3484 /******************************************************************************
3485 **
3486 ** Storage32InternalImpl_Commit
3487 **
3488 ** The non-root storages cannot be opened in transacted mode thus this function
3489 ** does nothing.
3490 */
3491 HRESULT WINAPI StorageInternalImpl_Commit(
3492   IStorage*            iface,
3493   DWORD                  grfCommitFlags)  /* [in] */
3494 {
3495   return S_OK;
3496 }
3497
3498 /******************************************************************************
3499 **
3500 ** Storage32InternalImpl_Revert
3501 **
3502 ** The non-root storages cannot be opened in transacted mode thus this function
3503 ** does nothing.
3504 */
3505 HRESULT WINAPI StorageInternalImpl_Revert(
3506   IStorage*            iface)
3507 {
3508   return S_OK;
3509 }
3510
3511 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3512 {
3513   IStorage_Release((IStorage*)This->parentStorage);
3514   HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3515   HeapFree(GetProcessHeap(), 0, This);
3516 }
3517
3518 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3519   IEnumSTATSTG*     iface,
3520   REFIID            riid,
3521   void**            ppvObject)
3522 {
3523   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3524
3525   /*
3526    * Perform a sanity check on the parameters.
3527    */
3528   if (ppvObject==0)
3529     return E_INVALIDARG;
3530
3531   /*
3532    * Initialize the return parameter.
3533    */
3534   *ppvObject = 0;
3535
3536   /*
3537    * Compare the riid with the interface IDs implemented by this object.
3538    */
3539   if (IsEqualGUID(&IID_IUnknown, riid) ||
3540       IsEqualGUID(&IID_IStorage, riid))
3541   {
3542     *ppvObject = (IEnumSTATSTG*)This;
3543     IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3544     return S_OK;
3545   }
3546
3547   return E_NOINTERFACE;
3548 }
3549
3550 ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
3551   IEnumSTATSTG* iface)
3552 {
3553   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3554   return InterlockedIncrement(&This->ref);
3555 }
3556
3557 ULONG   WINAPI IEnumSTATSTGImpl_Release(
3558   IEnumSTATSTG* iface)
3559 {
3560   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3561
3562   ULONG newRef;
3563
3564   newRef = InterlockedDecrement(&This->ref);
3565
3566   /*
3567    * If the reference count goes down to 0, perform suicide.
3568    */
3569   if (newRef==0)
3570   {
3571     IEnumSTATSTGImpl_Destroy(This);
3572   }
3573
3574   return newRef;
3575 }
3576
3577 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3578   IEnumSTATSTG* iface,
3579   ULONG             celt,
3580   STATSTG*          rgelt,
3581   ULONG*            pceltFetched)
3582 {
3583   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3584
3585   StgProperty currentProperty;
3586   STATSTG*    currentReturnStruct = rgelt;
3587   ULONG       objectFetched       = 0;
3588   ULONG      currentSearchNode;
3589
3590   /*
3591    * Perform a sanity check on the parameters.
3592    */
3593   if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3594     return E_INVALIDARG;
3595
3596   /*
3597    * To avoid the special case, get another pointer to a ULONG value if
3598    * the caller didn't supply one.
3599    */
3600   if (pceltFetched==0)
3601     pceltFetched = &objectFetched;
3602
3603   /*
3604    * Start the iteration, we will iterate until we hit the end of the
3605    * linked list or until we hit the number of items to iterate through
3606    */
3607   *pceltFetched = 0;
3608
3609   /*
3610    * Start with the node at the top of the stack.
3611    */
3612   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3613
3614   while ( ( *pceltFetched < celt) &&
3615           ( currentSearchNode!=PROPERTY_NULL) )
3616   {
3617     /*
3618      * Remove the top node from the stack
3619      */
3620     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3621
3622     /*
3623      * Read the property from the storage.
3624      */
3625     StorageImpl_ReadProperty(This->parentStorage,
3626       currentSearchNode,
3627       &currentProperty);
3628
3629     /*
3630      * Copy the information to the return buffer.
3631      */
3632     StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3633       &currentProperty,
3634       STATFLAG_DEFAULT);
3635
3636     /*
3637      * Step to the next item in the iteration
3638      */
3639     (*pceltFetched)++;
3640     currentReturnStruct++;
3641
3642     /*
3643      * Push the next search node in the search stack.
3644      */
3645     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3646
3647     /*
3648      * continue the iteration.
3649      */
3650     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3651   }
3652
3653   if (*pceltFetched == celt)
3654     return S_OK;
3655
3656   return S_FALSE;
3657 }
3658
3659
3660 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3661   IEnumSTATSTG* iface,
3662   ULONG             celt)
3663 {
3664   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3665
3666   StgProperty currentProperty;
3667   ULONG       objectFetched       = 0;
3668   ULONG       currentSearchNode;
3669
3670   /*
3671    * Start with the node at the top of the stack.
3672    */
3673   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3674
3675   while ( (objectFetched < celt) &&
3676           (currentSearchNode!=PROPERTY_NULL) )
3677   {
3678     /*
3679      * Remove the top node from the stack
3680      */
3681     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3682
3683     /*
3684      * Read the property from the storage.
3685      */
3686     StorageImpl_ReadProperty(This->parentStorage,
3687       currentSearchNode,
3688       &currentProperty);
3689
3690     /*
3691      * Step to the next item in the iteration
3692      */
3693     objectFetched++;
3694
3695     /*
3696      * Push the next search node in the search stack.
3697      */
3698     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3699
3700     /*
3701      * continue the iteration.
3702      */
3703     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3704   }
3705
3706   if (objectFetched == celt)
3707     return S_OK;
3708
3709   return S_FALSE;
3710 }
3711
3712 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3713   IEnumSTATSTG* iface)
3714 {
3715   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3716
3717   StgProperty rootProperty;
3718   BOOL      readSuccessful;
3719
3720   /*
3721    * Re-initialize the search stack to an empty stack
3722    */
3723   This->stackSize = 0;
3724
3725   /*
3726    * Read the root property from the storage.
3727    */
3728   readSuccessful = StorageImpl_ReadProperty(
3729                     This->parentStorage,
3730                     This->firstPropertyNode,
3731                     &rootProperty);
3732
3733   if (readSuccessful)
3734   {
3735     assert(rootProperty.sizeOfNameString!=0);
3736
3737     /*
3738      * Push the search node in the search stack.
3739      */
3740     IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3741   }
3742
3743   return S_OK;
3744 }
3745
3746 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3747   IEnumSTATSTG* iface,
3748   IEnumSTATSTG**    ppenum)
3749 {
3750   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3751
3752   IEnumSTATSTGImpl* newClone;
3753
3754   /*
3755    * Perform a sanity check on the parameters.
3756    */
3757   if (ppenum==0)
3758     return E_INVALIDARG;
3759
3760   newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3761                This->firstPropertyNode);
3762
3763
3764   /*
3765    * The new clone enumeration must point to the same current node as
3766    * the ole one.
3767    */
3768   newClone->stackSize    = This->stackSize    ;
3769   newClone->stackMaxSize = This->stackMaxSize ;
3770   newClone->stackToVisit =
3771     HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3772
3773   memcpy(
3774     newClone->stackToVisit,
3775     This->stackToVisit,
3776     sizeof(ULONG) * newClone->stackSize);
3777
3778   *ppenum = (IEnumSTATSTG*)newClone;
3779
3780   /*
3781    * Don't forget to nail down a reference to the clone before
3782    * returning it.
3783    */
3784   IEnumSTATSTGImpl_AddRef(*ppenum);
3785
3786   return S_OK;
3787 }
3788
3789 INT IEnumSTATSTGImpl_FindParentProperty(
3790   IEnumSTATSTGImpl *This,
3791   ULONG             childProperty,
3792   StgProperty      *currentProperty,
3793   ULONG            *thisNodeId)
3794 {
3795   ULONG currentSearchNode;
3796   ULONG foundNode;
3797
3798   /*
3799    * To avoid the special case, get another pointer to a ULONG value if
3800    * the caller didn't supply one.
3801    */
3802   if (thisNodeId==0)
3803     thisNodeId = &foundNode;
3804
3805   /*
3806    * Start with the node at the top of the stack.
3807    */
3808   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3809
3810
3811   while (currentSearchNode!=PROPERTY_NULL)
3812   {
3813     /*
3814      * Store the current node in the returned parameters
3815      */
3816     *thisNodeId = currentSearchNode;
3817
3818     /*
3819      * Remove the top node from the stack
3820      */
3821     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3822
3823     /*
3824      * Read the property from the storage.
3825      */
3826     StorageImpl_ReadProperty(
3827       This->parentStorage,
3828       currentSearchNode,
3829       currentProperty);
3830
3831     if (currentProperty->previousProperty == childProperty)
3832       return PROPERTY_RELATION_PREVIOUS;
3833
3834     else if (currentProperty->nextProperty == childProperty)
3835       return PROPERTY_RELATION_NEXT;
3836
3837     else if (currentProperty->dirProperty == childProperty)
3838       return PROPERTY_RELATION_DIR;
3839
3840     /*
3841      * Push the next search node in the search stack.
3842      */
3843     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3844
3845     /*
3846      * continue the iteration.
3847      */
3848     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3849   }
3850
3851   return PROPERTY_NULL;
3852 }
3853
3854 ULONG IEnumSTATSTGImpl_FindProperty(
3855   IEnumSTATSTGImpl* This,
3856   const OLECHAR*  lpszPropName,
3857   StgProperty*      currentProperty)
3858 {
3859   ULONG currentSearchNode;
3860
3861   /*
3862    * Start with the node at the top of the stack.
3863    */
3864   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3865
3866   while (currentSearchNode!=PROPERTY_NULL)
3867   {
3868     /*
3869      * Remove the top node from the stack
3870      */
3871     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3872
3873     /*
3874      * Read the property from the storage.
3875      */
3876     StorageImpl_ReadProperty(This->parentStorage,
3877       currentSearchNode,
3878       currentProperty);
3879
3880     if ( propertyNameCmp(
3881           (const OLECHAR*)currentProperty->name,
3882           (const OLECHAR*)lpszPropName) == 0)
3883       return currentSearchNode;
3884
3885     /*
3886      * Push the next search node in the search stack.
3887      */
3888     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3889
3890     /*
3891      * continue the iteration.
3892      */
3893     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3894   }
3895
3896   return PROPERTY_NULL;
3897 }
3898
3899 void IEnumSTATSTGImpl_PushSearchNode(
3900   IEnumSTATSTGImpl* This,
3901   ULONG             nodeToPush)
3902 {
3903   StgProperty rootProperty;
3904   BOOL      readSuccessful;
3905
3906   /*
3907    * First, make sure we're not trying to push an unexisting node.
3908    */
3909   if (nodeToPush==PROPERTY_NULL)
3910     return;
3911
3912   /*
3913    * First push the node to the stack
3914    */
3915   if (This->stackSize == This->stackMaxSize)
3916   {
3917     This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3918
3919     This->stackToVisit = HeapReAlloc(
3920                            GetProcessHeap(),
3921                            0,
3922                            This->stackToVisit,
3923                            sizeof(ULONG) * This->stackMaxSize);
3924   }
3925
3926   This->stackToVisit[This->stackSize] = nodeToPush;
3927   This->stackSize++;
3928
3929   /*
3930    * Read the root property from the storage.
3931    */
3932   readSuccessful = StorageImpl_ReadProperty(
3933                     This->parentStorage,
3934                     nodeToPush,
3935                     &rootProperty);
3936
3937   if (readSuccessful)
3938   {
3939     assert(rootProperty.sizeOfNameString!=0);
3940
3941     /*
3942      * Push the previous search node in the search stack.
3943      */
3944     IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3945   }
3946 }
3947
3948 ULONG IEnumSTATSTGImpl_PopSearchNode(
3949   IEnumSTATSTGImpl* This,
3950   BOOL            remove)
3951 {
3952   ULONG topNode;
3953
3954   if (This->stackSize == 0)
3955     return PROPERTY_NULL;
3956
3957   topNode = This->stackToVisit[This->stackSize-1];
3958
3959   if (remove)
3960     This->stackSize--;
3961
3962   return topNode;
3963 }
3964
3965 /*
3966  * Virtual function table for the IEnumSTATSTGImpl class.
3967  */
3968 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3969 {
3970     IEnumSTATSTGImpl_QueryInterface,
3971     IEnumSTATSTGImpl_AddRef,
3972     IEnumSTATSTGImpl_Release,
3973     IEnumSTATSTGImpl_Next,
3974     IEnumSTATSTGImpl_Skip,
3975     IEnumSTATSTGImpl_Reset,
3976     IEnumSTATSTGImpl_Clone
3977 };
3978
3979 /******************************************************************************
3980 ** IEnumSTATSTGImpl implementation
3981 */
3982
3983 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3984   StorageImpl* parentStorage,
3985   ULONG          firstPropertyNode)
3986 {
3987   IEnumSTATSTGImpl* newEnumeration;
3988
3989   newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3990
3991   if (newEnumeration!=0)
3992   {
3993     /*
3994      * Set-up the virtual function table and reference count.
3995      */
3996     newEnumeration->lpVtbl    = &IEnumSTATSTGImpl_Vtbl;
3997     newEnumeration->ref       = 0;
3998
3999     /*
4000      * We want to nail-down the reference to the storage in case the
4001      * enumeration out-lives the storage in the client application.
4002      */
4003     newEnumeration->parentStorage = parentStorage;
4004     IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4005
4006     newEnumeration->firstPropertyNode   = firstPropertyNode;
4007
4008     /*
4009      * Initialize the search stack
4010      */
4011     newEnumeration->stackSize    = 0;
4012     newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4013     newEnumeration->stackToVisit =
4014       HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4015
4016     /*
4017      * Make sure the current node of the iterator is the first one.
4018      */
4019     IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4020   }
4021
4022   return newEnumeration;
4023 }
4024
4025 /*
4026  * Virtual function table for the Storage32InternalImpl class.
4027  */
4028 static IStorageVtbl Storage32InternalImpl_Vtbl =
4029 {
4030     StorageBaseImpl_QueryInterface,
4031     StorageBaseImpl_AddRef,
4032     StorageBaseImpl_Release,
4033     StorageBaseImpl_CreateStream,
4034     StorageBaseImpl_OpenStream,
4035     StorageImpl_CreateStorage,
4036     StorageBaseImpl_OpenStorage,
4037     StorageImpl_CopyTo,
4038     StorageImpl_MoveElementTo,
4039     StorageInternalImpl_Commit,
4040     StorageInternalImpl_Revert,
4041     StorageBaseImpl_EnumElements,
4042     StorageImpl_DestroyElement,
4043     StorageBaseImpl_RenameElement,
4044     StorageImpl_SetElementTimes,
4045     StorageBaseImpl_SetClass,
4046     StorageImpl_SetStateBits,
4047     StorageBaseImpl_Stat
4048 };
4049
4050 /******************************************************************************
4051 ** Storage32InternalImpl implementation
4052 */
4053
4054 StorageInternalImpl* StorageInternalImpl_Construct(
4055   StorageImpl* ancestorStorage,
4056   DWORD        openFlags,
4057   ULONG        rootPropertyIndex)
4058 {
4059   StorageInternalImpl* newStorage;
4060
4061   /*
4062    * Allocate space for the new storage object
4063    */
4064   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4065
4066   if (newStorage!=0)
4067   {
4068     memset(newStorage, 0, sizeof(StorageInternalImpl));
4069
4070     /*
4071      * Initialize the virtual function table.
4072      */
4073     newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4074     newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4075     newStorage->base.openFlags = openFlags;
4076
4077     /*
4078      * Keep the ancestor storage pointer and nail a reference to it.
4079      */
4080     newStorage->base.ancestorStorage = ancestorStorage;
4081     StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4082
4083     /*
4084      * Keep the index of the root property set for this storage,
4085      */
4086     newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4087
4088     return newStorage;
4089   }
4090
4091   return 0;
4092 }
4093
4094 /******************************************************************************
4095 ** StorageUtl implementation
4096 */
4097
4098 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4099 {
4100   WORD tmp;
4101
4102   memcpy(&tmp, buffer+offset, sizeof(WORD));
4103   *value = le16toh(tmp);
4104 }
4105
4106 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4107 {
4108   value = htole16(value);
4109   memcpy(buffer+offset, &value, sizeof(WORD));
4110 }
4111
4112 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4113 {
4114   DWORD tmp;
4115
4116   memcpy(&tmp, buffer+offset, sizeof(DWORD));
4117   *value = le32toh(tmp);
4118 }
4119
4120 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4121 {
4122   value = htole32(value);
4123   memcpy(buffer+offset, &value, sizeof(DWORD));
4124 }
4125
4126 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4127 {
4128   StorageUtl_ReadDWord(buffer, offset,   &(value->Data1));
4129   StorageUtl_ReadWord(buffer,  offset+4, &(value->Data2));
4130   StorageUtl_ReadWord(buffer,  offset+6, &(value->Data3));
4131
4132   memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4133 }
4134
4135 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4136 {
4137   StorageUtl_WriteDWord(buffer, offset,   value->Data1);
4138   StorageUtl_WriteWord(buffer,  offset+4, value->Data2);
4139   StorageUtl_WriteWord(buffer,  offset+6, value->Data3);
4140
4141   memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4142 }
4143
4144 void StorageUtl_CopyPropertyToSTATSTG(
4145   STATSTG*     destination,
4146   StgProperty* source,
4147   int          statFlags)
4148 {
4149   /*
4150    * The copy of the string occurs only when the flag is not set
4151    */
4152   if( ((statFlags & STATFLAG_NONAME) != 0) || 
4153        (source->name == NULL) || 
4154        (source->name[0] == 0) )
4155   {
4156     destination->pwcsName = 0;
4157   }
4158   else
4159   {
4160     destination->pwcsName =
4161       CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4162
4163     strcpyW((LPWSTR)destination->pwcsName, source->name);
4164   }
4165
4166   switch (source->propertyType)
4167   {
4168     case PROPTYPE_STORAGE:
4169     case PROPTYPE_ROOT:
4170       destination->type = STGTY_STORAGE;
4171       break;
4172     case PROPTYPE_STREAM:
4173       destination->type = STGTY_STREAM;
4174       break;
4175     default:
4176       destination->type = STGTY_STREAM;
4177       break;
4178   }
4179
4180   destination->cbSize            = source->size;
4181 /*
4182   currentReturnStruct->mtime     = {0}; TODO
4183   currentReturnStruct->ctime     = {0};
4184   currentReturnStruct->atime     = {0};
4185 */
4186   destination->grfMode           = 0;
4187   destination->grfLocksSupported = 0;
4188   destination->clsid             = source->propertyUniqueID;
4189   destination->grfStateBits      = 0;
4190   destination->reserved          = 0;
4191 }
4192
4193 /******************************************************************************
4194 ** BlockChainStream implementation
4195 */
4196
4197 BlockChainStream* BlockChainStream_Construct(
4198   StorageImpl* parentStorage,
4199   ULONG*         headOfStreamPlaceHolder,
4200   ULONG          propertyIndex)
4201 {
4202   BlockChainStream* newStream;
4203   ULONG blockIndex;
4204
4205   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4206
4207   newStream->parentStorage           = parentStorage;
4208   newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4209   newStream->ownerPropertyIndex      = propertyIndex;
4210   newStream->lastBlockNoInSequence   = 0xFFFFFFFF;
4211   newStream->tailIndex               = BLOCK_END_OF_CHAIN;
4212   newStream->numBlocks               = 0;
4213
4214   blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4215
4216   while (blockIndex != BLOCK_END_OF_CHAIN)
4217   {
4218     newStream->numBlocks++;
4219     newStream->tailIndex = blockIndex;
4220
4221     if(FAILED(StorageImpl_GetNextBlockInChain(
4222               parentStorage,
4223               blockIndex,
4224               &blockIndex)))
4225     {
4226       HeapFree(GetProcessHeap(), 0, newStream);
4227       return NULL;
4228     }
4229   }
4230
4231   return newStream;
4232 }
4233
4234 void BlockChainStream_Destroy(BlockChainStream* This)
4235 {
4236   HeapFree(GetProcessHeap(), 0, This);
4237 }
4238
4239 /******************************************************************************
4240  *      BlockChainStream_GetHeadOfChain
4241  *
4242  * Returns the head of this stream chain.
4243  * Some special chains don't have properties, their heads are kept in
4244  * This->headOfStreamPlaceHolder.
4245  *
4246  */
4247 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4248 {
4249   StgProperty chainProperty;
4250   BOOL      readSuccessful;
4251
4252   if (This->headOfStreamPlaceHolder != 0)
4253     return *(This->headOfStreamPlaceHolder);
4254
4255   if (This->ownerPropertyIndex != PROPERTY_NULL)
4256   {
4257     readSuccessful = StorageImpl_ReadProperty(
4258                       This->parentStorage,
4259                       This->ownerPropertyIndex,
4260                       &chainProperty);
4261
4262     if (readSuccessful)
4263     {
4264       return chainProperty.startingBlock;
4265     }
4266   }
4267
4268   return BLOCK_END_OF_CHAIN;
4269 }
4270
4271 /******************************************************************************
4272  *       BlockChainStream_GetCount
4273  *
4274  * Returns the number of blocks that comprises this chain.
4275  * This is not the size of the stream as the last block may not be full!
4276  *
4277  */
4278 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4279 {
4280   ULONG blockIndex;
4281   ULONG count = 0;
4282
4283   blockIndex = BlockChainStream_GetHeadOfChain(This);
4284
4285   while (blockIndex != BLOCK_END_OF_CHAIN)
4286   {
4287     count++;
4288
4289     if(FAILED(StorageImpl_GetNextBlockInChain(
4290                    This->parentStorage,
4291                    blockIndex,
4292                    &blockIndex)))
4293       return 0;
4294   }
4295
4296   return count;
4297 }
4298
4299 /******************************************************************************
4300  *      BlockChainStream_ReadAt
4301  *
4302  * Reads a specified number of bytes from this chain at the specified offset.
4303  * bytesRead may be NULL.
4304  * Failure will be returned if the specified number of bytes has not been read.
4305  */
4306 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4307   ULARGE_INTEGER offset,
4308   ULONG          size,
4309   void*          buffer,
4310   ULONG*         bytesRead)
4311 {
4312   ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4313   ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
4314   ULONG bytesToReadInBuffer;
4315   ULONG blockIndex;
4316   BYTE* bufferWalker;
4317   BYTE* bigBlockBuffer;
4318
4319   /*
4320    * Find the first block in the stream that contains part of the buffer.
4321    */
4322   if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4323        (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4324        (blockNoInSequence < This->lastBlockNoInSequence) )
4325   {
4326     blockIndex = BlockChainStream_GetHeadOfChain(This);
4327     This->lastBlockNoInSequence = blockNoInSequence;
4328   }
4329   else
4330   {
4331     ULONG temp = blockNoInSequence;
4332
4333     blockIndex = This->lastBlockNoInSequenceIndex;
4334     blockNoInSequence -= This->lastBlockNoInSequence;
4335     This->lastBlockNoInSequence = temp;
4336   }
4337
4338   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4339   {
4340     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4341       return FALSE;
4342     blockNoInSequence--;
4343   }
4344
4345   This->lastBlockNoInSequenceIndex = blockIndex;
4346
4347   /*
4348    * Start reading the buffer.
4349    */
4350   *bytesRead   = 0;
4351   bufferWalker = buffer;
4352
4353   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4354   {
4355     /*
4356      * Calculate how many bytes we can copy from this big block.
4357      */
4358     bytesToReadInBuffer =
4359       min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4360
4361     /*
4362      * Copy those bytes to the buffer
4363      */
4364     bigBlockBuffer =
4365       StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4366
4367     memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4368
4369     StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4370
4371     /*
4372      * Step to the next big block.
4373      */
4374     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4375       return FALSE;
4376
4377     bufferWalker += bytesToReadInBuffer;
4378     size         -= bytesToReadInBuffer;
4379     *bytesRead   += bytesToReadInBuffer;
4380     offsetInBlock = 0;  /* There is no offset on the next block */
4381
4382   }
4383
4384   return (size == 0);
4385 }
4386
4387 /******************************************************************************
4388  *      BlockChainStream_WriteAt
4389  *
4390  * Writes the specified number of bytes to this chain at the specified offset.
4391  * bytesWritten may be NULL.
4392  * Will fail if not all specified number of bytes have been written.
4393  */
4394 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4395   ULARGE_INTEGER    offset,
4396   ULONG             size,
4397   const void*       buffer,
4398   ULONG*            bytesWritten)
4399 {
4400   ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4401   ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
4402   ULONG bytesToWrite;
4403   ULONG blockIndex;
4404   const BYTE* bufferWalker;
4405   BYTE* bigBlockBuffer;
4406
4407   /*
4408    * Find the first block in the stream that contains part of the buffer.
4409    */
4410   if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4411        (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4412        (blockNoInSequence < This->lastBlockNoInSequence) )
4413   {
4414     blockIndex = BlockChainStream_GetHeadOfChain(This);
4415     This->lastBlockNoInSequence = blockNoInSequence;
4416   }
4417   else
4418   {
4419     ULONG temp = blockNoInSequence;
4420
4421     blockIndex = This->lastBlockNoInSequenceIndex;
4422     blockNoInSequence -= This->lastBlockNoInSequence;
4423     This->lastBlockNoInSequence = temp;
4424   }
4425
4426   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4427   {
4428     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4429                                               &blockIndex)))
4430       return FALSE;
4431     blockNoInSequence--;
4432   }
4433
4434   This->lastBlockNoInSequenceIndex = blockIndex;
4435
4436   /*
4437    * Here, I'm casting away the constness on the buffer variable
4438    * This is OK since we don't intend to modify that buffer.
4439    */
4440   *bytesWritten   = 0;
4441   bufferWalker = (const BYTE*)buffer;
4442
4443   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4444   {
4445     /*
4446      * Calculate how many bytes we can copy from this big block.
4447      */
4448     bytesToWrite =
4449       min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4450
4451     /*
4452      * Copy those bytes to the buffer
4453      */
4454     bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4455
4456     memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4457
4458     StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4459
4460     /*
4461      * Step to the next big block.
4462      */
4463     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4464                                               &blockIndex)))
4465       return FALSE;
4466     bufferWalker  += bytesToWrite;
4467     size          -= bytesToWrite;
4468     *bytesWritten += bytesToWrite;
4469     offsetInBlock  = 0;      /* There is no offset on the next block */
4470   }
4471
4472   return (size == 0);
4473 }
4474
4475 /******************************************************************************
4476  *      BlockChainStream_Shrink
4477  *
4478  * Shrinks this chain in the big block depot.
4479  */
4480 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4481                                ULARGE_INTEGER    newSize)
4482 {
4483   ULONG blockIndex, extraBlock;
4484   ULONG numBlocks;
4485   ULONG count = 1;
4486
4487   /*
4488    * Reset the last accessed block cache.
4489    */
4490   This->lastBlockNoInSequence = 0xFFFFFFFF;
4491   This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4492
4493   /*
4494    * Figure out how many blocks are needed to contain the new size
4495    */
4496   numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4497
4498   if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4499     numBlocks++;
4500
4501   blockIndex = BlockChainStream_GetHeadOfChain(This);
4502
4503   /*
4504    * Go to the new end of chain
4505    */
4506   while (count < numBlocks)
4507   {
4508     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4509                                               &blockIndex)))
4510       return FALSE;
4511     count++;
4512   }
4513
4514   /* Get the next block before marking the new end */
4515   if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4516                                             &extraBlock)))
4517     return FALSE;
4518
4519   /* Mark the new end of chain */
4520   StorageImpl_SetNextBlockInChain(
4521     This->parentStorage,
4522     blockIndex,
4523     BLOCK_END_OF_CHAIN);
4524
4525   This->tailIndex = blockIndex;
4526   This->numBlocks = numBlocks;
4527
4528   /*
4529    * Mark the extra blocks as free
4530    */
4531   while (extraBlock != BLOCK_END_OF_CHAIN)
4532   {
4533     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4534                                               &blockIndex)))
4535       return FALSE;
4536     StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4537     extraBlock = blockIndex;
4538   }
4539
4540   return TRUE;
4541 }
4542
4543 /******************************************************************************
4544  *      BlockChainStream_Enlarge
4545  *
4546  * Grows this chain in the big block depot.
4547  */
4548 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4549                                 ULARGE_INTEGER    newSize)
4550 {
4551   ULONG blockIndex, currentBlock;
4552   ULONG newNumBlocks;
4553   ULONG oldNumBlocks = 0;
4554
4555   blockIndex = BlockChainStream_GetHeadOfChain(This);
4556
4557   /*
4558    * Empty chain. Create the head.
4559    */
4560   if (blockIndex == BLOCK_END_OF_CHAIN)
4561   {
4562     blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4563     StorageImpl_SetNextBlockInChain(This->parentStorage,
4564                                       blockIndex,
4565                                       BLOCK_END_OF_CHAIN);
4566
4567     if (This->headOfStreamPlaceHolder != 0)
4568     {
4569       *(This->headOfStreamPlaceHolder) = blockIndex;
4570     }
4571     else
4572     {
4573       StgProperty chainProp;
4574       assert(This->ownerPropertyIndex != PROPERTY_NULL);
4575
4576       StorageImpl_ReadProperty(
4577         This->parentStorage,
4578         This->ownerPropertyIndex,
4579         &chainProp);
4580
4581       chainProp.startingBlock = blockIndex;
4582
4583       StorageImpl_WriteProperty(
4584         This->parentStorage,
4585         This->ownerPropertyIndex,
4586         &chainProp);
4587     }
4588
4589     This->tailIndex = blockIndex;
4590     This->numBlocks = 1;
4591   }
4592
4593   /*
4594    * Figure out how many blocks are needed to contain this stream
4595    */
4596   newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4597
4598   if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4599     newNumBlocks++;
4600
4601   /*
4602    * Go to the current end of chain
4603    */
4604   if (This->tailIndex == BLOCK_END_OF_CHAIN)
4605   {
4606     currentBlock = blockIndex;
4607
4608     while (blockIndex != BLOCK_END_OF_CHAIN)
4609     {
4610       This->numBlocks++;
4611       currentBlock = blockIndex;
4612
4613       if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4614                                                 &blockIndex)))
4615         return FALSE;
4616     }
4617
4618     This->tailIndex = currentBlock;
4619   }
4620
4621   currentBlock = This->tailIndex;
4622   oldNumBlocks = This->numBlocks;
4623
4624   /*
4625    * Add new blocks to the chain
4626    */
4627   if (oldNumBlocks < newNumBlocks)
4628   {
4629     while (oldNumBlocks < newNumBlocks)
4630     {
4631       blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4632
4633       StorageImpl_SetNextBlockInChain(
4634         This->parentStorage,
4635         currentBlock,
4636         blockIndex);
4637
4638       StorageImpl_SetNextBlockInChain(
4639         This->parentStorage,
4640         blockIndex,
4641         BLOCK_END_OF_CHAIN);
4642
4643       currentBlock = blockIndex;
4644       oldNumBlocks++;
4645     }
4646
4647     This->tailIndex = blockIndex;
4648     This->numBlocks = newNumBlocks;
4649   }
4650
4651   return TRUE;
4652 }
4653
4654 /******************************************************************************
4655  *      BlockChainStream_SetSize
4656  *
4657  * Sets the size of this stream. The big block depot will be updated.
4658  * The file will grow if we grow the chain.
4659  *
4660  * TODO: Free the actual blocks in the file when we shrink the chain.
4661  *       Currently, the blocks are still in the file. So the file size
4662  *       doesn't shrink even if we shrink streams.
4663  */
4664 BOOL BlockChainStream_SetSize(
4665   BlockChainStream* This,
4666   ULARGE_INTEGER    newSize)
4667 {
4668   ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4669
4670   if (newSize.u.LowPart == size.u.LowPart)
4671     return TRUE;
4672
4673   if (newSize.u.LowPart < size.u.LowPart)
4674   {
4675     BlockChainStream_Shrink(This, newSize);
4676   }
4677   else
4678   {
4679     ULARGE_INTEGER fileSize =
4680       BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4681
4682     ULONG diff = newSize.u.LowPart - size.u.LowPart;
4683
4684     /*
4685      * Make sure the file stays a multiple of blocksize
4686      */
4687     if ((diff % This->parentStorage->bigBlockSize) != 0)
4688       diff += (This->parentStorage->bigBlockSize -
4689                 (diff % This->parentStorage->bigBlockSize) );
4690
4691     fileSize.u.LowPart += diff;
4692     BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4693
4694     BlockChainStream_Enlarge(This, newSize);
4695   }
4696
4697   return TRUE;
4698 }
4699
4700 /******************************************************************************
4701  *      BlockChainStream_GetSize
4702  *
4703  * Returns the size of this chain.
4704  * Will return the block count if this chain doesn't have a property.
4705  */
4706 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4707 {
4708   StgProperty chainProperty;
4709
4710   if(This->headOfStreamPlaceHolder == NULL)
4711   {
4712     /*
4713      * This chain is a data stream read the property and return
4714      * the appropriate size
4715      */
4716     StorageImpl_ReadProperty(
4717       This->parentStorage,
4718       This->ownerPropertyIndex,
4719       &chainProperty);
4720
4721     return chainProperty.size;
4722   }
4723   else
4724   {
4725     /*
4726      * this chain is a chain that does not have a property, figure out the
4727      * size by making the product number of used blocks times the
4728      * size of them
4729      */
4730     ULARGE_INTEGER result;
4731     result.u.HighPart = 0;
4732
4733     result.u.LowPart  =
4734       BlockChainStream_GetCount(This) *
4735       This->parentStorage->bigBlockSize;
4736
4737     return result;
4738   }
4739 }
4740
4741 /******************************************************************************
4742 ** SmallBlockChainStream implementation
4743 */
4744
4745 SmallBlockChainStream* SmallBlockChainStream_Construct(
4746   StorageImpl* parentStorage,
4747   ULONG          propertyIndex)
4748 {
4749   SmallBlockChainStream* newStream;
4750
4751   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4752
4753   newStream->parentStorage      = parentStorage;
4754   newStream->ownerPropertyIndex = propertyIndex;
4755
4756   return newStream;
4757 }
4758
4759 void SmallBlockChainStream_Destroy(
4760   SmallBlockChainStream* This)
4761 {
4762   HeapFree(GetProcessHeap(), 0, This);
4763 }
4764
4765 /******************************************************************************
4766  *      SmallBlockChainStream_GetHeadOfChain
4767  *
4768  * Returns the head of this chain of small blocks.
4769  */
4770 ULONG SmallBlockChainStream_GetHeadOfChain(
4771   SmallBlockChainStream* This)
4772 {
4773   StgProperty chainProperty;
4774   BOOL      readSuccessful;
4775
4776   if (This->ownerPropertyIndex)
4777   {
4778     readSuccessful = StorageImpl_ReadProperty(
4779                       This->parentStorage,
4780                       This->ownerPropertyIndex,
4781                       &chainProperty);
4782
4783     if (readSuccessful)
4784     {
4785       return chainProperty.startingBlock;
4786     }
4787
4788   }
4789
4790   return BLOCK_END_OF_CHAIN;
4791 }
4792
4793 /******************************************************************************
4794  *      SmallBlockChainStream_GetNextBlockInChain
4795  *
4796  * Returns the index of the next small block in this chain.
4797  *
4798  * Return Values:
4799  *    - BLOCK_END_OF_CHAIN: end of this chain
4800  *    - BLOCK_UNUSED: small block 'blockIndex' is free
4801  */
4802 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4803   SmallBlockChainStream* This,
4804   ULONG                  blockIndex,
4805   ULONG*                 nextBlockInChain)
4806 {
4807   ULARGE_INTEGER offsetOfBlockInDepot;
4808   DWORD  buffer;
4809   ULONG  bytesRead;
4810   BOOL success;
4811
4812   *nextBlockInChain = BLOCK_END_OF_CHAIN;
4813
4814   offsetOfBlockInDepot.u.HighPart = 0;
4815   offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
4816
4817   /*
4818    * Read those bytes in the buffer from the small block file.
4819    */
4820   success = BlockChainStream_ReadAt(
4821               This->parentStorage->smallBlockDepotChain,
4822               offsetOfBlockInDepot,
4823               sizeof(DWORD),
4824               &buffer,
4825               &bytesRead);
4826
4827   if (success)
4828   {
4829     StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4830     return S_OK;
4831   }
4832
4833   return STG_E_READFAULT;
4834 }
4835
4836 /******************************************************************************
4837  *       SmallBlockChainStream_SetNextBlockInChain
4838  *
4839  * Writes the index of the next block of the specified block in the small
4840  * block depot.
4841  * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4842  * To flag a block as free use BLOCK_UNUSED as nextBlock.
4843  */
4844 void SmallBlockChainStream_SetNextBlockInChain(
4845   SmallBlockChainStream* This,
4846   ULONG                  blockIndex,
4847   ULONG                  nextBlock)
4848 {
4849   ULARGE_INTEGER offsetOfBlockInDepot;
4850   DWORD  buffer;
4851   ULONG  bytesWritten;
4852
4853   offsetOfBlockInDepot.u.HighPart = 0;
4854   offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
4855
4856   StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4857
4858   /*
4859    * Read those bytes in the buffer from the small block file.
4860    */
4861   BlockChainStream_WriteAt(
4862     This->parentStorage->smallBlockDepotChain,
4863     offsetOfBlockInDepot,
4864     sizeof(DWORD),
4865     &buffer,
4866     &bytesWritten);
4867 }
4868
4869 /******************************************************************************
4870  *      SmallBlockChainStream_FreeBlock
4871  *
4872  * Flag small block 'blockIndex' as free in the small block depot.
4873  */
4874 void SmallBlockChainStream_FreeBlock(
4875   SmallBlockChainStream* This,
4876   ULONG                  blockIndex)
4877 {
4878   SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4879 }
4880
4881 /******************************************************************************
4882  *      SmallBlockChainStream_GetNextFreeBlock
4883  *
4884  * Returns the index of a free small block. The small block depot will be
4885  * enlarged if necessary. The small block chain will also be enlarged if
4886  * necessary.
4887  */
4888 ULONG SmallBlockChainStream_GetNextFreeBlock(
4889   SmallBlockChainStream* This)
4890 {
4891   ULARGE_INTEGER offsetOfBlockInDepot;
4892   DWORD buffer;
4893   ULONG bytesRead;
4894   ULONG blockIndex = 0;
4895   ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4896   BOOL success = TRUE;
4897   ULONG smallBlocksPerBigBlock;
4898
4899   offsetOfBlockInDepot.u.HighPart = 0;
4900
4901   /*
4902    * Scan the small block depot for a free block
4903    */
4904   while (nextBlockIndex != BLOCK_UNUSED)
4905   {
4906     offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4907
4908     success = BlockChainStream_ReadAt(
4909                 This->parentStorage->smallBlockDepotChain,
4910                 offsetOfBlockInDepot,
4911                 sizeof(DWORD),
4912                 &buffer,
4913                 &bytesRead);
4914
4915     /*
4916      * If we run out of space for the small block depot, enlarge it
4917      */
4918     if (success)
4919     {
4920       StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4921
4922       if (nextBlockIndex != BLOCK_UNUSED)
4923         blockIndex++;
4924     }
4925     else
4926     {
4927       ULONG count =
4928         BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4929
4930       ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4931       ULONG nextBlock, newsbdIndex;
4932       BYTE* smallBlockDepot;
4933
4934       nextBlock = sbdIndex;
4935       while (nextBlock != BLOCK_END_OF_CHAIN)
4936       {
4937         sbdIndex = nextBlock;
4938         StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4939       }
4940
4941       newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4942       if (sbdIndex != BLOCK_END_OF_CHAIN)
4943         StorageImpl_SetNextBlockInChain(
4944           This->parentStorage,
4945           sbdIndex,
4946           newsbdIndex);
4947
4948       StorageImpl_SetNextBlockInChain(
4949         This->parentStorage,
4950         newsbdIndex,
4951         BLOCK_END_OF_CHAIN);
4952
4953       /*
4954        * Initialize all the small blocks to free
4955        */
4956       smallBlockDepot =
4957         StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4958
4959       memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4960       StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4961
4962       if (count == 0)
4963       {
4964         /*
4965          * We have just created the small block depot.
4966          */
4967         StgProperty rootProp;
4968         ULONG sbStartIndex;
4969
4970         /*
4971          * Save it in the header
4972          */
4973         This->parentStorage->smallBlockDepotStart = newsbdIndex;
4974         StorageImpl_SaveFileHeader(This->parentStorage);
4975
4976         /*
4977          * And allocate the first big block that will contain small blocks
4978          */
4979         sbStartIndex =
4980           StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4981
4982         StorageImpl_SetNextBlockInChain(
4983           This->parentStorage,
4984           sbStartIndex,
4985           BLOCK_END_OF_CHAIN);
4986
4987         StorageImpl_ReadProperty(
4988           This->parentStorage,
4989           This->parentStorage->base.rootPropertySetIndex,
4990           &rootProp);
4991
4992         rootProp.startingBlock = sbStartIndex;
4993         rootProp.size.u.HighPart = 0;
4994         rootProp.size.u.LowPart  = This->parentStorage->bigBlockSize;
4995
4996         StorageImpl_WriteProperty(
4997           This->parentStorage,
4998           This->parentStorage->base.rootPropertySetIndex,
4999           &rootProp);
5000       }
5001     }
5002   }
5003
5004   smallBlocksPerBigBlock =
5005     This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5006
5007   /*
5008    * Verify if we have to allocate big blocks to contain small blocks
5009    */
5010   if (blockIndex % smallBlocksPerBigBlock == 0)
5011   {
5012     StgProperty rootProp;
5013     ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5014
5015     StorageImpl_ReadProperty(
5016       This->parentStorage,
5017       This->parentStorage->base.rootPropertySetIndex,
5018       &rootProp);
5019
5020     if (rootProp.size.u.LowPart <
5021        (blocksRequired * This->parentStorage->bigBlockSize))
5022     {
5023       rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5024
5025       BlockChainStream_SetSize(
5026         This->parentStorage->smallBlockRootChain,
5027         rootProp.size);
5028
5029       StorageImpl_WriteProperty(
5030         This->parentStorage,
5031         This->parentStorage->base.rootPropertySetIndex,
5032         &rootProp);
5033     }
5034   }
5035
5036   return blockIndex;
5037 }
5038
5039 /******************************************************************************
5040  *      SmallBlockChainStream_ReadAt
5041  *
5042  * Reads a specified number of bytes from this chain at the specified offset.
5043  * bytesRead may be NULL.
5044  * Failure will be returned if the specified number of bytes has not been read.
5045  */
5046 BOOL SmallBlockChainStream_ReadAt(
5047   SmallBlockChainStream* This,
5048   ULARGE_INTEGER         offset,
5049   ULONG                  size,
5050   void*                  buffer,
5051   ULONG*                 bytesRead)
5052 {
5053   ULARGE_INTEGER offsetInBigBlockFile;
5054   ULONG blockNoInSequence =
5055     offset.u.LowPart / This->parentStorage->smallBlockSize;
5056
5057   ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5058   ULONG bytesToReadInBuffer;
5059   ULONG blockIndex;
5060   ULONG bytesReadFromBigBlockFile;
5061   BYTE* bufferWalker;
5062
5063   /*
5064    * This should never happen on a small block file.
5065    */
5066   assert(offset.u.HighPart==0);
5067
5068   /*
5069    * Find the first block in the stream that contains part of the buffer.
5070    */
5071   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5072
5073   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
5074   {
5075     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5076                                                         &blockIndex)))
5077       return FALSE;
5078     blockNoInSequence--;
5079   }
5080
5081   /*
5082    * Start reading the buffer.
5083    */
5084   *bytesRead   = 0;
5085   bufferWalker = buffer;
5086
5087   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5088   {
5089     /*
5090      * Calculate how many bytes we can copy from this small block.
5091      */
5092     bytesToReadInBuffer =
5093       min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5094
5095     /*
5096      * Calculate the offset of the small block in the small block file.
5097      */
5098     offsetInBigBlockFile.u.HighPart  = 0;
5099     offsetInBigBlockFile.u.LowPart   =
5100       blockIndex * This->parentStorage->smallBlockSize;
5101
5102     offsetInBigBlockFile.u.LowPart  += offsetInBlock;
5103
5104     /*
5105      * Read those bytes in the buffer from the small block file.
5106      */
5107     BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5108       offsetInBigBlockFile,
5109       bytesToReadInBuffer,
5110       bufferWalker,
5111       &bytesReadFromBigBlockFile);
5112
5113     assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5114
5115     /*
5116      * Step to the next big block.
5117      */
5118     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5119       return FALSE;
5120     bufferWalker += bytesToReadInBuffer;
5121     size         -= bytesToReadInBuffer;
5122     *bytesRead   += bytesToReadInBuffer;
5123     offsetInBlock = 0;  /* There is no offset on the next block */
5124   }
5125
5126   return (size == 0);
5127 }
5128
5129 /******************************************************************************
5130  *       SmallBlockChainStream_WriteAt
5131  *
5132  * Writes the specified number of bytes to this chain at the specified offset.
5133  * bytesWritten may be NULL.
5134  * Will fail if not all specified number of bytes have been written.
5135  */
5136 BOOL SmallBlockChainStream_WriteAt(
5137   SmallBlockChainStream* This,
5138   ULARGE_INTEGER offset,
5139   ULONG          size,
5140   const void*    buffer,
5141   ULONG*         bytesWritten)
5142 {
5143   ULARGE_INTEGER offsetInBigBlockFile;
5144   ULONG blockNoInSequence =
5145     offset.u.LowPart / This->parentStorage->smallBlockSize;
5146
5147   ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5148   ULONG bytesToWriteInBuffer;
5149   ULONG blockIndex;
5150   ULONG bytesWrittenFromBigBlockFile;
5151   const BYTE* bufferWalker;
5152
5153   /*
5154    * This should never happen on a small block file.
5155    */
5156   assert(offset.u.HighPart==0);
5157
5158   /*
5159    * Find the first block in the stream that contains part of the buffer.
5160    */
5161   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5162
5163   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
5164   {
5165     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5166       return FALSE;
5167     blockNoInSequence--;
5168   }
5169
5170   /*
5171    * Start writing the buffer.
5172    *
5173    * Here, I'm casting away the constness on the buffer variable
5174    * This is OK since we don't intend to modify that buffer.
5175    */
5176   *bytesWritten   = 0;
5177   bufferWalker = (const BYTE*)buffer;
5178   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5179   {
5180     /*
5181      * Calculate how many bytes we can copy to this small block.
5182      */
5183     bytesToWriteInBuffer =
5184       min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5185
5186     /*
5187      * Calculate the offset of the small block in the small block file.
5188      */
5189     offsetInBigBlockFile.u.HighPart  = 0;
5190     offsetInBigBlockFile.u.LowPart   =
5191       blockIndex * This->parentStorage->smallBlockSize;
5192
5193     offsetInBigBlockFile.u.LowPart  += offsetInBlock;
5194
5195     /*
5196      * Write those bytes in the buffer to the small block file.
5197      */
5198     BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5199       offsetInBigBlockFile,
5200       bytesToWriteInBuffer,
5201       bufferWalker,
5202       &bytesWrittenFromBigBlockFile);
5203
5204     assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5205
5206     /*
5207      * Step to the next big block.
5208      */
5209     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5210                                                         &blockIndex)))
5211       return FALSE;
5212     bufferWalker  += bytesToWriteInBuffer;
5213     size          -= bytesToWriteInBuffer;
5214     *bytesWritten += bytesToWriteInBuffer;
5215     offsetInBlock  = 0;     /* There is no offset on the next block */
5216   }
5217
5218   return (size == 0);
5219 }
5220
5221 /******************************************************************************
5222  *       SmallBlockChainStream_Shrink
5223  *
5224  * Shrinks this chain in the small block depot.
5225  */
5226 BOOL SmallBlockChainStream_Shrink(
5227   SmallBlockChainStream* This,
5228   ULARGE_INTEGER newSize)
5229 {
5230   ULONG blockIndex, extraBlock;
5231   ULONG numBlocks;
5232   ULONG count = 0;
5233
5234   numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5235
5236   if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5237     numBlocks++;
5238
5239   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5240
5241   /*
5242    * Go to the new end of chain
5243    */
5244   while (count < numBlocks)
5245   {
5246     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5247                                                         &blockIndex)))
5248       return FALSE;
5249     count++;
5250   }
5251
5252   /*
5253    * If the count is 0, we have a special case, the head of the chain was
5254    * just freed.
5255    */
5256   if (count == 0)
5257   {
5258     StgProperty chainProp;
5259
5260     StorageImpl_ReadProperty(This->parentStorage,
5261                              This->ownerPropertyIndex,
5262                              &chainProp);
5263
5264     chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5265
5266     StorageImpl_WriteProperty(This->parentStorage,
5267                               This->ownerPropertyIndex,
5268                               &chainProp);
5269
5270     /*
5271      * We start freeing the chain at the head block.
5272      */
5273     extraBlock = blockIndex;
5274   }
5275   else
5276   {
5277     /* Get the next block before marking the new end */
5278     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5279                                                         &extraBlock)))
5280       return FALSE;
5281
5282     /* Mark the new end of chain */
5283     SmallBlockChainStream_SetNextBlockInChain(
5284       This,
5285       blockIndex,
5286       BLOCK_END_OF_CHAIN);
5287   }
5288
5289   /*
5290    * Mark the extra blocks as free
5291    */
5292   while (extraBlock != BLOCK_END_OF_CHAIN)
5293   {
5294     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5295                                                         &blockIndex)))
5296       return FALSE;
5297     SmallBlockChainStream_FreeBlock(This, extraBlock);
5298     extraBlock = blockIndex;
5299   }
5300
5301   return TRUE;
5302 }
5303
5304 /******************************************************************************
5305  *      SmallBlockChainStream_Enlarge
5306  *
5307  * Grows this chain in the small block depot.
5308  */
5309 BOOL SmallBlockChainStream_Enlarge(
5310   SmallBlockChainStream* This,
5311   ULARGE_INTEGER newSize)
5312 {
5313   ULONG blockIndex, currentBlock;
5314   ULONG newNumBlocks;
5315   ULONG oldNumBlocks = 0;
5316
5317   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5318
5319   /*
5320    * Empty chain
5321    */
5322   if (blockIndex == BLOCK_END_OF_CHAIN)
5323   {
5324
5325     StgProperty chainProp;
5326
5327     StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5328                                &chainProp);
5329
5330     chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5331
5332     StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5333                                 &chainProp);
5334
5335     blockIndex = chainProp.startingBlock;
5336     SmallBlockChainStream_SetNextBlockInChain(
5337       This,
5338       blockIndex,
5339       BLOCK_END_OF_CHAIN);
5340   }
5341
5342   currentBlock = blockIndex;
5343
5344   /*
5345    * Figure out how many blocks are needed to contain this stream
5346    */
5347   newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5348
5349   if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5350     newNumBlocks++;
5351
5352   /*
5353    * Go to the current end of chain
5354    */
5355   while (blockIndex != BLOCK_END_OF_CHAIN)
5356   {
5357     oldNumBlocks++;
5358     currentBlock = blockIndex;
5359     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5360       return FALSE;
5361   }
5362
5363   /*
5364    * Add new blocks to the chain
5365    */
5366   while (oldNumBlocks < newNumBlocks)
5367   {
5368     blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5369     SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5370
5371     SmallBlockChainStream_SetNextBlockInChain(
5372       This,
5373       blockIndex,
5374       BLOCK_END_OF_CHAIN);
5375
5376     currentBlock = blockIndex;
5377     oldNumBlocks++;
5378   }
5379
5380   return TRUE;
5381 }
5382
5383 /******************************************************************************
5384  *      SmallBlockChainStream_GetCount
5385  *
5386  * Returns the number of blocks that comprises this chain.
5387  * This is not the size of this chain as the last block may not be full!
5388  */
5389 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5390 {
5391   ULONG blockIndex;
5392   ULONG count = 0;
5393
5394   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5395
5396   while (blockIndex != BLOCK_END_OF_CHAIN)
5397   {
5398     count++;
5399
5400     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5401       return 0;
5402   }
5403
5404   return count;
5405 }
5406
5407 /******************************************************************************
5408  *      SmallBlockChainStream_SetSize
5409  *
5410  * Sets the size of this stream.
5411  * The file will grow if we grow the chain.
5412  *
5413  * TODO: Free the actual blocks in the file when we shrink the chain.
5414  *       Currently, the blocks are still in the file. So the file size
5415  *       doesn't shrink even if we shrink streams.
5416  */
5417 BOOL SmallBlockChainStream_SetSize(
5418                 SmallBlockChainStream* This,
5419                 ULARGE_INTEGER    newSize)
5420 {
5421   ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5422
5423   if (newSize.u.LowPart == size.u.LowPart)
5424     return TRUE;
5425
5426   if (newSize.u.LowPart < size.u.LowPart)
5427   {
5428     SmallBlockChainStream_Shrink(This, newSize);
5429   }
5430   else
5431   {
5432     SmallBlockChainStream_Enlarge(This, newSize);
5433   }
5434
5435   return TRUE;
5436 }
5437
5438 /******************************************************************************
5439  *      SmallBlockChainStream_GetSize
5440  *
5441  * Returns the size of this chain.
5442  */
5443 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5444 {
5445   StgProperty chainProperty;
5446
5447   StorageImpl_ReadProperty(
5448     This->parentStorage,
5449     This->ownerPropertyIndex,
5450     &chainProperty);
5451
5452   return chainProperty.size;
5453 }
5454
5455 /******************************************************************************
5456  *    StgCreateDocfile  [OLE32.@]
5457  */
5458 HRESULT WINAPI StgCreateDocfile(
5459   LPCOLESTR pwcsName,
5460   DWORD       grfMode,
5461   DWORD       reserved,
5462   IStorage  **ppstgOpen)
5463 {
5464   StorageImpl* newStorage = 0;
5465   HANDLE       hFile      = INVALID_HANDLE_VALUE;
5466   HRESULT        hr         = STG_E_INVALIDFLAG;
5467   DWORD          shareMode;
5468   DWORD          accessMode;
5469   DWORD          creationMode;
5470   DWORD          fileAttributes;
5471   WCHAR          tempFileName[MAX_PATH];
5472
5473   TRACE("(%s, %lx, %ld, %p)\n",
5474         debugstr_w(pwcsName), grfMode,
5475         reserved, ppstgOpen);
5476
5477   /*
5478    * Validate the parameters
5479    */
5480   if (ppstgOpen == 0)
5481     return STG_E_INVALIDPOINTER;
5482   if (reserved != 0)
5483     return STG_E_INVALIDPARAMETER;
5484
5485   /*
5486    * Validate the STGM flags
5487    */
5488   if ( FAILED( validateSTGM(grfMode) ))
5489     goto end;
5490
5491   /* StgCreateDocFile always opens for write */
5492   switch(STGM_ACCESS_MODE(grfMode))
5493   {
5494   case STGM_WRITE:
5495   case STGM_READWRITE:
5496     break;
5497   default:
5498     goto end;
5499   }
5500
5501   /* can't share write */
5502   switch(STGM_SHARE_MODE(grfMode))
5503   {
5504   case STGM_SHARE_EXCLUSIVE:
5505   case STGM_SHARE_DENY_WRITE:
5506     break;
5507   default:
5508     goto end;
5509   }
5510
5511   /* shared reading requires transacted mode */
5512   if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5513      !(grfMode&STGM_TRANSACTED) )
5514     goto end;
5515
5516   /*
5517    * Generate a unique name.
5518    */
5519   if (pwcsName == 0)
5520   {
5521     WCHAR tempPath[MAX_PATH];
5522     static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5523
5524     if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5525       goto end;
5526
5527     memset(tempPath, 0, sizeof(tempPath));
5528     memset(tempFileName, 0, sizeof(tempFileName));
5529
5530     if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5531       tempPath[0] = '.';
5532
5533     if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5534       pwcsName = tempFileName;
5535     else
5536     {
5537       hr = STG_E_INSUFFICIENTMEMORY;
5538       goto end;
5539     }
5540
5541     creationMode = TRUNCATE_EXISTING;
5542   }
5543   else
5544   {
5545     creationMode = GetCreationModeFromSTGM(grfMode);
5546   }
5547
5548   /*
5549    * Interpret the STGM value grfMode
5550    */
5551   shareMode    = GetShareModeFromSTGM(grfMode);
5552   accessMode   = GetAccessModeFromSTGM(grfMode);
5553
5554   if (grfMode & STGM_DELETEONRELEASE)
5555     fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5556   else
5557     fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5558
5559   if (grfMode & STGM_TRANSACTED)
5560     FIXME("Transacted mode not implemented.\n");
5561
5562   /*
5563    * Initialize the "out" parameter.
5564    */
5565   *ppstgOpen = 0;
5566
5567   hFile = CreateFileW(pwcsName,
5568                         accessMode,
5569                         shareMode,
5570                         NULL,
5571                         creationMode,
5572                         fileAttributes,
5573                         0);
5574
5575   if (hFile == INVALID_HANDLE_VALUE)
5576   {
5577     if(GetLastError() == ERROR_FILE_EXISTS)
5578       hr = STG_E_FILEALREADYEXISTS;
5579     else
5580       hr = E_FAIL;
5581     goto end;
5582   }
5583
5584   /*
5585    * Allocate and initialize the new IStorage32object.
5586    */
5587   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5588
5589   if (newStorage == 0)
5590   {
5591     hr = STG_E_INSUFFICIENTMEMORY;
5592     goto end;
5593   }
5594
5595   hr = StorageImpl_Construct(
5596          newStorage,
5597          hFile,
5598         pwcsName,
5599          NULL,
5600          grfMode,
5601          TRUE,
5602          TRUE);
5603
5604   if (FAILED(hr))
5605   {
5606     HeapFree(GetProcessHeap(), 0, newStorage);
5607     goto end;
5608   }
5609
5610   /*
5611    * Get an "out" pointer for the caller.
5612    */
5613   hr = StorageBaseImpl_QueryInterface(
5614          (IStorage*)newStorage,
5615          (REFIID)&IID_IStorage,
5616          (void**)ppstgOpen);
5617 end:
5618   TRACE("<-- %p  r = %08lx\n", *ppstgOpen, hr);
5619
5620   return hr;
5621 }
5622
5623 /******************************************************************************
5624  *              StgCreateStorageEx        [OLE32.@]
5625  */
5626 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5627 {
5628     TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5629           grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5630
5631     if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5632     {
5633         ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5634         return STG_E_INVALIDPARAMETER;  
5635     }
5636
5637     if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5638     {
5639         ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5640         return STG_E_INVALIDPARAMETER;  
5641     }
5642
5643     if (stgfmt == STGFMT_FILE)
5644     {
5645         ERR("Cannot use STGFMT_FILE - this is NTFS only\n");  
5646         return STG_E_INVALIDPARAMETER;
5647     }
5648
5649     if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5650     {
5651         FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5652         return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen); 
5653     }
5654
5655     ERR("Invalid stgfmt argument\n");
5656     return STG_E_INVALIDPARAMETER;
5657 }
5658
5659 /******************************************************************************
5660  *              StgCreatePropSetStg       [OLE32.@]
5661  */
5662 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5663  IPropertySetStorage **ppPropSetStg)
5664 {
5665     HRESULT hr;
5666
5667     TRACE("(%p, 0x%lx, %p): stub\n", pstg, reserved, ppPropSetStg);
5668     if (reserved)
5669         hr = STG_E_INVALIDPARAMETER;
5670     else
5671         hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5672          (void**)ppPropSetStg);
5673     return hr;
5674 }
5675
5676 /******************************************************************************
5677  *              StgOpenStorageEx      [OLE32.@]
5678  */
5679 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5680 {
5681     TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5682           grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5683
5684     if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5685     {
5686         ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5687         return STG_E_INVALIDPARAMETER;  
5688     }
5689
5690     if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5691     {
5692         ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5693         return STG_E_INVALIDPARAMETER;  
5694     }
5695
5696     if (stgfmt == STGFMT_FILE)
5697     {
5698         ERR("Cannot use STGFMT_FILE - this is NTFS only\n");  
5699         return STG_E_INVALIDPARAMETER;
5700     }
5701
5702     if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5703     {
5704         if (stgfmt == STGFMT_ANY) 
5705             WARN("STGFMT_ANY assuming storage\n");
5706         FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5707         return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen); 
5708     }
5709
5710     ERR("Invalid stgfmt argument\n");
5711     return STG_E_INVALIDPARAMETER;
5712 }
5713
5714
5715 /******************************************************************************
5716  *              StgOpenStorage        [OLE32.@]
5717  */
5718 HRESULT WINAPI StgOpenStorage(
5719   const OLECHAR *pwcsName,
5720   IStorage      *pstgPriority,
5721   DWORD           grfMode,
5722   SNB           snbExclude,
5723   DWORD           reserved,
5724   IStorage      **ppstgOpen)
5725 {
5726   StorageImpl* newStorage = 0;
5727   HRESULT        hr = S_OK;
5728   HANDLE       hFile = 0;
5729   DWORD          shareMode;
5730   DWORD          accessMode;
5731   WCHAR          fullname[MAX_PATH];
5732   DWORD          length;
5733
5734   TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5735         debugstr_w(pwcsName), pstgPriority, grfMode,
5736         snbExclude, reserved, ppstgOpen);
5737
5738   /*
5739    * Perform sanity checks
5740    */
5741   if (pwcsName == 0)
5742   {
5743     hr = STG_E_INVALIDNAME;
5744     goto end;
5745   }
5746
5747   if (ppstgOpen == 0)
5748   {
5749     hr = STG_E_INVALIDPOINTER;
5750     goto end;
5751   }
5752
5753   if (reserved)
5754   {
5755     hr = STG_E_INVALIDPARAMETER;
5756     goto end;
5757   }
5758
5759   /*
5760    * Validate the sharing mode
5761    */
5762   switch(STGM_SHARE_MODE(grfMode))
5763   {
5764   case STGM_SHARE_EXCLUSIVE:
5765   case STGM_SHARE_DENY_WRITE:
5766     break;
5767   default:
5768     hr = STG_E_INVALIDFLAG;
5769     goto end;
5770   }
5771
5772   /*
5773    * Validate the STGM flags
5774    */
5775   if ( FAILED( validateSTGM(grfMode) ) ||
5776        (grfMode&STGM_CREATE))
5777   {
5778     hr = STG_E_INVALIDFLAG;
5779     goto end;
5780   }
5781
5782   /* shared reading requires transacted mode */
5783   if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5784       STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5785      !(grfMode&STGM_TRANSACTED) )
5786   {
5787     hr = STG_E_INVALIDFLAG;
5788     goto end;
5789   }
5790
5791   /*
5792    * Interpret the STGM value grfMode
5793    */
5794   shareMode    = GetShareModeFromSTGM(grfMode);
5795   accessMode   = GetAccessModeFromSTGM(grfMode);
5796
5797   /*
5798    * Initialize the "out" parameter.
5799    */
5800   *ppstgOpen = 0;
5801
5802   hFile = CreateFileW( pwcsName,
5803                        accessMode,
5804                        shareMode,
5805                        NULL,
5806                        OPEN_EXISTING,
5807                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5808                        0);
5809
5810   if (hFile==INVALID_HANDLE_VALUE)
5811   {
5812     DWORD last_error = GetLastError();
5813
5814     hr = E_FAIL;
5815
5816     switch (last_error)
5817     {
5818       case ERROR_FILE_NOT_FOUND:
5819         hr = STG_E_FILENOTFOUND;
5820         break;
5821
5822       case ERROR_PATH_NOT_FOUND:
5823         hr = STG_E_PATHNOTFOUND;
5824         break;
5825
5826       case ERROR_ACCESS_DENIED:
5827       case ERROR_WRITE_PROTECT:
5828         hr = STG_E_ACCESSDENIED;
5829         break;
5830
5831       case ERROR_SHARING_VIOLATION:
5832         hr = STG_E_SHAREVIOLATION;
5833         break;
5834
5835       default:
5836         hr = E_FAIL;
5837     }
5838
5839     goto end;
5840   }
5841
5842   /*
5843    * Refuse to open the file if it's too small to be a structured storage file
5844    * FIXME: verify the file when reading instead of here
5845    */
5846   length = GetFileSize(hFile, NULL);
5847   if (length < 0x100)
5848   {
5849     CloseHandle(hFile);
5850     hr = STG_E_FILEALREADYEXISTS;
5851     goto end;
5852   }
5853
5854   /*
5855    * Allocate and initialize the new IStorage32object.
5856    */
5857   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5858
5859   if (newStorage == 0)
5860   {
5861     hr = STG_E_INSUFFICIENTMEMORY;
5862     goto end;
5863   }
5864
5865   /* if the file's length was zero, initialize the storage */
5866   hr = StorageImpl_Construct(
5867          newStorage,
5868          hFile,
5869         pwcsName,
5870          NULL,
5871          grfMode,
5872          TRUE,
5873          FALSE );
5874
5875   if (FAILED(hr))
5876   {
5877     HeapFree(GetProcessHeap(), 0, newStorage);
5878     /*
5879      * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5880      */
5881     if(hr == STG_E_INVALIDHEADER)
5882         hr = STG_E_FILEALREADYEXISTS;
5883     goto end;
5884   }
5885
5886   /* prepare the file name string given in lieu of the root property name */
5887   GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5888   memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5889   newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5890
5891   /*
5892    * Get an "out" pointer for the caller.
5893    */
5894   hr = StorageBaseImpl_QueryInterface(
5895          (IStorage*)newStorage,
5896          (REFIID)&IID_IStorage,
5897          (void**)ppstgOpen);
5898
5899 end:
5900   TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5901   return hr;
5902 }
5903
5904 /******************************************************************************
5905  *    StgCreateDocfileOnILockBytes    [OLE32.@]
5906  */
5907 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5908       ILockBytes *plkbyt,
5909       DWORD grfMode,
5910       DWORD reserved,
5911       IStorage** ppstgOpen)
5912 {
5913   StorageImpl*   newStorage = 0;
5914   HRESULT        hr         = S_OK;
5915
5916   /*
5917    * Validate the parameters
5918    */
5919   if ((ppstgOpen == 0) || (plkbyt == 0))
5920     return STG_E_INVALIDPOINTER;
5921
5922   /*
5923    * Allocate and initialize the new IStorage object.
5924    */
5925   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5926
5927   if (newStorage == 0)
5928     return STG_E_INSUFFICIENTMEMORY;
5929
5930   hr = StorageImpl_Construct(
5931          newStorage,
5932          0,
5933         0,
5934          plkbyt,
5935          grfMode,
5936          FALSE,
5937          TRUE);
5938
5939   if (FAILED(hr))
5940   {
5941     HeapFree(GetProcessHeap(), 0, newStorage);
5942     return hr;
5943   }
5944
5945   /*
5946    * Get an "out" pointer for the caller.
5947    */
5948   hr = StorageBaseImpl_QueryInterface(
5949          (IStorage*)newStorage,
5950          (REFIID)&IID_IStorage,
5951          (void**)ppstgOpen);
5952
5953   return hr;
5954 }
5955
5956 /******************************************************************************
5957  *    StgOpenStorageOnILockBytes    [OLE32.@]
5958  */
5959 HRESULT WINAPI StgOpenStorageOnILockBytes(
5960       ILockBytes *plkbyt,
5961       IStorage *pstgPriority,
5962       DWORD grfMode,
5963       SNB snbExclude,
5964       DWORD reserved,
5965       IStorage **ppstgOpen)
5966 {
5967   StorageImpl* newStorage = 0;
5968   HRESULT        hr = S_OK;
5969
5970   /*
5971    * Perform a sanity check
5972    */
5973   if ((plkbyt == 0) || (ppstgOpen == 0))
5974     return STG_E_INVALIDPOINTER;
5975
5976   /*
5977    * Validate the STGM flags
5978    */
5979   if ( FAILED( validateSTGM(grfMode) ))
5980     return STG_E_INVALIDFLAG;
5981
5982   /*
5983    * Initialize the "out" parameter.
5984    */
5985   *ppstgOpen = 0;
5986
5987   /*
5988    * Allocate and initialize the new IStorage object.
5989    */
5990   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5991
5992   if (newStorage == 0)
5993     return STG_E_INSUFFICIENTMEMORY;
5994
5995   hr = StorageImpl_Construct(
5996          newStorage,
5997          0,
5998          0,
5999          plkbyt,
6000          grfMode,
6001          FALSE,
6002          FALSE);
6003
6004   if (FAILED(hr))
6005   {
6006     HeapFree(GetProcessHeap(), 0, newStorage);
6007     return hr;
6008   }
6009
6010   /*
6011    * Get an "out" pointer for the caller.
6012    */
6013   hr = StorageBaseImpl_QueryInterface(
6014          (IStorage*)newStorage,
6015          (REFIID)&IID_IStorage,
6016          (void**)ppstgOpen);
6017
6018   return hr;
6019 }
6020
6021 /******************************************************************************
6022  *              StgSetTimes [ole32.@]
6023  *              StgSetTimes [OLE32.@]
6024  *
6025  *
6026  */
6027 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6028                            FILETIME const *patime, FILETIME const *pmtime)
6029 {
6030   IStorage *stg = NULL;
6031   HRESULT r;
6032  
6033   TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6034
6035   r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6036                      0, 0, &stg);
6037   if( SUCCEEDED(r) )
6038   {
6039     r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6040     IStorage_Release(stg);
6041   }
6042
6043   return r;
6044 }
6045
6046 /******************************************************************************
6047  *              StgIsStorageILockBytes        [OLE32.@]
6048  *
6049  * Determines if the ILockBytes contains a storage object.
6050  */
6051 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6052 {
6053   BYTE sig[8];
6054   ULARGE_INTEGER offset;
6055
6056   offset.u.HighPart = 0;
6057   offset.u.LowPart  = 0;
6058
6059   ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6060
6061   if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6062     return S_OK;
6063
6064   return S_FALSE;
6065 }
6066
6067 /******************************************************************************
6068  *              WriteClassStg        [OLE32.@]
6069  *
6070  * This method will store the specified CLSID in the specified storage object
6071  */
6072 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6073 {
6074   HRESULT hRes;
6075
6076   assert(pStg != 0);
6077
6078   hRes = IStorage_SetClass(pStg, rclsid);
6079
6080   return hRes;
6081 }
6082
6083 /***********************************************************************
6084  *    ReadClassStg (OLE32.@)
6085  *
6086  * This method reads the CLSID previously written to a storage object with the WriteClassStg.
6087  */
6088 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6089
6090     STATSTG pstatstg;
6091     HRESULT hRes;
6092
6093     TRACE("()\n");
6094
6095     if(pclsid==NULL)
6096         return E_POINTER;
6097    /*
6098     * read a STATSTG structure (contains the clsid) from the storage
6099     */
6100     hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6101
6102     if(SUCCEEDED(hRes))
6103         *pclsid=pstatstg.clsid;
6104
6105     return hRes;
6106 }
6107
6108 /***********************************************************************
6109  *    OleLoadFromStream (OLE32.@)
6110  *
6111  * This function loads an object from stream
6112  */
6113 HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6114 {
6115     CLSID       clsid;
6116     HRESULT     res;
6117     LPPERSISTSTREAM     xstm;
6118
6119     TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6120
6121     res=ReadClassStm(pStm,&clsid);
6122     if (!SUCCEEDED(res))
6123         return res;
6124     res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6125     if (!SUCCEEDED(res))
6126         return res;
6127     res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6128     if (!SUCCEEDED(res)) {
6129         IUnknown_Release((IUnknown*)*ppvObj);
6130         return res;
6131     }
6132     res=IPersistStream_Load(xstm,pStm);
6133     IPersistStream_Release(xstm);
6134     /* FIXME: all refcounts ok at this point? I think they should be:
6135      *          pStm    : unchanged
6136      *          ppvObj  : 1
6137      *          xstm    : 0 (released)
6138      */
6139     return res;
6140 }
6141
6142 /***********************************************************************
6143  *    OleSaveToStream (OLE32.@)
6144  *
6145  * This function saves an object with the IPersistStream interface on it
6146  * to the specified stream.
6147  */
6148 HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6149 {
6150
6151     CLSID clsid;
6152     HRESULT res;
6153
6154     TRACE("(%p,%p)\n",pPStm,pStm);
6155
6156     res=IPersistStream_GetClassID(pPStm,&clsid);
6157
6158     if (SUCCEEDED(res)){
6159
6160         res=WriteClassStm(pStm,&clsid);
6161
6162         if (SUCCEEDED(res))
6163
6164             res=IPersistStream_Save(pPStm,pStm,TRUE);
6165     }
6166
6167     TRACE("Finished Save\n");
6168     return res;
6169 }
6170
6171 /****************************************************************************
6172  * This method validate a STGM parameter that can contain the values below
6173  *
6174  * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6175  * The stgm values contained in 0xffff0000 are bitmasks.
6176  *
6177  * STGM_DIRECT               0x00000000
6178  * STGM_TRANSACTED           0x00010000
6179  * STGM_SIMPLE               0x08000000
6180  *
6181  * STGM_READ                 0x00000000
6182  * STGM_WRITE                0x00000001
6183  * STGM_READWRITE            0x00000002
6184  *
6185  * STGM_SHARE_DENY_NONE      0x00000040
6186  * STGM_SHARE_DENY_READ      0x00000030
6187  * STGM_SHARE_DENY_WRITE     0x00000020
6188  * STGM_SHARE_EXCLUSIVE      0x00000010
6189  *
6190  * STGM_PRIORITY             0x00040000
6191  * STGM_DELETEONRELEASE      0x04000000
6192  *
6193  * STGM_CREATE               0x00001000
6194  * STGM_CONVERT              0x00020000
6195  * STGM_FAILIFTHERE          0x00000000
6196  *
6197  * STGM_NOSCRATCH            0x00100000
6198  * STGM_NOSNAPSHOT           0x00200000
6199  */
6200 static HRESULT validateSTGM(DWORD stgm)
6201 {
6202   DWORD access = STGM_ACCESS_MODE(stgm);
6203   DWORD share  = STGM_SHARE_MODE(stgm);
6204   DWORD create = STGM_CREATE_MODE(stgm);
6205
6206   if (stgm&~STGM_KNOWN_FLAGS)
6207   {
6208     ERR("unknown flags %08lx\n", stgm);
6209     return E_FAIL;
6210   }
6211
6212   switch (access)
6213   {
6214   case STGM_READ:
6215   case STGM_WRITE:
6216   case STGM_READWRITE:
6217     break;
6218   default:
6219     return E_FAIL;
6220   }
6221
6222   switch (share)
6223   {
6224   case STGM_SHARE_DENY_NONE:
6225   case STGM_SHARE_DENY_READ:
6226   case STGM_SHARE_DENY_WRITE:
6227   case STGM_SHARE_EXCLUSIVE:
6228     break;
6229   default:
6230     return E_FAIL;
6231   }
6232
6233   switch (create)
6234   {
6235   case STGM_CREATE:
6236   case STGM_FAILIFTHERE:
6237     break;
6238   default:
6239     return E_FAIL;
6240   }
6241
6242   /*
6243    * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6244    */
6245   if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6246       return E_FAIL;
6247
6248   /*
6249    * STGM_CREATE | STGM_CONVERT
6250    * if both are false, STGM_FAILIFTHERE is set to TRUE
6251    */
6252   if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6253     return E_FAIL;
6254
6255   /*
6256    * STGM_NOSCRATCH requires STGM_TRANSACTED
6257    */
6258   if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6259     return E_FAIL;
6260
6261   /*
6262    * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6263    * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6264    */
6265   if ( (stgm & STGM_NOSNAPSHOT) &&
6266         (!(stgm & STGM_TRANSACTED) ||
6267          share == STGM_SHARE_EXCLUSIVE ||
6268          share == STGM_SHARE_DENY_WRITE) )
6269     return E_FAIL;
6270
6271   return S_OK;
6272 }
6273
6274 /****************************************************************************
6275  *      GetShareModeFromSTGM
6276  *
6277  * This method will return a share mode flag from a STGM value.
6278  * The STGM value is assumed valid.
6279  */
6280 static DWORD GetShareModeFromSTGM(DWORD stgm)
6281 {
6282   switch (STGM_SHARE_MODE(stgm))
6283   {
6284   case STGM_SHARE_DENY_NONE:
6285     return FILE_SHARE_READ | FILE_SHARE_WRITE;
6286   case STGM_SHARE_DENY_READ:
6287     return FILE_SHARE_WRITE;
6288   case STGM_SHARE_DENY_WRITE:
6289     return FILE_SHARE_READ;
6290   case STGM_SHARE_EXCLUSIVE:
6291     return 0;
6292   }
6293   ERR("Invalid share mode!\n");
6294   assert(0);
6295   return 0;
6296 }
6297
6298 /****************************************************************************
6299  *      GetAccessModeFromSTGM
6300  *
6301  * This method will return an access mode flag from a STGM value.
6302  * The STGM value is assumed valid.
6303  */
6304 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6305 {
6306   switch (STGM_ACCESS_MODE(stgm))
6307   {
6308   case STGM_READ:
6309     return GENERIC_READ;
6310   case STGM_WRITE:
6311   case STGM_READWRITE:
6312     return GENERIC_READ | GENERIC_WRITE;
6313   }
6314   ERR("Invalid access mode!\n");
6315   assert(0);
6316   return 0;
6317 }
6318
6319 /****************************************************************************
6320  *      GetCreationModeFromSTGM
6321  *
6322  * This method will return a creation mode flag from a STGM value.
6323  * The STGM value is assumed valid.
6324  */
6325 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6326 {
6327   switch(STGM_CREATE_MODE(stgm))
6328   {
6329   case STGM_CREATE:
6330     return CREATE_ALWAYS;
6331   case STGM_CONVERT:
6332     FIXME("STGM_CONVERT not implemented!\n");
6333     return CREATE_NEW;
6334   case STGM_FAILIFTHERE:
6335     return CREATE_NEW;
6336   }
6337   ERR("Invalid create mode!\n");
6338   assert(0);
6339   return 0;
6340 }
6341
6342
6343 /*************************************************************************
6344  * OLECONVERT_LoadOLE10 [Internal]
6345  *
6346  * Loads the OLE10 STREAM to memory
6347  *
6348  * PARAMS
6349  *     pOleStream   [I] The OLESTREAM
6350  *     pData        [I] Data Structure for the OLESTREAM Data
6351  *
6352  * RETURNS
6353  *     Success:  S_OK
6354  *     Failure:  CONVERT10_E_OLESTREAM_GET for invalid Get
6355  *               CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6356  *
6357  * NOTES
6358  *     This function is used by OleConvertOLESTREAMToIStorage only.
6359  *
6360  *     Memory allocated for pData must be freed by the caller
6361  */
6362 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6363 {
6364         DWORD dwSize;
6365         HRESULT hRes = S_OK;
6366         int nTryCnt=0;
6367         int max_try = 6;
6368
6369         pData->pData = NULL;
6370         pData->pstrOleObjFileName = (CHAR *) NULL;
6371
6372         for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6373         {
6374         /* Get the OleID */
6375         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6376         if(dwSize != sizeof(pData->dwOleID))
6377         {
6378                 hRes = CONVERT10_E_OLESTREAM_GET;
6379         }
6380         else if(pData->dwOleID != OLESTREAM_ID)
6381         {
6382                 hRes = CONVERT10_E_OLESTREAM_FMT;
6383         }
6384                 else
6385                 {
6386                         hRes = S_OK;
6387                         break;
6388                 }
6389         }
6390
6391         if(hRes == S_OK)
6392         {
6393                 /* Get the TypeID...more info needed for this field */
6394                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6395                 if(dwSize != sizeof(pData->dwTypeID))
6396                 {
6397                         hRes = CONVERT10_E_OLESTREAM_GET;
6398                 }
6399         }
6400         if(hRes == S_OK)
6401         {
6402                 if(pData->dwTypeID != 0)
6403                 {
6404                         /* Get the length of the OleTypeName */
6405                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6406                         if(dwSize != sizeof(pData->dwOleTypeNameLength))
6407                         {
6408                                 hRes = CONVERT10_E_OLESTREAM_GET;
6409                         }
6410
6411                         if(hRes == S_OK)
6412                         {
6413                                 if(pData->dwOleTypeNameLength > 0)
6414                                 {
6415                                         /* Get the OleTypeName */
6416                                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6417                                         if(dwSize != pData->dwOleTypeNameLength)
6418                                         {
6419                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6420                                         }
6421                                 }
6422                         }
6423                         if(bStrem1)
6424                         {
6425                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6426                                 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6427                                 {
6428                                         hRes = CONVERT10_E_OLESTREAM_GET;
6429                                 }
6430                         if(hRes == S_OK)
6431                         {
6432                                         if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6433                                                 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6434                                         pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6435                                         if(pData->pstrOleObjFileName)
6436                                         {
6437                                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6438                                                 if(dwSize != pData->dwOleObjFileNameLength)
6439                                                 {
6440                                                         hRes = CONVERT10_E_OLESTREAM_GET;
6441                                                 }
6442                                         }
6443                                         else
6444                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6445                                 }
6446                         }
6447                         else
6448                         {
6449                                 /* Get the Width of the Metafile */
6450                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6451                                 if(dwSize != sizeof(pData->dwMetaFileWidth))
6452                                 {
6453                                         hRes = CONVERT10_E_OLESTREAM_GET;
6454                                 }
6455                         if(hRes == S_OK)
6456                         {
6457                                 /* Get the Height of the Metafile */
6458                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6459                                 if(dwSize != sizeof(pData->dwMetaFileHeight))
6460                                 {
6461                                         hRes = CONVERT10_E_OLESTREAM_GET;
6462                                 }
6463                         }
6464                         }
6465                         if(hRes == S_OK)
6466                         {
6467                                 /* Get the Length of the Data */
6468                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6469                                 if(dwSize != sizeof(pData->dwDataLength))
6470                                 {
6471                                         hRes = CONVERT10_E_OLESTREAM_GET;
6472                                 }
6473                         }
6474
6475                         if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6476                         {
6477                                 if(!bStrem1) /* if it is a second OLE stream data */
6478                                 {
6479                                         pData->dwDataLength -= 8;
6480                                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6481                                         if(dwSize != sizeof(pData->strUnknown))
6482                                         {
6483                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6484                                         }
6485                                 }
6486                         }
6487                         if(hRes == S_OK)
6488                         {
6489                                 if(pData->dwDataLength > 0)
6490                                 {
6491                                         pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6492
6493                                         /* Get Data (ex. IStorage, Metafile, or BMP) */
6494                                         if(pData->pData)
6495                                         {
6496                                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6497                                                 if(dwSize != pData->dwDataLength)
6498                                                 {
6499                                                         hRes = CONVERT10_E_OLESTREAM_GET;
6500                                                 }
6501                                         }
6502                                         else
6503                                         {
6504                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6505                                         }
6506                                 }
6507                         }
6508                 }
6509         }
6510         return hRes;
6511 }
6512
6513 /*************************************************************************
6514  * OLECONVERT_SaveOLE10 [Internal]
6515  *
6516  * Saves the OLE10 STREAM From memory
6517  *
6518  * PARAMS
6519  *     pData        [I] Data Structure for the OLESTREAM Data
6520  *     pOleStream   [I] The OLESTREAM to save
6521  *
6522  * RETURNS
6523  *     Success:  S_OK
6524  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
6525  *
6526  * NOTES
6527  *     This function is used by OleConvertIStorageToOLESTREAM only.
6528  *
6529  */
6530 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6531 {
6532     DWORD dwSize;
6533     HRESULT hRes = S_OK;
6534
6535
6536    /* Set the OleID */
6537     dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6538     if(dwSize != sizeof(pData->dwOleID))
6539     {
6540         hRes = CONVERT10_E_OLESTREAM_PUT;
6541     }
6542
6543     if(hRes == S_OK)
6544     {
6545         /* Set the TypeID */
6546         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6547         if(dwSize != sizeof(pData->dwTypeID))
6548         {
6549             hRes = CONVERT10_E_OLESTREAM_PUT;
6550         }
6551     }
6552
6553     if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6554     {
6555         /* Set the Length of the OleTypeName */
6556         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6557         if(dwSize != sizeof(pData->dwOleTypeNameLength))
6558         {
6559             hRes = CONVERT10_E_OLESTREAM_PUT;
6560         }
6561
6562         if(hRes == S_OK)
6563         {
6564             if(pData->dwOleTypeNameLength > 0)
6565             {
6566                 /* Set the OleTypeName */
6567                 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->strOleTypeName, pData->dwOleTypeNameLength);
6568                 if(dwSize != pData->dwOleTypeNameLength)
6569                 {
6570                     hRes = CONVERT10_E_OLESTREAM_PUT;
6571                 }
6572             }
6573         }
6574
6575         if(hRes == S_OK)
6576         {
6577             /* Set the width of the Metafile */
6578             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6579             if(dwSize != sizeof(pData->dwMetaFileWidth))
6580             {
6581                 hRes = CONVERT10_E_OLESTREAM_PUT;
6582             }
6583         }
6584
6585         if(hRes == S_OK)
6586         {
6587             /* Set the height of the Metafile */
6588             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6589             if(dwSize != sizeof(pData->dwMetaFileHeight))
6590             {
6591                 hRes = CONVERT10_E_OLESTREAM_PUT;
6592             }
6593         }
6594
6595         if(hRes == S_OK)
6596         {
6597             /* Set the length of the Data */
6598             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6599             if(dwSize != sizeof(pData->dwDataLength))
6600             {
6601                 hRes = CONVERT10_E_OLESTREAM_PUT;
6602             }
6603         }
6604
6605         if(hRes == S_OK)
6606         {
6607             if(pData->dwDataLength > 0)
6608             {
6609                 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6610                 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->pData, pData->dwDataLength);
6611                 if(dwSize != pData->dwDataLength)
6612                 {
6613                     hRes = CONVERT10_E_OLESTREAM_PUT;
6614                 }
6615             }
6616         }
6617     }
6618     return hRes;
6619 }
6620
6621 /*************************************************************************
6622  * OLECONVERT_GetOLE20FromOLE10[Internal]
6623  *
6624  * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6625  * opens it, and copies the content to the dest IStorage for
6626  * OleConvertOLESTREAMToIStorage
6627  *
6628  *
6629  * PARAMS
6630  *     pDestStorage  [I] The IStorage to copy the data to
6631  *     pBuffer       [I] Buffer that contains the IStorage from the OLESTREAM
6632  *     nBufferLength [I] The size of the buffer
6633  *
6634  * RETURNS
6635  *     Nothing
6636  *
6637  * NOTES
6638  *
6639  *
6640  */
6641 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6642 {
6643     HRESULT hRes;
6644     HANDLE hFile;
6645     IStorage *pTempStorage;
6646     DWORD dwNumOfBytesWritten;
6647     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6648     static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6649
6650     /* Create a temp File */
6651     GetTempPathW(MAX_PATH, wstrTempDir);
6652     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6653     hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6654
6655     if(hFile != INVALID_HANDLE_VALUE)
6656     {
6657         /* Write IStorage Data to File */
6658         WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6659         CloseHandle(hFile);
6660
6661         /* Open and copy temp storage to the Dest Storage */
6662         hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6663         if(hRes == S_OK)
6664         {
6665             hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6666             StorageBaseImpl_Release(pTempStorage);
6667         }
6668         DeleteFileW(wstrTempFile);
6669     }
6670 }
6671
6672
6673 /*************************************************************************
6674  * OLECONVERT_WriteOLE20ToBuffer [Internal]
6675  *
6676  * Saves the OLE10 STREAM From memory
6677  *
6678  * PARAMS
6679  *     pStorage  [I] The Src IStorage to copy
6680  *     pData     [I] The Dest Memory to write to.
6681  *
6682  * RETURNS
6683  *     The size in bytes allocated for pData
6684  *
6685  * NOTES
6686  *     Memory allocated for pData must be freed by the caller
6687  *
6688  *     Used by OleConvertIStorageToOLESTREAM only.
6689  *
6690  */
6691 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6692 {
6693     HANDLE hFile;
6694     HRESULT hRes;
6695     DWORD nDataLength = 0;
6696     IStorage *pTempStorage;
6697     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6698     static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6699
6700     *pData = NULL;
6701
6702     /* Create temp Storage */
6703     GetTempPathW(MAX_PATH, wstrTempDir);
6704     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6705     hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6706
6707     if(hRes == S_OK)
6708     {
6709         /* Copy Src Storage to the Temp Storage */
6710         StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6711         StorageBaseImpl_Release(pTempStorage);
6712
6713         /* Open Temp Storage as a file and copy to memory */
6714         hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6715         if(hFile != INVALID_HANDLE_VALUE)
6716         {
6717             nDataLength = GetFileSize(hFile, NULL);
6718             *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6719             ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6720             CloseHandle(hFile);
6721         }
6722         DeleteFileW(wstrTempFile);
6723     }
6724     return nDataLength;
6725 }
6726
6727 /*************************************************************************
6728  * OLECONVERT_CreateOleStream [Internal]
6729  *
6730  * Creates the "\001OLE" stream in the IStorage if necessary.
6731  *
6732  * PARAMS
6733  *     pStorage     [I] Dest storage to create the stream in
6734  *
6735  * RETURNS
6736  *     Nothing
6737  *
6738  * NOTES
6739  *     This function is used by OleConvertOLESTREAMToIStorage only.
6740  *
6741  *     This stream is still unknown, MS Word seems to have extra data
6742  *     but since the data is stored in the OLESTREAM there should be
6743  *     no need to recreate the stream.  If the stream is manually
6744  *     deleted it will create it with this default data.
6745  *
6746  */
6747 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6748 {
6749     HRESULT hRes;
6750     IStream *pStream;
6751     static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6752     BYTE pOleStreamHeader [] =
6753     {
6754         0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6755         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6756         0x00, 0x00, 0x00, 0x00
6757     };
6758
6759     /* Create stream if not present */
6760     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6761         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6762
6763     if(hRes == S_OK)
6764     {
6765         /* Write default Data */
6766         hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6767         IStream_Release(pStream);
6768     }
6769 }
6770
6771 /* write a string to a stream, preceded by its length */
6772 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6773 {
6774     HRESULT r;
6775     LPSTR str;
6776     DWORD len = 0;
6777
6778     if( string )
6779         len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6780     r = IStream_Write( stm, &len, sizeof(len), NULL);
6781     if( FAILED( r ) )
6782         return r;
6783     if(len == 0)
6784         return r;
6785     str = CoTaskMemAlloc( len );
6786     WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6787     r = IStream_Write( stm, str, len, NULL);
6788     CoTaskMemFree( str );
6789     return r;
6790 }
6791
6792 /* read a string preceded by its length from a stream */
6793 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6794 {
6795     HRESULT r;
6796     DWORD len, count = 0;
6797     LPSTR str;
6798     LPWSTR wstr;
6799
6800     r = IStream_Read( stm, &len, sizeof(len), &count );
6801     if( FAILED( r ) )
6802         return r;
6803     if( count != sizeof(len) )
6804         return E_OUTOFMEMORY;
6805
6806     TRACE("%ld bytes\n",len);
6807     
6808     str = CoTaskMemAlloc( len );
6809     if( !str )
6810         return E_OUTOFMEMORY;
6811     count = 0;
6812     r = IStream_Read( stm, str, len, &count );
6813     if( FAILED( r ) )
6814         return r;
6815     if( count != len )
6816     {
6817         CoTaskMemFree( str );
6818         return E_OUTOFMEMORY;
6819     }
6820
6821     TRACE("Read string %s\n",debugstr_an(str,len));
6822
6823     len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6824     wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6825     if( wstr )
6826          MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6827     CoTaskMemFree( str );
6828
6829     *string = wstr;
6830
6831     return r;
6832 }
6833
6834
6835 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6836     LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6837 {
6838     IStream *pstm;
6839     HRESULT r = S_OK;
6840     static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6841
6842     static const BYTE unknown1[12] =
6843        { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6844          0xFF, 0xFF, 0xFF, 0xFF};
6845     static const BYTE unknown2[16] =
6846        { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6847          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6848
6849     TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6850            debugstr_w(lpszUserType), debugstr_w(szClipName),
6851            debugstr_w(szProgIDName));
6852
6853     /*  Create a CompObj stream if it doesn't exist */
6854     r = IStorage_CreateStream(pstg, szwStreamName,
6855         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6856     if( FAILED (r) )
6857         return r;
6858
6859     /* Write CompObj Structure to stream */
6860     r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6861
6862     if( SUCCEEDED( r ) )
6863         r = WriteClassStm( pstm, clsid );
6864
6865     if( SUCCEEDED( r ) )
6866         r = STREAM_WriteString( pstm, lpszUserType );
6867     if( SUCCEEDED( r ) )
6868         r = STREAM_WriteString( pstm, szClipName );
6869     if( SUCCEEDED( r ) )
6870         r = STREAM_WriteString( pstm, szProgIDName );
6871     if( SUCCEEDED( r ) )
6872         r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6873
6874     IStream_Release( pstm );
6875
6876     return r;
6877 }
6878
6879 /***********************************************************************
6880  *               WriteFmtUserTypeStg (OLE32.@)
6881  */
6882 HRESULT WINAPI WriteFmtUserTypeStg(
6883           LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6884 {
6885     HRESULT r;
6886     WCHAR szwClipName[0x40];
6887     CLSID clsid = CLSID_NULL;
6888     LPWSTR wstrProgID = NULL;
6889     DWORD n;
6890
6891     TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6892
6893     /* get the clipboard format name */
6894     n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6895     szwClipName[n]=0;
6896
6897     TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6898
6899     /* FIXME: There's room to save a CLSID and its ProgID, but
6900        the CLSID is not looked up in the registry and in all the
6901        tests I wrote it was CLSID_NULL.  Where does it come from?
6902     */
6903
6904     /* get the real program ID.  This may fail, but that's fine */
6905     ProgIDFromCLSID(&clsid, &wstrProgID);
6906
6907     TRACE("progid is %s\n",debugstr_w(wstrProgID));
6908
6909     r = STORAGE_WriteCompObj( pstg, &clsid, 
6910                               lpszUserType, szwClipName, wstrProgID );
6911
6912     CoTaskMemFree(wstrProgID);
6913
6914     return r;
6915 }
6916
6917
6918 /******************************************************************************
6919  *              ReadFmtUserTypeStg        [OLE32.@]
6920  */
6921 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6922 {
6923     HRESULT r;
6924     IStream *stm = 0;
6925     static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6926     unsigned char unknown1[12];
6927     unsigned char unknown2[16];
6928     DWORD count;
6929     LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6930     CLSID clsid;
6931
6932     TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6933
6934     r = IStorage_OpenStream( pstg, szCompObj, NULL, 
6935                     STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6936     if( FAILED ( r ) )
6937     {
6938         WARN("Failed to open stream r = %08lx\n", r);
6939         return r;
6940     }
6941
6942     /* read the various parts of the structure */
6943     r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6944     if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6945         goto end;
6946     r = ReadClassStm( stm, &clsid );
6947     if( FAILED( r ) )
6948         goto end;
6949
6950     r = STREAM_ReadString( stm, &szCLSIDName );
6951     if( FAILED( r ) )
6952         goto end;
6953
6954     r = STREAM_ReadString( stm, &szOleTypeName );
6955     if( FAILED( r ) )
6956         goto end;
6957
6958     r = STREAM_ReadString( stm, &szProgIDName );
6959     if( FAILED( r ) )
6960         goto end;
6961
6962     r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6963     if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6964         goto end;
6965
6966     /* ok, success... now we just need to store what we found */
6967     if( pcf )
6968         *pcf = RegisterClipboardFormatW( szOleTypeName );
6969     CoTaskMemFree( szOleTypeName );
6970
6971     if( lplpszUserType )
6972         *lplpszUserType = szCLSIDName;
6973     CoTaskMemFree( szProgIDName );
6974
6975 end:
6976     IStream_Release( stm );
6977
6978     return r;
6979 }
6980
6981
6982 /*************************************************************************
6983  * OLECONVERT_CreateCompObjStream [Internal]
6984  *
6985  * Creates a "\001CompObj" is the destination IStorage if necessary.
6986  *
6987  * PARAMS
6988  *     pStorage       [I] The dest IStorage to create the CompObj Stream
6989  *                        if necessary.
6990  *     strOleTypeName [I] The ProgID
6991  *
6992  * RETURNS
6993  *     Success:  S_OK
6994  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6995  *
6996  * NOTES
6997  *     This function is used by OleConvertOLESTREAMToIStorage only.
6998  *
6999  *     The stream data is stored in the OLESTREAM and there should be
7000  *     no need to recreate the stream.  If the stream is manually
7001  *     deleted it will attempt to create it by querying the registry.
7002  *
7003  *
7004  */
7005 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7006 {
7007     IStream *pStream;
7008     HRESULT hStorageRes, hRes = S_OK;
7009     OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7010     static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7011     WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7012
7013     BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7014     BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7015
7016     /* Initialize the CompObj structure */
7017     memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7018     memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7019     memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7020
7021
7022     /*  Create a CompObj stream if it doesn't exist */
7023     hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7024         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7025     if(hStorageRes == S_OK)
7026     {
7027         /* copy the OleTypeName to the compobj struct */
7028         IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7029         strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7030
7031         /* copy the OleTypeName to the compobj struct */
7032         /* Note: in the test made, these were Identical      */
7033         IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7034         strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7035
7036         /* Get the CLSID */
7037         MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7038                              bufferW, OLESTREAM_MAX_STR_LEN );
7039         hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7040
7041         if(hRes == S_OK)
7042         {
7043             HKEY hKey;
7044             LONG hErr;
7045             /* Get the CLSID Default Name from the Registry */
7046             hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7047             if(hErr == ERROR_SUCCESS)
7048             {
7049                 char strTemp[OLESTREAM_MAX_STR_LEN];
7050                 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7051                 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
7052                 if(hErr == ERROR_SUCCESS)
7053                 {
7054                     strcpy(IStorageCompObj.strCLSIDName, strTemp);
7055                 }
7056                 RegCloseKey(hKey);
7057             }
7058         }
7059
7060         /* Write CompObj Structure to stream */
7061         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7062
7063         WriteClassStm(pStream,&(IStorageCompObj.clsid));
7064
7065         hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7066         if(IStorageCompObj.dwCLSIDNameLength > 0)
7067         {
7068             hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7069         }
7070         hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7071         if(IStorageCompObj.dwOleTypeNameLength > 0)
7072         {
7073             hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7074         }
7075         hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7076         if(IStorageCompObj.dwProgIDNameLength > 0)
7077         {
7078             hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7079         }
7080         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7081         IStream_Release(pStream);
7082     }
7083     return hRes;
7084 }
7085
7086
7087 /*************************************************************************
7088  * OLECONVERT_CreateOlePresStream[Internal]
7089  *
7090  * Creates the "\002OlePres000" Stream with the Metafile data
7091  *
7092  * PARAMS
7093  *     pStorage     [I] The dest IStorage to create \002OLEPres000 stream in.
7094  *     dwExtentX    [I] Width of the Metafile
7095  *     dwExtentY    [I] Height of the Metafile
7096  *     pData        [I] Metafile data
7097  *     dwDataLength [I] Size of the Metafile data
7098  *
7099  * RETURNS
7100  *     Success:  S_OK
7101  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
7102  *
7103  * NOTES
7104  *     This function is used by OleConvertOLESTREAMToIStorage only.
7105  *
7106  */
7107 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7108 {
7109     HRESULT hRes;
7110     IStream *pStream;
7111     static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7112     BYTE pOlePresStreamHeader [] =
7113     {
7114         0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7115         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7116         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7117         0x00, 0x00, 0x00, 0x00
7118     };
7119
7120     BYTE pOlePresStreamHeaderEmpty [] =
7121     {
7122         0x00, 0x00, 0x00, 0x00,
7123         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7124         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7125         0x00, 0x00, 0x00, 0x00
7126     };
7127
7128     /* Create the OlePres000 Stream */
7129     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7130         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7131
7132     if(hRes == S_OK)
7133     {
7134         DWORD nHeaderSize;
7135         OLECONVERT_ISTORAGE_OLEPRES OlePres;
7136
7137         memset(&OlePres, 0, sizeof(OlePres));
7138         /* Do we have any metafile data to save */
7139         if(dwDataLength > 0)
7140         {
7141             memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7142             nHeaderSize = sizeof(pOlePresStreamHeader);
7143         }
7144         else
7145         {
7146             memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7147             nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7148         }
7149         /* Set width and height of the metafile */
7150         OlePres.dwExtentX = dwExtentX;
7151         OlePres.dwExtentY = -dwExtentY;
7152
7153         /* Set Data and Length */
7154         if(dwDataLength > sizeof(METAFILEPICT16))
7155         {
7156             OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7157             OlePres.pData = &(pData[8]);
7158         }
7159         /* Save OlePres000 Data to Stream */
7160         hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7161         hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7162         hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7163         hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7164         if(OlePres.dwSize > 0)
7165         {
7166             hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7167         }
7168         IStream_Release(pStream);
7169     }
7170 }
7171
7172 /*************************************************************************
7173  * OLECONVERT_CreateOle10NativeStream [Internal]
7174  *
7175  * Creates the "\001Ole10Native" Stream (should contain a BMP)
7176  *
7177  * PARAMS
7178  *     pStorage     [I] Dest storage to create the stream in
7179  *     pData        [I] Ole10 Native Data (ex. bmp)
7180  *     dwDataLength [I] Size of the Ole10 Native Data
7181  *
7182  * RETURNS
7183  *     Nothing
7184  *
7185  * NOTES
7186  *     This function is used by OleConvertOLESTREAMToIStorage only.
7187  *
7188  *     Might need to verify the data and return appropriate error message
7189  *
7190  */
7191 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7192 {
7193     HRESULT hRes;
7194     IStream *pStream;
7195     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7196
7197     /* Create the Ole10Native Stream */
7198     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7199         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7200
7201     if(hRes == S_OK)
7202     {
7203         /* Write info to stream */
7204         hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7205         hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7206         IStream_Release(pStream);
7207     }
7208
7209 }
7210
7211 /*************************************************************************
7212  * OLECONVERT_GetOLE10ProgID [Internal]
7213  *
7214  * Finds the ProgID (or OleTypeID) from the IStorage
7215  *
7216  * PARAMS
7217  *     pStorage        [I] The Src IStorage to get the ProgID
7218  *     strProgID       [I] the ProgID string to get
7219  *     dwSize          [I] the size of the string
7220  *
7221  * RETURNS
7222  *     Success:  S_OK
7223  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7224  *
7225  * NOTES
7226  *     This function is used by OleConvertIStorageToOLESTREAM only.
7227  *
7228  *
7229  */
7230 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7231 {
7232     HRESULT hRes;
7233     IStream *pStream;
7234     LARGE_INTEGER iSeekPos;
7235     OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7236     static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7237
7238     /* Open the CompObj Stream */
7239     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7240         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7241     if(hRes == S_OK)
7242     {
7243
7244         /*Get the OleType from the CompObj Stream */
7245         iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7246         iSeekPos.u.HighPart = 0;
7247
7248         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7249         IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7250         iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7251         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7252         IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7253         iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7254         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7255
7256         IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7257         if(*dwSize > 0)
7258         {
7259             IStream_Read(pStream, strProgID, *dwSize, NULL);
7260         }
7261         IStream_Release(pStream);
7262     }
7263     else
7264     {
7265         STATSTG stat;
7266         LPOLESTR wstrProgID;
7267
7268         /* Get the OleType from the registry */
7269         REFCLSID clsid = &(stat.clsid);
7270         IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7271         hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7272         if(hRes == S_OK)
7273         {
7274             *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7275         }
7276
7277     }
7278     return hRes;
7279 }
7280
7281 /*************************************************************************
7282  * OLECONVERT_GetOle10PresData [Internal]
7283  *
7284  * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7285  *
7286  * PARAMS
7287  *     pStorage     [I] Src IStroage
7288  *     pOleStream   [I] Dest OleStream Mem Struct
7289  *
7290  * RETURNS
7291  *     Nothing
7292  *
7293  * NOTES
7294  *     This function is used by OleConvertIStorageToOLESTREAM only.
7295  *
7296  *     Memory allocated for pData must be freed by the caller
7297  *
7298  *
7299  */
7300 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7301 {
7302
7303     HRESULT hRes;
7304     IStream *pStream;
7305     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7306
7307     /* Initialize Default data for OLESTREAM */
7308     pOleStreamData[0].dwOleID = OLESTREAM_ID;
7309     pOleStreamData[0].dwTypeID = 2;
7310     pOleStreamData[1].dwOleID = OLESTREAM_ID;
7311     pOleStreamData[1].dwTypeID = 0;
7312     pOleStreamData[0].dwMetaFileWidth = 0;
7313     pOleStreamData[0].dwMetaFileHeight = 0;
7314     pOleStreamData[0].pData = NULL;
7315     pOleStreamData[1].pData = NULL;
7316
7317     /* Open Ole10Native Stream */
7318     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7319         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7320     if(hRes == S_OK)
7321     {
7322
7323         /* Read Size and Data */
7324         IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7325         if(pOleStreamData->dwDataLength > 0)
7326         {
7327             pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7328             IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7329         }
7330         IStream_Release(pStream);
7331     }
7332
7333 }
7334
7335
7336 /*************************************************************************
7337  * OLECONVERT_GetOle20PresData[Internal]
7338  *
7339  * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7340  *
7341  * PARAMS
7342  *     pStorage         [I] Src IStroage
7343  *     pOleStreamData   [I] Dest OleStream Mem Struct
7344  *
7345  * RETURNS
7346  *     Nothing
7347  *
7348  * NOTES
7349  *     This function is used by OleConvertIStorageToOLESTREAM only.
7350  *
7351  *     Memory allocated for pData must be freed by the caller
7352  */
7353 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7354 {
7355     HRESULT hRes;
7356     IStream *pStream;
7357     OLECONVERT_ISTORAGE_OLEPRES olePress;
7358     static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7359
7360     /* Initialize Default data for OLESTREAM */
7361     pOleStreamData[0].dwOleID = OLESTREAM_ID;
7362     pOleStreamData[0].dwTypeID = 2;
7363     pOleStreamData[0].dwMetaFileWidth = 0;
7364     pOleStreamData[0].dwMetaFileHeight = 0;
7365     pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7366     pOleStreamData[1].dwOleID = OLESTREAM_ID;
7367     pOleStreamData[1].dwTypeID = 0;
7368     pOleStreamData[1].dwOleTypeNameLength = 0;
7369     pOleStreamData[1].strOleTypeName[0] = 0;
7370     pOleStreamData[1].dwMetaFileWidth = 0;
7371     pOleStreamData[1].dwMetaFileHeight = 0;
7372     pOleStreamData[1].pData = NULL;
7373     pOleStreamData[1].dwDataLength = 0;
7374
7375
7376     /* Open OlePress000 stream */
7377     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7378         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7379     if(hRes == S_OK)
7380     {
7381         LARGE_INTEGER iSeekPos;
7382         METAFILEPICT16 MetaFilePict;
7383         static const char strMetafilePictName[] = "METAFILEPICT";
7384
7385         /* Set the TypeID for a Metafile */
7386         pOleStreamData[1].dwTypeID = 5;
7387
7388         /* Set the OleTypeName to Metafile */
7389         pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7390         strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7391
7392         iSeekPos.u.HighPart = 0;
7393         iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7394
7395         /* Get Presentation Data */
7396         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7397         IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7398         IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7399         IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7400
7401         /*Set width and Height */
7402         pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7403         pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7404         if(olePress.dwSize > 0)
7405         {
7406             /* Set Length */
7407             pOleStreamData[1].dwDataLength  = olePress.dwSize + sizeof(METAFILEPICT16);
7408
7409             /* Set MetaFilePict struct */
7410             MetaFilePict.mm = 8;
7411             MetaFilePict.xExt = olePress.dwExtentX;
7412             MetaFilePict.yExt = olePress.dwExtentY;
7413             MetaFilePict.hMF = 0;
7414
7415             /* Get Metafile Data */
7416             pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7417             memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7418             IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7419         }
7420         IStream_Release(pStream);
7421     }
7422 }
7423
7424 /*************************************************************************
7425  * OleConvertOLESTREAMToIStorage [OLE32.@]
7426  *
7427  * Read info on MSDN
7428  *
7429  * TODO
7430  *      DVTARGETDEVICE paramenter is not handled
7431  *      Still unsure of some mem fields for OLE 10 Stream
7432  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7433  *      and "\001OLE" streams
7434  *
7435  */
7436 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7437     LPOLESTREAM pOleStream,
7438     LPSTORAGE pstg,
7439     const DVTARGETDEVICE* ptd)
7440 {
7441     int i;
7442     HRESULT hRes=S_OK;
7443     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7444
7445     memset(pOleStreamData, 0, sizeof(pOleStreamData));
7446
7447     if(ptd != NULL)
7448     {
7449         FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7450     }
7451
7452     if(pstg == NULL || pOleStream == NULL)
7453     {
7454         hRes = E_INVALIDARG;
7455     }
7456
7457     if(hRes == S_OK)
7458     {
7459         /* Load the OLESTREAM to Memory */
7460         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7461     }
7462
7463     if(hRes == S_OK)
7464     {
7465         /* Load the OLESTREAM to Memory (part 2)*/
7466         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7467     }
7468
7469     if(hRes == S_OK)
7470     {
7471
7472         if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7473         {
7474             /* Do we have the IStorage Data in the OLESTREAM */
7475             if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7476             {
7477                 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7478                 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7479             }
7480             else
7481             {
7482                 /* It must be an original OLE 1.0 source */
7483                 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7484             }
7485         }
7486         else
7487         {
7488             /* It must be an original OLE 1.0 source */
7489             OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7490         }
7491
7492         /* Create CompObj Stream if necessary */
7493         hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7494         if(hRes == S_OK)
7495         {
7496             /*Create the Ole Stream if necessary */
7497             OLECONVERT_CreateOleStream(pstg);
7498         }
7499     }
7500
7501
7502     /* Free allocated memory */
7503     for(i=0; i < 2; i++)
7504     {
7505         HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7506         HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7507         pOleStreamData[i].pstrOleObjFileName = NULL;
7508     }
7509     return hRes;
7510 }
7511
7512 /*************************************************************************
7513  * OleConvertIStorageToOLESTREAM [OLE32.@]
7514  *
7515  * Read info on MSDN
7516  *
7517  * Read info on MSDN
7518  *
7519  * TODO
7520  *      Still unsure of some mem fields for OLE 10 Stream
7521  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7522  *      and "\001OLE" streams.
7523  *
7524  */
7525 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7526     LPSTORAGE pstg,
7527     LPOLESTREAM pOleStream)
7528 {
7529     int i;
7530     HRESULT hRes = S_OK;
7531     IStream *pStream;
7532     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7533     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7534
7535
7536     memset(pOleStreamData, 0, sizeof(pOleStreamData));
7537
7538     if(pstg == NULL || pOleStream == NULL)
7539     {
7540         hRes = E_INVALIDARG;
7541     }
7542     if(hRes == S_OK)
7543     {
7544         /* Get the ProgID */
7545         pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7546         hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7547     }
7548     if(hRes == S_OK)
7549     {
7550         /* Was it originally Ole10 */
7551         hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7552         if(hRes == S_OK)
7553         {
7554             IStream_Release(pStream);
7555             /* Get Presentation Data for Ole10Native */
7556             OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7557         }
7558         else
7559         {
7560             /* Get Presentation Data (OLE20) */
7561             OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7562         }
7563
7564         /* Save OLESTREAM */
7565         hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7566         if(hRes == S_OK)
7567         {
7568             hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7569         }
7570
7571     }
7572
7573     /* Free allocated memory */
7574     for(i=0; i < 2; i++)
7575     {
7576         HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7577     }
7578
7579     return hRes;
7580 }
7581
7582 /***********************************************************************
7583  *              GetConvertStg (OLE32.@)
7584  */
7585 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7586     FIXME("unimplemented stub!\n");
7587     return E_FAIL;
7588 }
7589
7590 /******************************************************************************
7591  * StgIsStorageFile [OLE32.@]
7592  */
7593 HRESULT WINAPI
7594 StgIsStorageFile(LPCOLESTR fn)
7595 {
7596         HANDLE          hf;
7597         BYTE            magic[8];
7598         DWORD           bytes_read;
7599
7600         TRACE("(\'%s\')\n", debugstr_w(fn));
7601         hf = CreateFileW(fn, GENERIC_READ,
7602                          FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7603                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7604
7605         if (hf == INVALID_HANDLE_VALUE)
7606                 return STG_E_FILENOTFOUND;
7607
7608         if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7609         {
7610                 WARN(" unable to read file\n");
7611                 CloseHandle(hf);
7612                 return S_FALSE;
7613         }
7614
7615         CloseHandle(hf);
7616
7617         if (bytes_read != 8) {
7618                 WARN(" too short\n");
7619                 return S_FALSE;
7620         }
7621
7622         if (!memcmp(magic,STORAGE_magic,8)) {
7623                 WARN(" -> YES\n");
7624                 return S_OK;
7625         }
7626
7627         WARN(" -> Invalid header.\n");
7628         return S_FALSE;
7629 }