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