~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/ole32/storage32.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  1 /*
  2  * Compound Storage (32 bit version)
  3  * Storage implementation
  4  *
  5  * This file contains the compound file implementation
  6  * of the storage interface.
  7  *
  8  * Copyright 1999 Francis Beaudet
  9  * Copyright 1999 Sylvain St-Germain
 10  * Copyright 1999 Thuy Nguyen
 11  * Copyright 2005 Mike McCormack
 12  *
 13  * This library is free software; you can redistribute it and/or
 14  * modify it under the terms of the GNU Lesser General Public
 15  * License as published by the Free Software Foundation; either
 16  * version 2.1 of the License, or (at your option) any later version.
 17  *
 18  * This library is distributed in the hope that it will be useful,
 19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 21  * Lesser General Public License for more details.
 22  *
 23  * You should have received a copy of the GNU Lesser General Public
 24  * License along with this library; if not, write to the Free Software
 25  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 26  *
 27  * NOTES
 28  *  The compound file implementation of IStorage used for create
 29  *  and manage substorages and streams within a storage object
 30  *  residing in a compound file object.
 31  */
 32 
 33 #include <assert.h>
 34 #include <stdarg.h>
 35 #include <stdio.h>
 36 #include <stdlib.h>
 37 #include <string.h>
 38 
 39 #define COBJMACROS
 40 #define NONAMELESSUNION
 41 #define NONAMELESSSTRUCT
 42 
 43 #include "windef.h"
 44 #include "winbase.h"
 45 #include "winnls.h"
 46 #include "winuser.h"
 47 #include "wine/unicode.h"
 48 #include "wine/debug.h"
 49 
 50 #include "storage32.h"
 51 #include "ole2.h"      /* For Write/ReadClassStm */
 52 
 53 #include "winreg.h"
 54 #include "wine/wingdi16.h"
 55 
 56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
 57 
 58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
 59 #define OLESTREAM_ID 0x501
 60 #define OLESTREAM_MAX_STR_LEN 255
 61 
 62 /*
 63  * These are signatures to detect the type of Document file.
 64  */
 65 static const BYTE STORAGE_magic[8]    ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
 66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
 67 
 68 static const char rootPropertyName[] = "Root Entry";
 69 
 70 /****************************************************************************
 71  * Storage32InternalImpl definitions.
 72  *
 73  * Definition of the implementation structure for the IStorage32 interface.
 74  * This one implements the IStorage32 interface for storage that are
 75  * inside another storage.
 76  */
 77 struct StorageInternalImpl
 78 {
 79   struct StorageBaseImpl base;
 80   /*
 81    * There is no specific data for this class.
 82    */
 83 };
 84 typedef struct StorageInternalImpl StorageInternalImpl;
 85 
 86 /* Method definitions for the Storage32InternalImpl class. */
 87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
 88                                                           DWORD openFlags, ULONG rootTropertyIndex);
 89 static void StorageImpl_Destroy(StorageBaseImpl* iface);
 90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
 91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
 92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
 93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
 94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
 95 
 96 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
 97 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
 98 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
 99 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
101 
102 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
103 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
104 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
105 
106 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
107 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
108     ULONG blockIndex, ULONG offset, DWORD value);
109 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl*  This,
110     ULONG blockIndex, ULONG offset, DWORD* value);
111 
112 /* OLESTREAM memory structure to use for Get and Put Routines */
113 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
114 typedef struct
115 {
116     DWORD dwOleID;
117     DWORD dwTypeID;
118     DWORD dwOleTypeNameLength;
119     CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
120     CHAR  *pstrOleObjFileName;
121     DWORD dwOleObjFileNameLength;
122     DWORD dwMetaFileWidth;
123     DWORD dwMetaFileHeight;
124     CHAR  strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
125     DWORD dwDataLength;
126     BYTE *pData;
127 }OLECONVERT_OLESTREAM_DATA;
128 
129 /* CompObj Stream structure */
130 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
131 typedef struct
132 {
133     BYTE byUnknown1[12];
134     CLSID clsid;
135     DWORD dwCLSIDNameLength;
136     CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
137     DWORD dwOleTypeNameLength;
138     CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
139     DWORD dwProgIDNameLength;
140     CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
141     BYTE byUnknown2[16];
142 }OLECONVERT_ISTORAGE_COMPOBJ;
143 
144 
145 /* Ole Presentation Stream structure */
146 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
147 typedef struct
148 {
149     BYTE byUnknown1[28];
150     DWORD dwExtentX;
151     DWORD dwExtentY;
152     DWORD dwSize;
153     BYTE *pData;
154 }OLECONVERT_ISTORAGE_OLEPRES;
155 
156 
157 
158 /***********************************************************************
159  * Forward declaration of internal functions used by the method DestroyElement
160  */
161 static HRESULT deleteStorageProperty(
162   StorageImpl *parentStorage,
163   ULONG        foundPropertyIndexToDelete,
164   StgProperty  propertyToDelete);
165 
166 static HRESULT deleteStreamProperty(
167   StorageImpl *parentStorage,
168   ULONG         foundPropertyIndexToDelete,
169   StgProperty   propertyToDelete);
170 
171 static HRESULT findPlaceholder(
172   StorageImpl *storage,
173   ULONG         propertyIndexToStore,
174   ULONG         storagePropertyIndex,
175   INT         typeOfRelation);
176 
177 static HRESULT adjustPropertyChain(
178   StorageImpl *This,
179   StgProperty   propertyToDelete,
180   StgProperty   parentProperty,
181   ULONG         parentPropertyId,
182   INT         typeOfRelation);
183 
184 /***********************************************************************
185  * Declaration of the functions used to manipulate StgProperty
186  */
187 
188 static ULONG getFreeProperty(
189   StorageImpl *storage);
190 
191 static void updatePropertyChain(
192   StorageImpl *storage,
193   ULONG       newPropertyIndex,
194   StgProperty newProperty);
195 
196 static LONG propertyNameCmp(
197     const OLECHAR *newProperty,
198     const OLECHAR *currentProperty);
199 
200 
201 /***********************************************************************
202  * Declaration of miscellaneous functions...
203  */
204 static HRESULT validateSTGM(DWORD stgmValue);
205 
206 static DWORD GetShareModeFromSTGM(DWORD stgm);
207 static DWORD GetAccessModeFromSTGM(DWORD stgm);
208 static DWORD GetCreationModeFromSTGM(DWORD stgm);
209 
210 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
211 
212 
213 /****************************************************************************
214  * IEnumSTATSTGImpl definitions.
215  *
216  * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
217  * This class allows iterating through the content of a storage and to find
218  * specific items inside it.
219  */
220 struct IEnumSTATSTGImpl
221 {
222   const IEnumSTATSTGVtbl *lpVtbl;    /* Needs to be the first item in the struct
223                                 * since we want to cast this in an IEnumSTATSTG pointer */
224 
225   LONG           ref;                   /* Reference count */
226   StorageImpl*   parentStorage;         /* Reference to the parent storage */
227   ULONG          firstPropertyNode;     /* Index of the root of the storage to enumerate */
228 
229   /*
230    * The current implementation of the IEnumSTATSTGImpl class uses a stack
231    * to walk the property sets to get the content of a storage. This stack
232    * is implemented by the following 3 data members
233    */
234   ULONG          stackSize;
235   ULONG          stackMaxSize;
236   ULONG*         stackToVisit;
237 
238 #define ENUMSTATSGT_SIZE_INCREMENT 10
239 };
240 
241 
242 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
243 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
244 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
245 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
246 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
247                                            StgProperty* buffer);
248 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
249                                                StgProperty *currentProperty, ULONG *propertyId);
250 
251 /************************************************************************
252 ** Block Functions
253 */
254 
255 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
256 {
257     if (index == 0xffffffff)
258         index = 0;
259     else
260         index ++;
261 
262     return index * BIG_BLOCK_SIZE;
263 }
264 
265 /************************************************************************
266 ** Storage32BaseImpl implementation
267 */
268 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
269   ULARGE_INTEGER offset,
270   void*          buffer,
271   ULONG          size,
272   ULONG*         bytesRead)
273 {
274     return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
275 }
276 
277 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
278   ULARGE_INTEGER offset,
279   const void*    buffer,
280   const ULONG    size,
281   ULONG*         bytesWritten)
282 {
283     return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
284 }
285 
286 /************************************************************************
287  * Storage32BaseImpl_QueryInterface (IUnknown)
288  *
289  * This method implements the common QueryInterface for all IStorage32
290  * implementations contained in this file.
291  *
292  * See Windows documentation for more details on IUnknown methods.
293  */
294 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
295   IStorage*        iface,
296   REFIID             riid,
297   void**             ppvObject)
298 {
299   StorageBaseImpl *This = (StorageBaseImpl *)iface;
300   /*
301    * Perform a sanity check on the parameters.
302    */
303   if ( (This==0) || (ppvObject==0) )
304     return E_INVALIDARG;
305 
306   /*
307    * Initialize the return parameter.
308    */
309   *ppvObject = 0;
310 
311   /*
312    * Compare the riid with the interface IDs implemented by this object.
313    */
314   if (IsEqualGUID(&IID_IUnknown, riid) ||
315       IsEqualGUID(&IID_IStorage, riid))
316   {
317     *ppvObject = This;
318   }
319   else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
320   {
321     *ppvObject = &This->pssVtbl;
322   }
323 
324   /*
325    * Check that we obtained an interface.
326    */
327   if ((*ppvObject)==0)
328     return E_NOINTERFACE;
329 
330   /*
331    * Query Interface always increases the reference count by one when it is
332    * successful
333    */
334   IStorage_AddRef(iface);
335 
336   return S_OK;
337 }
338 
339 /************************************************************************
340  * Storage32BaseImpl_AddRef (IUnknown)
341  *
342  * This method implements the common AddRef for all IStorage32
343  * implementations contained in this file.
344  *
345  * See Windows documentation for more details on IUnknown methods.
346  */
347 static ULONG WINAPI StorageBaseImpl_AddRef(
348             IStorage* iface)
349 {
350   StorageBaseImpl *This = (StorageBaseImpl *)iface;
351   ULONG ref = InterlockedIncrement(&This->ref);
352 
353   TRACE("(%p) AddRef to %d\n", This, ref);
354 
355   return ref;
356 }
357 
358 /************************************************************************
359  * Storage32BaseImpl_Release (IUnknown)
360  *
361  * This method implements the common Release for all IStorage32
362  * implementations contained in this file.
363  *
364  * See Windows documentation for more details on IUnknown methods.
365  */
366 static ULONG WINAPI StorageBaseImpl_Release(
367       IStorage* iface)
368 {
369   StorageBaseImpl *This = (StorageBaseImpl *)iface;
370   /*
371    * Decrease the reference count on this object.
372    */
373   ULONG ref = InterlockedDecrement(&This->ref);
374 
375   TRACE("(%p) ReleaseRef to %d\n", This, ref);
376 
377   /*
378    * If the reference count goes down to 0, perform suicide.
379    */
380   if (ref == 0)
381   {
382     /*
383      * Since we are using a system of base-classes, we want to call the
384      * destructor of the appropriate derived class. To do this, we are
385      * using virtual functions to implement the destructor.
386      */
387     This->v_destructor(This);
388   }
389 
390   return ref;
391 }
392 
393 /************************************************************************
394  * Storage32BaseImpl_OpenStream (IStorage)
395  *
396  * This method will open the specified stream object from the current storage.
397  *
398  * See Windows documentation for more details on IStorage methods.
399  */
400 static HRESULT WINAPI StorageBaseImpl_OpenStream(
401   IStorage*        iface,
402   const OLECHAR*   pwcsName,  /* [string][in] */
403   void*            reserved1, /* [unique][in] */
404   DWORD            grfMode,   /* [in]  */
405   DWORD            reserved2, /* [in]  */
406   IStream**        ppstm)     /* [out] */
407 {
408   StorageBaseImpl *This = (StorageBaseImpl *)iface;
409   IEnumSTATSTGImpl* propertyEnumeration;
410   StgStreamImpl*    newStream;
411   StgProperty       currentProperty;
412   ULONG             foundPropertyIndex;
413   HRESULT           res = STG_E_UNKNOWN;
414 
415   TRACE("(%p, %s, %p, %x, %d, %p)\n",
416         iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
417 
418   /*
419    * Perform a sanity check on the parameters.
420    */
421   if ( (pwcsName==NULL) || (ppstm==0) )
422   {
423     res = E_INVALIDARG;
424     goto end;
425   }
426 
427   /*
428    * Initialize the out parameter
429    */
430   *ppstm = NULL;
431 
432   /*
433    * Validate the STGM flags
434    */
435   if ( FAILED( validateSTGM(grfMode) ) ||
436        STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
437   {
438     res = STG_E_INVALIDFLAG;
439     goto end;
440   }
441 
442   /*
443    * As documented.
444    */
445   if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
446   {
447     res = STG_E_INVALIDFUNCTION;
448     goto end;
449   }
450 
451   /*
452    * Check that we're compatible with the parent's storage mode, but
453    * only if we are not in transacted mode
454    */
455   if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
456     if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
457     {
458       res = STG_E_ACCESSDENIED;
459       goto end;
460     }
461   }
462 
463   /*
464    * Create a property enumeration to search the properties
465    */
466   propertyEnumeration = IEnumSTATSTGImpl_Construct(
467     This->ancestorStorage,
468     This->rootPropertySetIndex);
469 
470   /*
471    * Search the enumeration for the property with the given name
472    */
473   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
474     propertyEnumeration,
475     pwcsName,
476     &currentProperty);
477 
478   /*
479    * Delete the property enumeration since we don't need it anymore
480    */
481   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
482 
483   /*
484    * If it was found, construct the stream object and return a pointer to it.
485    */
486   if ( (foundPropertyIndex!=PROPERTY_NULL) &&
487        (currentProperty.propertyType==PROPTYPE_STREAM) )
488   {
489     newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
490 
491     if (newStream!=0)
492     {
493       newStream->grfMode = grfMode;
494       *ppstm = (IStream*)newStream;
495 
496       /*
497        * Since we are returning a pointer to the interface, we have to
498        * nail down the reference.
499        */
500       IStream_AddRef(*ppstm);
501 
502       res = S_OK;
503       goto end;
504     }
505 
506     res = E_OUTOFMEMORY;
507     goto end;
508   }
509 
510   res = STG_E_FILENOTFOUND;
511 
512 end:
513   if (res == S_OK)
514     TRACE("<-- IStream %p\n", *ppstm);
515   TRACE("<-- %08x\n", res);
516   return res;
517 }
518 
519 /************************************************************************
520  * Storage32BaseImpl_OpenStorage (IStorage)
521  *
522  * This method will open a new storage object from the current storage.
523  *
524  * See Windows documentation for more details on IStorage methods.
525  */
526 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
527   IStorage*        iface,
528   const OLECHAR*   pwcsName,      /* [string][unique][in] */
529   IStorage*        pstgPriority,  /* [unique][in] */
530   DWORD            grfMode,       /* [in] */
531   SNB              snbExclude,    /* [unique][in] */
532   DWORD            reserved,      /* [in] */
533   IStorage**       ppstg)         /* [out] */
534 {
535   StorageBaseImpl *This = (StorageBaseImpl *)iface;
536   StorageInternalImpl* newStorage;
537   IEnumSTATSTGImpl*      propertyEnumeration;
538   StgProperty            currentProperty;
539   ULONG                  foundPropertyIndex;
540   HRESULT                res = STG_E_UNKNOWN;
541 
542   TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
543         iface, debugstr_w(pwcsName), pstgPriority,
544         grfMode, snbExclude, reserved, ppstg);
545 
546   /*
547    * Perform a sanity check on the parameters.
548    */
549   if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
550   {
551     res = E_INVALIDARG;
552     goto end;
553   }
554 
555   /* as documented */
556   if (snbExclude != NULL)
557   {
558     res = STG_E_INVALIDPARAMETER;
559     goto end;
560   }
561 
562   /*
563    * Validate the STGM flags
564    */
565   if ( FAILED( validateSTGM(grfMode) ))
566   {
567     res = STG_E_INVALIDFLAG;
568     goto end;
569   }
570 
571   /*
572    * As documented.
573    */
574   if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
575         (grfMode & STGM_DELETEONRELEASE) ||
576         (grfMode & STGM_PRIORITY) )
577   {
578     res = STG_E_INVALIDFUNCTION;
579     goto end;
580   }
581 
582   /*
583    * Check that we're compatible with the parent's storage mode,
584    * but only if we are not transacted
585    */
586   if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
587     if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
588     {
589       res = STG_E_ACCESSDENIED;
590       goto end;
591     }
592   }
593 
594   /*
595    * Initialize the out parameter
596    */
597   *ppstg = NULL;
598 
599   /*
600    * Create a property enumeration to search the properties
601    */
602   propertyEnumeration = IEnumSTATSTGImpl_Construct(
603                           This->ancestorStorage,
604                           This->rootPropertySetIndex);
605 
606   /*
607    * Search the enumeration for the property with the given name
608    */
609   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
610                          propertyEnumeration,
611                          pwcsName,
612                          &currentProperty);
613 
614   /*
615    * Delete the property enumeration since we don't need it anymore
616    */
617   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
618 
619   /*
620    * If it was found, construct the stream object and return a pointer to it.
621    */
622   if ( (foundPropertyIndex!=PROPERTY_NULL) &&
623        (currentProperty.propertyType==PROPTYPE_STORAGE) )
624   {
625     /*
626      * Construct a new Storage object
627      */
628     newStorage = StorageInternalImpl_Construct(
629                    This->ancestorStorage,
630                    grfMode,
631                    foundPropertyIndex);
632 
633     if (newStorage != 0)
634     {
635       *ppstg = (IStorage*)newStorage;
636 
637       /*
638        * Since we are returning a pointer to the interface,
639        * we have to nail down the reference.
640        */
641       StorageBaseImpl_AddRef(*ppstg);
642 
643       res = S_OK;
644       goto end;
645     }
646 
647     res = STG_E_INSUFFICIENTMEMORY;
648     goto end;
649   }
650 
651   res = STG_E_FILENOTFOUND;
652 
653 end:
654   TRACE("<-- %08x\n", res);
655   return res;
656 }
657 
658 /************************************************************************
659  * Storage32BaseImpl_EnumElements (IStorage)
660  *
661  * This method will create an enumerator object that can be used to
662  * retrieve information about all the properties in the storage object.
663  *
664  * See Windows documentation for more details on IStorage methods.
665  */
666 static HRESULT WINAPI StorageBaseImpl_EnumElements(
667   IStorage*       iface,
668   DWORD           reserved1, /* [in] */
669   void*           reserved2, /* [size_is][unique][in] */
670   DWORD           reserved3, /* [in] */
671   IEnumSTATSTG**  ppenum)    /* [out] */
672 {
673   StorageBaseImpl *This = (StorageBaseImpl *)iface;
674   IEnumSTATSTGImpl* newEnum;
675 
676   TRACE("(%p, %d, %p, %d, %p)\n",
677         iface, reserved1, reserved2, reserved3, ppenum);
678 
679   /*
680    * Perform a sanity check on the parameters.
681    */
682   if ( (This==0) || (ppenum==0))
683     return E_INVALIDARG;
684 
685   /*
686    * Construct the enumerator.
687    */
688   newEnum = IEnumSTATSTGImpl_Construct(
689               This->ancestorStorage,
690               This->rootPropertySetIndex);
691 
692   if (newEnum!=0)
693   {
694     *ppenum = (IEnumSTATSTG*)newEnum;
695 
696     /*
697      * Don't forget to nail down a reference to the new object before
698      * returning it.
699      */
700     IEnumSTATSTG_AddRef(*ppenum);
701 
702     return S_OK;
703   }
704 
705   return E_OUTOFMEMORY;
706 }
707 
708 /************************************************************************
709  * Storage32BaseImpl_Stat (IStorage)
710  *
711  * This method will retrieve information about this storage object.
712  *
713  * See Windows documentation for more details on IStorage methods.
714  */
715 static HRESULT WINAPI StorageBaseImpl_Stat(
716   IStorage*        iface,
717   STATSTG*         pstatstg,     /* [out] */
718   DWORD            grfStatFlag)  /* [in] */
719 {
720   StorageBaseImpl *This = (StorageBaseImpl *)iface;
721   StgProperty    curProperty;
722   BOOL           readSuccessful;
723   HRESULT        res = STG_E_UNKNOWN;
724 
725   TRACE("(%p, %p, %x)\n",
726         iface, pstatstg, grfStatFlag);
727 
728   /*
729    * Perform a sanity check on the parameters.
730    */
731   if ( (This==0) || (pstatstg==0))
732   {
733     res = E_INVALIDARG;
734     goto end;
735   }
736 
737   /*
738    * Read the information from the property.
739    */
740   readSuccessful = StorageImpl_ReadProperty(
741                     This->ancestorStorage,
742                     This->rootPropertySetIndex,
743                     &curProperty);
744 
745   if (readSuccessful)
746   {
747     StorageUtl_CopyPropertyToSTATSTG(
748       pstatstg,
749       &curProperty,
750       grfStatFlag);
751 
752     pstatstg->grfMode = This->openFlags;
753     pstatstg->grfStateBits = This->stateBits;
754 
755     res = S_OK;
756     goto end;
757   }
758 
759   res = E_FAIL;
760 
761 end:
762   if (res == S_OK)
763   {
764     TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
765   }
766   TRACE("<-- %08x\n", res);
767   return res;
768 }
769 
770 /************************************************************************
771  * Storage32BaseImpl_RenameElement (IStorage)
772  *
773  * This method will rename the specified element.
774  *
775  * See Windows documentation for more details on IStorage methods.
776  *
777  * Implementation notes: The method used to rename consists of creating a clone
778  *    of the deleted StgProperty object setting it with the new name and to
779  *    perform a DestroyElement of the old StgProperty.
780  */
781 static HRESULT WINAPI StorageBaseImpl_RenameElement(
782             IStorage*        iface,
783             const OLECHAR*   pwcsOldName,  /* [in] */
784             const OLECHAR*   pwcsNewName)  /* [in] */
785 {
786   StorageBaseImpl *This = (StorageBaseImpl *)iface;
787   IEnumSTATSTGImpl* propertyEnumeration;
788   StgProperty       currentProperty;
789   ULONG             foundPropertyIndex;
790 
791   TRACE("(%p, %s, %s)\n",
792         iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
793 
794   /*
795    * Create a property enumeration to search the properties
796    */
797   propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
798                                                    This->rootPropertySetIndex);
799 
800   /*
801    * Search the enumeration for the new property name
802    */
803   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
804                                                      pwcsNewName,
805                                                      &currentProperty);
806 
807   if (foundPropertyIndex != PROPERTY_NULL)
808   {
809     /*
810      * There is already a property with the new name
811      */
812     IEnumSTATSTGImpl_Destroy(propertyEnumeration);
813     return STG_E_FILEALREADYEXISTS;
814   }
815 
816   IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
817 
818   /*
819    * Search the enumeration for the old property name
820    */
821   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
822                                                      pwcsOldName,
823                                                      &currentProperty);
824 
825   /*
826    * Delete the property enumeration since we don't need it anymore
827    */
828   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
829 
830   if (foundPropertyIndex != PROPERTY_NULL)
831   {
832     StgProperty renamedProperty;
833     ULONG       renamedPropertyIndex;
834 
835     /*
836      * Setup a new property for the renamed property
837      */
838     renamedProperty.sizeOfNameString =
839       ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
840 
841     if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
842       return STG_E_INVALIDNAME;
843 
844     strcpyW(renamedProperty.name, pwcsNewName);
845 
846     renamedProperty.propertyType  = currentProperty.propertyType;
847     renamedProperty.startingBlock = currentProperty.startingBlock;
848     renamedProperty.size.u.LowPart  = currentProperty.size.u.LowPart;
849     renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
850 
851     renamedProperty.previousProperty = PROPERTY_NULL;
852     renamedProperty.nextProperty     = PROPERTY_NULL;
853 
854     /*
855      * Bring the dirProperty link in case it is a storage and in which
856      * case the renamed storage elements don't require to be reorganized.
857      */
858     renamedProperty.dirProperty = currentProperty.dirProperty;
859 
860     /* call CoFileTime to get the current time
861     renamedProperty.timeStampS1
862     renamedProperty.timeStampD1
863     renamedProperty.timeStampS2
864     renamedProperty.timeStampD2
865     renamedProperty.propertyUniqueID
866     */
867 
868     /*
869      * Obtain a free property in the property chain
870      */
871     renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
872 
873     /*
874      * Save the new property into the new property spot
875      */
876     StorageImpl_WriteProperty(
877       This->ancestorStorage,
878       renamedPropertyIndex,
879       &renamedProperty);
880 
881     /*
882      * Find a spot in the property chain for our newly created property.
883      */
884     updatePropertyChain(
885       (StorageImpl*)This,
886       renamedPropertyIndex,
887       renamedProperty);
888 
889     /*
890      * At this point the renamed property has been inserted in the tree,
891      * now, before Destroying the old property we must zero its dirProperty
892      * otherwise the DestroyProperty below will zap it all and we do not want
893      * this to happen.
894      * Also, we fake that the old property is a storage so the DestroyProperty
895      * will not do a SetSize(0) on the stream data.
896      *
897      * This means that we need to tweak the StgProperty if it is a stream or a
898      * non empty storage.
899      */
900     StorageImpl_ReadProperty(This->ancestorStorage,
901                              foundPropertyIndex,
902                              &currentProperty);
903 
904     currentProperty.dirProperty  = PROPERTY_NULL;
905     currentProperty.propertyType = PROPTYPE_STORAGE;
906     StorageImpl_WriteProperty(
907       This->ancestorStorage,
908       foundPropertyIndex,
909       &currentProperty);
910 
911     /*
912      * Invoke Destroy to get rid of the ole property and automatically redo
913      * the linking of its previous and next members...
914      */
915     IStorage_DestroyElement(iface, pwcsOldName);
916 
917   }
918   else
919   {
920     /*
921      * There is no property with the old name
922      */
923     return STG_E_FILENOTFOUND;
924   }
925 
926   return S_OK;
927 }
928 
929 /************************************************************************
930  * Storage32BaseImpl_CreateStream (IStorage)
931  *
932  * This method will create a stream object within this storage
933  *
934  * See Windows documentation for more details on IStorage methods.
935  */
936 static HRESULT WINAPI StorageBaseImpl_CreateStream(
937             IStorage*        iface,
938             const OLECHAR*   pwcsName,  /* [string][in] */
939             DWORD            grfMode,   /* [in] */
940             DWORD            reserved1, /* [in] */
941             DWORD            reserved2, /* [in] */
942             IStream**        ppstm)     /* [out] */
943 {
944   StorageBaseImpl *This = (StorageBaseImpl *)iface;
945   IEnumSTATSTGImpl* propertyEnumeration;
946   StgStreamImpl*    newStream;
947   StgProperty       currentProperty, newStreamProperty;
948   ULONG             foundPropertyIndex, newPropertyIndex;
949 
950   TRACE("(%p, %s, %x, %d, %d, %p)\n",
951         iface, debugstr_w(pwcsName), grfMode,
952         reserved1, reserved2, ppstm);
953 
954   /*
955    * Validate parameters
956    */
957   if (ppstm == 0)
958     return STG_E_INVALIDPOINTER;
959 
960   if (pwcsName == 0)
961     return STG_E_INVALIDNAME;
962 
963   if (reserved1 || reserved2)
964     return STG_E_INVALIDPARAMETER;
965 
966   /*
967    * Validate the STGM flags
968    */
969   if ( FAILED( validateSTGM(grfMode) ))
970     return STG_E_INVALIDFLAG;
971 
972   if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) 
973     return STG_E_INVALIDFLAG;
974 
975   /*
976    * As documented.
977    */
978   if ((grfMode & STGM_DELETEONRELEASE) ||
979       (grfMode & STGM_TRANSACTED))
980     return STG_E_INVALIDFUNCTION;
981 
982   /* Can't create a stream on read-only storage */
983   if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
984     return STG_E_ACCESSDENIED;
985 
986   /*
987    * Check that we're compatible with the parent's storage mode
988    * if not in transacted mode
989    */
990   if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
991     if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
992       return STG_E_ACCESSDENIED;
993   }
994 
995   if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
996     if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
997 
998   /*
999    * Initialize the out parameter
1000    */
1001   *ppstm = 0;
1002 
1003   /*
1004    * Create a property enumeration to search the properties
1005    */
1006   propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1007                                                    This->rootPropertySetIndex);
1008 
1009   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1010                                                      pwcsName,
1011                                                      &currentProperty);
1012 
1013   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1014 
1015   if (foundPropertyIndex != PROPERTY_NULL)
1016   {
1017     /*
1018      * An element with this name already exists
1019      */
1020     if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1021     {
1022       StgStreamImpl *strm;
1023 
1024       LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1025       {
1026         if (strm->ownerProperty == foundPropertyIndex)
1027         {
1028           TRACE("Stream deleted %p\n", strm);
1029           strm->parentStorage = NULL;
1030           list_remove(&strm->StrmListEntry);
1031         }
1032       }
1033       IStorage_DestroyElement(iface, pwcsName);
1034     }
1035     else
1036       return STG_E_FILEALREADYEXISTS;
1037   }
1038   else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1039   {
1040     WARN("read-only storage\n");
1041     return STG_E_ACCESSDENIED;
1042   }
1043 
1044   /*
1045    * memset the empty property
1046    */
1047   memset(&newStreamProperty, 0, sizeof(StgProperty));
1048 
1049   newStreamProperty.sizeOfNameString =
1050       ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1051 
1052   if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1053     return STG_E_INVALIDNAME;
1054 
1055   strcpyW(newStreamProperty.name, pwcsName);
1056 
1057   newStreamProperty.propertyType  = PROPTYPE_STREAM;
1058   newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1059   newStreamProperty.size.u.LowPart  = 0;
1060   newStreamProperty.size.u.HighPart = 0;
1061 
1062   newStreamProperty.previousProperty = PROPERTY_NULL;
1063   newStreamProperty.nextProperty     = PROPERTY_NULL;
1064   newStreamProperty.dirProperty      = PROPERTY_NULL;
1065 
1066   /* call CoFileTime to get the current time
1067   newStreamProperty.timeStampS1
1068   newStreamProperty.timeStampD1
1069   newStreamProperty.timeStampS2
1070   newStreamProperty.timeStampD2
1071   */
1072 
1073   /*  newStreamProperty.propertyUniqueID */
1074 
1075   /*
1076    * Get a free property or create a new one
1077    */
1078   newPropertyIndex = getFreeProperty(This->ancestorStorage);
1079 
1080   /*
1081    * Save the new property into the new property spot
1082    */
1083   StorageImpl_WriteProperty(
1084     This->ancestorStorage,
1085     newPropertyIndex,
1086     &newStreamProperty);
1087 
1088   /*
1089    * Find a spot in the property chain for our newly created property.
1090    */
1091   updatePropertyChain(
1092     (StorageImpl*)This,
1093     newPropertyIndex,
1094     newStreamProperty);
1095 
1096   /*
1097    * Open the stream to return it.
1098    */
1099   newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1100 
1101   if (newStream != 0)
1102   {
1103     *ppstm = (IStream*)newStream;
1104 
1105     /*
1106      * Since we are returning a pointer to the interface, we have to nail down
1107      * the reference.
1108      */
1109     IStream_AddRef(*ppstm);
1110   }
1111   else
1112   {
1113     return STG_E_INSUFFICIENTMEMORY;
1114   }
1115 
1116   return S_OK;
1117 }
1118 
1119 /************************************************************************
1120  * Storage32BaseImpl_SetClass (IStorage)
1121  *
1122  * This method will write the specified CLSID in the property of this
1123  * storage.
1124  *
1125  * See Windows documentation for more details on IStorage methods.
1126  */
1127 static HRESULT WINAPI StorageBaseImpl_SetClass(
1128   IStorage*        iface,
1129   REFCLSID         clsid) /* [in] */
1130 {
1131   StorageBaseImpl *This = (StorageBaseImpl *)iface;
1132   HRESULT hRes = E_FAIL;
1133   StgProperty curProperty;
1134   BOOL success;
1135 
1136   TRACE("(%p, %p)\n", iface, clsid);
1137 
1138   success = StorageImpl_ReadProperty(This->ancestorStorage,
1139                                        This->rootPropertySetIndex,
1140                                        &curProperty);
1141   if (success)
1142   {
1143     curProperty.propertyUniqueID = *clsid;
1144 
1145     success =  StorageImpl_WriteProperty(This->ancestorStorage,
1146                                            This->rootPropertySetIndex,
1147                                            &curProperty);
1148     if (success)
1149       hRes = S_OK;
1150   }
1151 
1152   return hRes;
1153 }
1154 
1155 /************************************************************************
1156 ** Storage32Impl implementation
1157 */
1158 
1159 /************************************************************************
1160  * Storage32Impl_CreateStorage (IStorage)
1161  *
1162  * This method will create the storage object within the provided storage.
1163  *
1164  * See Windows documentation for more details on IStorage methods.
1165  */
1166 static HRESULT WINAPI StorageImpl_CreateStorage(
1167   IStorage*      iface,
1168   const OLECHAR  *pwcsName, /* [string][in] */
1169   DWORD            grfMode,   /* [in] */
1170   DWORD            reserved1, /* [in] */
1171   DWORD            reserved2, /* [in] */
1172   IStorage       **ppstg)   /* [out] */
1173 {
1174   StorageImpl* const This=(StorageImpl*)iface;
1175 
1176   IEnumSTATSTGImpl *propertyEnumeration;
1177   StgProperty      currentProperty;
1178   StgProperty      newProperty;
1179   ULONG            foundPropertyIndex;
1180   ULONG            newPropertyIndex;
1181   HRESULT          hr;
1182 
1183   TRACE("(%p, %s, %x, %d, %d, %p)\n",
1184         iface, debugstr_w(pwcsName), grfMode,
1185         reserved1, reserved2, ppstg);
1186 
1187   /*
1188    * Validate parameters
1189    */
1190   if (ppstg == 0)
1191     return STG_E_INVALIDPOINTER;
1192 
1193   if (pwcsName == 0)
1194     return STG_E_INVALIDNAME;
1195 
1196   /*
1197    * Initialize the out parameter
1198    */
1199   *ppstg = NULL;
1200 
1201   /*
1202    * Validate the STGM flags
1203    */
1204   if ( FAILED( validateSTGM(grfMode) ) ||
1205        (grfMode & STGM_DELETEONRELEASE) )
1206   {
1207     WARN("bad grfMode: 0x%x\n", grfMode);
1208     return STG_E_INVALIDFLAG;
1209   }
1210 
1211   /*
1212    * Check that we're compatible with the parent's storage mode
1213    */
1214   if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1215   {
1216     WARN("access denied\n");
1217     return STG_E_ACCESSDENIED;
1218   }
1219 
1220   /*
1221    * Create a property enumeration and search the properties
1222    */
1223   propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1224                                                     This->base.rootPropertySetIndex);
1225 
1226   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1227                                                      pwcsName,
1228                                                      &currentProperty);
1229   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1230 
1231   if (foundPropertyIndex != PROPERTY_NULL)
1232   {
1233     /*
1234      * An element with this name already exists
1235      */
1236     if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1237         STGM_ACCESS_MODE(This->base.openFlags) != STGM_READ)
1238     {
1239       hr = IStorage_DestroyElement(iface, pwcsName);
1240       if (FAILED(hr))
1241         return hr;
1242     }
1243     else
1244     {
1245       WARN("file already exists\n");
1246       return STG_E_FILEALREADYEXISTS;
1247     }
1248   }
1249   else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1250   {
1251     WARN("read-only storage\n");
1252     return STG_E_ACCESSDENIED;
1253   }
1254 
1255   /*
1256    * memset the empty property
1257    */
1258   memset(&newProperty, 0, sizeof(StgProperty));
1259 
1260   newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1261 
1262   if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1263   {
1264     FIXME("name too long\n");
1265     return STG_E_INVALIDNAME;
1266   }
1267 
1268   strcpyW(newProperty.name, pwcsName);
1269 
1270   newProperty.propertyType  = PROPTYPE_STORAGE;
1271   newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1272   newProperty.size.u.LowPart  = 0;
1273   newProperty.size.u.HighPart = 0;
1274 
1275   newProperty.previousProperty = PROPERTY_NULL;
1276   newProperty.nextProperty     = PROPERTY_NULL;
1277   newProperty.dirProperty      = PROPERTY_NULL;
1278 
1279   /* call CoFileTime to get the current time
1280   newProperty.timeStampS1
1281   newProperty.timeStampD1
1282   newProperty.timeStampS2
1283   newProperty.timeStampD2
1284   */
1285 
1286   /*  newStorageProperty.propertyUniqueID */
1287 
1288   /*
1289    * Obtain a free property in the property chain
1290    */
1291   newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1292 
1293   /*
1294    * Save the new property into the new property spot
1295    */
1296   StorageImpl_WriteProperty(
1297     This->base.ancestorStorage,
1298     newPropertyIndex,
1299     &newProperty);
1300 
1301   /*
1302    * Find a spot in the property chain for our newly created property.
1303    */
1304   updatePropertyChain(
1305     This,
1306     newPropertyIndex,
1307     newProperty);
1308 
1309   /*
1310    * Open it to get a pointer to return.
1311    */
1312   hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1313 
1314   if( (hr != S_OK) || (*ppstg == NULL))
1315   {
1316     return hr;
1317   }
1318 
1319 
1320   return S_OK;
1321 }
1322 
1323 
1324 /***************************************************************************
1325  *
1326  * Internal Method
1327  *
1328  * Get a free property or create a new one.
1329  */
1330 static ULONG getFreeProperty(
1331   StorageImpl *storage)
1332 {
1333   ULONG       currentPropertyIndex = 0;
1334   ULONG       newPropertyIndex     = PROPERTY_NULL;
1335   BOOL      readSuccessful        = TRUE;
1336   StgProperty currentProperty;
1337 
1338   do
1339   {
1340     /*
1341      * Start by reading the root property
1342      */
1343     readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1344                                                currentPropertyIndex,
1345                                                &currentProperty);
1346     if (readSuccessful)
1347     {
1348       if (currentProperty.sizeOfNameString == 0)
1349       {
1350         /*
1351          * The property existis and is available, we found it.
1352          */
1353         newPropertyIndex = currentPropertyIndex;
1354       }
1355     }
1356     else
1357     {
1358       /*
1359        * We exhausted the property list, we will create more space below
1360        */
1361       newPropertyIndex = currentPropertyIndex;
1362     }
1363     currentPropertyIndex++;
1364 
1365   } while (newPropertyIndex == PROPERTY_NULL);
1366 
1367   /*
1368    * grow the property chain
1369    */
1370   if (! readSuccessful)
1371   {
1372     StgProperty    emptyProperty;
1373     ULARGE_INTEGER newSize;
1374     ULONG          propertyIndex;
1375     ULONG          lastProperty  = 0;
1376     ULONG          blockCount    = 0;
1377 
1378     /*
1379      * obtain the new count of property blocks
1380      */
1381     blockCount = BlockChainStream_GetCount(
1382                    storage->base.ancestorStorage->rootBlockChain)+1;
1383 
1384     /*
1385      * initialize the size used by the property stream
1386      */
1387     newSize.u.HighPart = 0;
1388     newSize.u.LowPart  = storage->bigBlockSize * blockCount;
1389 
1390     /*
1391      * add a property block to the property chain
1392      */
1393     BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1394 
1395     /*
1396      * memset the empty property in order to initialize the unused newly
1397      * created property
1398      */
1399     memset(&emptyProperty, 0, sizeof(StgProperty));
1400 
1401     /*
1402      * initialize them
1403      */
1404     lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1405 
1406     for(
1407       propertyIndex = newPropertyIndex;
1408       propertyIndex < lastProperty;
1409       propertyIndex++)
1410     {
1411       StorageImpl_WriteProperty(
1412         storage->base.ancestorStorage,
1413         propertyIndex,
1414         &emptyProperty);
1415     }
1416   }
1417 
1418   return newPropertyIndex;
1419 }
1420 
1421 /****************************************************************************
1422  *
1423  * Internal Method
1424  *
1425  * Case insensitive comparison of StgProperty.name by first considering
1426  * their size.
1427  *
1428  * Returns <0 when newProperty < currentProperty
1429  *         >0 when newProperty > currentProperty
1430  *          0 when newProperty == currentProperty
1431  */
1432 static LONG propertyNameCmp(
1433     const OLECHAR *newProperty,
1434     const OLECHAR *currentProperty)
1435 {
1436   LONG diff      = lstrlenW(newProperty) - lstrlenW(currentProperty);
1437 
1438   if (diff == 0)
1439   {
1440     /*
1441      * We compare the string themselves only when they are of the same length
1442      */
1443     diff = lstrcmpiW( newProperty, currentProperty);
1444   }
1445 
1446   return diff;
1447 }
1448 
1449 /****************************************************************************
1450  *
1451  * Internal Method
1452  *
1453  * Properly link this new element in the property chain.
1454  */
1455 static void updatePropertyChain(
1456   StorageImpl *storage,
1457   ULONG         newPropertyIndex,
1458   StgProperty   newProperty)
1459 {
1460   StgProperty currentProperty;
1461 
1462   /*
1463    * Read the root property
1464    */
1465   StorageImpl_ReadProperty(storage->base.ancestorStorage,
1466                              storage->base.rootPropertySetIndex,
1467                              &currentProperty);
1468 
1469   if (currentProperty.dirProperty != PROPERTY_NULL)
1470   {
1471     /*
1472      * The root storage contains some element, therefore, start the research
1473      * for the appropriate location.
1474      */
1475     BOOL found = 0;
1476     ULONG  current, next, previous, currentPropertyId;
1477 
1478     /*
1479      * Keep the StgProperty sequence number of the storage first property
1480      */
1481     currentPropertyId = currentProperty.dirProperty;
1482 
1483     /*
1484      * Read
1485      */
1486     StorageImpl_ReadProperty(storage->base.ancestorStorage,
1487                                currentProperty.dirProperty,
1488                                &currentProperty);
1489 
1490     previous = currentProperty.previousProperty;
1491     next     = currentProperty.nextProperty;
1492     current  = currentPropertyId;
1493 
1494     while (found == 0)
1495     {
1496       LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1497 
1498       if (diff < 0)
1499       {
1500         if (previous != PROPERTY_NULL)
1501         {
1502           StorageImpl_ReadProperty(storage->base.ancestorStorage,
1503                                      previous,
1504                                      &currentProperty);
1505           current = previous;
1506         }
1507         else
1508         {
1509           currentProperty.previousProperty = newPropertyIndex;
1510           StorageImpl_WriteProperty(storage->base.ancestorStorage,
1511                                       current,
1512                                       &currentProperty);
1513           found = 1;
1514         }
1515       }
1516       else if (diff > 0)
1517       {
1518         if (next != PROPERTY_NULL)
1519         {
1520           StorageImpl_ReadProperty(storage->base.ancestorStorage,
1521                                      next,
1522                                      &currentProperty);
1523           current = next;
1524         }
1525         else
1526         {
1527           currentProperty.nextProperty = newPropertyIndex;
1528           StorageImpl_WriteProperty(storage->base.ancestorStorage,
1529                                       current,
1530                                       &currentProperty);
1531           found = 1;
1532         }
1533       }
1534       else
1535       {
1536         /*
1537          * Trying to insert an item with the same name in the
1538          * subtree structure.
1539          */
1540         assert(FALSE);
1541       }
1542 
1543       previous = currentProperty.previousProperty;
1544       next     = currentProperty.nextProperty;
1545     }
1546   }
1547   else
1548   {
1549     /*
1550      * The root storage is empty, link the new property to its dir property
1551      */
1552     currentProperty.dirProperty = newPropertyIndex;
1553     StorageImpl_WriteProperty(storage->base.ancestorStorage,
1554                                 storage->base.rootPropertySetIndex,
1555                                 &currentProperty);
1556   }
1557 }
1558 
1559 
1560 /*************************************************************************
1561  * CopyTo (IStorage)
1562  */
1563 static HRESULT WINAPI StorageImpl_CopyTo(
1564   IStorage*   iface,
1565   DWORD       ciidExclude,  /* [in] */
1566   const IID*  rgiidExclude, /* [size_is][unique][in] */
1567   SNB         snbExclude,   /* [unique][in] */
1568   IStorage*   pstgDest)     /* [unique][in] */
1569 {
1570   IEnumSTATSTG *elements     = 0;
1571   STATSTG      curElement, strStat;
1572   HRESULT      hr;
1573   IStorage     *pstgTmp, *pstgChild;
1574   IStream      *pstrTmp, *pstrChild;
1575 
1576   if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1577     FIXME("Exclude option not implemented\n");
1578 
1579   TRACE("(%p, %d, %p, %p, %p)\n",
1580         iface, ciidExclude, rgiidExclude,
1581         snbExclude, pstgDest);
1582 
1583   /*
1584    * Perform a sanity check
1585    */
1586   if ( pstgDest == 0 )
1587     return STG_E_INVALIDPOINTER;
1588 
1589   /*
1590    * Enumerate the elements
1591    */
1592   hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1593 
1594   if ( hr != S_OK )
1595     return hr;
1596 
1597   /*
1598    * set the class ID
1599    */
1600   IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1601   IStorage_SetClass( pstgDest, &curElement.clsid );
1602 
1603   do
1604   {
1605     /*
1606      * Obtain the next element
1607      */
1608     hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1609 
1610     if ( hr == S_FALSE )
1611     {
1612       hr = S_OK;   /* done, every element has been copied */
1613       break;
1614     }
1615 
1616     if (curElement.type == STGTY_STORAGE)
1617     {
1618       /*
1619        * open child source storage
1620        */
1621       hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1622                                  STGM_READ|STGM_SHARE_EXCLUSIVE,
1623                                  NULL, 0, &pstgChild );
1624 
1625       if (hr != S_OK)
1626         break;
1627 
1628       /*
1629        * Check if destination storage is not a child of the source
1630        * storage, which will cause an infinite loop
1631        */
1632       if (pstgChild == pstgDest)
1633       {
1634         IEnumSTATSTG_Release(elements);
1635 
1636         return STG_E_ACCESSDENIED;
1637       }
1638 
1639       /*
1640        * create a new storage in destination storage
1641        */
1642       hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1643                                    STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1644                                    0, 0,
1645                                    &pstgTmp );
1646       /*
1647        * if it already exist, don't create a new one use this one
1648        */
1649       if (hr == STG_E_FILEALREADYEXISTS)
1650       {
1651         hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1652                                    STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1653                                    NULL, 0, &pstgTmp );
1654       }
1655 
1656       if (hr != S_OK)
1657         break;
1658 
1659 
1660       /*
1661        * do the copy recursively
1662        */
1663       hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1664                                snbExclude, pstgTmp );
1665 
1666       IStorage_Release( pstgTmp );
1667       IStorage_Release( pstgChild );
1668     }
1669     else if (curElement.type == STGTY_STREAM)
1670     {
1671       /*
1672        * create a new stream in destination storage. If the stream already
1673        * exist, it will be deleted and a new one will be created.
1674        */
1675       hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1676                                   STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1677                                   0, 0, &pstrTmp );
1678 
1679       if (hr != S_OK)
1680         break;
1681 
1682       /*
1683        * open child stream storage
1684        */
1685       hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1686                                 STGM_READ|STGM_SHARE_EXCLUSIVE,
1687                                 0, &pstrChild );
1688 
1689       if (hr != S_OK)
1690         break;
1691 
1692       /*
1693        * Get the size of the source stream
1694        */
1695       IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1696 
1697       /*
1698        * Set the size of the destination stream.
1699        */
1700       IStream_SetSize(pstrTmp, strStat.cbSize);
1701 
1702       /*
1703        * do the copy
1704        */
1705       hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1706                            NULL, NULL );
1707 
1708       IStream_Release( pstrTmp );
1709       IStream_Release( pstrChild );
1710     }
1711     else
1712     {
1713       WARN("unknown element type: %d\n", curElement.type);
1714     }
1715 
1716   } while (hr == S_OK);
1717 
1718   /*
1719    * Clean-up
1720    */
1721   IEnumSTATSTG_Release(elements);
1722 
1723   return hr;
1724 }
1725 
1726 /*************************************************************************
1727  * MoveElementTo (IStorage)
1728  */
1729 static HRESULT WINAPI StorageImpl_MoveElementTo(
1730   IStorage*     iface,
1731   const OLECHAR *pwcsName,   /* [string][in] */
1732   IStorage      *pstgDest,   /* [unique][in] */
1733   const OLECHAR *pwcsNewName,/* [string][in] */
1734   DWORD           grfFlags)    /* [in] */
1735 {
1736   FIXME("(%p %s %p %s %u): stub\n", iface,
1737          debugstr_w(pwcsName), pstgDest,
1738          debugstr_w(pwcsNewName), grfFlags);
1739   return E_NOTIMPL;
1740 }
1741 
1742 /*************************************************************************
1743  * Commit (IStorage)
1744  *
1745  * Ensures that any changes made to a storage object open in transacted mode
1746  * are reflected in the parent storage
1747  *
1748  * NOTES
1749  *  Wine doesn't implement transacted mode, which seems to be a basic
1750  *  optimization, so we can ignore this stub for now.
1751  */
1752 static HRESULT WINAPI StorageImpl_Commit(
1753   IStorage*   iface,
1754   DWORD         grfCommitFlags)/* [in] */
1755 {
1756   FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1757   return S_OK;
1758 }
1759 
1760 /*************************************************************************
1761  * Revert (IStorage)
1762  *
1763  * Discard all changes that have been made since the last commit operation
1764  */
1765 static HRESULT WINAPI StorageImpl_Revert(
1766   IStorage* iface)
1767 {
1768   FIXME("(%p): stub\n", iface);
1769   return E_NOTIMPL;
1770 }
1771 
1772 /*************************************************************************
1773  * DestroyElement (IStorage)
1774  *
1775  * Strategy: This implementation is built this way for simplicity not for speed.
1776  *          I always delete the topmost element of the enumeration and adjust
1777  *          the deleted element pointer all the time.  This takes longer to
1778  *          do but allow to reinvoke DestroyElement whenever we encounter a
1779  *          storage object.  The optimisation resides in the usage of another
1780  *          enumeration strategy that would give all the leaves of a storage
1781  *          first. (postfix order)
1782  */
1783 static HRESULT WINAPI StorageImpl_DestroyElement(
1784   IStorage*     iface,
1785   const OLECHAR *pwcsName)/* [string][in] */
1786 {
1787   StorageImpl* const This=(StorageImpl*)iface;
1788 
1789   IEnumSTATSTGImpl* propertyEnumeration;
1790   HRESULT           hr = S_OK;
1791   BOOL            res;
1792   StgProperty       propertyToDelete;
1793   StgProperty       parentProperty;
1794   ULONG             foundPropertyIndexToDelete;
1795   ULONG             typeOfRelation;
1796   ULONG             parentPropertyId = 0;
1797 
1798   TRACE("(%p, %s)\n",
1799         iface, debugstr_w(pwcsName));
1800 
1801   /*
1802    * Perform a sanity check on the parameters.
1803    */
1804   if (pwcsName==NULL)
1805     return STG_E_INVALIDPOINTER;
1806 
1807   if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
1808     return STG_E_ACCESSDENIED;
1809 
1810   /*
1811    * Create a property enumeration to search the property with the given name
1812    */
1813   propertyEnumeration = IEnumSTATSTGImpl_Construct(
1814     This->base.ancestorStorage,
1815     This->base.rootPropertySetIndex);
1816 
1817   foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1818     propertyEnumeration,
1819     pwcsName,
1820     &propertyToDelete);
1821 
1822   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1823 
1824   if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1825   {
1826     return STG_E_FILENOTFOUND;
1827   }
1828 
1829   /*
1830    * Find the parent property of the property to delete (the one that
1831    * link to it).  If This->dirProperty == foundPropertyIndexToDelete,
1832    * the parent is This. Otherwise, the parent is one of its sibling...
1833    */
1834 
1835   /*
1836    * First, read This's StgProperty..
1837    */
1838   res = StorageImpl_ReadProperty(
1839           This->base.ancestorStorage,
1840           This->base.rootPropertySetIndex,
1841           &parentProperty);
1842 
1843   assert(res);
1844 
1845   /*
1846    * Second, check to see if by any chance the actual storage (This) is not
1847    * the parent of the property to delete... We never know...
1848    */
1849   if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1850   {
1851     /*
1852      * Set data as it would have been done in the else part...
1853      */
1854     typeOfRelation   = PROPERTY_RELATION_DIR;
1855     parentPropertyId = This->base.rootPropertySetIndex;
1856   }
1857   else
1858   {
1859     /*
1860      * Create a property enumeration to search the parent properties, and
1861      * delete it once done.
1862      */
1863     IEnumSTATSTGImpl* propertyEnumeration2;
1864 
1865     propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1866       This->base.ancestorStorage,
1867       This->base.rootPropertySetIndex);
1868 
1869     typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1870       propertyEnumeration2,
1871       foundPropertyIndexToDelete,
1872       &parentProperty,
1873       &parentPropertyId);
1874 
1875     IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1876   }
1877 
1878   if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1879   {
1880     hr = deleteStorageProperty(
1881            This,
1882            foundPropertyIndexToDelete,
1883            propertyToDelete);
1884   }
1885   else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1886   {
1887     hr = deleteStreamProperty(
1888            This,
1889            foundPropertyIndexToDelete,
1890            propertyToDelete);
1891   }
1892 
1893   if (hr!=S_OK)
1894     return hr;
1895 
1896   /*
1897    * Adjust the property chain
1898    */
1899   hr = adjustPropertyChain(
1900         This,
1901         propertyToDelete,
1902         parentProperty,
1903         parentPropertyId,
1904         typeOfRelation);
1905 
1906   return hr;
1907 }
1908 
1909 
1910 /************************************************************************
1911  * StorageImpl_Stat (IStorage)
1912  *
1913  * This method will retrieve information about this storage object.
1914  *
1915  * See Windows documentation for more details on IStorage methods.
1916  */
1917 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1918                                  STATSTG*  pstatstg,     /* [out] */
1919                                  DWORD     grfStatFlag)  /* [in] */
1920 {
1921   StorageImpl* const This = (StorageImpl*)iface;
1922   HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1923 
1924   if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1925   {
1926       CoTaskMemFree(pstatstg->pwcsName);
1927       pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1928       strcpyW(pstatstg->pwcsName, This->pwcsName);
1929   }
1930 
1931   return result;
1932 }
1933 
1934 /******************************************************************************
1935  * Internal stream list handlers
1936  */
1937 
1938 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1939 {
1940   TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1941   list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1942 }
1943 
1944 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1945 {
1946   TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1947   list_remove(&(strm->StrmListEntry));
1948 }
1949 
1950 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1951 {
1952   struct list *cur, *cur2;
1953   StgStreamImpl *strm=NULL;
1954 
1955   LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1956     strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1957     TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1958     strm->parentStorage = NULL;
1959     list_remove(cur);
1960   }
1961 }
1962 
1963 
1964 /*********************************************************************
1965  *
1966  * Internal Method
1967  *
1968  * Perform the deletion of a complete storage node
1969  *
1970  */
1971 static HRESULT deleteStorageProperty(
1972   StorageImpl *parentStorage,
1973   ULONG        indexOfPropertyToDelete,
1974   StgProperty  propertyToDelete)
1975 {
1976   IEnumSTATSTG *elements     = 0;
1977   IStorage   *childStorage = 0;
1978   STATSTG      currentElement;
1979   HRESULT      hr;
1980   HRESULT      destroyHr = S_OK;
1981 
1982   /*
1983    * Open the storage and enumerate it
1984    */
1985   hr = StorageBaseImpl_OpenStorage(
1986         (IStorage*)parentStorage,
1987         propertyToDelete.name,
1988         0,
1989         STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1990         0,
1991         0,
1992         &childStorage);
1993 
1994   if (hr != S_OK)
1995   {
1996     return hr;
1997   }
1998 
1999   /*
2000    * Enumerate the elements
2001    */
2002   IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2003 
2004   do
2005   {
2006     /*
2007      * Obtain the next element
2008      */
2009     hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2010     if (hr==S_OK)
2011     {
2012       destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
2013 
2014       CoTaskMemFree(currentElement.pwcsName);
2015     }
2016 
2017     /*
2018      * We need to Reset the enumeration every time because we delete elements
2019      * and the enumeration could be invalid
2020      */
2021     IEnumSTATSTG_Reset(elements);
2022 
2023   } while ((hr == S_OK) && (destroyHr == S_OK));
2024 
2025   /*
2026    * Invalidate the property by zeroing its name member.
2027    */
2028   propertyToDelete.sizeOfNameString = 0;
2029 
2030   StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2031                             indexOfPropertyToDelete,
2032                             &propertyToDelete);
2033 
2034   IStorage_Release(childStorage);
2035   IEnumSTATSTG_Release(elements);
2036 
2037   return destroyHr;
2038 }
2039 
2040 /*********************************************************************
2041  *
2042  * Internal Method
2043  *
2044  * Perform the deletion of a stream node
2045  *
2046  */
2047 static HRESULT deleteStreamProperty(
2048   StorageImpl *parentStorage,
2049   ULONG         indexOfPropertyToDelete,
2050   StgProperty   propertyToDelete)
2051 {
2052   IStream      *pis;
2053   HRESULT        hr;
2054   ULARGE_INTEGER size;
2055 
2056   size.u.HighPart = 0;
2057   size.u.LowPart = 0;
2058 
2059   hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2060         propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2061 
2062   if (hr!=S_OK)
2063   {
2064     return(hr);
2065   }
2066 
2067   /*
2068    * Zap the stream
2069    */
2070   hr = IStream_SetSize(pis, size);
2071 
2072   if(hr != S_OK)
2073   {
2074     return hr;
2075   }
2076 
2077   /*
2078    * Release the stream object.
2079    */
2080   IStream_Release(pis);
2081 
2082   /*
2083    * Invalidate the property by zeroing its name member.
2084    */
2085   propertyToDelete.sizeOfNameString = 0;
2086 
2087   /*
2088    * Here we should re-read the property so we get the updated pointer
2089    * but since we are here to zap it, I don't do it...
2090    */
2091   StorageImpl_WriteProperty(
2092     parentStorage->base.ancestorStorage,
2093     indexOfPropertyToDelete,
2094     &propertyToDelete);
2095 
2096   return S_OK;
2097 }
2098 
2099 /*********************************************************************
2100  *
2101  * Internal Method
2102  *
2103  * Finds a placeholder for the StgProperty within the Storage
2104  *
2105  */
2106 static HRESULT findPlaceholder(
2107   StorageImpl *storage,
2108   ULONG         propertyIndexToStore,
2109   ULONG         storePropertyIndex,
2110   INT         typeOfRelation)
2111 {
2112   StgProperty storeProperty;
2113   BOOL      res = TRUE;
2114 
2115   /*
2116    * Read the storage property
2117    */
2118   res = StorageImpl_ReadProperty(
2119           storage->base.ancestorStorage,
2120           storePropertyIndex,
2121           &storeProperty);
2122 
2123   if(! res)
2124   {
2125     return E_FAIL;
2126   }
2127 
2128   if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2129   {
2130     if (storeProperty.previousProperty != PROPERTY_NULL)
2131     {
2132       return findPlaceholder(
2133                storage,
2134                propertyIndexToStore,
2135                storeProperty.previousProperty,
2136                typeOfRelation);
2137     }
2138     else
2139     {
2140       storeProperty.previousProperty = propertyIndexToStore;
2141     }
2142   }
2143   else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2144   {
2145     if (storeProperty.nextProperty != PROPERTY_NULL)
2146     {
2147       return findPlaceholder(
2148                storage,
2149                propertyIndexToStore,
2150                storeProperty.nextProperty,
2151                typeOfRelation);
2152     }
2153     else
2154     {
2155       storeProperty.nextProperty = propertyIndexToStore;
2156     }
2157   }
2158   else if (typeOfRelation == PROPERTY_RELATION_DIR)
2159   {
2160     if (storeProperty.dirProperty != PROPERTY_NULL)
2161     {
2162       return findPlaceholder(
2163                storage,
2164                propertyIndexToStore,
2165                storeProperty.dirProperty,
2166                typeOfRelation);
2167     }
2168     else
2169     {
2170       storeProperty.dirProperty = propertyIndexToStore;
2171     }
2172   }
2173 
2174   res = StorageImpl_WriteProperty(
2175          storage->base.ancestorStorage,
2176          storePropertyIndex,
2177          &storeProperty);
2178 
2179   if(!res)
2180   {
2181     return E_FAIL;
2182   }
2183 
2184   return S_OK;
2185 }
2186 
2187 /*************************************************************************
2188  *
2189  * Internal Method
2190  *
2191  * This method takes the previous and the next property link of a property
2192  * to be deleted and find them a place in the Storage.
2193  */
2194 static HRESULT adjustPropertyChain(
2195   StorageImpl *This,
2196   StgProperty   propertyToDelete,
2197   StgProperty   parentProperty,
2198   ULONG         parentPropertyId,
2199   INT         typeOfRelation)
2200 {
2201   ULONG   newLinkProperty        = PROPERTY_NULL;
2202   BOOL  needToFindAPlaceholder = FALSE;
2203   ULONG   storeNode              = PROPERTY_NULL;
2204   ULONG   toStoreNode            = PROPERTY_NULL;
2205   INT   relationType           = 0;
2206   HRESULT hr                     = S_OK;
2207   BOOL  res                    = TRUE;
2208 
2209   if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2210   {
2211     if (propertyToDelete.previousProperty != PROPERTY_NULL)
2212     {
2213       /*
2214        * Set the parent previous to the property to delete previous
2215        */
2216       newLinkProperty = propertyToDelete.previousProperty;
2217 
2218       if (propertyToDelete.nextProperty != PROPERTY_NULL)
2219       {
2220         /*
2221          * We also need to find a storage for the other link, setup variables
2222          * to do this at the end...
2223          */
2224         needToFindAPlaceholder = TRUE;
2225         storeNode              = propertyToDelete.previousProperty;
2226         toStoreNode            = propertyToDelete.nextProperty;
2227         relationType           = PROPERTY_RELATION_NEXT;
2228       }
2229     }
2230     else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2231     {
2232       /*
2233        * Set the parent previous to the property to delete next
2234        */
2235       newLinkProperty = propertyToDelete.nextProperty;
2236     }
2237 
2238     /*
2239      * Link it for real...
2240      */
2241     parentProperty.previousProperty = newLinkProperty;
2242 
2243   }
2244   else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2245   {
2246     if (propertyToDelete.previousProperty != PROPERTY_NULL)
2247     {
2248       /*
2249        * Set the parent next to the property to delete next previous
2250        */
2251       newLinkProperty = propertyToDelete.previousProperty;
2252 
2253       if (propertyToDelete.nextProperty != PROPERTY_NULL)
2254       {
2255         /*
2256          * We also need to find a storage for the other link, setup variables
2257          * to do this at the end...
2258          */
2259         needToFindAPlaceholder = TRUE;
2260         storeNode              = propertyToDelete.previousProperty;
2261         toStoreNode            = propertyToDelete.nextProperty;
2262         relationType           = PROPERTY_RELATION_NEXT;
2263       }
2264     }
2265     else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2266     {
2267       /*
2268        * Set the parent next to the property to delete next
2269        */
2270       newLinkProperty = propertyToDelete.nextProperty;
2271     }
2272 
2273     /*
2274      * Link it for real...
2275      */
2276     parentProperty.nextProperty = newLinkProperty;
2277   }
2278   else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2279   {
2280     if (propertyToDelete.previousProperty != PROPERTY_NULL)
2281     {
2282       /*
2283        * Set the parent dir to the property to delete previous
2284        */
2285       newLinkProperty = propertyToDelete.previousProperty;
2286 
2287       if (propertyToDelete.nextProperty != PROPERTY_NULL)
2288       {
2289         /*
2290          * We also need to find a storage for the other link, setup variables
2291          * to do this at the end...
2292          */
2293         needToFindAPlaceholder = TRUE;
2294         storeNode              = propertyToDelete.previousProperty;
2295         toStoreNode            = propertyToDelete.nextProperty;
2296         relationType           = PROPERTY_RELATION_NEXT;
2297       }
2298     }
2299     else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2300     {
2301       /*
2302        * Set the parent dir to the property to delete next
2303        */
2304       newLinkProperty = propertyToDelete.nextProperty;
2305     }
2306 
2307     /*
2308      * Link it for real...
2309      */
2310     parentProperty.dirProperty = newLinkProperty;
2311   }
2312 
2313   /*
2314    * Write back the parent property
2315    */
2316   res = StorageImpl_WriteProperty(
2317           This->base.ancestorStorage,
2318           parentPropertyId,
2319           &parentProperty);
2320   if(! res)
2321   {
2322     return E_FAIL;
2323   }
2324 
2325   /*
2326    * If a placeholder is required for the other link, then, find one and
2327    * get out of here...
2328    */
2329   if (needToFindAPlaceholder)
2330   {
2331     hr = findPlaceholder(
2332            This,
2333            toStoreNode,
2334            storeNode,
2335            relationType);
2336   }
2337 
2338   return hr;
2339 }
2340 
2341 
2342 /******************************************************************************
2343  * SetElementTimes (IStorage)
2344  */
2345 static HRESULT WINAPI StorageImpl_SetElementTimes(
2346   IStorage*     iface,
2347   const OLECHAR *pwcsName,/* [string][in] */
2348   const FILETIME  *pctime,  /* [in] */
2349   const FILETIME  *patime,  /* [in] */
2350   const FILETIME  *pmtime)  /* [in] */
2351 {
2352   FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2353   return S_OK;
2354 }
2355 
2356 /******************************************************************************
2357  * SetStateBits (IStorage)
2358  */
2359 static HRESULT WINAPI StorageImpl_SetStateBits(
2360   IStorage*   iface,
2361   DWORD         grfStateBits,/* [in] */
2362   DWORD         grfMask)     /* [in] */
2363 {
2364   StorageImpl* const This = (StorageImpl*)iface;
2365   This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2366   return S_OK;
2367 }
2368 
2369 /*
2370  * Virtual function table for the IStorage32Impl class.
2371  */
2372 static const IStorageVtbl Storage32Impl_Vtbl =
2373 {
2374     StorageBaseImpl_QueryInterface,
2375     StorageBaseImpl_AddRef,
2376     StorageBaseImpl_Release,
2377     StorageBaseImpl_CreateStream,
2378     StorageBaseImpl_OpenStream,
2379     StorageImpl_CreateStorage,
2380     StorageBaseImpl_OpenStorage,
2381     StorageImpl_CopyTo,
2382     StorageImpl_MoveElementTo,
2383     StorageImpl_Commit,
2384     StorageImpl_Revert,
2385     StorageBaseImpl_EnumElements,
2386     StorageImpl_DestroyElement,
2387     StorageBaseImpl_RenameElement,
2388     StorageImpl_SetElementTimes,
2389     StorageBaseImpl_SetClass,
2390     StorageImpl_SetStateBits,
2391     StorageImpl_Stat
2392 };
2393 
2394 static HRESULT StorageImpl_Construct(
2395   StorageImpl* This,
2396   HANDLE       hFile,
2397   LPCOLESTR    pwcsName,
2398   ILockBytes*  pLkbyt,
2399   DWORD        openFlags,
2400   BOOL         fileBased,
2401   BOOL         create)
2402 {
2403   HRESULT     hr = S_OK;
2404   StgProperty currentProperty;
2405   BOOL      readSuccessful;
2406   ULONG       currentPropertyIndex;
2407 
2408   if ( FAILED( validateSTGM(openFlags) ))
2409     return STG_E_INVALIDFLAG;
2410 
2411   memset(This, 0, sizeof(StorageImpl));
2412 
2413   list_init(&This->base.strmHead);
2414 
2415   This->base.lpVtbl = &Storage32Impl_Vtbl;
2416   This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2417   This->base.v_destructor = StorageImpl_Destroy;
2418   This->base.openFlags = (openFlags & ~STGM_CREATE);
2419   This->create = create;
2420 
2421   /*
2422    * This is the top-level storage so initialize the ancestor pointer
2423    * to this.
2424    */
2425   This->base.ancestorStorage = This;
2426 
2427   This->hFile = hFile;
2428 
2429   if(pwcsName) {
2430       This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2431                                 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2432       if (!This->pwcsName)
2433          return STG_E_INSUFFICIENTMEMORY;
2434       strcpyW(This->pwcsName, pwcsName);
2435   }
2436 
2437   /*
2438    * Initialize the big block cache.
2439    */
2440   This->bigBlockSize   = DEF_BIG_BLOCK_SIZE;
2441   This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2442   This->bigBlockFile   = BIGBLOCKFILE_Construct(hFile,
2443                                                 pLkbyt,
2444                                                 openFlags,
2445                                                 This->bigBlockSize,
2446                                                 fileBased);
2447 
2448   if (This->bigBlockFile == 0)
2449     return E_FAIL;
2450 
2451   if (create)
2452   {
2453     ULARGE_INTEGER size;
2454     BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2455 
2456     /*
2457      * Initialize all header variables:
2458      * - The big block depot consists of one block and it is at block 0
2459      * - The properties start at block 1
2460      * - There is no small block depot
2461      */
2462     memset( This->bigBlockDepotStart,
2463             BLOCK_UNUSED,
2464             sizeof(This->bigBlockDepotStart));
2465 
2466     This->bigBlockDepotCount    = 1;
2467     This->bigBlockDepotStart[0] = 0;
2468     This->rootStartBlock        = 1;
2469     This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
2470     This->bigBlockSizeBits      = DEF_BIG_BLOCK_SIZE_BITS;
2471     This->smallBlockSizeBits    = DEF_SMALL_BLOCK_SIZE_BITS;
2472     This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2473     This->extBigBlockDepotCount = 0;
2474 
2475     StorageImpl_SaveFileHeader(This);
2476 
2477     /*
2478      * Add one block for the big block depot and one block for the properties
2479      */
2480     size.u.HighPart = 0;
2481     size.u.LowPart  = This->bigBlockSize * 3;
2482     BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2483 
2484     /*
2485      * Initialize the big block depot
2486      */
2487     memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2488     StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2489     StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2490     StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2491   }
2492   else
2493   {
2494     /*
2495      * Load the header for the file.
2496      */
2497     hr = StorageImpl_LoadFileHeader(This);
2498 
2499     if (FAILED(hr))
2500     {
2501       BIGBLOCKFILE_Destructor(This->bigBlockFile);
2502 
2503       return hr;
2504     }
2505   }
2506 
2507   /*
2508    * There is no block depot cached yet.
2509    */
2510   This->indexBlockDepotCached = 0xFFFFFFFF;
2511 
2512   /*
2513    * Start searching for free blocks with block 0.
2514    */
2515   This->prevFreeBlock = 0;
2516 
2517   /*
2518    * Create the block chain abstractions.
2519    */
2520   if(!(This->rootBlockChain =
2521        BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2522     return STG_E_READFAULT;
2523 
2524   if(!(This->smallBlockDepotChain =
2525        BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2526                                   PROPERTY_NULL)))
2527     return STG_E_READFAULT;
2528 
2529   /*
2530    * Write the root property (memory only)
2531    */
2532   if (create)
2533   {
2534     StgProperty rootProp;
2535     /*
2536      * Initialize the property chain
2537      */
2538     memset(&rootProp, 0, sizeof(rootProp));
2539     MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2540                          sizeof(rootProp.name)/sizeof(WCHAR) );
2541     rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2542     rootProp.propertyType     = PROPTYPE_ROOT;
2543     rootProp.previousProperty = PROPERTY_NULL;
2544     rootProp.nextProperty     = PROPERTY_NULL;
2545     rootProp.dirProperty      = PROPERTY_NULL;
2546     rootProp.startingBlock    = BLOCK_END_OF_CHAIN;
2547     rootProp.size.u.HighPart    = 0;
2548     rootProp.size.u.LowPart     = 0;
2549 
2550     StorageImpl_WriteProperty(This, 0, &rootProp);
2551   }
2552 
2553   /*
2554    * Find the ID of the root in the property sets.
2555    */
2556   currentPropertyIndex = 0;
2557 
2558   do
2559   {
2560     readSuccessful = StorageImpl_ReadProperty(
2561                       This,
2562                       currentPropertyIndex,
2563                       &currentProperty);
2564 
2565     if (readSuccessful)
2566     {
2567       if ( (currentProperty.sizeOfNameString != 0 ) &&
2568            (currentProperty.propertyType     == PROPTYPE_ROOT) )
2569       {
2570         This->base.rootPropertySetIndex = currentPropertyIndex;
2571       }
2572     }
2573 
2574     currentPropertyIndex++;
2575 
2576   } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2577 
2578   if (!readSuccessful)
2579   {
2580     /* TODO CLEANUP */
2581     return STG_E_READFAULT;
2582   }
2583 
2584   /*
2585    * Create the block chain abstraction for the small block root chain.
2586    */
2587   if(!(This->smallBlockRootChain =
2588        BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2589     return STG_E_READFAULT;
2590 
2591   return hr;
2592 }
2593 
2594 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2595 {
2596   StorageImpl *This = (StorageImpl*) iface;
2597   TRACE("(%p)\n", This);
2598 
2599   StorageBaseImpl_DeleteAll(&This->base);
2600 
2601   HeapFree(GetProcessHeap(), 0, This->pwcsName);
2602 
2603   BlockChainStream_Destroy(This->smallBlockRootChain);
2604   BlockChainStream_Destroy(This->rootBlockChain);
2605   BlockChainStream_Destroy(This->smallBlockDepotChain);
2606 
2607   BIGBLOCKFILE_Destructor(This->bigBlockFile);
2608   HeapFree(GetProcessHeap(), 0, This);
2609 }
2610 
2611 /******************************************************************************
2612  *      Storage32Impl_GetNextFreeBigBlock
2613  *
2614  * Returns the index of the next free big block.
2615  * If the big block depot is filled, this method will enlarge it.
2616  *
2617  */
2618 static ULONG StorageImpl_GetNextFreeBigBlock(
2619   StorageImpl* This)
2620 {
2621   ULONG depotBlockIndexPos;
2622   BYTE depotBuffer[BIG_BLOCK_SIZE];
2623   BOOL success;
2624   ULONG depotBlockOffset;
2625   ULONG blocksPerDepot    = This->bigBlockSize / sizeof(ULONG);
2626   ULONG nextBlockIndex    = BLOCK_SPECIAL;
2627   int   depotIndex        = 0;
2628   ULONG freeBlock         = BLOCK_UNUSED;
2629 
2630   depotIndex = This->prevFreeBlock / blocksPerDepot;
2631   depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2632 
2633   /*
2634    * Scan the entire big block depot until we find a block marked free
2635    */
2636   while (nextBlockIndex != BLOCK_UNUSED)
2637   {
2638     if (depotIndex < COUNT_BBDEPOTINHEADER)
2639     {
2640       depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2641 
2642       /*
2643        * Grow the primary depot.
2644        */
2645       if (depotBlockIndexPos == BLOCK_UNUSED)
2646       {
2647         depotBlockIndexPos = depotIndex*blocksPerDepot;
2648 
2649         /*
2650          * Add a block depot.
2651          */
2652         Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2653         This->bigBlockDepotCount++;
2654         This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2655 
2656         /*
2657          * Flag it as a block depot.
2658          */
2659         StorageImpl_SetNextBlockInChain(This,
2660                                           depotBlockIndexPos,
2661                                           BLOCK_SPECIAL);
2662 
2663         /* Save new header information.
2664          */
2665         StorageImpl_SaveFileHeader(This);
2666       }
2667     }
2668     else
2669     {
2670       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2671 
2672       if (depotBlockIndexPos == BLOCK_UNUSED)
2673       {
2674         /*
2675          * Grow the extended depot.
2676          */
2677         ULONG extIndex       = BLOCK_UNUSED;
2678         ULONG numExtBlocks   = depotIndex - COUNT_BBDEPOTINHEADER;
2679         ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2680 
2681         if (extBlockOffset == 0)
2682         {
2683           /* We need an extended block.
2684            */
2685           extIndex = Storage32Impl_AddExtBlockDepot(This);
2686           This->extBigBlockDepotCount++;
2687           depotBlockIndexPos = extIndex + 1;
2688         }
2689         else
2690           depotBlockIndexPos = depotIndex * blocksPerDepot;
2691 
2692         /*
2693          * Add a block depot and mark it in the extended block.
2694          */
2695         Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2696         This->bigBlockDepotCount++;
2697         Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2698 
2699         /* Flag the block depot.
2700          */
2701         StorageImpl_SetNextBlockInChain(This,
2702                                           depotBlockIndexPos,
2703                                           BLOCK_SPECIAL);
2704 
2705         /* If necessary, flag the extended depot block.
2706          */
2707         if (extIndex != BLOCK_UNUSED)
2708           StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2709 
2710         /* Save header information.
2711          */
2712         StorageImpl_SaveFileHeader(This);
2713       }
2714     }
2715 
2716     success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2717 
2718     if (success)
2719     {
2720       while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2721               ( nextBlockIndex != BLOCK_UNUSED))
2722       {
2723         StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2724 
2725         if (nextBlockIndex == BLOCK_UNUSED)
2726         {
2727           freeBlock = (depotIndex * blocksPerDepot) +
2728                       (depotBlockOffset/sizeof(ULONG));
2729         }
2730 
2731         depotBlockOffset += sizeof(ULONG);
2732       }
2733     }
2734 
2735     depotIndex++;
2736     depotBlockOffset = 0;
2737   }
2738 
2739   /*
2740    * make sure that the block physically exists before using it
2741    */
2742   BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2743 
2744   This->prevFreeBlock = freeBlock;
2745 
2746   return freeBlock;
2747 }
2748 
2749 /******************************************************************************
2750  *      Storage32Impl_AddBlockDepot
2751  *
2752  * This will create a depot block, essentially it is a block initialized
2753  * to BLOCK_UNUSEDs.
2754  */
2755 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2756 {
2757   BYTE blockBuffer[BIG_BLOCK_SIZE];
2758 
2759   /*
2760    * Initialize blocks as free
2761    */
2762   memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2763   StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2764 }
2765 
2766 /******************************************************************************
2767  *      Storage32Impl_GetExtDepotBlock
2768  *
2769  * Returns the index of the block that corresponds to the specified depot
2770  * index. This method is only for depot indexes equal or greater than
2771  * COUNT_BBDEPOTINHEADER.
2772  */
2773 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2774 {
2775   ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2776   ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
2777   ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
2778   ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
2779   ULONG blockIndex             = BLOCK_UNUSED;
2780   ULONG extBlockIndex          = This->extBigBlockDepotStart;
2781 
2782   assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2783 
2784   if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2785     return BLOCK_UNUSED;
2786 
2787   while (extBlockCount > 0)
2788   {
2789     extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2790     extBlockCount--;
2791   }
2792 
2793   if (extBlockIndex != BLOCK_UNUSED)
2794     StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2795                         extBlockOffset * sizeof(ULONG), &blockIndex);
2796 
2797   return blockIndex;
2798 }
2799 
2800 /******************************************************************************
2801  *      Storage32Impl_SetExtDepotBlock
2802  *
2803  * Associates the specified block index to the specified depot index.
2804  * This method is only for depot indexes equal or greater than
2805  * COUNT_BBDEPOTINHEADER.
2806  */
2807 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2808 {
2809   ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2810   ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
2811   ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
2812   ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
2813   ULONG extBlockIndex          = This->extBigBlockDepotStart;
2814 
2815   assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2816 
2817   while (extBlockCount > 0)
2818   {
2819     extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2820     extBlockCount--;
2821   }
2822 
2823   if (extBlockIndex != BLOCK_UNUSED)
2824   {
2825     StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2826                         extBlockOffset * sizeof(ULONG),
2827                         blockIndex);
2828   }
2829 }
2830 
2831 /******************************************************************************
2832  *      Storage32Impl_AddExtBlockDepot
2833  *
2834  * Creates an extended depot block.
2835  */
2836 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2837 {
2838   ULONG numExtBlocks           = This->extBigBlockDepotCount;
2839   ULONG nextExtBlock           = This->extBigBlockDepotStart;
2840   BYTE  depotBuffer[BIG_BLOCK_SIZE];
2841   ULONG index                  = BLOCK_UNUSED;
2842   ULONG nextBlockOffset        = This->bigBlockSize - sizeof(ULONG);
2843   ULONG blocksPerDepotBlock    = This->bigBlockSize / sizeof(ULONG);
2844   ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2845 
2846   index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2847           blocksPerDepotBlock;
2848 
2849   if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2850   {
2851     /*
2852      * The first extended block.
2853      */
2854     This->extBigBlockDepotStart = index;
2855   }
2856   else
2857   {
2858     unsigned int i;
2859     /*
2860      * Follow the chain to the last one.
2861      */
2862     for (i = 0; i < (numExtBlocks - 1); i++)
2863     {
2864       nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2865     }
2866 
2867     /*
2868      * Add the new extended block to the chain.
2869      */
2870     StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2871                                      index);
2872   }
2873 
2874   /*
2875    * Initialize this block.
2876    */
2877   memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2878   StorageImpl_WriteBigBlock(This, index, depotBuffer);
2879 
2880   return index;
2881 }
2882 
2883 /******************************************************************************
2884  *      Storage32Impl_FreeBigBlock
2885  *
2886  * This method will flag the specified block as free in the big block depot.
2887  */
2888 static void StorageImpl_FreeBigBlock(
2889   StorageImpl* This,
2890   ULONG          blockIndex)
2891 {
2892   StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2893 
2894   if (blockIndex < This->prevFreeBlock)
2895     This->prevFreeBlock = blockIndex;
2896 }
2897 
2898 /************************************************************************
2899  * Storage32Impl_GetNextBlockInChain
2900  *
2901  * This method will retrieve the block index of the next big block in
2902  * in the chain.
2903  *
2904  * Params:  This       - Pointer to the Storage object.
2905  *          blockIndex - Index of the block to retrieve the chain
2906  *                       for.
2907  *          nextBlockIndex - receives the return value.
2908  *
2909  * Returns: This method returns the index of the next block in the chain.
2910  *          It will return the constants:
2911  *              BLOCK_SPECIAL - If the block given was not part of a
2912  *                              chain.
2913  *              BLOCK_END_OF_CHAIN - If the block given was the last in
2914  *                                   a chain.
2915  *              BLOCK_UNUSED - If the block given was not past of a chain
2916  *                             and is available.
2917  *              BLOCK_EXTBBDEPOT - This block is part of the extended
2918  *                                 big block depot.
2919  *
2920  * See Windows documentation for more details on IStorage methods.
2921  */
2922 static HRESULT StorageImpl_GetNextBlockInChain(
2923   StorageImpl* This,
2924   ULONG        blockIndex,
2925   ULONG*       nextBlockIndex)
2926 {
2927   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
2928   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
2929   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2930   BYTE depotBuffer[BIG_BLOCK_SIZE];
2931   BOOL success;
2932   ULONG depotBlockIndexPos;
2933   int index;
2934 
2935   *nextBlockIndex   = BLOCK_SPECIAL;
2936 
2937   if(depotBlockCount >= This->bigBlockDepotCount)
2938   {
2939     WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2940          This->bigBlockDepotCount);
2941     return STG_E_READFAULT;
2942   }
2943 
2944   /*
2945    * Cache the currently accessed depot block.
2946    */
2947   if (depotBlockCount != This->indexBlockDepotCached)
2948   {
2949     This->indexBlockDepotCached = depotBlockCount;
2950 
2951     if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2952     {
2953       depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2954     }
2955     else
2956     {
2957       /*
2958        * We have to look in the extended depot.
2959        */
2960       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2961     }
2962 
2963     success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2964 
2965     if (!success)
2966       return STG_E_READFAULT;
2967 
2968     for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2969     {
2970       StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2971       This->blockDepotCached[index] = *nextBlockIndex;
2972     }
2973   }
2974 
2975   *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2976 
2977   return S_OK;
2978 }
2979 
2980 /******************************************************************************
2981  *      Storage32Impl_GetNextExtendedBlock
2982  *
2983  * Given an extended block this method will return the next extended block.
2984  *
2985  * NOTES:
2986  * The last ULONG of an extended block is the block index of the next
2987  * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2988  * depot.
2989  *
2990  * Return values:
2991  *    - The index of the next extended block
2992  *    - BLOCK_UNUSED: there is no next extended block.
2993  *    - Any other return values denotes failure.
2994  */
2995 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2996 {
2997   ULONG nextBlockIndex   = BLOCK_SPECIAL;
2998   ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2999 
3000   StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3001                         &nextBlockIndex);
3002 
3003   return nextBlockIndex;
3004 }
3005 
3006 /******************************************************************************
3007  *      Storage32Impl_SetNextBlockInChain
3008  *
3009  * This method will write the index of the specified block's next block
3010  * in the big block depot.
3011  *
3012  * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3013  *              do the following
3014  *
3015  * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3016  * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3017  * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3018  *
3019  */
3020 static void StorageImpl_SetNextBlockInChain(
3021           StorageImpl* This,
3022           ULONG          blockIndex,
3023           ULONG          nextBlock)
3024 {
3025   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
3026   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
3027   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3028   ULONG depotBlockIndexPos;
3029 
3030   assert(depotBlockCount < This->bigBlockDepotCount);
3031   assert(blockIndex != nextBlock);
3032 
3033   if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3034   {
3035     depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3036   }
3037   else
3038   {
3039     /*
3040      * We have to look in the extended depot.
3041      */
3042     depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3043   }
3044 
3045   StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3046                         nextBlock);
3047   /*
3048    * Update the cached block depot, if necessary.
3049    */
3050   if (depotBlockCount == This->indexBlockDepotCached)
3051   {
3052     This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3053   }
3054 }
3055 
3056 /******************************************************************************
3057  *      Storage32Impl_LoadFileHeader
3058  *
3059  * This method will read in the file header, i.e. big block index -1.
3060  */
3061 static HRESULT StorageImpl_LoadFileHeader(
3062           StorageImpl* This)
3063 {
3064   HRESULT hr = STG_E_FILENOTFOUND;
3065   BYTE    headerBigBlock[BIG_BLOCK_SIZE];
3066   BOOL    success;
3067   int     index;
3068 
3069   TRACE("\n");
3070   /*
3071    * Get a pointer to the big block of data containing the header.
3072    */
3073   success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3074 
3075   /*
3076    * Extract the information from the header.
3077    */
3078   if (success)
3079   {
3080     /*
3081      * Check for the "magic number" signature and return an error if it is not
3082      * found.
3083      */
3084     if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3085     {
3086       return STG_E_OLDFORMAT;
3087     }
3088 
3089     if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3090     {
3091       return STG_E_INVALIDHEADER;
3092     }
3093 
3094     StorageUtl_ReadWord(
3095       headerBigBlock,
3096       OFFSET_BIGBLOCKSIZEBITS,
3097       &This->bigBlockSizeBits);
3098 
3099     StorageUtl_ReadWord(
3100       headerBigBlock,
3101       OFFSET_SMALLBLOCKSIZEBITS,
3102       &This->smallBlockSizeBits);
3103 
3104     StorageUtl_ReadDWord(
3105       headerBigBlock,
3106       OFFSET_BBDEPOTCOUNT,
3107       &This->bigBlockDepotCount);
3108 
3109     StorageUtl_ReadDWord(
3110       headerBigBlock,
3111       OFFSET_ROOTSTARTBLOCK,
3112       &This->rootStartBlock);
3113 
3114     StorageUtl_ReadDWord(
3115       headerBigBlock,
3116       OFFSET_SBDEPOTSTART,
3117       &This->smallBlockDepotStart);
3118 
3119     StorageUtl_ReadDWord(
3120       headerBigBlock,
3121       OFFSET_EXTBBDEPOTSTART,
3122       &This->extBigBlockDepotStart);
3123 
3124     StorageUtl_ReadDWord(
3125       headerBigBlock,
3126       OFFSET_EXTBBDEPOTCOUNT,
3127       &This->extBigBlockDepotCount);
3128 
3129     for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3130     {
3131       StorageUtl_ReadDWord(
3132         headerBigBlock,
3133         OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3134         &(This->bigBlockDepotStart[index]));
3135     }
3136 
3137     /*
3138      * Make the bitwise arithmetic to get the size of the blocks in bytes.
3139      */
3140     This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3141     This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3142 
3143     /*
3144      * Right now, the code is making some assumptions about the size of the
3145      * blocks, just make sure they are what we're expecting.
3146      */
3147     if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3148         This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3149     {
3150         WARN("Broken OLE storage file\n");
3151         hr = STG_E_INVALIDHEADER;
3152     }
3153     else
3154         hr = S_OK;
3155   }
3156 
3157   return hr;
3158 }
3159 
3160 /******************************************************************************
3161  *      Storage32Impl_SaveFileHeader
3162  *
3163  * This method will save to the file the header, i.e. big block -1.
3164  */
3165 static void StorageImpl_SaveFileHeader(
3166           StorageImpl* This)
3167 {
3168   BYTE   headerBigBlock[BIG_BLOCK_SIZE];
3169   int    index;
3170   BOOL success;
3171 
3172   /*
3173    * Get a pointer to the big block of data containing the header.
3174    */
3175   success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3176 
3177   /*
3178    * If the block read failed, the file is probably new.
3179    */
3180   if (!success)
3181   {
3182     /*
3183      * Initialize for all unknown fields.
3184      */
3185     memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3186 
3187     /*
3188      * Initialize the magic number.
3189      */
3190     memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3191 
3192     /*
3193      * And a bunch of things we don't know what they mean
3194      */
3195     StorageUtl_WriteWord(headerBigBlock,  0x18, 0x3b);
3196     StorageUtl_WriteWord(headerBigBlock,  0x1a, 0x3);
3197     StorageUtl_WriteWord(headerBigBlock,  0x1c, (WORD)-2);
3198     StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3199   }
3200 
3201   /*
3202    * Write the information to the header.
3203    */
3204   StorageUtl_WriteWord(
3205     headerBigBlock,
3206     OFFSET_BIGBLOCKSIZEBITS,
3207     This->bigBlockSizeBits);
3208 
3209   StorageUtl_WriteWord(
3210     headerBigBlock,
3211     OFFSET_SMALLBLOCKSIZEBITS,
3212     This->smallBlockSizeBits);
3213 
3214   StorageUtl_WriteDWord(
3215     headerBigBlock,
3216     OFFSET_BBDEPOTCOUNT,
3217     This->bigBlockDepotCount);
3218 
3219   StorageUtl_WriteDWord(
3220     headerBigBlock,
3221     OFFSET_ROOTSTARTBLOCK,
3222     This->rootStartBlock);
3223 
3224   StorageUtl_WriteDWord(
3225     headerBigBlock,
3226     OFFSET_SBDEPOTSTART,
3227     This->smallBlockDepotStart);
3228 
3229   StorageUtl_WriteDWord(
3230     headerBigBlock,
3231     OFFSET_SBDEPOTCOUNT,
3232     This->smallBlockDepotChain ?
3233      BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3234 
3235   StorageUtl_WriteDWord(
3236     headerBigBlock,
3237     OFFSET_EXTBBDEPOTSTART,
3238     This->extBigBlockDepotStart);
3239 
3240   StorageUtl_WriteDWord(
3241     headerBigBlock,
3242     OFFSET_EXTBBDEPOTCOUNT,
3243     This->extBigBlockDepotCount);
3244 
3245   for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3246   {
3247     StorageUtl_WriteDWord(
3248       headerBigBlock,
3249       OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3250       (This->bigBlockDepotStart[index]));
3251   }
3252 
3253   /*
3254    * Write the big block back to the file.
3255    */
3256   StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3257 }
3258 
3259 /******************************************************************************
3260  *      Storage32Impl_ReadProperty
3261  *
3262  * This method will read the specified property from the property chain.
3263  */
3264 BOOL StorageImpl_ReadProperty(
3265   StorageImpl* This,
3266   ULONG          index,
3267   StgProperty*   buffer)
3268 {
3269   BYTE           currentProperty[PROPSET_BLOCK_SIZE];
3270   ULARGE_INTEGER offsetInPropSet;
3271   HRESULT        readRes;
3272   ULONG          bytesRead;
3273 
3274   offsetInPropSet.u.HighPart = 0;
3275   offsetInPropSet.u.LowPart  = index * PROPSET_BLOCK_SIZE;
3276 
3277   readRes = BlockChainStream_ReadAt(
3278                     This->rootBlockChain,
3279                     offsetInPropSet,
3280                     PROPSET_BLOCK_SIZE,
3281                     currentProperty,
3282                     &bytesRead);
3283 
3284   if (SUCCEEDED(readRes))
3285   {
3286     /* replace the name of root entry (often "Root Entry") by the file name */
3287     WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3288                         This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3289 
3290     memset(buffer->name, 0, sizeof(buffer->name));
3291     memcpy(
3292       buffer->name,
3293       propName,
3294       PROPERTY_NAME_BUFFER_LEN );
3295     TRACE("storage name: %s\n", debugstr_w(buffer->name));
3296 
3297     memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3298 
3299     StorageUtl_ReadWord(
3300       currentProperty,
3301       OFFSET_PS_NAMELENGTH,
3302       &buffer->sizeOfNameString);
3303 
3304     StorageUtl_ReadDWord(
3305       currentProperty,
3306       OFFSET_PS_PREVIOUSPROP,
3307       &buffer->previousProperty);
3308 
3309     StorageUtl_ReadDWord(
3310       currentProperty,
3311       OFFSET_PS_NEXTPROP,
3312       &buffer->nextProperty);
3313 
3314     StorageUtl_ReadDWord(
3315       currentProperty,
3316       OFFSET_PS_DIRPROP,
3317       &buffer->dirProperty);
3318 
3319     StorageUtl_ReadGUID(
3320       currentProperty,
3321       OFFSET_PS_GUID,
3322       &buffer->propertyUniqueID);
3323 
3324     StorageUtl_ReadDWord(
3325       currentProperty,
3326       OFFSET_PS_TSS1,
3327       &buffer->timeStampS1);
3328 
3329     StorageUtl_ReadDWord(
3330       currentProperty,
3331       OFFSET_PS_TSD1,
3332       &buffer->timeStampD1);
3333 
3334     StorageUtl_ReadDWord(
3335       currentProperty,
3336       OFFSET_PS_TSS2,
3337       &buffer->timeStampS2);
3338 
3339     StorageUtl_ReadDWord(
3340       currentProperty,
3341       OFFSET_PS_TSD2,
3342       &buffer->timeStampD2);
3343 
3344     StorageUtl_ReadDWord(
3345       currentProperty,
3346       OFFSET_PS_STARTBLOCK,
3347       &buffer->startingBlock);
3348 
3349     StorageUtl_ReadDWord(
3350       currentProperty,
3351       OFFSET_PS_SIZE,
3352       &buffer->size.u.LowPart);
3353 
3354     buffer->size.u.HighPart = 0;
3355   }
3356 
3357   return SUCCEEDED(readRes) ? TRUE : FALSE;
3358 }
3359 
3360 /*********************************************************************
3361  * Write the specified property into the property chain
3362  */
3363 BOOL StorageImpl_WriteProperty(
3364   StorageImpl*          This,
3365   ULONG                 index,
3366   const StgProperty*    buffer)
3367 {
3368   BYTE           currentProperty[PROPSET_BLOCK_SIZE];
3369   ULARGE_INTEGER offsetInPropSet;
3370   HRESULT        writeRes;
3371   ULONG          bytesWritten;
3372 
3373   offsetInPropSet.u.HighPart = 0;
3374   offsetInPropSet.u.LowPart  = index * PROPSET_BLOCK_SIZE;
3375 
3376   memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3377 
3378   memcpy(
3379     currentProperty + OFFSET_PS_NAME,
3380     buffer->name,
3381     PROPERTY_NAME_BUFFER_LEN );
3382 
3383   memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3384 
3385   StorageUtl_WriteWord(
3386     currentProperty,
3387       OFFSET_PS_NAMELENGTH,
3388       buffer->sizeOfNameString);
3389 
3390   StorageUtl_WriteDWord(
3391     currentProperty,
3392       OFFSET_PS_PREVIOUSPROP,
3393       buffer->previousProperty);
3394 
3395   StorageUtl_WriteDWord(
3396     currentProperty,
3397       OFFSET_PS_NEXTPROP,
3398       buffer->nextProperty);
3399 
3400   StorageUtl_WriteDWord(
3401     currentProperty,
3402       OFFSET_PS_DIRPROP,
3403       buffer->dirProperty);
3404 
3405   StorageUtl_WriteGUID(
3406     currentProperty,
3407       OFFSET_PS_GUID,
3408       &buffer->propertyUniqueID);
3409 
3410   StorageUtl_WriteDWord(
3411     currentProperty,
3412       OFFSET_PS_TSS1,
3413       buffer->timeStampS1);
3414 
3415   StorageUtl_WriteDWord(
3416     currentProperty,
3417       OFFSET_PS_TSD1,
3418       buffer->timeStampD1);
3419 
3420   StorageUtl_WriteDWord(
3421     currentProperty,
3422       OFFSET_PS_TSS2,
3423       buffer->timeStampS2);
3424 
3425   StorageUtl_WriteDWord(
3426     currentProperty,
3427       OFFSET_PS_TSD2,
3428       buffer->timeStampD2);
3429 
3430   StorageUtl_WriteDWord(
3431     currentProperty,
3432       OFFSET_PS_STARTBLOCK,
3433       buffer->startingBlock);
3434 
3435   StorageUtl_WriteDWord(
3436     currentProperty,
3437       OFFSET_PS_SIZE,
3438       buffer->size.u.LowPart);
3439 
3440   writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3441                                       offsetInPropSet,
3442                                       PROPSET_BLOCK_SIZE,
3443                                       currentProperty,
3444                                       &bytesWritten);
3445   return SUCCEEDED(writeRes) ? TRUE : FALSE;
3446 }
3447 
3448 static BOOL StorageImpl_ReadBigBlock(
3449   StorageImpl* This,
3450   ULONG          blockIndex,
3451   void*          buffer)
3452 {
3453   ULARGE_INTEGER ulOffset;
3454   DWORD  read;
3455 
3456   ulOffset.u.HighPart = 0;
3457   ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3458 
3459   StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3460   return (read == This->bigBlockSize);
3461 }
3462 
3463 static BOOL StorageImpl_ReadDWordFromBigBlock(
3464   StorageImpl*  This,
3465   ULONG         blockIndex,
3466   ULONG         offset,
3467   DWORD*        value)
3468 {
3469   ULARGE_INTEGER ulOffset;
3470   DWORD  read;
3471   DWORD  tmp;
3472 
3473   ulOffset.u.HighPart = 0;
3474   ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3475   ulOffset.u.LowPart += offset;
3476 
3477   StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3478   *value = lendian32toh(tmp);
3479   return (read == sizeof(DWORD));
3480 }
3481 
3482 static BOOL StorageImpl_WriteBigBlock(
3483   StorageImpl*  This,
3484   ULONG         blockIndex,
3485   const void*   buffer)
3486 {
3487   ULARGE_INTEGER ulOffset;
3488   DWORD  wrote;
3489 
3490   ulOffset.u.HighPart = 0;
3491   ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3492 
3493   StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3494   return (wrote == This->bigBlockSize);
3495 }
3496 
3497 static BOOL StorageImpl_WriteDWordToBigBlock(
3498   StorageImpl* This,
3499   ULONG         blockIndex,
3500   ULONG         offset,
3501   DWORD         value)
3502 {
3503   ULARGE_INTEGER ulOffset;
3504   DWORD  wrote;
3505 
3506   ulOffset.u.HighPart = 0;
3507   ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3508   ulOffset.u.LowPart += offset;
3509 
3510   value = htole32(value);
3511   StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3512   return (wrote == sizeof(DWORD));
3513 }
3514 
3515 /******************************************************************************
3516  *              Storage32Impl_SmallBlocksToBigBlocks
3517  *
3518  * This method will convert a small block chain to a big block chain.
3519  * The small block chain will be destroyed.
3520  */
3521 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3522                       StorageImpl* This,
3523                       SmallBlockChainStream** ppsbChain)
3524 {
3525   ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3526   ULARGE_INTEGER size, offset;
3527   ULONG cbRead, cbWritten;
3528   ULARGE_INTEGER cbTotalRead;
3529   ULONG propertyIndex;
3530   HRESULT resWrite = S_OK;
3531   HRESULT resRead;
3532   StgProperty chainProperty;
3533   BYTE *buffer;
3534   BlockChainStream *bbTempChain = NULL;
3535   BlockChainStream *bigBlockChain = NULL;
3536 
3537   /*
3538    * Create a temporary big block chain that doesn't have
3539    * an associated property. This temporary chain will be
3540    * used to copy data from small blocks to big blocks.
3541    */
3542   bbTempChain = BlockChainStream_Construct(This,
3543                                            &bbHeadOfChain,
3544                                            PROPERTY_NULL);
3545   if(!bbTempChain) return NULL;
3546   /*
3547    * Grow the big block chain.
3548    */
3549   size = SmallBlockChainStream_GetSize(*ppsbChain);
3550   BlockChainStream_SetSize(bbTempChain, size);
3551 
3552   /*
3553    * Copy the contents of the small block chain to the big block chain
3554    * by small block size increments.
3555    */
3556   offset.u.LowPart = 0;
3557   offset.u.HighPart = 0;
3558   cbTotalRead.QuadPart = 0;
3559 
3560   buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3561   do
3562   {
3563     resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3564                                            offset,
3565                                            This->smallBlockSize,
3566                                            buffer,
3567                                            &cbRead);
3568     if (FAILED(resRead))
3569         break;
3570 
3571     if (cbRead > 0)
3572     {
3573         cbTotalRead.QuadPart += cbRead;
3574 
3575         resWrite = BlockChainStream_WriteAt(bbTempChain,
3576                                             offset,
3577                                             cbRead,
3578                                             buffer,
3579                                             &cbWritten);
3580 
3581         if (FAILED(resWrite))
3582             break;
3583 
3584         offset.u.LowPart += This->smallBlockSize;
3585     }
3586   } while (cbTotalRead.QuadPart < size.QuadPart);
3587   HeapFree(GetProcessHeap(),0,buffer);
3588 
3589   if (FAILED(resRead) || FAILED(resWrite))
3590   {
3591     ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3592     BlockChainStream_Destroy(bbTempChain);
3593     return NULL;
3594   }
3595 
3596   /*
3597    * Destroy the small block chain.
3598    */
3599   propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3600   size.u.HighPart = 0;
3601   size.u.LowPart  = 0;
3602   SmallBlockChainStream_SetSize(*ppsbChain, size);
3603   SmallBlockChainStream_Destroy(*ppsbChain);
3604   *ppsbChain = 0;
3605 
3606   /*
3607    * Change the property information. This chain is now a big block chain
3608    * and it doesn't reside in the small blocks chain anymore.
3609    */
3610   StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3611 
3612   chainProperty.startingBlock = bbHeadOfChain;
3613 
3614   StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3615 
3616   /*
3617    * Destroy the temporary propertyless big block chain.
3618    * Create a new big block chain associated with this property.
3619    */
3620   BlockChainStream_Destroy(bbTempChain);
3621   bigBlockChain = BlockChainStream_Construct(This,
3622                                              NULL,
3623                                              propertyIndex);
3624 
3625   return bigBlockChain;
3626 }
3627 
3628 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3629 {
3630   StorageInternalImpl* This = (StorageInternalImpl*) iface;
3631 
3632   StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3633   HeapFree(GetProcessHeap(), 0, This);
3634 }
3635 
3636 /******************************************************************************
3637 **
3638 ** Storage32InternalImpl_Commit
3639 **
3640 ** The non-root storages cannot be opened in transacted mode thus this function
3641 ** does nothing.
3642 */
3643 static HRESULT WINAPI StorageInternalImpl_Commit(
3644   IStorage*            iface,
3645   DWORD                  grfCommitFlags)  /* [in] */
3646 {
3647   return S_OK;
3648 }
3649 
3650 /******************************************************************************
3651 **
3652 ** Storage32InternalImpl_Revert
3653 **
3654 ** The non-root storages cannot be opened in transacted mode thus this function
3655 ** does nothing.
3656 */
3657 static HRESULT WINAPI StorageInternalImpl_Revert(
3658   IStorage*            iface)
3659 {
3660   return S_OK;
3661 }
3662 
3663 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3664 {
3665   IStorage_Release((IStorage*)This->parentStorage);
3666   HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3667   HeapFree(GetProcessHeap(), 0, This);
3668 }
3669 
3670 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3671   IEnumSTATSTG*     iface,
3672   REFIID            riid,
3673   void**            ppvObject)
3674 {
3675   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3676 
3677   /*
3678    * Perform a sanity check on the parameters.
3679    */
3680   if (ppvObject==0)
3681     return E_INVALIDARG;
3682 
3683   /*
3684    * Initialize the return parameter.
3685    */
3686   *ppvObject = 0;
3687 
3688   /*
3689    * Compare the riid with the interface IDs implemented by this object.
3690    */
3691   if (IsEqualGUID(&IID_IUnknown, riid) ||
3692       IsEqualGUID(&IID_IEnumSTATSTG, riid))
3693   {
3694     *ppvObject = This;
3695     IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3696     return S_OK;
3697   }
3698 
3699   return E_NOINTERFACE;
3700 }
3701 
3702 static ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
3703   IEnumSTATSTG* iface)
3704 {
3705   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3706   return InterlockedIncrement(&This->ref);
3707 }
3708 
3709 static ULONG   WINAPI IEnumSTATSTGImpl_Release(
3710   IEnumSTATSTG* iface)
3711 {
3712   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3713 
3714   ULONG newRef;
3715 
3716   newRef = InterlockedDecrement(&This->ref);
3717 
3718   /*
3719    * If the reference count goes down to 0, perform suicide.