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