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