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