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