Added better missing function emulation.
[wine] / dlls / ole32 / storage32.c
1 /*
2  * Compound Storage (32 bit version)
3  * Storage implementation
4  *
5  * This file contains the compound file implementation
6  * of the storage interface.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Sylvain St-Germain
10  * Copyright 1999 Thuy Nguyen
11  */
12
13 #include <assert.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17
18 #include "winbase.h" /* for lstrlenW() and the likes */
19 #include "debugtools.h"
20
21 #include "storage32.h"
22 #include "ole2.h"      /* For Write/ReadClassStm */
23
24 DEFAULT_DEBUG_CHANNEL(storage)
25
26 #define FILE_BEGIN 0
27
28 static const char rootPropertyName[] = "Root Entry";
29
30 /***********************************************************************
31  * Forward declaration of internal functions used by the method DestroyElement
32  */
33 static HRESULT deleteStorageProperty(
34   StorageImpl *parentStorage,
35   OLECHAR     *propertyToDeleteName);
36
37 static HRESULT deleteStreamProperty(
38   StorageImpl *parentStorage,
39   ULONG         foundPropertyIndexToDelete,
40   StgProperty   propertyToDelete);
41
42 static HRESULT findPlaceholder(
43   StorageImpl *storage,
44   ULONG         propertyIndexToStore,
45   ULONG         storagePropertyIndex,
46   INT         typeOfRelation);
47
48 static HRESULT adjustPropertyChain( 
49   StorageImpl *This,
50   StgProperty   propertyToDelete,
51   StgProperty   parentProperty,
52   ULONG         parentPropertyId,
53   INT         typeOfRelation);
54
55 /***********************************************************************
56  * Declaration of the functions used to manipulate StgProperty
57  */
58
59 static ULONG getFreeProperty(
60   StorageImpl *storage);
61
62 static void updatePropertyChain(
63   StorageImpl *storage,
64   ULONG       newPropertyIndex,
65   StgProperty newProperty);
66
67 static LONG propertyNameCmp(
68   OLECHAR *newProperty,
69   OLECHAR *currentProperty);
70
71
72 /***********************************************************************
73  * Declaration of miscellaneous functions...
74  */
75 static HRESULT validateSTGM(DWORD stgmValue); 
76
77 static DWORD GetShareModeFromSTGM(DWORD stgm);
78 static DWORD GetAccessModeFromSTGM(DWORD stgm);
79 static DWORD GetCreationModeFromSTGM(DWORD stgm);
80
81 /*
82  * Virtual function table for the IStorage32Impl class.
83  */
84 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
85 {
86     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
87     StorageBaseImpl_QueryInterface,
88     StorageBaseImpl_AddRef,
89     StorageBaseImpl_Release,
90     StorageBaseImpl_CreateStream,
91     StorageBaseImpl_OpenStream,
92     StorageImpl_CreateStorage,
93     StorageBaseImpl_OpenStorage,
94     StorageImpl_CopyTo,
95     StorageImpl_MoveElementTo,
96     StorageImpl_Commit,
97     StorageImpl_Revert,
98     StorageBaseImpl_EnumElements,
99     StorageImpl_DestroyElement,
100     StorageBaseImpl_RenameElement,
101     StorageImpl_SetElementTimes,
102     StorageBaseImpl_SetClass,
103     StorageImpl_SetStateBits,
104     StorageBaseImpl_Stat
105 };
106
107 /*
108  * Virtual function table for the Storage32InternalImpl class.
109  */
110 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
111   {
112     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
113     StorageBaseImpl_QueryInterface,
114     StorageBaseImpl_AddRef,
115     StorageBaseImpl_Release,
116     StorageBaseImpl_CreateStream,
117     StorageBaseImpl_OpenStream,
118     StorageImpl_CreateStorage,
119     StorageBaseImpl_OpenStorage,
120     StorageImpl_CopyTo,
121     StorageImpl_MoveElementTo,
122     StorageInternalImpl_Commit,
123     StorageInternalImpl_Revert,
124     StorageBaseImpl_EnumElements,
125     StorageImpl_DestroyElement,
126     StorageBaseImpl_RenameElement,
127     StorageImpl_SetElementTimes,
128     StorageBaseImpl_SetClass,
129     StorageImpl_SetStateBits,
130     StorageBaseImpl_Stat
131 };
132
133 /*
134  * Virtual function table for the IEnumSTATSTGImpl class.
135  */
136 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
137 {
138     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
139     IEnumSTATSTGImpl_QueryInterface,
140     IEnumSTATSTGImpl_AddRef,
141     IEnumSTATSTGImpl_Release,
142     IEnumSTATSTGImpl_Next,
143     IEnumSTATSTGImpl_Skip,
144     IEnumSTATSTGImpl_Reset,
145     IEnumSTATSTGImpl_Clone
146 };
147
148
149
150
151
152 /************************************************************************
153 ** Storage32BaseImpl implementatiion
154 */
155
156 /************************************************************************
157  * Storage32BaseImpl_QueryInterface (IUnknown)
158  *
159  * This method implements the common QueryInterface for all IStorage32
160  * implementations contained in this file.
161  * 
162  * See Windows documentation for more details on IUnknown methods.
163  */
164 HRESULT WINAPI StorageBaseImpl_QueryInterface(
165   IStorage*        iface,
166   REFIID             riid,
167   void**             ppvObject)
168 {
169   ICOM_THIS(StorageBaseImpl,iface);
170   /*
171    * Perform a sanity check on the parameters.
172    */
173   if ( (This==0) || (ppvObject==0) )
174     return E_INVALIDARG;
175   
176   /*
177    * Initialize the return parameter.
178    */
179   *ppvObject = 0;
180   
181   /*
182    * Compare the riid with the interface IDs implemented by this object.
183    */
184   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0) 
185   {
186     *ppvObject = (IStorage*)This;
187   }
188   else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0) 
189   {
190     *ppvObject = (IStorage*)This;
191   }
192   
193   /*
194    * Check that we obtained an interface.
195    */
196   if ((*ppvObject)==0)
197     return E_NOINTERFACE;
198   
199   /*
200    * Query Interface always increases the reference count by one when it is
201    * successful
202    */
203   StorageBaseImpl_AddRef(iface);
204
205   return S_OK;
206 }
207         
208 /************************************************************************
209  * Storage32BaseImpl_AddRef (IUnknown)
210  *
211  * This method implements the common AddRef for all IStorage32
212  * implementations contained in this file.
213  * 
214  * See Windows documentation for more details on IUnknown methods.
215  */
216 ULONG WINAPI StorageBaseImpl_AddRef( 
217             IStorage* iface)
218 {
219   ICOM_THIS(StorageBaseImpl,iface);
220   This->ref++;
221
222   return This->ref;
223 }
224         
225 /************************************************************************
226  * Storage32BaseImpl_Release (IUnknown)
227  *
228  * This method implements the common Release for all IStorage32
229  * implementations contained in this file.
230  * 
231  * See Windows documentation for more details on IUnknown methods.
232  */
233 ULONG WINAPI StorageBaseImpl_Release( 
234       IStorage* iface)
235 {
236   ICOM_THIS(StorageBaseImpl,iface);
237   /*
238    * Decrease the reference count on this object.
239    */
240   This->ref--;
241
242   /*
243    * If the reference count goes down to 0, perform suicide.
244    */
245   if (This->ref==0)
246   {
247     /*
248      * Since we are using a system of base-classes, we want to call the 
249      * destructor of the appropriate derived class. To do this, we are 
250      * using virtual functions to implement the destructor.
251      */
252     This->v_destructor(This);
253
254     return 0;
255   }
256   
257   return This->ref;
258 }
259
260 /************************************************************************
261  * Storage32BaseImpl_OpenStream (IStorage)
262  *
263  * This method will open the specified stream object from the current storage.
264  *
265  * See Windows documentation for more details on IStorage methods.
266  */
267 HRESULT WINAPI StorageBaseImpl_OpenStream( 
268   IStorage*        iface,
269   const OLECHAR*   pwcsName,  /* [string][in] */
270   void*            reserved1, /* [unique][in] */
271   DWORD            grfMode,   /* [in]  */        
272   DWORD            reserved2, /* [in]  */        
273   IStream**        ppstm)     /* [out] */       
274 {
275   ICOM_THIS(StorageBaseImpl,iface);
276   IEnumSTATSTGImpl* propertyEnumeration;
277   StgStreamImpl*    newStream;
278   StgProperty       currentProperty;
279   ULONG             foundPropertyIndex;
280
281   TRACE("(%p, %s, %p, %lx, %ld, %p)\n", 
282         iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
283
284   /*
285    * Perform a sanity check on the parameters.
286    */
287   if ( (pwcsName==NULL) || (ppstm==0) )
288     return E_INVALIDARG;
289   
290   /*
291    * Initialize the out parameter
292    */
293   *ppstm = 0;
294   
295   /*
296    * Validate the STGM flags
297    */
298   if ( FAILED( validateSTGM(grfMode) ))
299     return STG_E_INVALIDFLAG;
300
301   /*
302    * As documented.
303    */
304   if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
305         (grfMode & STGM_DELETEONRELEASE) ||
306         (grfMode & STGM_TRANSACTED) )
307     return STG_E_INVALIDFUNCTION;
308
309   /*
310    * Create a property enumeration to search the properties
311    */
312   propertyEnumeration = IEnumSTATSTGImpl_Construct(
313     This->ancestorStorage, 
314     This->rootPropertySetIndex);
315   
316   /*
317    * Search the enumeration for the property with the given name
318    */
319   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
320     propertyEnumeration,
321     pwcsName,
322     &currentProperty);
323   
324   /*
325    * Delete the property enumeration since we don't need it anymore
326    */
327   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
328   
329   /*
330    * If it was found, construct the stream object and return a pointer to it.
331    */
332   if ( (foundPropertyIndex!=PROPERTY_NULL) && 
333        (currentProperty.propertyType==PROPTYPE_STREAM) )
334   {
335     newStream = StgStreamImpl_Construct(This, foundPropertyIndex);
336     
337     if (newStream!=0)
338     {
339       *ppstm = (IStream*)newStream;
340
341       /*
342        * Since we are returning a pointer to the interface, we have to 
343        * nail down the reference.
344        */
345       StgStreamImpl_AddRef(*ppstm);
346       
347       return S_OK;
348     }
349     
350     return E_OUTOFMEMORY;
351   }
352   
353   return STG_E_FILENOTFOUND;
354 }
355
356 /************************************************************************
357  * Storage32BaseImpl_OpenStorage (IStorage)
358  *
359  * This method will open a new storage object from the current storage.
360  * 
361  * See Windows documentation for more details on IStorage methods.
362  */        
363 HRESULT WINAPI StorageBaseImpl_OpenStorage( 
364   IStorage*        iface,
365   const OLECHAR*   pwcsName,      /* [string][unique][in] */ 
366   IStorage*        pstgPriority,  /* [unique][in] */         
367   DWORD            grfMode,       /* [in] */                 
368   SNB              snbExclude,    /* [unique][in] */         
369   DWORD            reserved,      /* [in] */                 
370   IStorage**       ppstg)         /* [out] */                        
371 {
372   ICOM_THIS(StorageBaseImpl,iface);
373   StorageInternalImpl* newStorage;
374   IEnumSTATSTGImpl*      propertyEnumeration;
375   StgProperty            currentProperty;
376   ULONG                  foundPropertyIndex;
377
378   TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n", 
379         iface, debugstr_w(pwcsName), pstgPriority, 
380         grfMode, snbExclude, reserved, ppstg);
381   
382   /*
383    * Perform a sanity check on the parameters.
384    */
385   if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
386     return E_INVALIDARG;
387   
388   /*
389    * Validate the STGM flags
390    */
391   if ( FAILED( validateSTGM(grfMode) ))
392     return STG_E_INVALIDFLAG;
393
394   /*
395    * As documented.
396    */
397   if ( !(grfMode & STGM_SHARE_EXCLUSIVE) || 
398         (grfMode & STGM_DELETEONRELEASE) ||
399         (grfMode & STGM_PRIORITY) )
400     return STG_E_INVALIDFUNCTION;
401
402   /*
403    * Initialize the out parameter
404    */
405   *ppstg = 0;
406   
407   /*
408    * Create a property enumeration to search the properties
409    */
410   propertyEnumeration = IEnumSTATSTGImpl_Construct(
411                           This->ancestorStorage, 
412                           This->rootPropertySetIndex);
413   
414   /*
415    * Search the enumeration for the property with the given name
416    */
417   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
418                          propertyEnumeration,
419                          pwcsName,
420                          &currentProperty);
421   
422   /*
423    * Delete the property enumeration since we don't need it anymore
424    */
425   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
426   
427   /*
428    * If it was found, construct the stream object and return a pointer to it.
429    */
430   if ( (foundPropertyIndex!=PROPERTY_NULL) && 
431        (currentProperty.propertyType==PROPTYPE_STORAGE) )
432   {
433     /*
434      * Construct a new Storage object
435      */
436     newStorage = StorageInternalImpl_Construct(
437                    This->ancestorStorage,
438                    foundPropertyIndex);
439     
440     if (newStorage != 0)
441     {
442       *ppstg = (IStorage*)newStorage;
443
444       /*
445        * Since we are returning a pointer to the interface, 
446        * we have to nail down the reference.
447        */
448       StorageBaseImpl_AddRef(*ppstg);
449       
450       return S_OK;
451     }
452     
453     return STG_E_INSUFFICIENTMEMORY;
454   }
455   
456   return STG_E_FILENOTFOUND;
457 }
458
459 /************************************************************************
460  * Storage32BaseImpl_EnumElements (IStorage)
461  *
462  * This method will create an enumerator object that can be used to 
463  * retrieve informatino about all the properties in the storage object.
464  * 
465  * See Windows documentation for more details on IStorage methods.
466  */        
467 HRESULT WINAPI StorageBaseImpl_EnumElements( 
468   IStorage*       iface,
469   DWORD           reserved1, /* [in] */                  
470   void*           reserved2, /* [size_is][unique][in] */ 
471   DWORD           reserved3, /* [in] */                  
472   IEnumSTATSTG**  ppenum)    /* [out] */                 
473 {
474   ICOM_THIS(StorageBaseImpl,iface);
475   IEnumSTATSTGImpl* newEnum;
476
477   TRACE("(%p, %ld, %p, %ld, %p)\n", 
478         iface, reserved1, reserved2, reserved3, ppenum);
479
480   /*
481    * Perform a sanity check on the parameters.
482    */
483   if ( (This==0) || (ppenum==0))
484     return E_INVALIDARG;
485   
486   /*
487    * Construct the enumerator.
488    */
489   newEnum = IEnumSTATSTGImpl_Construct(
490               This->ancestorStorage,
491               This->rootPropertySetIndex);
492
493   if (newEnum!=0)
494   {
495     *ppenum = (IEnumSTATSTG*)newEnum;
496
497     /*
498      * Don't forget to nail down a reference to the new object before
499      * returning it.
500      */
501     IEnumSTATSTGImpl_AddRef(*ppenum);
502     
503     return S_OK;
504   }
505
506   return E_OUTOFMEMORY;
507 }
508
509 /************************************************************************
510  * Storage32BaseImpl_Stat (IStorage)
511  *
512  * This method will retrieve information about this storage object.
513  * 
514  * See Windows documentation for more details on IStorage methods.
515  */        
516 HRESULT WINAPI StorageBaseImpl_Stat( 
517   IStorage*        iface,
518   STATSTG*         pstatstg,     /* [out] */ 
519   DWORD            grfStatFlag)  /* [in] */  
520 {
521   ICOM_THIS(StorageBaseImpl,iface);
522   StgProperty    curProperty;
523   BOOL         readSucessful;
524
525   TRACE("(%p, %p, %lx)\n", 
526         iface, pstatstg, grfStatFlag);
527
528   /*
529    * Perform a sanity check on the parameters.
530    */
531   if ( (This==0) || (pstatstg==0))
532     return E_INVALIDARG;
533
534   /*
535    * Read the information from the property.
536    */
537   readSucessful = StorageImpl_ReadProperty(
538                     This->ancestorStorage,
539                     This->rootPropertySetIndex,
540                     &curProperty);
541
542   if (readSucessful)
543   {
544     StorageUtl_CopyPropertyToSTATSTG(
545       pstatstg, 
546       &curProperty, 
547       grfStatFlag);
548     
549     return S_OK;
550   }
551   
552   return E_FAIL;
553 }
554
555 /************************************************************************
556  * Storage32BaseImpl_RenameElement (IStorage)
557  *
558  * This method will rename the specified element. 
559  *
560  * See Windows documentation for more details on IStorage methods.
561  * 
562  * Implementation notes: The method used to rename consists of creating a clone 
563  *    of the deleted StgProperty object setting it with the new name and to 
564  *    perform a DestroyElement of the old StgProperty.
565  */
566 HRESULT WINAPI StorageBaseImpl_RenameElement(
567             IStorage*        iface,
568             const OLECHAR*   pwcsOldName,  /* [in] */
569             const OLECHAR*   pwcsNewName)  /* [in] */
570 {
571   ICOM_THIS(StorageBaseImpl,iface);
572   IEnumSTATSTGImpl* propertyEnumeration;
573   StgProperty       currentProperty;
574   ULONG             foundPropertyIndex;
575
576   TRACE("(%p, %s, %s)\n", 
577         iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
578
579   /*
580    * Create a property enumeration to search the properties
581    */
582   propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
583                                                    This->rootPropertySetIndex);
584
585   /*
586    * Search the enumeration for the new property name
587    */
588   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
589                                                      pwcsNewName,
590                                                      &currentProperty);
591
592   if (foundPropertyIndex != PROPERTY_NULL)
593   {
594     /*
595      * There is already a property with the new name
596      */
597     IEnumSTATSTGImpl_Destroy(propertyEnumeration);
598     return STG_E_FILEALREADYEXISTS;
599   }
600
601   IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
602
603   /*
604    * Search the enumeration for the old property name
605    */
606   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
607                                                      pwcsOldName,
608                                                      &currentProperty);
609
610   /*
611    * Delete the property enumeration since we don't need it anymore
612    */
613   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
614
615   if (foundPropertyIndex != PROPERTY_NULL)
616   {
617     StgProperty renamedProperty;
618     ULONG       renamedPropertyIndex;
619
620     /*
621      * Setup a new property for the renamed property
622      */
623     renamedProperty.sizeOfNameString = 
624       ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
625   
626     if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
627       return STG_E_INVALIDNAME;
628   
629     lstrcpyW(renamedProperty.name, pwcsNewName);
630  
631     renamedProperty.propertyType  = currentProperty.propertyType;
632     renamedProperty.startingBlock = currentProperty.startingBlock;
633     renamedProperty.size.s.LowPart  = currentProperty.size.s.LowPart;
634     renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
635   
636     renamedProperty.previousProperty = PROPERTY_NULL;
637     renamedProperty.nextProperty     = PROPERTY_NULL;
638   
639     /*
640      * Bring the dirProperty link in case it is a storage and in which
641      * case the renamed storage elements don't require to be reorganized.
642      */
643     renamedProperty.dirProperty = currentProperty.dirProperty;
644   
645     /* call CoFileTime to get the current time 
646     renamedProperty.timeStampS1
647     renamedProperty.timeStampD1
648     renamedProperty.timeStampS2
649     renamedProperty.timeStampD2
650     renamedProperty.propertyUniqueID 
651     */
652   
653     /* 
654      * Obtain a free property in the property chain
655      */
656     renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
657   
658     /*
659      * Save the new property into the new property spot
660      */  
661     StorageImpl_WriteProperty(
662       This->ancestorStorage,
663       renamedPropertyIndex, 
664       &renamedProperty);
665   
666     /* 
667      * Find a spot in the property chain for our newly created property.
668      */
669     updatePropertyChain(
670       (StorageImpl*)This,
671       renamedPropertyIndex, 
672       renamedProperty);
673
674     /*
675      * At this point the renamed property has been inserted in the tree, 
676      * now, before to Destroy the old property we must zeroed it's dirProperty 
677      * otherwise the DestroyProperty below will zap it all and we do not want 
678      * this to happen.
679      * Also, we fake that the old property is a storage so the DestroyProperty
680      * will not do a SetSize(0) on the stream data.
681      * 
682      * This means that we need to tweek the StgProperty if it is a stream or a
683      * non empty storage.
684      */
685     currentProperty.dirProperty  = PROPERTY_NULL;
686     currentProperty.propertyType = PROPTYPE_STORAGE;
687     StorageImpl_WriteProperty(
688       This->ancestorStorage,
689       foundPropertyIndex, 
690       &currentProperty);
691
692     /* 
693      * Invoke Destroy to get rid of the ole property and automatically redo 
694      * the linking of it's previous and next members... 
695      */ 
696     StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName); 
697
698   }
699   else
700   {
701     /*
702      * There is no property with the old name
703      */
704     return STG_E_FILENOTFOUND;
705   }
706
707   return S_OK;
708 }
709
710 /************************************************************************
711  * Storage32BaseImpl_CreateStream (IStorage)
712  *
713  * This method will create a stream object within this storage 
714  *
715  * See Windows documentation for more details on IStorage methods.
716  */
717 HRESULT WINAPI StorageBaseImpl_CreateStream(
718             IStorage*        iface,
719             const OLECHAR*   pwcsName,  /* [string][in] */
720             DWORD            grfMode,   /* [in] */
721             DWORD            reserved1, /* [in] */
722             DWORD            reserved2, /* [in] */
723             IStream**        ppstm)     /* [out] */
724 {
725   ICOM_THIS(StorageBaseImpl,iface);
726   IEnumSTATSTGImpl* propertyEnumeration;
727   StgStreamImpl*    newStream;
728   StgProperty       currentProperty, newStreamProperty;
729   ULONG             foundPropertyIndex, newPropertyIndex;
730
731   TRACE("(%p, %s, %lx, %ld, %ld, %p)\n", 
732         iface, debugstr_w(pwcsName), grfMode, 
733         reserved1, reserved2, ppstm);
734
735   /*
736    * Validate parameters
737    */
738   if (ppstm == 0)
739     return STG_E_INVALIDPOINTER;
740
741   if (pwcsName == 0)
742     return STG_E_INVALIDNAME;
743
744   /*
745    * Validate the STGM flags
746    */
747   if ( FAILED( validateSTGM(grfMode) ))
748     return STG_E_INVALIDFLAG;
749
750   /*
751    * As documented.
752    */
753   if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
754         (grfMode & STGM_DELETEONRELEASE) ||
755         (grfMode & STGM_TRANSACTED) )
756     return STG_E_INVALIDFUNCTION;
757
758   /*
759    * Initialize the out parameter
760    */
761   *ppstm = 0;
762
763   /*
764    * Create a property enumeration to search the properties
765    */
766   propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
767                                                    This->rootPropertySetIndex);
768
769   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
770                                                      pwcsName,
771                                                      &currentProperty);
772
773   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
774
775   if (foundPropertyIndex != PROPERTY_NULL)
776   {
777     /*
778      * An element with this name already exists 
779      */
780     if (grfMode & STGM_CREATE)
781     {
782       IStorage_DestroyElement(iface, pwcsName); 
783     }
784     else 
785       return STG_E_FILEALREADYEXISTS;
786   }
787
788   /* 
789    * memset the empty property 
790    */
791   memset(&newStreamProperty, 0, sizeof(StgProperty));
792
793   newStreamProperty.sizeOfNameString =
794       ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
795
796   if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
797     return STG_E_INVALIDNAME;
798
799   lstrcpyW(newStreamProperty.name, pwcsName);
800
801   newStreamProperty.propertyType  = PROPTYPE_STREAM;
802   newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
803   newStreamProperty.size.s.LowPart  = 0;
804   newStreamProperty.size.s.HighPart = 0;
805
806   newStreamProperty.previousProperty = PROPERTY_NULL;
807   newStreamProperty.nextProperty     = PROPERTY_NULL;
808   newStreamProperty.dirProperty      = PROPERTY_NULL;
809
810   /* call CoFileTime to get the current time 
811   newStreamProperty.timeStampS1
812   newStreamProperty.timeStampD1
813   newStreamProperty.timeStampS2
814   newStreamProperty.timeStampD2
815   */
816
817   /*  newStreamProperty.propertyUniqueID */
818
819   /*
820    * Get a free property or create a new one 
821    */
822   newPropertyIndex = getFreeProperty(This->ancestorStorage);
823
824   /*
825    * Save the new property into the new property spot
826    */  
827   StorageImpl_WriteProperty(
828     This->ancestorStorage,
829     newPropertyIndex, 
830     &newStreamProperty);
831
832   /* 
833    * Find a spot in the property chain for our newly created property.
834    */
835   updatePropertyChain(
836     (StorageImpl*)This,
837     newPropertyIndex, 
838     newStreamProperty);
839
840   /* 
841    * Open the stream to return it.
842    */
843   newStream = StgStreamImpl_Construct(This, newPropertyIndex);
844
845   if (newStream != 0)
846   {
847     *ppstm = (IStream*)newStream;
848
849     /*
850      * Since we are returning a pointer to the interface, we have to nail down
851      * the reference.
852      */
853     StgStreamImpl_AddRef(*ppstm);
854   }
855   else
856   {
857     return STG_E_INSUFFICIENTMEMORY;
858   }
859
860   return S_OK;
861 }
862
863 /************************************************************************
864  * Storage32BaseImpl_SetClass (IStorage)
865  *
866  * This method will write the specified CLSID in the property of this 
867  * storage.
868  *
869  * See Windows documentation for more details on IStorage methods.
870  */
871 HRESULT WINAPI StorageBaseImpl_SetClass(
872   IStorage*        iface,
873   REFCLSID         clsid) /* [in] */
874 {
875   ICOM_THIS(StorageBaseImpl,iface);
876   HRESULT hRes = E_FAIL;
877   StgProperty curProperty;
878   BOOL success;
879
880   TRACE("(%p, %p)\n", iface, clsid);
881   
882   success = StorageImpl_ReadProperty(This->ancestorStorage,
883                                        This->rootPropertySetIndex,
884                                        &curProperty);
885   if (success)
886   {
887     curProperty.propertyUniqueID = *clsid;
888
889     success =  StorageImpl_WriteProperty(This->ancestorStorage,
890                                            This->rootPropertySetIndex,
891                                            &curProperty);
892     if (success)
893       hRes = S_OK;
894   }
895
896   return hRes;
897 }
898
899 /************************************************************************
900 ** Storage32Impl implementation
901 */
902         
903 /************************************************************************
904  * Storage32Impl_CreateStorage (IStorage)
905  *
906  * This method will create the storage object within the provided storage.
907  *
908  * See Windows documentation for more details on IStorage methods.
909  */
910 HRESULT WINAPI StorageImpl_CreateStorage( 
911   IStorage*      iface,
912   const OLECHAR  *pwcsName, /* [string][in] */ 
913   DWORD            grfMode,   /* [in] */ 
914   DWORD            reserved1, /* [in] */ 
915   DWORD            reserved2, /* [in] */ 
916   IStorage       **ppstg)   /* [out] */ 
917 {
918   StorageImpl* const This=(StorageImpl*)iface;
919
920   IEnumSTATSTGImpl *propertyEnumeration;
921   StgProperty      currentProperty;
922   StgProperty      newProperty;
923   ULONG            foundPropertyIndex;
924   ULONG            newPropertyIndex;
925   HRESULT          hr;
926
927   TRACE("(%p, %s, %lx, %ld, %ld, %p)\n", 
928         iface, debugstr_w(pwcsName), grfMode, 
929         reserved1, reserved2, ppstg);
930   
931   /*
932    * Validate parameters
933    */
934   if (ppstg == 0)
935     return STG_E_INVALIDPOINTER;
936
937   if (pwcsName == 0)
938     return STG_E_INVALIDNAME;
939
940   /*
941    * Validate the STGM flags
942    */
943   if ( FAILED( validateSTGM(grfMode) ) ||
944        (grfMode & STGM_DELETEONRELEASE) )
945     return STG_E_INVALIDFLAG;
946
947   /*
948    * Initialize the out parameter
949    */
950   *ppstg = 0;
951
952   /*
953    * Create a property enumeration and search the properties
954    */
955   propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
956                                                     This->rootPropertySetIndex);
957
958   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
959                                                      pwcsName,
960                                                      &currentProperty);
961   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
962
963   if (foundPropertyIndex != PROPERTY_NULL)
964   {
965     /*
966      * An element with this name already exists 
967      */
968     if (grfMode & STGM_CREATE)
969       IStorage_DestroyElement(iface, pwcsName); 
970     else 
971       return STG_E_FILEALREADYEXISTS;
972   }
973
974   /* 
975    * memset the empty property 
976    */
977   memset(&newProperty, 0, sizeof(StgProperty));
978
979   newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
980
981   if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
982     return STG_E_INVALIDNAME;
983
984   lstrcpyW(newProperty.name, pwcsName);
985
986   newProperty.propertyType  = PROPTYPE_STORAGE;
987   newProperty.startingBlock = BLOCK_END_OF_CHAIN;
988   newProperty.size.s.LowPart  = 0;
989   newProperty.size.s.HighPart = 0;
990
991   newProperty.previousProperty = PROPERTY_NULL;
992   newProperty.nextProperty     = PROPERTY_NULL;
993   newProperty.dirProperty      = PROPERTY_NULL;
994
995   /* call CoFileTime to get the current time 
996   newProperty.timeStampS1
997   newProperty.timeStampD1
998   newProperty.timeStampS2
999   newProperty.timeStampD2
1000   */
1001
1002   /*  newStorageProperty.propertyUniqueID */
1003
1004   /* 
1005    * Obtain a free property in the property chain
1006    */
1007   newPropertyIndex = getFreeProperty(This->ancestorStorage);
1008
1009   /*
1010    * Save the new property into the new property spot
1011    */  
1012   StorageImpl_WriteProperty(
1013     This->ancestorStorage,
1014     newPropertyIndex, 
1015     &newProperty);
1016
1017   /* 
1018    * Find a spot in the property chain for our newly created property.
1019    */
1020   updatePropertyChain(
1021     This,
1022     newPropertyIndex, 
1023     newProperty);
1024
1025   /* 
1026    * Open it to get a pointer to return.
1027    */
1028   hr = IStorage_OpenStorage(
1029          iface,
1030          (OLECHAR*)pwcsName,
1031          0,
1032          grfMode,
1033          0,
1034          0,
1035          ppstg);
1036
1037   if( (hr != S_OK) || (*ppstg == NULL))
1038   {
1039     return hr;
1040   }
1041
1042   
1043   return S_OK;
1044 }
1045
1046
1047 /***************************************************************************
1048  *
1049  * Internal Method
1050  *
1051  * Get a free property or create a new one.
1052  */
1053 static ULONG getFreeProperty(
1054   StorageImpl *storage)
1055 {
1056   ULONG       currentPropertyIndex = 0;
1057   ULONG       newPropertyIndex     = PROPERTY_NULL;
1058   BOOL      readSucessful        = TRUE;
1059   StgProperty currentProperty;
1060
1061   do
1062   {
1063     /*
1064      * Start by reading the root property
1065      */
1066     readSucessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1067                                                currentPropertyIndex,
1068                                                &currentProperty);
1069     if (readSucessful)
1070     {
1071       if (currentProperty.sizeOfNameString == 0)
1072       {
1073         /* 
1074          * The property existis and is available, we found it.
1075          */
1076         newPropertyIndex = currentPropertyIndex;
1077       }
1078     }
1079     else
1080     {
1081       /*
1082        * We exhausted the property list, we will create more space below
1083        */
1084       newPropertyIndex = currentPropertyIndex;
1085     }
1086     currentPropertyIndex++;
1087
1088   } while (newPropertyIndex == PROPERTY_NULL);
1089
1090   /* 
1091    * grow the property chain 
1092    */
1093   if (! readSucessful)
1094   {
1095     StgProperty    emptyProperty;
1096     ULARGE_INTEGER newSize;
1097     ULONG          propertyIndex;
1098     ULONG          lastProperty  = 0;
1099     ULONG          blockCount    = 0;
1100
1101     /* 
1102      * obtain the new count of property blocks 
1103      */
1104     blockCount = BlockChainStream_GetCount(
1105                    storage->ancestorStorage->rootBlockChain)+1;
1106
1107     /* 
1108      * initialize the size used by the property stream 
1109      */
1110     newSize.s.HighPart = 0;
1111     newSize.s.LowPart  = storage->bigBlockSize * blockCount;
1112
1113     /* 
1114      * add a property block to the property chain 
1115      */
1116     BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1117
1118     /* 
1119      * memset the empty property in order to initialize the unused newly 
1120      * created property
1121      */
1122     memset(&emptyProperty, 0, sizeof(StgProperty));
1123
1124     /* 
1125      * initialize them
1126      */
1127     lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount; 
1128     
1129     for(
1130       propertyIndex = newPropertyIndex;
1131       propertyIndex < lastProperty;
1132       propertyIndex++)
1133     {
1134       StorageImpl_WriteProperty(
1135         storage->ancestorStorage,
1136         propertyIndex, 
1137         &emptyProperty);
1138     }
1139   }
1140
1141   return newPropertyIndex;
1142 }
1143
1144 /****************************************************************************
1145  *
1146  * Internal Method
1147  *
1148  * Case insensitive comparaison of StgProperty.name by first considering 
1149  * their size.
1150  *
1151  * Returns <0 when newPrpoerty < currentProperty
1152  *         >0 when newPrpoerty > currentProperty
1153  *          0 when newPrpoerty == currentProperty
1154  */
1155 static LONG propertyNameCmp(
1156   OLECHAR *newProperty,
1157   OLECHAR *currentProperty)
1158 {
1159   LONG diff      = lstrlenW(newProperty) - lstrlenW(currentProperty);
1160
1161   if (diff == 0) 
1162   {
1163     /* 
1164      * We compare the string themselves only when they are of the same lenght
1165      */
1166     diff = lstrcmpiW( newProperty, currentProperty);
1167   }
1168
1169   return diff;  
1170 }
1171
1172 /****************************************************************************
1173  *
1174  * Internal Method
1175  *
1176  * Properly link this new element in the property chain.
1177  */
1178 static void updatePropertyChain(
1179   StorageImpl *storage,
1180   ULONG         newPropertyIndex,
1181   StgProperty   newProperty) 
1182 {
1183   StgProperty currentProperty;
1184
1185   /*
1186    * Read the root property
1187    */
1188   StorageImpl_ReadProperty(storage->ancestorStorage,
1189                              storage->rootPropertySetIndex,
1190                              &currentProperty);
1191
1192   if (currentProperty.dirProperty != PROPERTY_NULL)
1193   {
1194     /* 
1195      * The root storage contains some element, therefore, start the research
1196      * for the appropriate location.
1197      */
1198     BOOL found = 0;
1199     ULONG  current, next, previous, currentPropertyId;
1200
1201     /*
1202      * Keep the StgProperty sequence number of the storage first property
1203      */
1204     currentPropertyId = currentProperty.dirProperty;
1205
1206     /*
1207      * Read 
1208      */
1209     StorageImpl_ReadProperty(storage->ancestorStorage,
1210                                currentProperty.dirProperty,
1211                                &currentProperty);
1212
1213     previous = currentProperty.previousProperty;
1214     next     = currentProperty.nextProperty;
1215     current  = currentPropertyId;
1216
1217     while (found == 0)
1218     {
1219       LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1220   
1221       if (diff < 0)
1222       {
1223         if (previous != PROPERTY_NULL)
1224         {
1225           StorageImpl_ReadProperty(storage->ancestorStorage,
1226                                      previous,
1227                                      &currentProperty);
1228           current = previous;
1229         }
1230         else
1231         {
1232           currentProperty.previousProperty = newPropertyIndex;
1233           StorageImpl_WriteProperty(storage->ancestorStorage,
1234                                       current,
1235                                       &currentProperty);
1236           found = 1;
1237         }
1238       }
1239       else if (diff > 0)
1240       {
1241         if (next != PROPERTY_NULL)
1242         {
1243           StorageImpl_ReadProperty(storage->ancestorStorage,
1244                                      next,
1245                                      &currentProperty);
1246           current = next;
1247         }
1248         else
1249         {
1250           currentProperty.nextProperty = newPropertyIndex;
1251           StorageImpl_WriteProperty(storage->ancestorStorage,
1252                                       current,
1253                                       &currentProperty);
1254           found = 1;
1255         }
1256       }
1257       else
1258       {
1259         /*
1260          * Trying to insert an item with the same name in the
1261          * subtree structure.
1262          */
1263         assert(FALSE);
1264       }
1265
1266       previous = currentProperty.previousProperty;
1267       next     = currentProperty.nextProperty;
1268     }
1269   }
1270   else
1271   {
1272     /* 
1273      * The root storage is empty, link the new property to it's dir property
1274      */
1275     currentProperty.dirProperty = newPropertyIndex;
1276     StorageImpl_WriteProperty(storage->ancestorStorage,
1277                                 storage->rootPropertySetIndex,
1278                                 &currentProperty);
1279   }
1280 }
1281
1282       
1283 /*************************************************************************
1284  * CopyTo (IStorage)
1285  */
1286 HRESULT WINAPI StorageImpl_CopyTo( 
1287   IStorage*   iface,
1288   DWORD       ciidExclude,  /* [in] */ 
1289   const IID*  rgiidExclude, /* [size_is][unique][in] */ 
1290   SNB         snbExclude,   /* [unique][in] */ 
1291   IStorage*   pstgDest)     /* [unique][in] */ 
1292 {
1293   IEnumSTATSTG *elements     = 0;
1294   STATSTG      curElement, strStat;
1295   HRESULT      hr;
1296   IStorage     *pstgTmp, *pstgChild;
1297   IStream      *pstrTmp, *pstrChild;
1298
1299   if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1300     FIXME("Exclude option not implemented\n");
1301
1302   TRACE("(%p, %ld, %p, %p, %p)\n", 
1303         iface, ciidExclude, rgiidExclude, 
1304         snbExclude, pstgDest);
1305
1306   /*
1307    * Perform a sanity check
1308    */
1309   if ( pstgDest == 0 )
1310     return STG_E_INVALIDPOINTER;
1311
1312   /* 
1313    * Enumerate the elements
1314    */
1315   hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1316
1317   if ( hr != S_OK )
1318     return hr;
1319
1320   /*
1321    * set the class ID
1322    */
1323   IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1324   IStorage_SetClass( pstgDest, &curElement.clsid );
1325   
1326   do
1327   {
1328     /*
1329      * Obtain the next element
1330      */
1331     hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1332
1333     if ( hr == S_FALSE )
1334     {
1335       hr = S_OK;   /* done, every element has been copied */
1336       break;
1337     }
1338
1339     if (curElement.type == STGTY_STORAGE)
1340     {
1341       /*
1342        * open child source storage
1343        */
1344       hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1345                                  STGM_READ|STGM_SHARE_EXCLUSIVE,
1346                                  NULL, 0, &pstgChild );
1347
1348       if (hr != S_OK)
1349         break;
1350
1351       /*
1352        * Check if destination storage is not a child of the source
1353        * storage, which will cause an infinite loop
1354        */
1355       if (pstgChild == pstgDest)
1356       {
1357         IEnumSTATSTG_Release(elements);
1358
1359         return STG_E_ACCESSDENIED;
1360       }
1361             
1362       /*
1363        * create a new storage in destination storage
1364        */
1365       hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1366                                    STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1367                                    0, 0,
1368                                    &pstgTmp );
1369       /*
1370        * if it already exist, don't create a new one use this one
1371        */
1372       if (hr == STG_E_FILEALREADYEXISTS)
1373       {
1374         hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1375                                    STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1376                                    NULL, 0, &pstgTmp );
1377       }
1378         
1379       if (hr != S_OK)
1380         break;
1381
1382         
1383       /*
1384        * do the copy recursively
1385        */
1386       hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1387                                snbExclude, pstgTmp );
1388                                 
1389       IStorage_Release( pstgTmp );
1390       IStorage_Release( pstgChild );
1391     }
1392     else if (curElement.type == STGTY_STREAM)
1393     {
1394       /*
1395        * create a new stream in destination storage. If the stream already
1396        * exist, it will be deleted and a new one will be created.
1397        */
1398       hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1399                                   STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1400                                   0, 0, &pstrTmp );
1401
1402       if (hr != S_OK)
1403         break;
1404
1405       /*
1406        * open child stream storage
1407        */
1408       hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1409                                 STGM_READ|STGM_SHARE_EXCLUSIVE,
1410                                 0, &pstrChild );
1411
1412       if (hr != S_OK)
1413         break;
1414
1415       /*
1416        * Get the size of the source stream
1417        */
1418       IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1419
1420       /*
1421        * Set the size of the destination stream.
1422        */
1423       IStream_SetSize(pstrTmp, strStat.cbSize);
1424       
1425       /*
1426        * do the copy
1427        */
1428       hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1429                            NULL, NULL );
1430                                 
1431       IStream_Release( pstrTmp );
1432       IStream_Release( pstrChild );
1433     }
1434     else
1435     {
1436       WARN("unknown element type: %ld\n", curElement.type);
1437     }
1438
1439   } while (hr == S_OK);
1440
1441   /*
1442    * Clean-up
1443    */
1444   IEnumSTATSTG_Release(elements);
1445   
1446   return hr;
1447 }
1448         
1449 /*************************************************************************
1450  * MoveElementTo (IStorage)
1451  */
1452 HRESULT WINAPI StorageImpl_MoveElementTo( 
1453   IStorage*     iface,
1454   const OLECHAR *pwcsName,   /* [string][in] */ 
1455   IStorage      *pstgDest,   /* [unique][in] */ 
1456   const OLECHAR *pwcsNewName,/* [string][in] */ 
1457   DWORD           grfFlags)    /* [in] */ 
1458 {
1459   FIXME("not implemented!\n");
1460   return E_NOTIMPL;
1461 }
1462         
1463 /*************************************************************************
1464  * Commit (IStorage)
1465  */
1466 HRESULT WINAPI StorageImpl_Commit( 
1467   IStorage*   iface,
1468   DWORD         grfCommitFlags)/* [in] */ 
1469 {
1470   FIXME("(%ld): stub!\n", grfCommitFlags);
1471   return S_OK;
1472 }
1473         
1474 /*************************************************************************
1475  * Revert (IStorage)
1476  */
1477 HRESULT WINAPI StorageImpl_Revert( 
1478   IStorage* iface)
1479 {
1480   FIXME("not implemented!\n");
1481   return E_NOTIMPL;
1482 }
1483
1484 /*************************************************************************
1485  * DestroyElement (IStorage)
1486  *
1487  * Stategy: This implementation is build this way for simplicity not for speed. 
1488  *          I always delete the top most element of the enumeration and adjust
1489  *          the deleted element pointer all the time.  This takes longer to 
1490  *          do but allow to reinvoke DestroyElement whenever we encounter a 
1491  *          storage object.  The optimisation reside in the usage of another
1492  *          enumeration stategy that would give all the leaves of a storage 
1493  *          first. (postfix order)
1494  */
1495 HRESULT WINAPI StorageImpl_DestroyElement( 
1496   IStorage*     iface,
1497   const OLECHAR *pwcsName)/* [string][in] */ 
1498 {
1499   StorageImpl* const This=(StorageImpl*)iface;
1500
1501   IEnumSTATSTGImpl* propertyEnumeration;
1502   HRESULT           hr = S_OK;
1503   BOOL            res;
1504   StgProperty       propertyToDelete;
1505   StgProperty       parentProperty;
1506   ULONG             foundPropertyIndexToDelete;
1507   ULONG             typeOfRelation;
1508   ULONG             parentPropertyId;
1509
1510   TRACE("(%p, %s)\n", 
1511         iface, debugstr_w(pwcsName));
1512
1513   /*
1514    * Perform a sanity check on the parameters.
1515    */
1516   if (pwcsName==NULL) 
1517     return STG_E_INVALIDPOINTER;
1518   
1519   /*
1520    * Create a property enumeration to search the property with the given name
1521    */
1522   propertyEnumeration = IEnumSTATSTGImpl_Construct(
1523     This->ancestorStorage, 
1524     This->rootPropertySetIndex);
1525   
1526   foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1527     propertyEnumeration,
1528     pwcsName,
1529     &propertyToDelete);
1530
1531   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1532
1533   if ( foundPropertyIndexToDelete == PROPERTY_NULL )  
1534   {
1535     return STG_E_FILENOTFOUND;
1536   }
1537
1538   /* 
1539    * Find the parent property of the property to delete (the one that 
1540    * link to it).  If This->dirProperty == foundPropertyIndexToDelete, 
1541    * the parent is This. Otherwise, the parent is one of it's sibling...
1542    */
1543
1544   /* 
1545    * First, read This's StgProperty..
1546    */
1547   res = StorageImpl_ReadProperty( 
1548           This->ancestorStorage,
1549           This->rootPropertySetIndex,
1550           &parentProperty);
1551
1552   assert(res==TRUE);
1553
1554   /* 
1555    * Second, check to see if by any chance the actual storage (This) is not
1556    * the parent of the property to delete... We never know...
1557    */
1558   if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1559   {
1560     /* 
1561      * Set data as it would have been done in the else part...
1562      */
1563     typeOfRelation   = PROPERTY_RELATION_DIR;
1564     parentPropertyId = This->rootPropertySetIndex;
1565   }
1566   else 
1567   { 
1568     /*
1569      * Create a property enumeration to search the parent properties, and 
1570      * delete it once done.
1571      */
1572     IEnumSTATSTGImpl* propertyEnumeration2;
1573
1574     propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1575       This->ancestorStorage, 
1576       This->rootPropertySetIndex);
1577   
1578     typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1579       propertyEnumeration2,
1580       foundPropertyIndexToDelete,
1581       &parentProperty,
1582       &parentPropertyId);
1583
1584     IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1585   }
1586
1587   if ( propertyToDelete.propertyType == PROPTYPE_STORAGE ) 
1588   {
1589     hr = deleteStorageProperty(
1590            This, 
1591            propertyToDelete.name);
1592   } 
1593   else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1594   {
1595     hr = deleteStreamProperty(
1596            This, 
1597            foundPropertyIndexToDelete,
1598            propertyToDelete);
1599   }
1600
1601   if (hr!=S_OK) 
1602     return hr;
1603
1604   /*
1605    * Adjust the property chain
1606    */
1607   hr = adjustPropertyChain(
1608         This,
1609         propertyToDelete, 
1610         parentProperty,
1611         parentPropertyId,
1612         typeOfRelation);
1613
1614   return hr;
1615 }
1616
1617
1618 /*********************************************************************
1619  *
1620  * Internal Method
1621  *
1622  * Perform the deletion of a complete storage node
1623  *
1624  */
1625 static HRESULT deleteStorageProperty(
1626   StorageImpl *parentStorage,
1627   OLECHAR     *propertyToDeleteName)
1628 {
1629   IEnumSTATSTG *elements     = 0;
1630   IStorage   *childStorage = 0;
1631   STATSTG      currentElement;
1632   HRESULT      hr;
1633   HRESULT      destroyHr = S_OK;
1634
1635   /*
1636    * Open the storage and enumerate it
1637    */
1638   hr = StorageBaseImpl_OpenStorage(
1639         (IStorage*)parentStorage,
1640         propertyToDeleteName,
1641         0,
1642         STGM_SHARE_EXCLUSIVE,
1643         0,
1644         0,
1645         &childStorage);
1646
1647   if (hr != S_OK)
1648   {
1649     return hr;
1650   }
1651
1652   /* 
1653    * Enumerate the elements
1654    */
1655   IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1656
1657   do
1658   {
1659     /*
1660      * Obtain the next element
1661      */
1662     hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1663     if (hr==S_OK)
1664     {
1665       destroyHr = StorageImpl_DestroyElement(
1666                     (IStorage*)childStorage, 
1667                     (OLECHAR*)currentElement.pwcsName);
1668
1669       CoTaskMemFree(currentElement.pwcsName);
1670     }
1671
1672     /*
1673      * We need to Reset the enumeration every time because we delete elements
1674      * and the enumeration could be invalid
1675      */
1676     IEnumSTATSTG_Reset(elements);
1677
1678   } while ((hr == S_OK) && (destroyHr == S_OK));
1679
1680   IStorage_Release(childStorage);
1681   IEnumSTATSTG_Release(elements);
1682     
1683   return destroyHr;
1684 }
1685
1686 /*********************************************************************
1687  *
1688  * Internal Method
1689  *
1690  * Perform the deletion of a stream node
1691  *
1692  */
1693 static HRESULT deleteStreamProperty(
1694   StorageImpl *parentStorage,
1695   ULONG         indexOfPropertyToDelete,
1696   StgProperty   propertyToDelete)
1697 {
1698   IStream      *pis;
1699   HRESULT        hr;
1700   ULARGE_INTEGER size;
1701
1702   size.s.HighPart = 0;
1703   size.s.LowPart = 0;
1704
1705   hr = StorageBaseImpl_OpenStream(
1706          (IStorage*)parentStorage,
1707          (OLECHAR*)propertyToDelete.name,
1708          NULL,
1709          STGM_SHARE_EXCLUSIVE,
1710          0,
1711          &pis);
1712     
1713   if (hr!=S_OK)
1714   {
1715     return(hr);
1716   }
1717
1718   /* 
1719    * Zap the stream 
1720    */ 
1721   hr = IStream_SetSize(pis, size); 
1722
1723   if(hr != S_OK)
1724   {
1725     return hr;
1726   }
1727
1728   /*
1729    * Release the stream object.
1730    */
1731   IStream_Release(pis);
1732
1733   /* 
1734    * Invalidate the property by zeroing it's name member.
1735    */
1736   propertyToDelete.sizeOfNameString = 0;
1737
1738   /* 
1739    * Here we should re-read the property so we get the updated pointer
1740    * but since we are here to zap it, I don't do it...
1741    */
1742   StorageImpl_WriteProperty(
1743     parentStorage->ancestorStorage, 
1744     indexOfPropertyToDelete,
1745     &propertyToDelete);
1746
1747   return S_OK;
1748 }
1749
1750 /*********************************************************************
1751  *
1752  * Internal Method
1753  *
1754  * Finds a placeholder for the StgProperty within the Storage
1755  *
1756  */
1757 static HRESULT findPlaceholder(
1758   StorageImpl *storage,
1759   ULONG         propertyIndexToStore,
1760   ULONG         storePropertyIndex,
1761   INT         typeOfRelation)
1762 {
1763   StgProperty storeProperty;
1764   HRESULT     hr = S_OK;
1765   BOOL      res = TRUE;
1766
1767   /*
1768    * Read the storage property
1769    */
1770   res = StorageImpl_ReadProperty(
1771           storage->ancestorStorage,
1772           storePropertyIndex, 
1773           &storeProperty);
1774
1775   if(! res)
1776   {
1777     return E_FAIL;
1778   }
1779
1780   if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1781   {
1782     if (storeProperty.previousProperty != PROPERTY_NULL)
1783     {
1784       return findPlaceholder(
1785                storage,
1786                propertyIndexToStore, 
1787                storeProperty.previousProperty,
1788                typeOfRelation);
1789     }
1790     else
1791     {
1792       storeProperty.previousProperty = propertyIndexToStore;
1793     }
1794   }
1795   else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1796   {
1797     if (storeProperty.nextProperty != PROPERTY_NULL)
1798     {
1799       return findPlaceholder(
1800                storage,
1801                propertyIndexToStore, 
1802                storeProperty.nextProperty,
1803                typeOfRelation);
1804     }
1805     else
1806     {
1807       storeProperty.nextProperty = propertyIndexToStore;
1808     }
1809   } 
1810   else if (typeOfRelation == PROPERTY_RELATION_DIR)
1811   {
1812     if (storeProperty.dirProperty != PROPERTY_NULL)
1813     {
1814       return findPlaceholder(
1815                storage,
1816                propertyIndexToStore, 
1817                storeProperty.dirProperty,
1818                typeOfRelation);
1819     }
1820     else
1821     {
1822       storeProperty.dirProperty = propertyIndexToStore;
1823     }
1824   }
1825
1826   hr = StorageImpl_WriteProperty(
1827          storage->ancestorStorage,
1828          storePropertyIndex, 
1829          &storeProperty);
1830
1831   if(! hr)
1832   {
1833     return E_FAIL;
1834   }
1835
1836   return S_OK;
1837 }
1838
1839 /*************************************************************************
1840  *
1841  * Internal Method
1842  *
1843  * This method takes the previous and the next property link of a property 
1844  * to be deleted and find them a place in the Storage.
1845  */
1846 static HRESULT adjustPropertyChain( 
1847   StorageImpl *This,
1848   StgProperty   propertyToDelete,
1849   StgProperty   parentProperty,
1850   ULONG         parentPropertyId,
1851   INT         typeOfRelation)
1852 {
1853   ULONG   newLinkProperty        = PROPERTY_NULL;
1854   BOOL  needToFindAPlaceholder = FALSE;
1855   ULONG   storeNode              = PROPERTY_NULL;
1856   ULONG   toStoreNode            = PROPERTY_NULL;
1857   INT   relationType           = 0;
1858   HRESULT hr                     = S_OK;
1859   BOOL  res                    = TRUE;
1860   
1861   if (typeOfRelation == PROPERTY_RELATION_PREVIOUS) 
1862   {
1863     if (propertyToDelete.previousProperty != PROPERTY_NULL)  
1864     {
1865       /* 
1866        * Set the parent previous to the property to delete previous
1867        */
1868       newLinkProperty = propertyToDelete.previousProperty;
1869
1870       if (propertyToDelete.nextProperty != PROPERTY_NULL)  
1871       {
1872         /*
1873          * We also need to find a storage for the other link, setup variables 
1874          * to do this at the end...
1875          */      
1876         needToFindAPlaceholder = TRUE;
1877         storeNode              = propertyToDelete.previousProperty;
1878         toStoreNode            = propertyToDelete.nextProperty;
1879         relationType           = PROPERTY_RELATION_NEXT;
1880       }
1881     } 
1882     else if (propertyToDelete.nextProperty != PROPERTY_NULL)  
1883     {
1884       /* 
1885        * Set the parent previous to the property to delete next
1886        */
1887       newLinkProperty = propertyToDelete.nextProperty;
1888     }
1889    
1890     /* 
1891      * Link it for real...
1892      */ 
1893     parentProperty.previousProperty = newLinkProperty;
1894   
1895   } 
1896   else if (typeOfRelation == PROPERTY_RELATION_NEXT) 
1897   {
1898     if (propertyToDelete.previousProperty != PROPERTY_NULL)  
1899     {
1900       /* 
1901        * Set the parent next to the property to delete next previous
1902        */
1903       newLinkProperty = propertyToDelete.previousProperty;
1904       
1905       if (propertyToDelete.nextProperty != PROPERTY_NULL)  
1906       {
1907         /*
1908          * We also need to find a storage for the other link, setup variables 
1909          * to do this at the end...
1910          */      
1911         needToFindAPlaceholder = TRUE;
1912         storeNode              = propertyToDelete.previousProperty;
1913         toStoreNode            = propertyToDelete.nextProperty;
1914         relationType           = PROPERTY_RELATION_NEXT;
1915       }
1916     } 
1917     else if (propertyToDelete.nextProperty != PROPERTY_NULL)  
1918     {
1919       /* 
1920        * Set the parent next to the property to delete next
1921        */
1922       newLinkProperty = propertyToDelete.nextProperty;
1923     }
1924
1925     /* 
1926      * Link it for real...
1927      */ 
1928     parentProperty.nextProperty = newLinkProperty;
1929   } 
1930   else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
1931   {
1932     if (propertyToDelete.previousProperty != PROPERTY_NULL) 
1933     {
1934       /* 
1935        * Set the parent dir to the property to delete previous
1936        */
1937       newLinkProperty = propertyToDelete.previousProperty;
1938
1939       if (propertyToDelete.nextProperty != PROPERTY_NULL)  
1940       {
1941         /*
1942          * We also need to find a storage for the other link, setup variables 
1943          * to do this at the end...
1944          */      
1945         needToFindAPlaceholder = TRUE;
1946         storeNode              = propertyToDelete.previousProperty;
1947         toStoreNode            = propertyToDelete.nextProperty;
1948         relationType           = PROPERTY_RELATION_NEXT;
1949       }
1950     } 
1951     else if (propertyToDelete.nextProperty != PROPERTY_NULL)  
1952     {
1953       /* 
1954        * Set the parent dir to the property to delete next
1955        */
1956       newLinkProperty = propertyToDelete.nextProperty;
1957     }
1958
1959     /* 
1960      * Link it for real...
1961      */ 
1962     parentProperty.dirProperty = newLinkProperty;
1963   }
1964
1965   /* 
1966    * Write back the parent property    
1967    */
1968   res = StorageImpl_WriteProperty(
1969           This->ancestorStorage, 
1970           parentPropertyId,
1971           &parentProperty);
1972   if(! res)
1973   {
1974     return E_FAIL;
1975   }
1976
1977   /*
1978    * If a placeholder is required for the other link, then, find one and 
1979    * get out of here...
1980    */
1981   if (needToFindAPlaceholder) 
1982   {
1983     hr = findPlaceholder(
1984            This, 
1985            toStoreNode, 
1986            storeNode,
1987            relationType);
1988   }
1989
1990   return hr;
1991 }
1992
1993
1994 /******************************************************************************
1995  * SetElementTimes (IStorage)
1996  */
1997 HRESULT WINAPI StorageImpl_SetElementTimes( 
1998   IStorage*     iface,
1999   const OLECHAR *pwcsName,/* [string][in] */ 
2000   const FILETIME  *pctime,  /* [in] */ 
2001   const FILETIME  *patime,  /* [in] */ 
2002   const FILETIME  *pmtime)  /* [in] */ 
2003 {
2004   FIXME("not implemented!\n");
2005   return E_NOTIMPL;
2006 }
2007
2008 /******************************************************************************
2009  * SetStateBits (IStorage)
2010  */
2011 HRESULT WINAPI StorageImpl_SetStateBits( 
2012   IStorage*   iface,
2013   DWORD         grfStateBits,/* [in] */ 
2014   DWORD         grfMask)     /* [in] */ 
2015 {
2016   FIXME("not implemented!\n");
2017   return E_NOTIMPL;
2018 }
2019
2020 HRESULT StorageImpl_Construct(
2021   StorageImpl* This,
2022   HANDLE       hFile,
2023   ILockBytes*  pLkbyt,
2024   DWORD        openFlags,
2025   BOOL         fileBased)
2026 {
2027   HRESULT     hr = S_OK;
2028   StgProperty currentProperty;
2029   BOOL      readSucessful;
2030   ULONG       currentPropertyIndex;
2031   
2032   if ( FAILED( validateSTGM(openFlags) ))
2033     return STG_E_INVALIDFLAG;
2034
2035   memset(This, 0, sizeof(StorageImpl));
2036   
2037   /*
2038    * Initialize the virtual fgunction table.
2039    */
2040   ICOM_VTBL(This)    = &Storage32Impl_Vtbl;
2041   This->v_destructor = &StorageImpl_Destroy;
2042   
2043   /*
2044    * This is the top-level storage so initialize the ancester pointer
2045    * to this.
2046    */
2047   This->ancestorStorage = This;
2048   
2049   /*
2050    * Initialize the physical support of the storage.
2051    */
2052   This->hFile = hFile;
2053   
2054   /*
2055    * Initialize the big block cache.
2056    */
2057   This->bigBlockSize   = DEF_BIG_BLOCK_SIZE;
2058   This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2059   This->bigBlockFile   = BIGBLOCKFILE_Construct(hFile,
2060                                                 pLkbyt,
2061                                                 openFlags,
2062                                                 This->bigBlockSize,
2063                                                 fileBased);
2064
2065   if (This->bigBlockFile == 0)
2066     return E_FAIL;
2067  
2068   if (openFlags & STGM_CREATE)
2069   {
2070     ULARGE_INTEGER size;
2071     BYTE* bigBlockBuffer;
2072
2073     /*
2074      * Initialize all header variables:
2075      * - The big block depot consists of one block and it is at block 0
2076      * - The properties start at block 1
2077      * - There is no small block depot
2078      */
2079     memset( This->bigBlockDepotStart,     
2080             BLOCK_UNUSED, 
2081             sizeof(This->bigBlockDepotStart));
2082
2083     This->bigBlockDepotCount    = 1;
2084     This->bigBlockDepotStart[0] = 0;
2085     This->rootStartBlock        = 1;
2086     This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
2087     This->bigBlockSizeBits      = DEF_BIG_BLOCK_SIZE_BITS;
2088     This->smallBlockSizeBits    = DEF_SMALL_BLOCK_SIZE_BITS;
2089     This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2090     This->extBigBlockDepotCount = 0;
2091
2092     StorageImpl_SaveFileHeader(This);
2093
2094     /*
2095      * Add one block for the big block depot and one block for the properties
2096      */
2097     size.s.HighPart = 0;
2098     size.s.LowPart  = This->bigBlockSize * 3;
2099     BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2100
2101     /*
2102      * Initialize the big block depot
2103      */
2104     bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2105     memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2106     StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2107     StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2108     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2109   }
2110   else
2111   {
2112     /*
2113      * Load the header for the file.
2114      */
2115     hr = StorageImpl_LoadFileHeader(This);
2116
2117     if (FAILED(hr))
2118     {
2119       BIGBLOCKFILE_Destructor(This->bigBlockFile);
2120
2121       return hr;
2122     }
2123   }
2124
2125   /*
2126    * There is no block depot cached yet.
2127    */
2128   This->indexBlockDepotCached = 0xFFFFFFFF;
2129
2130   /*
2131    * Start searching for free blocks with block 0.
2132    */
2133   This->prevFreeBlock = 0;
2134   
2135   /*
2136    * Create the block chain abstractions.
2137    */
2138   This->rootBlockChain = 
2139     BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2140
2141   This->smallBlockDepotChain = BlockChainStream_Construct(
2142                                  This, 
2143                                  &This->smallBlockDepotStart, 
2144                                  PROPERTY_NULL);
2145
2146   /*
2147    * Write the root property 
2148    */
2149   if (openFlags & STGM_CREATE)
2150   {
2151     StgProperty rootProp;
2152     /*
2153      * Initialize the property chain
2154      */
2155     memset(&rootProp, 0, sizeof(rootProp));
2156     lstrcpyAtoW(rootProp.name, rootPropertyName);
2157
2158     rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2159     rootProp.propertyType     = PROPTYPE_ROOT;
2160     rootProp.previousProperty = PROPERTY_NULL;
2161     rootProp.nextProperty     = PROPERTY_NULL;
2162     rootProp.dirProperty      = PROPERTY_NULL;
2163     rootProp.startingBlock    = BLOCK_END_OF_CHAIN;
2164     rootProp.size.s.HighPart    = 0;
2165     rootProp.size.s.LowPart     = 0;
2166
2167     StorageImpl_WriteProperty(This, 0, &rootProp);
2168   }
2169
2170   /*
2171    * Find the ID of the root int he property sets.
2172    */
2173   currentPropertyIndex = 0;
2174   
2175   do
2176   {
2177     readSucessful = StorageImpl_ReadProperty(
2178                       This, 
2179                       currentPropertyIndex, 
2180                       &currentProperty);
2181     
2182     if (readSucessful)
2183     {
2184       if ( (currentProperty.sizeOfNameString != 0 ) &&
2185            (currentProperty.propertyType     == PROPTYPE_ROOT) )
2186       {
2187         This->rootPropertySetIndex = currentPropertyIndex;
2188       }
2189     }
2190
2191     currentPropertyIndex++;
2192     
2193   } while (readSucessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2194   
2195   if (!readSucessful)
2196   {
2197     /* TODO CLEANUP */
2198     return E_FAIL;
2199   }
2200
2201   /*
2202    * Create the block chain abstraction for the small block root chain.
2203    */
2204   This->smallBlockRootChain = BlockChainStream_Construct(
2205                                 This, 
2206                                 NULL, 
2207                                 This->rootPropertySetIndex);
2208   
2209   return hr;
2210 }
2211
2212 void StorageImpl_Destroy(
2213   StorageImpl* This)
2214 {
2215   TRACE("(%p)\n", This);
2216
2217   BlockChainStream_Destroy(This->smallBlockRootChain);
2218   BlockChainStream_Destroy(This->rootBlockChain);
2219   BlockChainStream_Destroy(This->smallBlockDepotChain);
2220
2221   BIGBLOCKFILE_Destructor(This->bigBlockFile);
2222   return;
2223 }
2224
2225 /******************************************************************************
2226  *      Storage32Impl_GetNextFreeBigBlock
2227  *
2228  * Returns the index of the next free big block.
2229  * If the big block depot is filled, this method will enlarge it.
2230  *
2231  */
2232 ULONG StorageImpl_GetNextFreeBigBlock(
2233   StorageImpl* This)
2234 {
2235   ULONG depotBlockIndexPos;
2236   void  *depotBuffer;
2237   ULONG depotBlockOffset;
2238   ULONG blocksPerDepot    = This->bigBlockSize / sizeof(ULONG);
2239   ULONG nextBlockIndex    = BLOCK_SPECIAL;
2240   int   depotIndex        = 0;
2241   ULONG freeBlock         = BLOCK_UNUSED;
2242
2243   depotIndex = This->prevFreeBlock / blocksPerDepot;
2244   depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2245
2246   /*
2247    * Scan the entire big block depot until we find a block marked free
2248    */
2249   while (nextBlockIndex != BLOCK_UNUSED)
2250   {
2251     if (depotIndex < COUNT_BBDEPOTINHEADER)
2252     {
2253       depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2254
2255       /*
2256        * Grow the primary depot.
2257        */
2258       if (depotBlockIndexPos == BLOCK_UNUSED)
2259       {
2260         depotBlockIndexPos = depotIndex*blocksPerDepot;
2261
2262         /*
2263          * Add a block depot.
2264          */
2265         Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2266         This->bigBlockDepotCount++;
2267         This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2268
2269         /*
2270          * Flag it as a block depot.
2271          */
2272         StorageImpl_SetNextBlockInChain(This,
2273                                           depotBlockIndexPos,
2274                                           BLOCK_SPECIAL);
2275
2276         /* Save new header information.
2277          */
2278         StorageImpl_SaveFileHeader(This);
2279       }
2280     }
2281     else
2282     {
2283       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2284
2285       if (depotBlockIndexPos == BLOCK_UNUSED)
2286       {
2287         /*
2288          * Grow the extended depot.
2289          */
2290         ULONG extIndex       = BLOCK_UNUSED;
2291         ULONG numExtBlocks   = depotIndex - COUNT_BBDEPOTINHEADER;
2292         ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2293
2294         if (extBlockOffset == 0)
2295         {
2296           /* We need an extended block.
2297            */
2298           extIndex = Storage32Impl_AddExtBlockDepot(This);
2299           This->extBigBlockDepotCount++;
2300           depotBlockIndexPos = extIndex + 1;
2301         }
2302         else
2303           depotBlockIndexPos = depotIndex * blocksPerDepot;
2304
2305         /*
2306          * Add a block depot and mark it in the extended block.
2307          */
2308         Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2309         This->bigBlockDepotCount++;
2310         Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2311
2312         /* Flag the block depot.
2313          */
2314         StorageImpl_SetNextBlockInChain(This,
2315                                           depotBlockIndexPos,
2316                                           BLOCK_SPECIAL);
2317
2318         /* If necessary, flag the extended depot block.
2319          */
2320         if (extIndex != BLOCK_UNUSED)
2321           StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2322
2323         /* Save header information.
2324          */
2325         StorageImpl_SaveFileHeader(This);
2326       }
2327     }
2328
2329     depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2330
2331     if (depotBuffer != 0)
2332     {
2333       while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2334               ( nextBlockIndex != BLOCK_UNUSED))
2335       {
2336         StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2337
2338         if (nextBlockIndex == BLOCK_UNUSED)
2339         {
2340           freeBlock = (depotIndex * blocksPerDepot) +
2341                       (depotBlockOffset/sizeof(ULONG));
2342         }
2343
2344         depotBlockOffset += sizeof(ULONG);
2345       }
2346
2347       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2348     }
2349
2350     depotIndex++;
2351     depotBlockOffset = 0;
2352   }
2353
2354   This->prevFreeBlock = freeBlock;
2355
2356   return freeBlock;
2357 }
2358
2359 /******************************************************************************
2360  *      Storage32Impl_AddBlockDepot
2361  *
2362  * This will create a depot block, essentially it is a block initialized
2363  * to BLOCK_UNUSEDs.
2364  */
2365 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2366 {
2367   BYTE* blockBuffer;
2368
2369   blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2370
2371   /*
2372    * Initialize blocks as free
2373    */
2374   memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2375
2376   StorageImpl_ReleaseBigBlock(This, blockBuffer);
2377 }
2378
2379 /******************************************************************************
2380  *      Storage32Impl_GetExtDepotBlock
2381  *
2382  * Returns the index of the block that corresponds to the specified depot
2383  * index. This method is only for depot indexes equal or greater than
2384  * COUNT_BBDEPOTINHEADER.
2385  */
2386 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2387 {
2388   ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2389   ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
2390   ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
2391   ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
2392   ULONG blockIndex             = BLOCK_UNUSED;
2393   ULONG extBlockIndex          = This->extBigBlockDepotStart;
2394
2395   assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2396
2397   if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2398     return BLOCK_UNUSED;
2399
2400   while (extBlockCount > 0)
2401   {
2402     extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2403     extBlockCount--;
2404   }
2405
2406   if (extBlockIndex != BLOCK_UNUSED)
2407   {
2408     BYTE* depotBuffer;
2409
2410     depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2411
2412     if (depotBuffer != 0)
2413     {
2414       StorageUtl_ReadDWord(depotBuffer,
2415                            extBlockOffset * sizeof(ULONG),
2416                            &blockIndex);
2417
2418       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2419     }
2420   }
2421
2422   return blockIndex;
2423 }
2424
2425 /******************************************************************************
2426  *      Storage32Impl_SetExtDepotBlock
2427  *
2428  * Associates the specified block index to the specified depot index.
2429  * This method is only for depot indexes equal or greater than
2430  * COUNT_BBDEPOTINHEADER.
2431  */
2432 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2433                                     ULONG depotIndex,
2434                                     ULONG blockIndex)
2435 {
2436   ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2437   ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
2438   ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
2439   ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
2440   ULONG extBlockIndex          = This->extBigBlockDepotStart;
2441
2442   assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2443
2444   while (extBlockCount > 0)
2445   {
2446     extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2447     extBlockCount--;
2448   }
2449
2450   if (extBlockIndex != BLOCK_UNUSED)
2451   {
2452     BYTE* depotBuffer;
2453
2454     depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2455
2456     if (depotBuffer != 0)
2457     {
2458       StorageUtl_WriteDWord(depotBuffer,
2459                             extBlockOffset * sizeof(ULONG),
2460                             blockIndex);
2461
2462       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2463     }
2464   }
2465 }
2466
2467 /******************************************************************************
2468  *      Storage32Impl_AddExtBlockDepot
2469  *
2470  * Creates an extended depot block.
2471  */
2472 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2473 {
2474   ULONG numExtBlocks           = This->extBigBlockDepotCount;
2475   ULONG nextExtBlock           = This->extBigBlockDepotStart;
2476   BYTE* depotBuffer            = NULL;
2477   ULONG index                  = BLOCK_UNUSED;
2478   ULONG nextBlockOffset        = This->bigBlockSize - sizeof(ULONG);
2479   ULONG blocksPerDepotBlock    = This->bigBlockSize / sizeof(ULONG);
2480   ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2481
2482   index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2483           blocksPerDepotBlock;
2484
2485   if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2486   {
2487     /*
2488      * The first extended block.
2489      */
2490     This->extBigBlockDepotStart = index;
2491   }
2492   else
2493   {
2494     int i;
2495     /*
2496      * Follow the chain to the last one.
2497      */
2498     for (i = 0; i < (numExtBlocks - 1); i++)
2499     {
2500       nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2501     }
2502
2503     /*
2504      * Add the new extended block to the chain.
2505      */
2506     depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2507     StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2508     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2509   }
2510
2511   /*
2512    * Initialize this block.
2513    */
2514   depotBuffer = StorageImpl_GetBigBlock(This, index);
2515   memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2516   StorageImpl_ReleaseBigBlock(This, depotBuffer);
2517
2518   return index;
2519 }
2520
2521 /******************************************************************************
2522  *      Storage32Impl_FreeBigBlock
2523  *
2524  * This method will flag the specified block as free in the big block depot.
2525  */
2526 void  StorageImpl_FreeBigBlock(
2527   StorageImpl* This,
2528   ULONG          blockIndex)
2529 {
2530   StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2531
2532   if (blockIndex < This->prevFreeBlock)
2533     This->prevFreeBlock = blockIndex;
2534 }
2535
2536 /************************************************************************
2537  * Storage32Impl_GetNextBlockInChain
2538  *
2539  * This method will retrieve the block index of the next big block in
2540  * in the chain.
2541  *
2542  * Params:  This       - Pointer to the Storage object.
2543  *          blockIndex - Index of the block to retrieve the chain
2544  *                       for.
2545  *
2546  * Returns: This method returns the index of the next block in the chain.
2547  *          It will return the constants:
2548  *              BLOCK_SPECIAL - If the block given was not part of a
2549  *                              chain.
2550  *              BLOCK_END_OF_CHAIN - If the block given was the last in
2551  *                                   a chain.
2552  *              BLOCK_UNUSED - If the block given was not past of a chain
2553  *                             and is available.
2554  *              BLOCK_EXTBBDEPOT - This block is part of the extended
2555  *                                 big block depot.
2556  *
2557  * See Windows documentation for more details on IStorage methods.
2558  */
2559 ULONG StorageImpl_GetNextBlockInChain(
2560   StorageImpl* This,
2561   ULONG          blockIndex)
2562 {
2563   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
2564   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
2565   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2566   ULONG nextBlockIndex   = BLOCK_SPECIAL;
2567   void* depotBuffer;
2568   ULONG depotBlockIndexPos;
2569
2570   assert(depotBlockCount < This->bigBlockDepotCount);
2571
2572   /*
2573    * Cache the currently accessed depot block.
2574    */
2575   if (depotBlockCount != This->indexBlockDepotCached)
2576   {
2577     This->indexBlockDepotCached = depotBlockCount;
2578
2579     if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2580     {
2581       depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2582     }
2583     else
2584     {
2585       /*
2586        * We have to look in the extended depot.
2587        */
2588       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2589     }
2590
2591     depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2592
2593     if (depotBuffer!=0)
2594     {
2595       int index;
2596
2597       for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2598       {
2599         StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2600         This->blockDepotCached[index] = nextBlockIndex;
2601       }
2602
2603       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2604     }
2605   }
2606
2607   nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2608
2609   return nextBlockIndex;
2610 }
2611
2612 /******************************************************************************
2613  *      Storage32Impl_GetNextExtendedBlock
2614  *
2615  * Given an extended block this method will return the next extended block.
2616  *
2617  * NOTES:
2618  * The last ULONG of an extended block is the block index of the next
2619  * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2620  * depot.
2621  *
2622  * Return values:
2623  *    - The index of the next extended block
2624  *    - BLOCK_UNUSED: there is no next extended block.
2625  *    - Any other return values denotes failure.
2626  */
2627 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2628 {
2629   ULONG nextBlockIndex   = BLOCK_SPECIAL;
2630   ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2631   void* depotBuffer;
2632
2633   depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2634
2635   if (depotBuffer!=0)
2636   {
2637     StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2638
2639     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2640   }
2641
2642   return nextBlockIndex;
2643 }
2644
2645 /******************************************************************************
2646  *      Storage32Impl_SetNextBlockInChain
2647  *
2648  * This method will write the index of the specified block's next block
2649  * in the big block depot.
2650  *
2651  * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2652  *              do the following
2653  *
2654  * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2655  * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2656  * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2657  *
2658  */
2659 void  StorageImpl_SetNextBlockInChain(
2660           StorageImpl* This,
2661           ULONG          blockIndex,
2662           ULONG          nextBlock)
2663 {
2664   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
2665   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
2666   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2667   ULONG depotBlockIndexPos;
2668   void* depotBuffer;
2669
2670   assert(depotBlockCount < This->bigBlockDepotCount);
2671   assert(blockIndex != nextBlock);
2672
2673   if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2674   {
2675     depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2676   }
2677   else
2678   {
2679     /*
2680      * We have to look in the extended depot.
2681      */
2682     depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2683   }
2684
2685   depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2686
2687   if (depotBuffer!=0)
2688   {
2689     StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2690     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2691   }
2692
2693   /*
2694    * Update the cached block depot, if necessary.
2695    */
2696   if (depotBlockCount == This->indexBlockDepotCached)
2697   {
2698     This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2699   }
2700 }
2701
2702 /******************************************************************************
2703  *      Storage32Impl_LoadFileHeader
2704  *
2705  * This method will read in the file header, i.e. big block index -1.
2706  */
2707 HRESULT StorageImpl_LoadFileHeader(
2708           StorageImpl* This)
2709 {
2710   HRESULT hr = STG_E_FILENOTFOUND;
2711   void*   headerBigBlock = NULL;
2712   int     index;
2713
2714   /*
2715    * Get a pointer to the big block of data containing the header.
2716    */
2717   headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2718
2719   /*
2720    * Extract the information from the header.
2721    */
2722   if (headerBigBlock!=0)
2723   {
2724     /*
2725      * Check for the "magic number" signature and return an error if it is not
2726      * found.
2727      */
2728     if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2729     {
2730       StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2731       return STG_E_OLDFORMAT;
2732     }
2733
2734     if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2735     {
2736       StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2737       return STG_E_INVALIDHEADER;
2738     }
2739
2740     StorageUtl_ReadWord(
2741       headerBigBlock, 
2742       OFFSET_BIGBLOCKSIZEBITS,   
2743       &This->bigBlockSizeBits);
2744
2745     StorageUtl_ReadWord(
2746       headerBigBlock, 
2747       OFFSET_SMALLBLOCKSIZEBITS, 
2748       &This->smallBlockSizeBits);
2749
2750     StorageUtl_ReadDWord(
2751       headerBigBlock, 
2752       OFFSET_BBDEPOTCOUNT,      
2753       &This->bigBlockDepotCount);
2754
2755     StorageUtl_ReadDWord(
2756       headerBigBlock, 
2757       OFFSET_ROOTSTARTBLOCK,    
2758       &This->rootStartBlock);
2759
2760     StorageUtl_ReadDWord(
2761       headerBigBlock, 
2762       OFFSET_SBDEPOTSTART,      
2763       &This->smallBlockDepotStart);
2764
2765     StorageUtl_ReadDWord( 
2766       headerBigBlock, 
2767       OFFSET_EXTBBDEPOTSTART,   
2768       &This->extBigBlockDepotStart);
2769
2770     StorageUtl_ReadDWord(
2771       headerBigBlock, 
2772       OFFSET_EXTBBDEPOTCOUNT,   
2773       &This->extBigBlockDepotCount);
2774     
2775     for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2776     {
2777       StorageUtl_ReadDWord(
2778         headerBigBlock, 
2779         OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2780         &(This->bigBlockDepotStart[index]));
2781     }
2782     
2783     /*
2784      * Make the bitwise arithmetic to get the size of the blocks in bytes.
2785      */
2786     if ((1 << 2) == 4)
2787     {
2788       This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2789       This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2790     }
2791     else
2792     {
2793       This->bigBlockSize   = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2794       This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2795     }
2796     
2797     /*
2798      * Right now, the code is making some assumptions about the size of the 
2799      * blocks, just make sure they are what we're expecting.
2800      */
2801     assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) && 
2802             (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2803     
2804     /*
2805      * Release the block.
2806      */
2807     StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2808
2809     hr = S_OK;
2810   }
2811   
2812   return hr;
2813 }
2814
2815 /******************************************************************************
2816  *      Storage32Impl_SaveFileHeader
2817  *
2818  * This method will save to the file the header, i.e. big block -1.
2819  */
2820 void StorageImpl_SaveFileHeader(
2821           StorageImpl* This)
2822 {
2823   BYTE   headerBigBlock[BIG_BLOCK_SIZE];
2824   int    index;
2825   BOOL success;
2826
2827   /*
2828    * Get a pointer to the big block of data containing the header.
2829    */
2830   success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2831   
2832   /*
2833    * If the block read failed, the file is probably new.
2834    */
2835   if (!success)
2836   {
2837     /*
2838      * Initialize for all unknown fields.
2839      */
2840     memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2841     
2842     /*
2843      * Initialize the magic number.
2844      */
2845     memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2846     
2847     /*
2848      * And a bunch of things we don't know what they mean
2849      */
2850     StorageUtl_WriteWord(headerBigBlock,  0x18, 0x3b);
2851     StorageUtl_WriteWord(headerBigBlock,  0x1a, 0x3);
2852     StorageUtl_WriteWord(headerBigBlock,  0x1c, (WORD)-2);
2853     StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2854     StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2855   }
2856   
2857   /*
2858    * Write the information to the header.
2859    */
2860   if (headerBigBlock!=0)
2861   {
2862     StorageUtl_WriteWord(
2863       headerBigBlock, 
2864       OFFSET_BIGBLOCKSIZEBITS,   
2865       This->bigBlockSizeBits);
2866
2867     StorageUtl_WriteWord(
2868       headerBigBlock, 
2869       OFFSET_SMALLBLOCKSIZEBITS, 
2870       This->smallBlockSizeBits);
2871
2872     StorageUtl_WriteDWord(
2873       headerBigBlock, 
2874       OFFSET_BBDEPOTCOUNT,      
2875       This->bigBlockDepotCount);
2876
2877     StorageUtl_WriteDWord(
2878       headerBigBlock, 
2879       OFFSET_ROOTSTARTBLOCK,    
2880       This->rootStartBlock);
2881
2882     StorageUtl_WriteDWord(
2883       headerBigBlock, 
2884       OFFSET_SBDEPOTSTART,      
2885       This->smallBlockDepotStart);
2886
2887     StorageUtl_WriteDWord(
2888       headerBigBlock, 
2889       OFFSET_EXTBBDEPOTSTART,   
2890       This->extBigBlockDepotStart);
2891
2892     StorageUtl_WriteDWord(
2893       headerBigBlock, 
2894       OFFSET_EXTBBDEPOTCOUNT,   
2895       This->extBigBlockDepotCount);
2896
2897     for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2898     {
2899       StorageUtl_WriteDWord(
2900         headerBigBlock, 
2901         OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2902         (This->bigBlockDepotStart[index]));
2903     }
2904   }
2905   
2906   /*
2907    * Write the big block back to the file.
2908    */
2909   StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2910 }
2911
2912 /******************************************************************************
2913  *      Storage32Impl_ReadProperty
2914  *
2915  * This method will read the specified property from the property chain.
2916  */
2917 BOOL StorageImpl_ReadProperty(
2918   StorageImpl* This,
2919   ULONG          index,
2920   StgProperty*   buffer)
2921 {
2922   BYTE           currentProperty[PROPSET_BLOCK_SIZE];
2923   ULARGE_INTEGER offsetInPropSet;
2924   BOOL         readSucessful;
2925   ULONG          bytesRead;
2926
2927   offsetInPropSet.s.HighPart = 0;
2928   offsetInPropSet.s.LowPart  = index * PROPSET_BLOCK_SIZE;
2929   
2930   readSucessful = BlockChainStream_ReadAt(
2931                     This->rootBlockChain,
2932                     offsetInPropSet,
2933                     PROPSET_BLOCK_SIZE,
2934                     currentProperty,
2935                     &bytesRead);
2936   
2937   if (readSucessful)
2938   {
2939     memset(buffer->name, 0, sizeof(buffer->name));
2940     memcpy(
2941       buffer->name, 
2942       currentProperty+OFFSET_PS_NAME, 
2943       PROPERTY_NAME_BUFFER_LEN );
2944
2945     memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
2946     
2947     StorageUtl_ReadWord(
2948       currentProperty,  
2949       OFFSET_PS_NAMELENGTH,  
2950       &buffer->sizeOfNameString);
2951
2952     StorageUtl_ReadDWord(
2953       currentProperty, 
2954       OFFSET_PS_PREVIOUSPROP, 
2955       &buffer->previousProperty);
2956
2957     StorageUtl_ReadDWord(
2958       currentProperty, 
2959       OFFSET_PS_NEXTPROP,     
2960       &buffer->nextProperty);
2961
2962     StorageUtl_ReadDWord(
2963       currentProperty, 
2964       OFFSET_PS_DIRPROP,      
2965       &buffer->dirProperty);
2966
2967     StorageUtl_ReadGUID(
2968       currentProperty,  
2969       OFFSET_PS_GUID,        
2970       &buffer->propertyUniqueID);
2971
2972     StorageUtl_ReadDWord(
2973       currentProperty, 
2974       OFFSET_PS_TSS1,         
2975       &buffer->timeStampS1);
2976
2977     StorageUtl_ReadDWord(
2978       currentProperty, 
2979       OFFSET_PS_TSD1,         
2980       &buffer->timeStampD1);
2981
2982     StorageUtl_ReadDWord(
2983       currentProperty, 
2984       OFFSET_PS_TSS2,         
2985       &buffer->timeStampS2);
2986
2987     StorageUtl_ReadDWord(
2988       currentProperty, 
2989       OFFSET_PS_TSD2,         
2990       &buffer->timeStampD2);
2991
2992     StorageUtl_ReadDWord(
2993       currentProperty, 
2994       OFFSET_PS_STARTBLOCK,   
2995       &buffer->startingBlock);
2996
2997     StorageUtl_ReadDWord(
2998       currentProperty, 
2999       OFFSET_PS_SIZE,         
3000       &buffer->size.s.LowPart);
3001
3002     buffer->size.s.HighPart = 0;
3003   }
3004
3005   return readSucessful;
3006 }
3007
3008 /*********************************************************************
3009  * Write the specified property into the property chain
3010  */
3011 BOOL StorageImpl_WriteProperty(
3012   StorageImpl* This,
3013   ULONG          index,
3014   StgProperty*   buffer)
3015 {
3016   BYTE           currentProperty[PROPSET_BLOCK_SIZE];
3017   ULARGE_INTEGER offsetInPropSet;
3018   BOOL         writeSucessful;
3019   ULONG          bytesWritten;
3020
3021   offsetInPropSet.s.HighPart = 0;
3022   offsetInPropSet.s.LowPart  = index * PROPSET_BLOCK_SIZE;
3023
3024   memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3025
3026   memcpy(
3027     currentProperty + OFFSET_PS_NAME, 
3028     buffer->name, 
3029     PROPERTY_NAME_BUFFER_LEN );
3030
3031   memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3032
3033   /* 
3034    * Reassign the size in case of mistake....
3035    */
3036   buffer->sizeOfNameString = (lstrlenW(buffer->name)+1) * sizeof(WCHAR);
3037
3038   StorageUtl_WriteWord(
3039     currentProperty,  
3040       OFFSET_PS_NAMELENGTH,   
3041       buffer->sizeOfNameString);
3042
3043   StorageUtl_WriteDWord(
3044     currentProperty, 
3045       OFFSET_PS_PREVIOUSPROP, 
3046       buffer->previousProperty);
3047
3048   StorageUtl_WriteDWord(
3049     currentProperty, 
3050       OFFSET_PS_NEXTPROP,     
3051       buffer->nextProperty);
3052
3053   StorageUtl_WriteDWord(
3054     currentProperty, 
3055       OFFSET_PS_DIRPROP,      
3056       buffer->dirProperty);
3057
3058   StorageUtl_WriteGUID(
3059     currentProperty,  
3060       OFFSET_PS_GUID,        
3061       &buffer->propertyUniqueID);
3062
3063   StorageUtl_WriteDWord(
3064     currentProperty, 
3065       OFFSET_PS_TSS1,         
3066       buffer->timeStampS1);
3067
3068   StorageUtl_WriteDWord(
3069     currentProperty, 
3070       OFFSET_PS_TSD1,         
3071       buffer->timeStampD1);
3072
3073   StorageUtl_WriteDWord(
3074     currentProperty, 
3075       OFFSET_PS_TSS2,         
3076       buffer->timeStampS2);
3077
3078   StorageUtl_WriteDWord(
3079     currentProperty, 
3080       OFFSET_PS_TSD2,         
3081       buffer->timeStampD2);
3082
3083   StorageUtl_WriteDWord(
3084     currentProperty, 
3085       OFFSET_PS_STARTBLOCK,   
3086       buffer->startingBlock);
3087
3088   StorageUtl_WriteDWord(
3089     currentProperty, 
3090       OFFSET_PS_SIZE,         
3091       buffer->size.s.LowPart);
3092
3093   writeSucessful = BlockChainStream_WriteAt(This->rootBlockChain,
3094                                             offsetInPropSet,
3095                                             PROPSET_BLOCK_SIZE,
3096                                             currentProperty,
3097                                             &bytesWritten);
3098   return writeSucessful;
3099 }
3100
3101 BOOL StorageImpl_ReadBigBlock(
3102   StorageImpl* This,
3103   ULONG          blockIndex,
3104   void*          buffer)
3105 {
3106   void* bigBlockBuffer;
3107
3108   bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3109
3110   if (bigBlockBuffer!=0)
3111   {
3112     memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3113
3114     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3115
3116     return TRUE;
3117   }
3118
3119   return FALSE;
3120 }
3121
3122 BOOL StorageImpl_WriteBigBlock(
3123   StorageImpl* This,
3124   ULONG          blockIndex,
3125   void*          buffer)
3126 {
3127   void* bigBlockBuffer;
3128
3129   bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3130
3131   if (bigBlockBuffer!=0)
3132   {
3133     memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3134
3135     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3136     
3137     return TRUE;
3138   }
3139   
3140   return FALSE;
3141 }
3142
3143 void* StorageImpl_GetROBigBlock(
3144   StorageImpl* This,
3145   ULONG          blockIndex)
3146 {
3147   return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3148 }
3149
3150 void* StorageImpl_GetBigBlock(
3151   StorageImpl* This,
3152   ULONG          blockIndex)
3153 {
3154   return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3155 }
3156
3157 void StorageImpl_ReleaseBigBlock(
3158   StorageImpl* This,
3159   void*          pBigBlock)
3160 {
3161   BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3162 }
3163
3164 /******************************************************************************
3165  *              Storage32Impl_SmallBlocksToBigBlocks
3166  *
3167  * This method will convert a small block chain to a big block chain.
3168  * The small block chain will be destroyed.
3169  */
3170 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3171                       StorageImpl* This,
3172                       SmallBlockChainStream** ppsbChain)
3173 {
3174   ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3175   ULARGE_INTEGER size, offset;
3176   ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3177   ULONG propertyIndex;
3178   BOOL successRead, successWrite;
3179   StgProperty chainProperty;
3180   BYTE *buffer;
3181   BlockChainStream *bbTempChain = NULL;
3182   BlockChainStream *bigBlockChain = NULL;
3183
3184   /*
3185    * Create a temporary big block chain that doesn't have
3186    * an associated property. This temporary chain will be
3187    * used to copy data from small blocks to big blocks.
3188    */
3189   bbTempChain = BlockChainStream_Construct(This,
3190                                            &bbHeadOfChain,
3191                                            PROPERTY_NULL);
3192
3193   /*
3194    * Grow the big block chain.
3195    */
3196   size = SmallBlockChainStream_GetSize(*ppsbChain);
3197   BlockChainStream_SetSize(bbTempChain, size);
3198
3199   /*
3200    * Copy the contents of the small block chain to the big block chain
3201    * by small block size increments.
3202    */
3203   offset.s.LowPart = 0;
3204   offset.s.HighPart = 0;
3205   cbTotalRead = 0;
3206   cbTotalWritten = 0;
3207
3208   buffer = (BYTE *) malloc(DEF_SMALL_BLOCK_SIZE);
3209   do
3210   {
3211     successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3212                                                offset,
3213                                                DEF_SMALL_BLOCK_SIZE,
3214                                                buffer,
3215                                                &cbRead);
3216     cbTotalRead += cbRead;
3217
3218     successWrite = BlockChainStream_WriteAt(bbTempChain,
3219                                             offset,
3220                                             cbRead,
3221                                             buffer,
3222                                             &cbWritten);
3223     cbTotalWritten += cbWritten;
3224
3225     offset.s.LowPart += This->smallBlockSize;
3226
3227   } while (successRead && successWrite);
3228   free(buffer);
3229
3230   assert(cbTotalRead == cbTotalWritten);
3231
3232   /*
3233    * Destroy the small block chain.
3234    */
3235   propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3236   size.s.HighPart = 0;
3237   size.s.LowPart  = 0;
3238   SmallBlockChainStream_SetSize(*ppsbChain, size);
3239   SmallBlockChainStream_Destroy(*ppsbChain);
3240   *ppsbChain = 0;
3241
3242   /*
3243    * Change the property information. This chain is now a big block chain
3244    * and it doesn't reside in the small blocks chain anymore.
3245    */
3246   StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3247
3248   chainProperty.startingBlock = bbHeadOfChain;
3249
3250   StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3251
3252   /*
3253    * Destroy the temporary propertyless big block chain.
3254    * Create a new big block chain associated with this property.
3255    */
3256   BlockChainStream_Destroy(bbTempChain);
3257   bigBlockChain = BlockChainStream_Construct(This,
3258                                              NULL,
3259                                              propertyIndex);
3260
3261   return bigBlockChain;
3262 }
3263
3264 /******************************************************************************
3265 ** Storage32InternalImpl implementation
3266 */
3267
3268 StorageInternalImpl* StorageInternalImpl_Construct(
3269   StorageImpl* ancestorStorage,  
3270   ULONG          rootPropertyIndex)
3271 {
3272   StorageInternalImpl* newStorage;
3273
3274   /*
3275    * Allocate space for the new storage object
3276    */
3277   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3278
3279   if (newStorage!=0)
3280   {
3281     memset(newStorage, 0, sizeof(StorageInternalImpl));
3282
3283     /*
3284      * Initialize the virtual function table.
3285      */
3286     ICOM_VTBL(newStorage)    = &Storage32InternalImpl_Vtbl;
3287     newStorage->v_destructor = &StorageInternalImpl_Destroy;
3288
3289     /*
3290      * Keep the ancestor storage pointer and nail a reference to it.
3291      */
3292     newStorage->ancestorStorage = ancestorStorage;
3293     StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3294
3295     /*
3296      * Keep the index of the root property set for this storage,
3297      */
3298     newStorage->rootPropertySetIndex = rootPropertyIndex;
3299
3300     return newStorage;
3301   }
3302
3303   return 0;
3304 }
3305
3306 void StorageInternalImpl_Destroy(
3307   StorageInternalImpl* This)
3308 {
3309   StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3310   HeapFree(GetProcessHeap(), 0, This);
3311 }
3312
3313 /******************************************************************************
3314 **
3315 ** Storage32InternalImpl_Commit
3316 **
3317 ** The non-root storages cannot be opened in transacted mode thus this function
3318 ** does nothing.
3319 */
3320 HRESULT WINAPI StorageInternalImpl_Commit( 
3321   IStorage*            iface,
3322   DWORD                  grfCommitFlags)  /* [in] */ 
3323 {
3324   return S_OK;
3325 }
3326         
3327 /******************************************************************************
3328 **
3329 ** Storage32InternalImpl_Revert
3330 **
3331 ** The non-root storages cannot be opened in transacted mode thus this function
3332 ** does nothing.
3333 */
3334 HRESULT WINAPI StorageInternalImpl_Revert( 
3335   IStorage*            iface)
3336 {
3337   return S_OK;
3338 }
3339
3340 /******************************************************************************
3341 ** IEnumSTATSTGImpl implementation
3342 */
3343
3344 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3345   StorageImpl* parentStorage,
3346   ULONG          firstPropertyNode)
3347 {
3348   IEnumSTATSTGImpl* newEnumeration;
3349
3350   newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3351   
3352   if (newEnumeration!=0)
3353   {
3354     /*
3355      * Set-up the virtual function table and reference count.
3356      */
3357     ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3358     newEnumeration->ref       = 0;
3359     
3360     /*
3361      * We want to nail-down the reference to the storage in case the
3362      * enumeration out-lives the storage in the client application.
3363      */
3364     newEnumeration->parentStorage = parentStorage;
3365     IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3366     
3367     newEnumeration->firstPropertyNode   = firstPropertyNode;
3368     
3369     /*
3370      * Initialize the search stack
3371      */
3372     newEnumeration->stackSize    = 0;
3373     newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3374     newEnumeration->stackToVisit = 
3375       HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3376     
3377     /*
3378      * Make sure the current node of the iterator is the first one.
3379      */
3380     IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3381   }
3382   
3383   return newEnumeration;
3384 }
3385
3386 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3387 {
3388   IStorage_Release((IStorage*)This->parentStorage);
3389   HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3390   HeapFree(GetProcessHeap(), 0, This);
3391 }
3392
3393 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3394   IEnumSTATSTG*     iface,
3395   REFIID            riid,
3396   void**            ppvObject)
3397 {
3398   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3399
3400   /*
3401    * Perform a sanity check on the parameters.
3402    */
3403   if (ppvObject==0)
3404     return E_INVALIDARG;
3405
3406   /*
3407    * Initialize the return parameter.
3408    */
3409   *ppvObject = 0;
3410
3411   /*
3412    * Compare the riid with the interface IDs implemented by this object.
3413    */
3414   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0) 
3415   {
3416     *ppvObject = (IEnumSTATSTG*)This;
3417   }
3418   else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0) 
3419   {
3420     *ppvObject = (IEnumSTATSTG*)This;
3421   }
3422
3423   /*
3424    * Check that we obtained an interface.
3425    */
3426   if ((*ppvObject)==0)
3427     return E_NOINTERFACE;
3428
3429   /*
3430    * Query Interface always increases the reference count by one when it is
3431    * successful
3432    */
3433   IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3434
3435   return S_OK;
3436 }
3437         
3438 ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
3439   IEnumSTATSTG* iface)
3440 {
3441   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3442
3443   This->ref++;
3444   return This->ref;
3445 }
3446         
3447 ULONG   WINAPI IEnumSTATSTGImpl_Release(
3448   IEnumSTATSTG* iface)
3449 {
3450   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3451
3452   ULONG newRef;
3453
3454   This->ref--;
3455   newRef = This->ref;
3456
3457   /*
3458    * If the reference count goes down to 0, perform suicide.
3459    */
3460   if (newRef==0)
3461   {
3462     IEnumSTATSTGImpl_Destroy(This);
3463   }
3464
3465   return newRef;;
3466 }
3467
3468 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3469   IEnumSTATSTG* iface,
3470   ULONG             celt,
3471   STATSTG*          rgelt,
3472   ULONG*            pceltFetched)
3473 {
3474   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3475
3476   StgProperty currentProperty;
3477   STATSTG*    currentReturnStruct = rgelt;
3478   ULONG       objectFetched       = 0;
3479   ULONG      currentSearchNode;
3480
3481   /*
3482    * Perform a sanity check on the parameters.
3483    */
3484   if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3485     return E_INVALIDARG;  
3486   
3487   /*
3488    * To avoid the special case, get another pointer to a ULONG value if
3489    * the caller didn't supply one.
3490    */
3491   if (pceltFetched==0)
3492     pceltFetched = &objectFetched;
3493   
3494   /*
3495    * Start the iteration, we will iterate until we hit the end of the
3496    * linked list or until we hit the number of items to iterate through
3497    */
3498   *pceltFetched = 0;
3499
3500   /*
3501    * Start with the node at the top of the stack.
3502    */
3503   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3504
3505   while ( ( *pceltFetched < celt) && 
3506           ( currentSearchNode!=PROPERTY_NULL) )
3507   {
3508     /* 
3509      * Remove the top node from the stack
3510      */
3511     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3512
3513     /*
3514      * Read the property from the storage.
3515      */
3516     StorageImpl_ReadProperty(This->parentStorage,
3517       currentSearchNode, 
3518       &currentProperty);
3519
3520     /*
3521      * Copy the information to the return buffer.
3522      */
3523     StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3524       &currentProperty,
3525       STATFLAG_DEFAULT);
3526         
3527     /*
3528      * Step to the next item in the iteration
3529      */
3530     (*pceltFetched)++;
3531     currentReturnStruct++;
3532
3533     /*
3534      * Push the next search node in the search stack.
3535      */
3536     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3537
3538     /*
3539      * continue the iteration.
3540      */
3541     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3542   }
3543
3544   if (*pceltFetched == celt)
3545     return S_OK;
3546
3547   return S_FALSE;
3548 }
3549
3550         
3551 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3552   IEnumSTATSTG* iface,
3553   ULONG             celt)
3554 {
3555   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3556
3557   StgProperty currentProperty;
3558   ULONG       objectFetched       = 0;
3559   ULONG       currentSearchNode;
3560
3561   /*
3562    * Start with the node at the top of the stack.
3563    */
3564   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3565
3566   while ( (objectFetched < celt) && 
3567           (currentSearchNode!=PROPERTY_NULL) )
3568   {
3569     /* 
3570      * Remove the top node from the stack
3571      */
3572     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3573
3574     /*
3575      * Read the property from the storage.
3576      */
3577     StorageImpl_ReadProperty(This->parentStorage,
3578       currentSearchNode, 
3579       &currentProperty);
3580     
3581     /*
3582      * Step to the next item in the iteration
3583      */
3584     objectFetched++;
3585
3586     /*
3587      * Push the next search node in the search stack.
3588      */
3589     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3590
3591     /*
3592      * continue the iteration.
3593      */
3594     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3595   }
3596
3597   if (objectFetched == celt)
3598     return S_OK;
3599
3600   return S_FALSE;
3601 }
3602         
3603 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3604   IEnumSTATSTG* iface)
3605 {
3606   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3607
3608   StgProperty rootProperty;
3609   BOOL      readSucessful;
3610
3611   /*
3612    * Re-initialize the search stack to an empty stack
3613    */
3614   This->stackSize = 0;
3615
3616   /*
3617    * Read the root property from the storage.
3618    */
3619   readSucessful = StorageImpl_ReadProperty(
3620                     This->parentStorage,
3621                     This->firstPropertyNode, 
3622                     &rootProperty);
3623
3624   if (readSucessful)
3625   {
3626     assert(rootProperty.sizeOfNameString!=0);
3627
3628     /*
3629      * Push the search node in the search stack.
3630      */
3631     IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3632   }
3633
3634   return S_OK;
3635 }
3636         
3637 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3638   IEnumSTATSTG* iface,
3639   IEnumSTATSTG**    ppenum)
3640 {
3641   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3642
3643   IEnumSTATSTGImpl* newClone;
3644
3645   /*
3646    * Perform a sanity check on the parameters.
3647    */
3648   if (ppenum==0)
3649     return E_INVALIDARG;
3650   
3651   newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3652                This->firstPropertyNode);
3653
3654   
3655   /*
3656    * The new clone enumeration must point to the same current node as
3657    * the ole one.
3658    */
3659   newClone->stackSize    = This->stackSize    ;
3660   newClone->stackMaxSize = This->stackMaxSize ;
3661   newClone->stackToVisit = 
3662     HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3663
3664   memcpy(
3665     newClone->stackToVisit, 
3666     This->stackToVisit, 
3667     sizeof(ULONG) * newClone->stackSize);
3668
3669   *ppenum = (IEnumSTATSTG*)newClone;
3670
3671   /*
3672    * Don't forget to nail down a reference to the clone before
3673    * returning it.
3674    */
3675   IEnumSTATSTGImpl_AddRef(*ppenum);
3676
3677   return S_OK;
3678 }
3679
3680 INT IEnumSTATSTGImpl_FindParentProperty(
3681   IEnumSTATSTGImpl *This,
3682   ULONG             childProperty, 
3683   StgProperty      *currentProperty,
3684   ULONG            *thisNodeId)
3685 {
3686   ULONG currentSearchNode;
3687   ULONG foundNode;
3688
3689   /*
3690    * To avoid the special case, get another pointer to a ULONG value if
3691    * the caller didn't supply one.
3692    */
3693   if (thisNodeId==0)
3694     thisNodeId = &foundNode;
3695
3696   /*
3697    * Start with the node at the top of the stack.
3698    */
3699   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3700   
3701
3702   while (currentSearchNode!=PROPERTY_NULL)
3703   {
3704     /*
3705      * Store the current node in the returned parameters
3706      */
3707     *thisNodeId = currentSearchNode;
3708
3709     /* 
3710      * Remove the top node from the stack
3711      */
3712     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3713
3714     /*
3715      * Read the property from the storage.
3716      */
3717     StorageImpl_ReadProperty(
3718       This->parentStorage,
3719       currentSearchNode, 
3720       currentProperty);
3721       
3722     if (currentProperty->previousProperty == childProperty)
3723       return PROPERTY_RELATION_PREVIOUS;
3724
3725     else if (currentProperty->nextProperty == childProperty)  
3726       return PROPERTY_RELATION_NEXT;
3727   
3728     else if (currentProperty->dirProperty == childProperty)
3729       return PROPERTY_RELATION_DIR;
3730        
3731     /*
3732      * Push the next search node in the search stack.
3733      */
3734     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3735
3736     /*
3737      * continue the iteration.
3738      */
3739     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3740   }
3741
3742   return PROPERTY_NULL;
3743 }
3744
3745 ULONG IEnumSTATSTGImpl_FindProperty(
3746   IEnumSTATSTGImpl* This,
3747   const OLECHAR*  lpszPropName,
3748   StgProperty*      currentProperty)
3749 {
3750   ULONG currentSearchNode;
3751
3752   /*
3753    * Start with the node at the top of the stack.
3754    */
3755   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3756
3757   while (currentSearchNode!=PROPERTY_NULL)
3758   {
3759     /* 
3760      * Remove the top node from the stack
3761      */
3762     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3763
3764     /*
3765      * Read the property from the storage.
3766      */
3767     StorageImpl_ReadProperty(This->parentStorage,
3768       currentSearchNode, 
3769       currentProperty);
3770
3771     if ( propertyNameCmp(
3772           (OLECHAR*)currentProperty->name, 
3773           (OLECHAR*)lpszPropName) == 0)
3774       return currentSearchNode;
3775
3776     /*
3777      * Push the next search node in the search stack.
3778      */
3779     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3780
3781     /*
3782      * continue the iteration.
3783      */
3784     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3785   }
3786
3787   return PROPERTY_NULL;
3788 }
3789
3790 void IEnumSTATSTGImpl_PushSearchNode(
3791   IEnumSTATSTGImpl* This,
3792   ULONG             nodeToPush)
3793 {
3794   StgProperty rootProperty;
3795   BOOL      readSucessful;
3796
3797   /*
3798    * First, make sure we're not trying to push an unexisting node.
3799    */
3800   if (nodeToPush==PROPERTY_NULL)
3801     return;
3802
3803   /*
3804    * First push the node to the stack
3805    */
3806   if (This->stackSize == This->stackMaxSize)
3807   {
3808     This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3809
3810     This->stackToVisit = HeapReAlloc(
3811                            GetProcessHeap(), 
3812                            0,
3813                            This->stackToVisit,
3814                            sizeof(ULONG) * This->stackMaxSize);
3815   }
3816
3817   This->stackToVisit[This->stackSize] = nodeToPush;
3818   This->stackSize++;
3819
3820   /*
3821    * Read the root property from the storage.
3822    */
3823   readSucessful = StorageImpl_ReadProperty(
3824                     This->parentStorage,
3825                     nodeToPush, 
3826                     &rootProperty);
3827
3828   if (readSucessful)
3829   {
3830     assert(rootProperty.sizeOfNameString!=0);
3831
3832     /*
3833      * Push the previous search node in the search stack.
3834      */
3835     IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3836   }
3837 }
3838
3839 ULONG IEnumSTATSTGImpl_PopSearchNode(
3840   IEnumSTATSTGImpl* This,
3841   BOOL            remove)
3842 {
3843   ULONG topNode;
3844
3845   if (This->stackSize == 0)
3846     return PROPERTY_NULL;
3847
3848   topNode = This->stackToVisit[This->stackSize-1];
3849
3850   if (remove)
3851     This->stackSize--;
3852
3853   return topNode;
3854 }
3855
3856 /******************************************************************************
3857 ** StorageUtl implementation
3858 */
3859
3860 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3861 {
3862   memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3863 }
3864
3865 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3866 {
3867   memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3868 }
3869
3870 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3871 {
3872   memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3873 }
3874
3875 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3876 {
3877   memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3878 }
3879
3880 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3881 {
3882   StorageUtl_ReadDWord(buffer, offset,   &(value->Data1));
3883   StorageUtl_ReadWord(buffer,  offset+4, &(value->Data2));
3884   StorageUtl_ReadWord(buffer,  offset+6, &(value->Data3));
3885
3886   memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3887 }
3888
3889 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3890 {
3891   StorageUtl_WriteDWord(buffer, offset,   value->Data1);
3892   StorageUtl_WriteWord(buffer,  offset+4, value->Data2);
3893   StorageUtl_WriteWord(buffer,  offset+6, value->Data3);
3894
3895   memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3896 }
3897
3898 void StorageUtl_CopyPropertyToSTATSTG(
3899   STATSTG*     destination,
3900   StgProperty* source,
3901   int          statFlags)
3902 {
3903   /*
3904    * The copy of the string occurs only when the flag is not set
3905    */
3906   if ((statFlags & STATFLAG_NONAME) != 0)
3907   {
3908     destination->pwcsName = 0;
3909   }
3910   else
3911   {
3912     destination->pwcsName = 
3913       CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3914
3915     lstrcpyW((LPWSTR)destination->pwcsName, source->name);
3916   }
3917   
3918   switch (source->propertyType)
3919   {
3920     case PROPTYPE_STORAGE:
3921     case PROPTYPE_ROOT:
3922       destination->type = STGTY_STORAGE;
3923       break;
3924     case PROPTYPE_STREAM:
3925       destination->type = STGTY_STREAM;
3926       break;
3927     default:
3928       destination->type = STGTY_STREAM;
3929       break;        
3930   }
3931
3932   destination->cbSize            = source->size;
3933 /*    
3934   currentReturnStruct->mtime     = {0}; TODO
3935   currentReturnStruct->ctime     = {0};
3936   currentReturnStruct->atime     = {0}; 
3937 */
3938   destination->grfMode           = 0;
3939   destination->grfLocksSupported = 0; 
3940   destination->clsid             = source->propertyUniqueID;
3941   destination->grfStateBits      = 0; 
3942   destination->reserved          = 0; 
3943 }
3944
3945 /******************************************************************************
3946 ** BlockChainStream implementation
3947 */
3948
3949 BlockChainStream* BlockChainStream_Construct(
3950   StorageImpl* parentStorage,  
3951   ULONG*         headOfStreamPlaceHolder,
3952   ULONG          propertyIndex)
3953 {
3954   BlockChainStream* newStream;
3955   ULONG blockIndex;
3956
3957   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
3958
3959   newStream->parentStorage           = parentStorage;
3960   newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
3961   newStream->ownerPropertyIndex      = propertyIndex;
3962   newStream->lastBlockNoInSequence   = 0xFFFFFFFF;
3963   newStream->tailIndex               = BLOCK_END_OF_CHAIN;
3964   newStream->numBlocks               = 0;
3965
3966   blockIndex = BlockChainStream_GetHeadOfChain(newStream);
3967
3968   while (blockIndex != BLOCK_END_OF_CHAIN)
3969   {
3970     newStream->numBlocks++;
3971     newStream->tailIndex = blockIndex;
3972
3973     blockIndex = StorageImpl_GetNextBlockInChain(
3974                    parentStorage,
3975                    blockIndex);
3976   }
3977
3978   return newStream;
3979 }
3980
3981 void BlockChainStream_Destroy(BlockChainStream* This)
3982 {
3983   HeapFree(GetProcessHeap(), 0, This);
3984 }
3985
3986 /******************************************************************************
3987  *      BlockChainStream_GetHeadOfChain
3988  *
3989  * Returns the head of this stream chain.
3990  * Some special chains don't have properties, their heads are kept in
3991  * This->headOfStreamPlaceHolder.
3992  *
3993  */
3994 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
3995 {
3996   StgProperty chainProperty;
3997   BOOL      readSucessful;
3998
3999   if (This->headOfStreamPlaceHolder != 0)
4000     return *(This->headOfStreamPlaceHolder);
4001
4002   if (This->ownerPropertyIndex != PROPERTY_NULL)
4003   {
4004     readSucessful = StorageImpl_ReadProperty(
4005                       This->parentStorage,
4006                       This->ownerPropertyIndex,
4007                       &chainProperty);
4008
4009     if (readSucessful)
4010     {
4011       return chainProperty.startingBlock;
4012     }
4013   }
4014
4015   return BLOCK_END_OF_CHAIN;
4016 }
4017
4018 /******************************************************************************
4019  *       BlockChainStream_GetCount
4020  *
4021  * Returns the number of blocks that comprises this chain.
4022  * This is not the size of the stream as the last block may not be full!
4023  * 
4024  */
4025 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4026 {
4027   ULONG blockIndex;
4028   ULONG count = 0;
4029
4030   blockIndex = BlockChainStream_GetHeadOfChain(This);
4031
4032   while (blockIndex != BLOCK_END_OF_CHAIN)
4033   {
4034     count++;
4035
4036     blockIndex = StorageImpl_GetNextBlockInChain(
4037                    This->parentStorage, 
4038                    blockIndex);
4039   }
4040
4041   return count;
4042 }
4043
4044 /******************************************************************************
4045  *      BlockChainStream_ReadAt 
4046  *
4047  * Reads a specified number of bytes from this chain at the specified offset.
4048  * bytesRead may be NULL.
4049  * Failure will be returned if the specified number of bytes has not been read.
4050  */
4051 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4052   ULARGE_INTEGER offset,
4053   ULONG          size,
4054   void*          buffer,
4055   ULONG*         bytesRead)
4056 {
4057   ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4058   ULONG offsetInBlock     = offset.s.LowPart % This->parentStorage->bigBlockSize;
4059   ULONG bytesToReadInBuffer;
4060   ULONG blockIndex;
4061   BYTE* bufferWalker;
4062   BYTE* bigBlockBuffer;
4063
4064   /*
4065    * Find the first block in the stream that contains part of the buffer.
4066    */
4067   if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4068        (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4069        (blockNoInSequence < This->lastBlockNoInSequence) )
4070   {
4071     blockIndex = BlockChainStream_GetHeadOfChain(This);
4072     This->lastBlockNoInSequence = blockNoInSequence;
4073   }
4074   else
4075   {
4076     ULONG temp = blockNoInSequence;
4077
4078     blockIndex = This->lastBlockNoInSequenceIndex;
4079     blockNoInSequence -= This->lastBlockNoInSequence;
4080     This->lastBlockNoInSequence = temp;
4081   }
4082
4083   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4084   {
4085     blockIndex = 
4086       StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4087     
4088     blockNoInSequence--;
4089   }
4090
4091   This->lastBlockNoInSequenceIndex = blockIndex;
4092
4093   /*
4094    * Start reading the buffer.
4095    */
4096   *bytesRead   = 0;
4097   bufferWalker = buffer;
4098   
4099   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4100   {
4101     /*
4102      * Calculate how many bytes we can copy from this big block.
4103      */
4104     bytesToReadInBuffer = 
4105       MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4106     
4107     /*
4108      * Copy those bytes to the buffer
4109      */
4110     bigBlockBuffer = 
4111       StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4112     
4113     memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4114     
4115     StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4116     
4117     /*
4118      * Step to the next big block.
4119      */
4120     blockIndex    = 
4121       StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4122
4123     bufferWalker += bytesToReadInBuffer;
4124     size         -= bytesToReadInBuffer;
4125     *bytesRead   += bytesToReadInBuffer;
4126     offsetInBlock = 0;  /* There is no offset on the next block */
4127
4128   }
4129   
4130   return (size == 0);
4131 }
4132
4133 /******************************************************************************
4134  *      BlockChainStream_WriteAt
4135  *
4136  * Writes the specified number of bytes to this chain at the specified offset.
4137  * bytesWritten may be NULL.
4138  * Will fail if not all specified number of bytes have been written.
4139  */
4140 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4141   ULARGE_INTEGER    offset,
4142   ULONG             size,
4143   const void*       buffer,
4144   ULONG*            bytesWritten)
4145 {
4146   ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4147   ULONG offsetInBlock     = offset.s.LowPart % This->parentStorage->bigBlockSize;
4148   ULONG bytesToWrite;
4149   ULONG blockIndex;
4150   BYTE* bufferWalker;
4151   BYTE* bigBlockBuffer;
4152
4153   /*
4154    * Find the first block in the stream that contains part of the buffer.
4155    */
4156   if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4157        (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4158        (blockNoInSequence < This->lastBlockNoInSequence) )
4159   {
4160     blockIndex = BlockChainStream_GetHeadOfChain(This);
4161     This->lastBlockNoInSequence = blockNoInSequence;
4162   }
4163   else
4164   {
4165     ULONG temp = blockNoInSequence;
4166
4167     blockIndex = This->lastBlockNoInSequenceIndex;
4168     blockNoInSequence -= This->lastBlockNoInSequence;
4169     This->lastBlockNoInSequence = temp;
4170   }
4171
4172   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4173   {
4174     blockIndex = 
4175       StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4176     
4177     blockNoInSequence--;
4178   }
4179
4180   This->lastBlockNoInSequenceIndex = blockIndex;
4181
4182   /*
4183    * Here, I'm casting away the constness on the buffer variable
4184    * This is OK since we don't intend to modify that buffer.
4185    */
4186   *bytesWritten   = 0;
4187   bufferWalker = (BYTE*)buffer;
4188
4189   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4190   {
4191     /*
4192      * Calculate how many bytes we can copy from this big block.
4193      */
4194     bytesToWrite = 
4195       MIN(This->parentStorage->bigBlockSize - offsetInBlock, size);
4196     
4197     /*
4198      * Copy those bytes to the buffer
4199      */
4200     bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4201     
4202     memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4203     
4204     StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4205     
4206     /*
4207      * Step to the next big block.
4208      */
4209     blockIndex    = 
4210       StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4211
4212     bufferWalker  += bytesToWrite;
4213     size          -= bytesToWrite;
4214     *bytesWritten += bytesToWrite;
4215     offsetInBlock  = 0;      /* There is no offset on the next block */
4216   }
4217   
4218   return (size == 0);
4219 }
4220
4221 /******************************************************************************
4222  *      BlockChainStream_Shrink
4223  *
4224  * Shrinks this chain in the big block depot.
4225  */
4226 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4227                                ULARGE_INTEGER    newSize)
4228 {
4229   ULONG blockIndex, extraBlock;
4230   ULONG numBlocks;
4231   ULONG count = 1;
4232
4233   /*
4234    * Reset the last accessed block cache.
4235    */
4236   This->lastBlockNoInSequence = 0xFFFFFFFF;
4237   This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4238
4239   /*
4240    * Figure out how many blocks are needed to contain the new size
4241    */
4242   numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4243
4244   if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4245     numBlocks++;
4246
4247   blockIndex = BlockChainStream_GetHeadOfChain(This);
4248
4249   /*
4250    * Go to the new end of chain
4251    */
4252   while (count < numBlocks)
4253   {
4254     blockIndex = 
4255       StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4256
4257     count++;
4258   }
4259
4260   /* Get the next block before marking the new end */
4261   extraBlock = 
4262     StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4263
4264   /* Mark the new end of chain */
4265   StorageImpl_SetNextBlockInChain(
4266     This->parentStorage, 
4267     blockIndex, 
4268     BLOCK_END_OF_CHAIN);
4269
4270   This->tailIndex = blockIndex;
4271   This->numBlocks = numBlocks;
4272
4273   /*
4274    * Mark the extra blocks as free
4275    */
4276   while (extraBlock != BLOCK_END_OF_CHAIN)
4277   {
4278     blockIndex = 
4279       StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4280
4281     StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4282     extraBlock = blockIndex;
4283   }
4284
4285   return TRUE;
4286 }
4287
4288 /******************************************************************************
4289  *      BlockChainStream_Enlarge
4290  *
4291  * Grows this chain in the big block depot.
4292  */
4293 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4294                                 ULARGE_INTEGER    newSize)
4295 {
4296   ULONG blockIndex, currentBlock;
4297   ULONG newNumBlocks;
4298   ULONG oldNumBlocks = 0;
4299
4300   blockIndex = BlockChainStream_GetHeadOfChain(This);
4301
4302   /*
4303    * Empty chain. Create the head.
4304    */
4305   if (blockIndex == BLOCK_END_OF_CHAIN)
4306   {
4307     blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4308     StorageImpl_SetNextBlockInChain(This->parentStorage,
4309                                       blockIndex,
4310                                       BLOCK_END_OF_CHAIN);
4311
4312     if (This->headOfStreamPlaceHolder != 0)
4313     {
4314       *(This->headOfStreamPlaceHolder) = blockIndex;
4315     }
4316     else
4317     {
4318       StgProperty chainProp;
4319       assert(This->ownerPropertyIndex != PROPERTY_NULL);
4320
4321       StorageImpl_ReadProperty(
4322         This->parentStorage, 
4323         This->ownerPropertyIndex,
4324         &chainProp);
4325
4326       chainProp.startingBlock = blockIndex; 
4327
4328       StorageImpl_WriteProperty(
4329         This->parentStorage, 
4330         This->ownerPropertyIndex,
4331         &chainProp);
4332     }
4333
4334     This->tailIndex = blockIndex;
4335     This->numBlocks = 1;
4336   }
4337
4338   /*
4339    * Figure out how many blocks are needed to contain this stream
4340    */
4341   newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4342
4343   if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4344     newNumBlocks++;
4345
4346   /*
4347    * Go to the current end of chain
4348    */
4349   if (This->tailIndex == BLOCK_END_OF_CHAIN)
4350   {
4351     currentBlock = blockIndex;
4352
4353     while (blockIndex != BLOCK_END_OF_CHAIN)
4354     {
4355       This->numBlocks++;
4356       currentBlock = blockIndex;
4357
4358       blockIndex =
4359         StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4360     }
4361
4362     This->tailIndex = currentBlock;
4363   }
4364
4365   currentBlock = This->tailIndex;
4366   oldNumBlocks = This->numBlocks;
4367
4368   /*
4369    * Add new blocks to the chain
4370    */
4371   if (oldNumBlocks < newNumBlocks)
4372   {
4373     while (oldNumBlocks < newNumBlocks)
4374     {
4375       blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4376
4377       StorageImpl_SetNextBlockInChain(
4378         This->parentStorage, 
4379         currentBlock, 
4380         blockIndex);
4381
4382       StorageImpl_SetNextBlockInChain(
4383         This->parentStorage, 
4384         blockIndex, 
4385         BLOCK_END_OF_CHAIN);
4386
4387       currentBlock = blockIndex;
4388       oldNumBlocks++;
4389     }
4390
4391     This->tailIndex = blockIndex;
4392     This->numBlocks = newNumBlocks;
4393   }
4394
4395   return TRUE;
4396 }
4397
4398 /******************************************************************************
4399  *      BlockChainStream_SetSize
4400  *
4401  * Sets the size of this stream. The big block depot will be updated.
4402  * The file will grow if we grow the chain.
4403  *
4404  * TODO: Free the actual blocks in the file when we shrink the chain.
4405  *       Currently, the blocks are still in the file. So the file size
4406  *       doesn't shrink even if we shrink streams. 
4407  */
4408 BOOL BlockChainStream_SetSize(
4409   BlockChainStream* This,
4410   ULARGE_INTEGER    newSize)
4411 {
4412   ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4413
4414   if (newSize.s.LowPart == size.s.LowPart)
4415     return TRUE;
4416
4417   if (newSize.s.LowPart < size.s.LowPart)
4418   {
4419     BlockChainStream_Shrink(This, newSize);
4420   }
4421   else
4422   {
4423     ULARGE_INTEGER fileSize = 
4424       BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4425
4426     ULONG diff = newSize.s.LowPart - size.s.LowPart;
4427
4428     /*
4429      * Make sure the file stays a multiple of blocksize
4430      */
4431     if ((diff % This->parentStorage->bigBlockSize) != 0)
4432       diff += (This->parentStorage->bigBlockSize - 
4433                 (diff % This->parentStorage->bigBlockSize) );
4434
4435     fileSize.s.LowPart += diff;
4436     BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4437
4438     BlockChainStream_Enlarge(This, newSize);
4439   }
4440
4441   return TRUE;
4442 }
4443
4444 /******************************************************************************
4445  *      BlockChainStream_GetSize
4446  *
4447  * Returns the size of this chain.
4448  * Will return the block count if this chain doesn't have a property.
4449  */
4450 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4451 {
4452   StgProperty chainProperty;
4453
4454   if(This->headOfStreamPlaceHolder == NULL)
4455   {
4456     /* 
4457      * This chain is a data stream read the property and return 
4458      * the appropriate size
4459      */
4460     StorageImpl_ReadProperty(
4461       This->parentStorage,
4462       This->ownerPropertyIndex,
4463       &chainProperty);
4464
4465     return chainProperty.size;
4466   }
4467   else
4468   {
4469     /*
4470      * this chain is a chain that does not have a property, figure out the 
4471      * size by making the product number of used blocks times the 
4472      * size of them
4473      */
4474     ULARGE_INTEGER result;
4475     result.s.HighPart = 0;
4476
4477     result.s.LowPart  = 
4478       BlockChainStream_GetCount(This) * 
4479       This->parentStorage->bigBlockSize;
4480
4481     return result;
4482   }
4483 }
4484
4485 /******************************************************************************
4486 ** SmallBlockChainStream implementation
4487 */
4488
4489 SmallBlockChainStream* SmallBlockChainStream_Construct(
4490   StorageImpl* parentStorage,  
4491   ULONG          propertyIndex)
4492 {
4493   SmallBlockChainStream* newStream;
4494
4495   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4496
4497   newStream->parentStorage      = parentStorage;
4498   newStream->ownerPropertyIndex = propertyIndex;
4499
4500   return newStream;
4501 }
4502
4503 void SmallBlockChainStream_Destroy(
4504   SmallBlockChainStream* This)
4505 {
4506   HeapFree(GetProcessHeap(), 0, This);
4507 }
4508
4509 /******************************************************************************
4510  *      SmallBlockChainStream_GetHeadOfChain
4511  *
4512  * Returns the head of this chain of small blocks.
4513  */
4514 ULONG SmallBlockChainStream_GetHeadOfChain(
4515   SmallBlockChainStream* This)
4516 {
4517   StgProperty chainProperty;
4518   BOOL      readSucessful;
4519
4520   if (This->ownerPropertyIndex)
4521   {
4522     readSucessful = StorageImpl_ReadProperty(
4523                       This->parentStorage,
4524                       This->ownerPropertyIndex,
4525                       &chainProperty);
4526
4527     if (readSucessful)
4528     {
4529       return chainProperty.startingBlock;
4530     }
4531
4532   }
4533
4534   return BLOCK_END_OF_CHAIN;
4535 }
4536
4537 /******************************************************************************
4538  *      SmallBlockChainStream_GetNextBlockInChain
4539  *
4540  * Returns the index of the next small block in this chain.
4541  * 
4542  * Return Values:
4543  *    - BLOCK_END_OF_CHAIN: end of this chain
4544  *    - BLOCK_UNUSED: small block 'blockIndex' is free
4545  */
4546 ULONG SmallBlockChainStream_GetNextBlockInChain(
4547   SmallBlockChainStream* This,
4548   ULONG                  blockIndex)
4549 {
4550   ULARGE_INTEGER offsetOfBlockInDepot;
4551   DWORD  buffer;
4552   ULONG  nextBlockInChain = BLOCK_END_OF_CHAIN;
4553   ULONG  bytesRead;
4554   BOOL success;
4555
4556   offsetOfBlockInDepot.s.HighPart = 0;
4557   offsetOfBlockInDepot.s.LowPart  = blockIndex * sizeof(ULONG);
4558
4559   /*
4560    * Read those bytes in the buffer from the small block file.
4561    */
4562   success = BlockChainStream_ReadAt(
4563               This->parentStorage->smallBlockDepotChain,
4564               offsetOfBlockInDepot,
4565               sizeof(DWORD),
4566               &buffer,
4567               &bytesRead);
4568
4569   if (success)
4570   {
4571     StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4572   }
4573
4574   return nextBlockInChain;
4575 }
4576
4577 /******************************************************************************
4578  *       SmallBlockChainStream_SetNextBlockInChain
4579  *
4580  * Writes the index of the next block of the specified block in the small
4581  * block depot.
4582  * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4583  * To flag a block as free use BLOCK_UNUSED as nextBlock.
4584  */
4585 void SmallBlockChainStream_SetNextBlockInChain(
4586   SmallBlockChainStream* This,
4587   ULONG                  blockIndex,
4588   ULONG                  nextBlock)
4589 {
4590   ULARGE_INTEGER offsetOfBlockInDepot;
4591   DWORD  buffer;
4592   ULONG  bytesWritten;
4593
4594   offsetOfBlockInDepot.s.HighPart = 0;
4595   offsetOfBlockInDepot.s.LowPart  = blockIndex * sizeof(ULONG);
4596
4597   StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4598
4599   /*
4600    * Read those bytes in the buffer from the small block file.
4601    */
4602   BlockChainStream_WriteAt(
4603     This->parentStorage->smallBlockDepotChain,
4604     offsetOfBlockInDepot,
4605     sizeof(DWORD),
4606     &buffer,
4607     &bytesWritten);
4608 }
4609
4610 /******************************************************************************
4611  *      SmallBlockChainStream_FreeBlock
4612  *
4613  * Flag small block 'blockIndex' as free in the small block depot.
4614  */
4615 void SmallBlockChainStream_FreeBlock(
4616   SmallBlockChainStream* This,
4617   ULONG                  blockIndex)
4618 {
4619   SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4620 }
4621
4622 /******************************************************************************
4623  *      SmallBlockChainStream_GetNextFreeBlock
4624  *
4625  * Returns the index of a free small block. The small block depot will be
4626  * enlarged if necessary. The small block chain will also be enlarged if
4627  * necessary.
4628  */
4629 ULONG SmallBlockChainStream_GetNextFreeBlock(
4630   SmallBlockChainStream* This)
4631 {
4632   ULARGE_INTEGER offsetOfBlockInDepot;
4633   DWORD buffer;
4634   ULONG bytesRead;
4635   ULONG blockIndex = 0;
4636   ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4637   BOOL success = TRUE;
4638   ULONG smallBlocksPerBigBlock;
4639
4640   offsetOfBlockInDepot.s.HighPart = 0;
4641
4642   /*
4643    * Scan the small block depot for a free block
4644    */
4645   while (nextBlockIndex != BLOCK_UNUSED)
4646   {
4647     offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4648
4649     success = BlockChainStream_ReadAt(
4650                 This->parentStorage->smallBlockDepotChain,
4651                 offsetOfBlockInDepot,
4652                 sizeof(DWORD),
4653                 &buffer,
4654                 &bytesRead);
4655
4656     /*
4657      * If we run out of space for the small block depot, enlarge it
4658      */
4659     if (success)
4660     {
4661       StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4662
4663       if (nextBlockIndex != BLOCK_UNUSED)
4664         blockIndex++;
4665     }
4666     else
4667     {
4668       ULONG count = 
4669         BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4670
4671       ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4672       ULONG nextBlock, newsbdIndex;
4673       BYTE* smallBlockDepot;
4674
4675       nextBlock = sbdIndex;
4676       while (nextBlock != BLOCK_END_OF_CHAIN)
4677       {
4678         sbdIndex = nextBlock;
4679         nextBlock = 
4680           StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4681       }
4682
4683       newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4684       if (sbdIndex != BLOCK_END_OF_CHAIN)
4685         StorageImpl_SetNextBlockInChain(
4686           This->parentStorage, 
4687           sbdIndex, 
4688           newsbdIndex);
4689
4690       StorageImpl_SetNextBlockInChain(
4691         This->parentStorage, 
4692         newsbdIndex, 
4693         BLOCK_END_OF_CHAIN);
4694
4695       /*
4696        * Initialize all the small blocks to free
4697        */
4698       smallBlockDepot = 
4699         StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4700
4701       memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4702       StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4703
4704       if (count == 0)
4705       {
4706         /*
4707          * We have just created the small block depot.
4708          */
4709         StgProperty rootProp;
4710         ULONG sbStartIndex; 
4711
4712         /*
4713          * Save it in the header
4714          */
4715         This->parentStorage->smallBlockDepotStart = newsbdIndex;
4716         StorageImpl_SaveFileHeader(This->parentStorage);
4717
4718         /*
4719          * And allocate the first big block that will contain small blocks 
4720          */
4721         sbStartIndex = 
4722           StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4723
4724         StorageImpl_SetNextBlockInChain(
4725           This->parentStorage, 
4726           sbStartIndex, 
4727           BLOCK_END_OF_CHAIN);
4728
4729         StorageImpl_ReadProperty(
4730           This->parentStorage, 
4731           This->parentStorage->rootPropertySetIndex, 
4732           &rootProp);
4733
4734         rootProp.startingBlock = sbStartIndex;
4735         rootProp.size.s.HighPart = 0;
4736         rootProp.size.s.LowPart  = This->parentStorage->bigBlockSize;
4737
4738         StorageImpl_WriteProperty(
4739           This->parentStorage, 
4740           This->parentStorage->rootPropertySetIndex, 
4741           &rootProp);
4742       }
4743     }
4744   }
4745
4746   smallBlocksPerBigBlock = 
4747     This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4748
4749   /*
4750    * Verify if we have to allocate big blocks to contain small blocks
4751    */
4752   if (blockIndex % smallBlocksPerBigBlock == 0)
4753   {
4754     StgProperty rootProp;
4755     ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4756
4757     StorageImpl_ReadProperty(
4758       This->parentStorage, 
4759       This->parentStorage->rootPropertySetIndex, 
4760       &rootProp);
4761
4762     if (rootProp.size.s.LowPart < 
4763        (blocksRequired * This->parentStorage->bigBlockSize))
4764     {
4765       rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4766
4767       BlockChainStream_SetSize(
4768         This->parentStorage->smallBlockRootChain, 
4769         rootProp.size);
4770
4771       StorageImpl_WriteProperty(
4772         This->parentStorage, 
4773         This->parentStorage->rootPropertySetIndex, 
4774         &rootProp);
4775     }
4776   }
4777
4778   return blockIndex;
4779 }
4780
4781 /******************************************************************************
4782  *      SmallBlockChainStream_ReadAt
4783  *
4784  * Reads a specified number of bytes from this chain at the specified offset.
4785  * bytesRead may be NULL.
4786  * Failure will be returned if the specified number of bytes has not been read. 
4787  */
4788 BOOL SmallBlockChainStream_ReadAt(
4789   SmallBlockChainStream* This,
4790   ULARGE_INTEGER         offset,
4791   ULONG                  size,
4792   void*                  buffer,
4793   ULONG*                 bytesRead)
4794 {
4795   ULARGE_INTEGER offsetInBigBlockFile;
4796   ULONG blockNoInSequence = 
4797     offset.s.LowPart / This->parentStorage->smallBlockSize;
4798
4799   ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4800   ULONG bytesToReadInBuffer;
4801   ULONG blockIndex;
4802   ULONG bytesReadFromBigBlockFile;
4803   BYTE* bufferWalker;
4804
4805   /*
4806    * This should never happen on a small block file.
4807    */
4808   assert(offset.s.HighPart==0);
4809
4810   /*
4811    * Find the first block in the stream that contains part of the buffer.
4812    */
4813   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4814
4815   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4816   {
4817     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4818
4819     blockNoInSequence--;
4820   }
4821
4822   /*
4823    * Start reading the buffer.
4824    */
4825   *bytesRead   = 0;
4826   bufferWalker = buffer;
4827
4828   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4829   {
4830     /*
4831      * Calculate how many bytes we can copy from this small block.
4832      */
4833     bytesToReadInBuffer = 
4834       MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4835
4836     /*
4837      * Calculate the offset of the small block in the small block file.
4838      */
4839     offsetInBigBlockFile.s.HighPart  = 0;
4840     offsetInBigBlockFile.s.LowPart   = 
4841       blockIndex * This->parentStorage->smallBlockSize;
4842
4843     offsetInBigBlockFile.s.LowPart  += offsetInBlock;
4844
4845     /*
4846      * Read those bytes in the buffer from the small block file.
4847      */
4848     BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4849       offsetInBigBlockFile,
4850       bytesToReadInBuffer,
4851       bufferWalker,
4852       &bytesReadFromBigBlockFile);
4853
4854     assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4855
4856     /*
4857      * Step to the next big block.
4858      */
4859     blockIndex    = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4860     bufferWalker += bytesToReadInBuffer;
4861     size         -= bytesToReadInBuffer;
4862     *bytesRead   += bytesToReadInBuffer;
4863     offsetInBlock = 0;  /* There is no offset on the next block */
4864   }
4865
4866   return (size == 0);
4867 }
4868
4869 /******************************************************************************
4870  *       SmallBlockChainStream_WriteAt
4871  *
4872  * Writes the specified number of bytes to this chain at the specified offset.
4873  * bytesWritten may be NULL.
4874  * Will fail if not all specified number of bytes have been written.
4875  */
4876 BOOL SmallBlockChainStream_WriteAt(
4877   SmallBlockChainStream* This,
4878   ULARGE_INTEGER offset,
4879   ULONG          size,
4880   const void*    buffer,
4881   ULONG*         bytesWritten)
4882 {
4883   ULARGE_INTEGER offsetInBigBlockFile;
4884   ULONG blockNoInSequence = 
4885     offset.s.LowPart / This->parentStorage->smallBlockSize;
4886
4887   ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4888   ULONG bytesToWriteInBuffer;
4889   ULONG blockIndex;
4890   ULONG bytesWrittenFromBigBlockFile;
4891   BYTE* bufferWalker;
4892   
4893   /*
4894    * This should never happen on a small block file.
4895    */
4896   assert(offset.s.HighPart==0);
4897   
4898   /*
4899    * Find the first block in the stream that contains part of the buffer.
4900    */
4901   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4902   
4903   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4904   {
4905     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4906     
4907     blockNoInSequence--;
4908   }
4909   
4910   /*
4911    * Start writing the buffer.
4912    *
4913    * Here, I'm casting away the constness on the buffer variable
4914    * This is OK since we don't intend to modify that buffer.
4915    */
4916   *bytesWritten   = 0;
4917   bufferWalker = (BYTE*)buffer;
4918   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4919   {
4920     /*
4921      * Calculate how many bytes we can copy to this small block.
4922      */
4923     bytesToWriteInBuffer = 
4924       MIN(This->parentStorage->smallBlockSize - offsetInBlock, size);
4925     
4926     /*
4927      * Calculate the offset of the small block in the small block file.
4928      */
4929     offsetInBigBlockFile.s.HighPart  = 0;
4930     offsetInBigBlockFile.s.LowPart   = 
4931       blockIndex * This->parentStorage->smallBlockSize;
4932
4933     offsetInBigBlockFile.s.LowPart  += offsetInBlock;
4934     
4935     /*
4936      * Write those bytes in the buffer to the small block file.
4937      */
4938     BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
4939       offsetInBigBlockFile,
4940       bytesToWriteInBuffer,
4941       bufferWalker,
4942       &bytesWrittenFromBigBlockFile);
4943     
4944     assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
4945     
4946     /*
4947      * Step to the next big block.
4948      */
4949     blockIndex    = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4950     bufferWalker  += bytesToWriteInBuffer;
4951     size          -= bytesToWriteInBuffer;
4952     *bytesWritten += bytesToWriteInBuffer;
4953     offsetInBlock  = 0;     /* There is no offset on the next block */
4954   }
4955   
4956   return (size == 0);
4957 }
4958
4959 /******************************************************************************
4960  *       SmallBlockChainStream_Shrink
4961  *
4962  * Shrinks this chain in the small block depot. 
4963  */
4964 BOOL SmallBlockChainStream_Shrink(
4965   SmallBlockChainStream* This,
4966   ULARGE_INTEGER newSize)
4967 {
4968   ULONG blockIndex, extraBlock;
4969   ULONG numBlocks;
4970   ULONG count = 0;
4971
4972   numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
4973
4974   if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
4975     numBlocks++;
4976
4977   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4978
4979   /*
4980    * Go to the new end of chain
4981    */
4982   while (count < numBlocks)
4983   {
4984     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4985     count++;
4986   }
4987
4988   /*
4989    * If the count is 0, we have a special case, the head of the chain was
4990    * just freed. 
4991    */
4992   if (count == 0)
4993   {
4994     StgProperty chainProp;
4995
4996     StorageImpl_ReadProperty(This->parentStorage, 
4997                              This->ownerPropertyIndex,
4998                              &chainProp);
4999
5000     chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5001
5002     StorageImpl_WriteProperty(This->parentStorage,
5003                               This->ownerPropertyIndex,
5004                               &chainProp);
5005
5006     /*
5007      * We start freeing the chain at the head block.
5008      */
5009     extraBlock = blockIndex;
5010   }
5011   else
5012   {
5013     /* Get the next block before marking the new end */
5014     extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5015
5016     /* Mark the new end of chain */
5017     SmallBlockChainStream_SetNextBlockInChain(
5018       This, 
5019       blockIndex, 
5020       BLOCK_END_OF_CHAIN);
5021   }
5022
5023   /*
5024    * Mark the extra blocks as free
5025    */
5026   while (extraBlock != BLOCK_END_OF_CHAIN)
5027   {
5028     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5029     SmallBlockChainStream_FreeBlock(This, extraBlock);
5030     extraBlock = blockIndex;
5031   }
5032
5033   return TRUE;  
5034 }
5035
5036 /******************************************************************************
5037  *      SmallBlockChainStream_Enlarge
5038  *
5039  * Grows this chain in the small block depot.
5040  */
5041 BOOL SmallBlockChainStream_Enlarge(
5042   SmallBlockChainStream* This,
5043   ULARGE_INTEGER newSize)
5044 {
5045   ULONG blockIndex, currentBlock;
5046   ULONG newNumBlocks;
5047   ULONG oldNumBlocks = 0;
5048
5049   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5050
5051   /*
5052    * Empty chain
5053    */
5054   if (blockIndex == BLOCK_END_OF_CHAIN)
5055   {
5056
5057     StgProperty chainProp;
5058
5059     StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5060                                &chainProp);
5061
5062     chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5063
5064     StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5065                                 &chainProp);
5066
5067     blockIndex = chainProp.startingBlock;
5068     SmallBlockChainStream_SetNextBlockInChain(
5069       This, 
5070       blockIndex, 
5071       BLOCK_END_OF_CHAIN);
5072   }
5073
5074   currentBlock = blockIndex;
5075
5076   /*
5077    * Figure out how many blocks are needed to contain this stream
5078    */
5079   newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5080
5081   if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5082     newNumBlocks++;
5083
5084   /*
5085    * Go to the current end of chain
5086    */
5087   while (blockIndex != BLOCK_END_OF_CHAIN)
5088   {
5089     oldNumBlocks++;
5090     currentBlock = blockIndex;
5091     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5092   }
5093
5094   /*
5095    * Add new blocks to the chain
5096    */
5097   while (oldNumBlocks < newNumBlocks)
5098   {
5099     blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5100     SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5101
5102     SmallBlockChainStream_SetNextBlockInChain(
5103       This, 
5104       blockIndex, 
5105       BLOCK_END_OF_CHAIN);
5106
5107     currentBlock = blockIndex;
5108     oldNumBlocks++;
5109   }
5110
5111   return TRUE;
5112 }
5113
5114 /******************************************************************************
5115  *      SmallBlockChainStream_GetCount
5116  *
5117  * Returns the number of blocks that comprises this chain.
5118  * This is not the size of this chain as the last block may not be full!
5119  */
5120 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5121 {
5122   ULONG blockIndex;
5123   ULONG count = 0;
5124
5125   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5126
5127   while (blockIndex != BLOCK_END_OF_CHAIN)
5128   {
5129     count++;
5130
5131     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5132   }
5133
5134   return count;
5135 }
5136
5137 /******************************************************************************
5138  *      SmallBlockChainStream_SetSize
5139  *
5140  * Sets the size of this stream.
5141  * The file will grow if we grow the chain.
5142  *
5143  * TODO: Free the actual blocks in the file when we shrink the chain.
5144  *       Currently, the blocks are still in the file. So the file size
5145  *       doesn't shrink even if we shrink streams. 
5146  */
5147 BOOL SmallBlockChainStream_SetSize(
5148                 SmallBlockChainStream* This,
5149                 ULARGE_INTEGER    newSize)
5150 {
5151   ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5152
5153   if (newSize.s.LowPart == size.s.LowPart)
5154     return TRUE;
5155
5156   if (newSize.s.LowPart < size.s.LowPart)
5157   {
5158     SmallBlockChainStream_Shrink(This, newSize);
5159   }
5160   else
5161   {
5162     SmallBlockChainStream_Enlarge(This, newSize);
5163   }
5164
5165   return TRUE;
5166 }
5167
5168 /******************************************************************************
5169  *      SmallBlockChainStream_GetSize
5170  *
5171  * Returns the size of this chain.
5172  */
5173 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5174 {
5175   StgProperty chainProperty;
5176
5177   StorageImpl_ReadProperty(
5178     This->parentStorage,
5179     This->ownerPropertyIndex,
5180     &chainProperty);
5181
5182   return chainProperty.size;
5183 }
5184
5185 /******************************************************************************
5186  *    StgCreateDocfile32  [OLE32.144]
5187  */
5188 HRESULT WINAPI StgCreateDocfile(
5189   LPCOLESTR pwcsName,
5190   DWORD       grfMode,
5191   DWORD       reserved,
5192   IStorage  **ppstgOpen)
5193 {
5194   StorageImpl* newStorage = 0;
5195   HANDLE       hFile      = INVALID_HANDLE_VALUE;
5196   HRESULT        hr         = S_OK;
5197   DWORD          shareMode;
5198   DWORD          accessMode;
5199   DWORD          creationMode;
5200   DWORD          fileAttributes;
5201   WCHAR          tempFileName[MAX_PATH];
5202
5203   TRACE("(%s, %lx, %ld, %p)\n", 
5204         debugstr_w(pwcsName), grfMode, 
5205         reserved, ppstgOpen);
5206
5207   /*
5208    * Validate the parameters
5209    */
5210   if (ppstgOpen == 0)
5211     return STG_E_INVALIDPOINTER;
5212
5213   /*
5214    * Validate the STGM flags
5215    */
5216   if ( FAILED( validateSTGM(grfMode) ))
5217     return STG_E_INVALIDFLAG;
5218
5219   /*
5220    * Generate a unique name.
5221    */
5222   if (pwcsName == 0)
5223   {
5224     WCHAR tempPath[MAX_PATH];
5225     WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5226
5227     memset(tempPath, 0, sizeof(tempPath));
5228     memset(tempFileName, 0, sizeof(tempFileName));
5229
5230     if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5231       tempPath[0] = '.';
5232
5233     if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5234       pwcsName = tempFileName;
5235     else
5236       return STG_E_INSUFFICIENTMEMORY;
5237   }
5238
5239   /*
5240    * Interpret the STGM value grfMode 
5241    */
5242   shareMode    = GetShareModeFromSTGM(grfMode);
5243   accessMode   = GetAccessModeFromSTGM(grfMode);
5244   creationMode = GetCreationModeFromSTGM(grfMode);
5245
5246   if (grfMode & STGM_DELETEONRELEASE)
5247     fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5248   else
5249     fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5250
5251   if (grfMode & STGM_TRANSACTED)
5252     FIXME("Transacted mode not implemented.\n");
5253
5254   /*
5255    * Initialize the "out" parameter.
5256    */
5257   *ppstgOpen = 0;
5258
5259   hFile = CreateFileW(pwcsName,
5260                         accessMode,
5261                         shareMode,
5262             NULL,
5263                         creationMode,
5264                         fileAttributes,
5265             0);
5266  
5267   if (hFile == INVALID_HANDLE_VALUE)
5268   {
5269     return E_FAIL;
5270   }
5271
5272   /*
5273    * Allocate and initialize the new IStorage32object.
5274    */
5275   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5276  
5277   if (newStorage == 0)
5278     return STG_E_INSUFFICIENTMEMORY;
5279
5280   hr = StorageImpl_Construct(
5281          newStorage,
5282          hFile,
5283          NULL,
5284          grfMode,
5285          TRUE);
5286  
5287   if (FAILED(hr))
5288   {
5289     HeapFree(GetProcessHeap(), 0, newStorage);
5290     return hr;
5291   }
5292
5293   /*
5294    * Get an "out" pointer for the caller.
5295    */
5296   hr = StorageBaseImpl_QueryInterface(
5297          (IStorage*)newStorage,
5298          (REFIID)&IID_IStorage,
5299          (void**)ppstgOpen);
5300
5301   return hr;
5302 }
5303
5304 /******************************************************************************
5305  *              StgOpenStorage32        [OLE32.148]
5306  */
5307 HRESULT WINAPI StgOpenStorage(
5308   const OLECHAR *pwcsName,
5309   IStorage      *pstgPriority,
5310   DWORD           grfMode,
5311   SNB           snbExclude,
5312   DWORD           reserved, 
5313   IStorage      **ppstgOpen)
5314 {
5315   StorageImpl* newStorage = 0;
5316   HRESULT        hr = S_OK;
5317   HANDLE       hFile = 0;
5318   DWORD          shareMode;
5319   DWORD          accessMode;
5320
5321   TRACE("(%s, %p, %lx, %p, %ld, %p)\n", 
5322         debugstr_w(pwcsName), pstgPriority, grfMode,
5323         snbExclude, reserved, ppstgOpen);
5324
5325   /*
5326    * Perform a sanity check
5327    */
5328   if (( pwcsName == 0) || (ppstgOpen == 0) )
5329     return STG_E_INVALIDPOINTER;
5330
5331   /*
5332    * Validate the STGM flags
5333    */
5334   if ( FAILED( validateSTGM(grfMode) ))
5335     return STG_E_INVALIDFLAG;
5336
5337   /*
5338    * Interpret the STGM value grfMode
5339    */
5340   shareMode    = GetShareModeFromSTGM(grfMode);
5341   accessMode   = GetAccessModeFromSTGM(grfMode);
5342
5343   /*
5344    * Initialize the "out" parameter.
5345    */
5346   *ppstgOpen = 0;
5347   
5348   hFile = CreateFileW( pwcsName, 
5349                         accessMode,
5350                         shareMode,
5351             NULL,
5352             OPEN_EXISTING,
5353             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5354             0);
5355   
5356   
5357   if (hFile==INVALID_HANDLE_VALUE)
5358   {
5359     return E_FAIL;
5360   }
5361
5362   /*
5363    * Allocate and initialize the new IStorage32object.
5364    */
5365   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5366   
5367   if (newStorage == 0)
5368     return STG_E_INSUFFICIENTMEMORY;
5369
5370   hr = StorageImpl_Construct(
5371          newStorage,
5372          hFile,
5373          NULL,
5374          grfMode,
5375          TRUE);
5376   
5377   if (FAILED(hr))
5378   {
5379     HeapFree(GetProcessHeap(), 0, newStorage);
5380     return hr;
5381   }
5382   
5383   /*
5384    * Get an "out" pointer for the caller.
5385    */
5386   hr = StorageBaseImpl_QueryInterface(
5387          (IStorage*)newStorage,
5388          (REFIID)&IID_IStorage,
5389          (void**)ppstgOpen);
5390   
5391   return hr;
5392 }
5393
5394 /******************************************************************************
5395  *    StgCreateDocfileOnILockBytes    [OLE32.145]
5396  */
5397 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5398       ILockBytes *plkbyt,
5399       DWORD grfMode,
5400       DWORD reserved,
5401       IStorage** ppstgOpen)
5402 {
5403   StorageImpl*   newStorage = 0;
5404   HRESULT        hr         = S_OK;
5405
5406   /*
5407    * Validate the parameters
5408    */
5409   if ((ppstgOpen == 0) || (plkbyt == 0))
5410     return STG_E_INVALIDPOINTER;
5411
5412   /*
5413    * Allocate and initialize the new IStorage object.
5414    */
5415   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5416
5417   if (newStorage == 0)
5418     return STG_E_INSUFFICIENTMEMORY;
5419
5420   hr = StorageImpl_Construct(
5421          newStorage,
5422          0,
5423          plkbyt,
5424          grfMode,
5425          FALSE);
5426
5427   if (FAILED(hr))
5428   {
5429     HeapFree(GetProcessHeap(), 0, newStorage);
5430     return hr;
5431   }
5432
5433   /*
5434    * Get an "out" pointer for the caller.
5435    */
5436   hr = StorageBaseImpl_QueryInterface(
5437          (IStorage*)newStorage,
5438          (REFIID)&IID_IStorage,
5439          (void**)ppstgOpen);
5440
5441   return hr;  
5442 }
5443
5444 /******************************************************************************
5445  *    StgOpenStorageOnILockBytes    [OLE32.149]
5446  */
5447 HRESULT WINAPI StgOpenStorageOnILockBytes(
5448       ILockBytes *plkbyt,
5449       IStorage *pstgPriority,
5450       DWORD grfMode,
5451       SNB snbExclude,
5452       DWORD reserved,
5453       IStorage **ppstgOpen)
5454 {
5455   StorageImpl* newStorage = 0;
5456   HRESULT        hr = S_OK;
5457
5458   /*
5459    * Perform a sanity check
5460    */
5461   if ((plkbyt == 0) || (ppstgOpen == 0))
5462     return STG_E_INVALIDPOINTER;
5463
5464   /*
5465    * Validate the STGM flags
5466    */
5467   if ( FAILED( validateSTGM(grfMode) ))
5468     return STG_E_INVALIDFLAG;
5469
5470   /*
5471    * Initialize the "out" parameter.
5472    */
5473   *ppstgOpen = 0;
5474
5475   /*
5476    * Allocate and initialize the new IStorage object.
5477    */
5478   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5479  
5480   if (newStorage == 0)
5481     return STG_E_INSUFFICIENTMEMORY;
5482
5483   hr = StorageImpl_Construct(
5484          newStorage,
5485          0,
5486          plkbyt,
5487          grfMode,
5488          FALSE);
5489
5490   if (FAILED(hr))
5491   {
5492     HeapFree(GetProcessHeap(), 0, newStorage);
5493     return hr;
5494   }
5495
5496   /*
5497    * Get an "out" pointer for the caller.
5498    */
5499   hr = StorageBaseImpl_QueryInterface(
5500          (IStorage*)newStorage,
5501          (REFIID)&IID_IStorage,
5502          (void**)ppstgOpen);
5503
5504   return hr;
5505 }
5506
5507 /******************************************************************************
5508  *              StgIsStorageILockBytes        [OLE32.147]
5509  *
5510  * Determines if the ILockBytes contains a storage object.
5511  */
5512 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5513 {
5514   BYTE sig[8];
5515   ULARGE_INTEGER offset;
5516
5517   offset.s.HighPart = 0;
5518   offset.s.LowPart  = 0;
5519
5520   ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5521
5522   if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5523     return S_OK;
5524
5525   return S_FALSE;
5526 }
5527
5528 /******************************************************************************
5529  *              WriteClassStg32        [OLE32.158]
5530  *
5531  * This method will store the specified CLSID in the specified storage object
5532  */
5533 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5534 {
5535   HRESULT hRes;
5536
5537   assert(pStg != 0);
5538
5539   hRes = IStorage_SetClass(pStg, rclsid);
5540
5541   return hRes;
5542 }
5543
5544 /*******************************************************************************************
5545  *    ReadClassStg
5546  *
5547  * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5548  */
5549 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5550
5551     STATSTG pstatstg;
5552     HRESULT hRes;
5553     
5554     TRACE("()\n");
5555
5556     if(pclsid==NULL)
5557         return E_POINTER;
5558    /*
5559     * read a STATSTG structure (contains the clsid) from the storage
5560     */
5561     hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5562
5563     if(SUCCEEDED(hRes))
5564         *pclsid=pstatstg.clsid;
5565
5566     return hRes;
5567 }
5568
5569 /*************************************************************************************
5570  *    OleLoadFromStream
5571  *
5572  * This function loads an object from stream
5573  */
5574 HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5575 {
5576     CLSID clsid;
5577     HRESULT res;
5578
5579     FIXME("(),stub!\n");
5580
5581     res=ReadClassStm(pStm,&clsid);
5582
5583     if (SUCCEEDED(res)){
5584         
5585         res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5586
5587         if (SUCCEEDED(res))
5588
5589             res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5590     }
5591
5592     return res;
5593 }
5594
5595 /************************************************************************************************
5596  *    OleSaveToStream
5597  *
5598  * This function saves an object with the IPersistStream interface on it to the specified stream
5599  */
5600 HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5601 {
5602
5603     CLSID clsid;
5604     HRESULT res;
5605     
5606     TRACE("(%p,%p)\n",pPStm,pStm);
5607
5608     res=IPersistStream_GetClassID(pPStm,&clsid);
5609
5610     if (SUCCEEDED(res)){
5611         
5612         res=WriteClassStm(pStm,&clsid);
5613
5614         if (SUCCEEDED(res))
5615
5616             res=IPersistStream_Save(pPStm,pStm,FALSE);
5617     }
5618
5619     return res;
5620 }
5621
5622 /****************************************************************************
5623  * This method validate a STGM parameter that can contain the values below
5624  *
5625  * STGM_DIRECT               0x00000000
5626  * STGM_TRANSACTED           0x00010000
5627  * STGM_SIMPLE               0x08000000
5628  * 
5629  * STGM_READ                 0x00000000
5630  * STGM_WRITE                0x00000001
5631  * STGM_READWRITE            0x00000002
5632  * 
5633  * STGM_SHARE_DENY_NONE      0x00000040
5634  * STGM_SHARE_DENY_READ      0x00000030
5635  * STGM_SHARE_DENY_WRITE     0x00000020
5636  * STGM_SHARE_EXCLUSIVE      0x00000010
5637  * 
5638  * STGM_PRIORITY             0x00040000
5639  * STGM_DELETEONRELEASE      0x04000000
5640  *
5641  * STGM_CREATE               0x00001000
5642  * STGM_CONVERT              0x00020000
5643  * STGM_FAILIFTHERE          0x00000000
5644  *
5645  * STGM_NOSCRATCH            0x00100000
5646  * STGM_NOSNAPSHOT           0x00200000
5647  */
5648 static HRESULT validateSTGM(DWORD stgm)
5649 {
5650   BOOL bSTGM_TRANSACTED       = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5651   BOOL bSTGM_SIMPLE           = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5652   BOOL bSTGM_DIRECT           = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5653    
5654   BOOL bSTGM_WRITE            = ((stgm & STGM_WRITE) == STGM_WRITE);
5655   BOOL bSTGM_READWRITE        = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5656   BOOL bSTGM_READ             = ! (bSTGM_WRITE || bSTGM_READWRITE);
5657    
5658   BOOL bSTGM_SHARE_DENY_NONE  =
5659                      ((stgm & STGM_SHARE_DENY_NONE)  == STGM_SHARE_DENY_NONE);
5660
5661   BOOL bSTGM_SHARE_DENY_READ  =
5662                      ((stgm & STGM_SHARE_DENY_READ)  == STGM_SHARE_DENY_READ);
5663
5664   BOOL bSTGM_SHARE_DENY_WRITE =
5665                      ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5666
5667   BOOL bSTGM_SHARE_EXCLUSIVE  =
5668                      ((stgm & STGM_SHARE_EXCLUSIVE)  == STGM_SHARE_EXCLUSIVE);
5669
5670   BOOL bSTGM_CREATE           = ((stgm & STGM_CREATE) == STGM_CREATE);
5671   BOOL bSTGM_CONVERT          = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5672    
5673   BOOL bSTGM_NOSCRATCH        = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5674   BOOL bSTGM_NOSNAPSHOT       = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5675
5676   /* 
5677    * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5678    */
5679   if ( ! bSTGM_DIRECT )
5680     if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5681       return E_FAIL;
5682
5683   /* 
5684    * STGM_WRITE |  STGM_READWRITE | STGM_READ
5685    */
5686   if ( ! bSTGM_READ )
5687     if( bSTGM_WRITE && bSTGM_READWRITE )
5688       return E_FAIL;
5689
5690   /*
5691    * STGM_SHARE_DENY_NONE | others 
5692    * (I assume here that DENY_READ implies DENY_WRITE)
5693    */
5694   if ( bSTGM_SHARE_DENY_NONE )
5695     if ( bSTGM_SHARE_DENY_READ ||
5696          bSTGM_SHARE_DENY_WRITE || 
5697          bSTGM_SHARE_EXCLUSIVE) 
5698       return E_FAIL;
5699
5700   /*
5701    * STGM_CREATE | STGM_CONVERT
5702    * if both are false, STGM_FAILIFTHERE is set to TRUE
5703    */
5704   if ( bSTGM_CREATE && bSTGM_CONVERT )
5705     return E_FAIL;
5706
5707   /*
5708    * STGM_NOSCRATCH requires STGM_TRANSACTED
5709    */
5710   if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5711     return E_FAIL;
5712   
5713   /*
5714    * STGM_NOSNAPSHOT requires STGM_TRANSACTED and 
5715    * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5716    */
5717   if (bSTGM_NOSNAPSHOT)
5718   {
5719     if ( ! ( bSTGM_TRANSACTED && 
5720            !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5721     return E_FAIL;
5722   }
5723
5724   return S_OK;
5725 }
5726
5727 /****************************************************************************
5728  *      GetShareModeFromSTGM
5729  *
5730  * This method will return a share mode flag from a STGM value.
5731  * The STGM value is assumed valid. 
5732  */
5733 static DWORD GetShareModeFromSTGM(DWORD stgm)
5734 {
5735   DWORD dwShareMode = 0;
5736   BOOL bSTGM_SHARE_DENY_NONE  =
5737                      ((stgm & STGM_SHARE_DENY_NONE)  == STGM_SHARE_DENY_NONE);
5738
5739   BOOL bSTGM_SHARE_DENY_READ  =
5740                      ((stgm & STGM_SHARE_DENY_READ)  == STGM_SHARE_DENY_READ);
5741
5742   BOOL bSTGM_SHARE_DENY_WRITE =
5743                      ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5744
5745   BOOL bSTGM_SHARE_EXCLUSIVE  =
5746                      ((stgm & STGM_SHARE_EXCLUSIVE)  == STGM_SHARE_EXCLUSIVE);
5747
5748   if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5749     dwShareMode = 0;
5750
5751   if (bSTGM_SHARE_DENY_NONE)
5752     dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5753
5754   if (bSTGM_SHARE_DENY_WRITE)
5755     dwShareMode = FILE_SHARE_READ;
5756
5757   return dwShareMode;
5758 }
5759
5760 /****************************************************************************
5761  *      GetAccessModeFromSTGM
5762  *
5763  * This method will return an access mode flag from a STGM value.
5764  * The STGM value is assumed valid.
5765  */
5766 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5767 {
5768   DWORD dwDesiredAccess = GENERIC_READ;
5769   BOOL bSTGM_WRITE     = ((stgm & STGM_WRITE) == STGM_WRITE);
5770   BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5771   BOOL bSTGM_READ      = ! (bSTGM_WRITE || bSTGM_READWRITE);
5772
5773   if (bSTGM_READ)
5774     dwDesiredAccess = GENERIC_READ;
5775
5776   if (bSTGM_WRITE)
5777     dwDesiredAccess |= GENERIC_WRITE;
5778
5779   if (bSTGM_READWRITE)
5780     dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5781
5782   return dwDesiredAccess;
5783 }
5784
5785 /****************************************************************************
5786  *      GetCreationModeFromSTGM
5787  *
5788  * This method will return a creation mode flag from a STGM value.
5789  * The STGM value is assumed valid.
5790  */
5791 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5792 {
5793   if ( stgm & STGM_CREATE)
5794     return CREATE_ALWAYS;
5795   if (stgm & STGM_CONVERT) {
5796     FIXME("STGM_CONVERT not implemented!\n");
5797     return CREATE_NEW;
5798   }
5799   /* All other cases */
5800   if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5801         FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5802   return CREATE_NEW;
5803 }