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