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