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