[1/2] ole32: Reorder storage32 functions to avoid forward declarations.

Francois Gouget fgouget at free.fr
Sat May 2 16:52:44 CDT 2015


---
 dlls/ole32/storage32.c | 12253 +++++++++++++++++++++++------------------------
 dlls/ole32/storage32.h |    42 +-
 2 files changed, 6107 insertions(+), 6188 deletions(-)

diff --git a/dlls/ole32/storage32.c b/dlls/ole32/storage32.c
index 441b319..3ef1077 100644
--- a/dlls/ole32/storage32.c
+++ b/dlls/ole32/storage32.c
@@ -55,9 +55,6 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(storage);
 
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
-#define OLESTREAM_ID 0x501
-#define OLESTREAM_MAX_STR_LEN 255
 
 /*
  * These are signatures to detect the type of Document file.
@@ -65,15 +62,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(storage);
 static const BYTE STORAGE_magic[8]    ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
 
-static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
-{
-    return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
-}
-
-static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
-{
-    return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
-}
+extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
 
 /****************************************************************************
  * StorageInternalImpl definitions.
@@ -82,7 +71,7 @@ static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *i
  * This one implements the IStorage interface for storage that are
  * inside another storage.
  */
-struct StorageInternalImpl
+typedef struct StorageInternalImpl
 {
   struct StorageBaseImpl base;
 
@@ -92,45 +81,10 @@ struct StorageInternalImpl
   struct list ParentListEntry;
 
   StorageBaseImpl *parentStorage;
-};
-typedef struct StorageInternalImpl StorageInternalImpl;
+} StorageInternalImpl;
 
-static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
 static const IStorageVtbl StorageInternalImpl_Vtbl;
-
-/* Method definitions for the StorageInternalImpl class. */
-static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl* parentStorage,
-                                                          DWORD openFlags, DirRef storageDirEntry);
-static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create);
-static void StorageImpl_Destroy(StorageBaseImpl* iface);
-static void StorageImpl_Invalidate(StorageBaseImpl* iface);
-static HRESULT StorageImpl_Flush(StorageBaseImpl* iface);
-static HRESULT StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer, ULONG *read );
-static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
-static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
-static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
-static void StorageImpl_SaveFileHeader(StorageImpl* This);
-static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, ULARGE_INTEGER cb, DWORD dwLockType);
-
-static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex);
-static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
-static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
-static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
-static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
-
-static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
-static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
-static ULONG BlockChainStream_GetCount(BlockChainStream* This);
-
-static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
-static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
-static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
-    ULONG blockIndex, ULONG offset, DWORD value);
-static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl*  This,
-    ULONG blockIndex, ULONG offset, DWORD* value);
-
-static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
-static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
+static StorageInternalImpl* StorageInternalImpl_Construct(StorageBaseImpl*,DWORD,DirRef);
 
 typedef struct TransactedDirEntry
 {
@@ -197,6 +151,9 @@ typedef struct TransactedSnapshotImpl
   ULONG lastTransactionSig;
 } TransactedSnapshotImpl;
 
+static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
+static HRESULT Storage_ConstructTransacted(StorageBaseImpl*,BOOL,StorageBaseImpl**);
+
 typedef struct TransactedSharedImpl
 {
   struct StorageBaseImpl base;
@@ -215,5411 +172,5183 @@ typedef struct TransactedSharedImpl
   ULONG lastTransactionSig;
 } TransactedSharedImpl;
 
-/* Generic function to create a transacted wrapper for a direct storage object. */
-static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, BOOL toplevel, StorageBaseImpl** result);
-
-/* OLESTREAM memory structure to use for Get and Put Routines */
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
-typedef struct
-{
-    DWORD dwOleID;
-    DWORD dwTypeID;
-    DWORD dwOleTypeNameLength;
-    CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
-    CHAR  *pstrOleObjFileName;
-    DWORD dwOleObjFileNameLength;
-    DWORD dwMetaFileWidth;
-    DWORD dwMetaFileHeight;
-    CHAR  strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
-    DWORD dwDataLength;
-    BYTE *pData;
-}OLECONVERT_OLESTREAM_DATA;
-
-/* CompObj Stream structure */
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
-typedef struct
-{
-    BYTE byUnknown1[12];
-    CLSID clsid;
-    DWORD dwCLSIDNameLength;
-    CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
-    DWORD dwOleTypeNameLength;
-    CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
-    DWORD dwProgIDNameLength;
-    CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
-    BYTE byUnknown2[16];
-}OLECONVERT_ISTORAGE_COMPOBJ;
 
+static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream*);
+static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream*);
 
-/* Ole Presentation Stream structure */
-/* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
-typedef struct
-{
-    BYTE byUnknown1[28];
-    DWORD dwExtentX;
-    DWORD dwExtentY;
-    DWORD dwSize;
-    BYTE *pData;
-}OLECONVERT_ISTORAGE_OLEPRES;
 
 
+/************************************************************************
+ * STGM Functions
+ ***********************************************************************/
 
-/***********************************************************************
- * Forward declaration of internal functions used by the method DestroyElement
+/************************************************************************
+ * This method validates an STGM parameter that can contain the values below
+ *
+ * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
+ * The stgm values contained in 0xffff0000 are bitmasks.
+ *
+ * STGM_DIRECT               0x00000000
+ * STGM_TRANSACTED           0x00010000
+ * STGM_SIMPLE               0x08000000
+ *
+ * STGM_READ                 0x00000000
+ * STGM_WRITE                0x00000001
+ * STGM_READWRITE            0x00000002
+ *
+ * STGM_SHARE_DENY_NONE      0x00000040
+ * STGM_SHARE_DENY_READ      0x00000030
+ * STGM_SHARE_DENY_WRITE     0x00000020
+ * STGM_SHARE_EXCLUSIVE      0x00000010
+ *
+ * STGM_PRIORITY             0x00040000
+ * STGM_DELETEONRELEASE      0x04000000
+ *
+ * STGM_CREATE               0x00001000
+ * STGM_CONVERT              0x00020000
+ * STGM_FAILIFTHERE          0x00000000
+ *
+ * STGM_NOSCRATCH            0x00100000
+ * STGM_NOSNAPSHOT           0x00200000
  */
-static HRESULT deleteStorageContents(
-  StorageBaseImpl *parentStorage,
-  DirRef       indexToDelete,
-  DirEntry     entryDataToDelete);
+static HRESULT validateSTGM(DWORD stgm)
+{
+  DWORD access = STGM_ACCESS_MODE(stgm);
+  DWORD share  = STGM_SHARE_MODE(stgm);
+  DWORD create = STGM_CREATE_MODE(stgm);
 
-static HRESULT deleteStreamContents(
-  StorageBaseImpl *parentStorage,
-  DirRef        indexToDelete,
-  DirEntry      entryDataToDelete);
+  if (stgm&~STGM_KNOWN_FLAGS)
+  {
+    ERR("unknown flags %08x\n", stgm);
+    return E_FAIL;
+  }
 
-static HRESULT removeFromTree(
-  StorageBaseImpl *This,
-  DirRef        parentStorageIndex,
-  DirRef        deletedIndex);
+  switch (access)
+  {
+  case STGM_READ:
+  case STGM_WRITE:
+  case STGM_READWRITE:
+    break;
+  default:
+    return E_FAIL;
+  }
 
-/***********************************************************************
- * Declaration of the functions used to manipulate DirEntry
- */
+  switch (share)
+  {
+  case STGM_SHARE_DENY_NONE:
+  case STGM_SHARE_DENY_READ:
+  case STGM_SHARE_DENY_WRITE:
+  case STGM_SHARE_EXCLUSIVE:
+    break;
+  case 0:
+    if (!(stgm & STGM_TRANSACTED))
+      return E_FAIL;
+    break;
+  default:
+    return E_FAIL;
+  }
 
-static HRESULT insertIntoTree(
-  StorageBaseImpl *This,
-  DirRef        parentStorageIndex,
-  DirRef        newEntryIndex);
+  switch (create)
+  {
+  case STGM_CREATE:
+  case STGM_FAILIFTHERE:
+    break;
+  default:
+    return E_FAIL;
+  }
 
-static LONG entryNameCmp(
-    const OLECHAR *name1,
-    const OLECHAR *name2);
-
-static DirRef findElement(
-    StorageBaseImpl *storage,
-    DirRef storageEntry,
-    const OLECHAR *name,
-    DirEntry *data);
-
-static HRESULT findTreeParent(
-    StorageBaseImpl *storage,
-    DirRef storageEntry,
-    const OLECHAR *childName,
-    DirEntry *parentData,
-    DirRef *parentEntry,
-    ULONG *relation);
+  /*
+   * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
+   */
+  if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
+      return E_FAIL;
 
-/***********************************************************************
- * Declaration of miscellaneous functions...
- */
-static HRESULT validateSTGM(DWORD stgmValue);
+  /*
+   * STGM_CREATE | STGM_CONVERT
+   * if both are false, STGM_FAILIFTHERE is set to TRUE
+   */
+  if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
+    return E_FAIL;
 
-static DWORD GetShareModeFromSTGM(DWORD stgm);
-static DWORD GetAccessModeFromSTGM(DWORD stgm);
-static DWORD GetCreationModeFromSTGM(DWORD stgm);
+  /*
+   * STGM_NOSCRATCH requires STGM_TRANSACTED
+   */
+  if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
+    return E_FAIL;
 
-extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
+  /*
+   * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
+   * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
+   */
+  if ( (stgm & STGM_NOSNAPSHOT) &&
+        (!(stgm & STGM_TRANSACTED) ||
+         share == STGM_SHARE_EXCLUSIVE ||
+         share == STGM_SHARE_DENY_WRITE) )
+    return E_FAIL;
 
+  return S_OK;
+}
 
-/****************************************************************************
- * IEnumSTATSTGImpl definitions.
+/************************************************************************
+ *      GetShareModeFromSTGM
  *
- * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
- * This class allows iterating through the content of a storage and to find
- * specific items inside it.
+ * This method will return a share mode flag from a STGM value.
+ * The STGM value is assumed valid.
  */
-struct IEnumSTATSTGImpl
+static DWORD GetShareModeFromSTGM(DWORD stgm)
 {
-  IEnumSTATSTG   IEnumSTATSTG_iface;
-
-  LONG           ref;                   /* Reference count */
-  StorageBaseImpl* parentStorage;         /* Reference to the parent storage */
-  DirRef         storageDirEntry;     /* Directory entry of the storage to enumerate */
-
-  WCHAR	         name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
-};
+  switch (STGM_SHARE_MODE(stgm))
+  {
+  case 0:
+    assert(stgm & STGM_TRANSACTED);
+    /* fall-through */
+  case STGM_SHARE_DENY_NONE:
+    return FILE_SHARE_READ | FILE_SHARE_WRITE;
+  case STGM_SHARE_DENY_READ:
+    return FILE_SHARE_WRITE;
+  case STGM_SHARE_DENY_WRITE:
+  case STGM_SHARE_EXCLUSIVE:
+    return FILE_SHARE_READ;
+  }
+  ERR("Invalid share mode!\n");
+  assert(0);
+  return 0;
+}
 
-static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
+/************************************************************************
+ *      GetAccessModeFromSTGM
+ *
+ * This method will return an access mode flag from a STGM value.
+ * The STGM value is assumed valid.
+ */
+static DWORD GetAccessModeFromSTGM(DWORD stgm)
 {
-  return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
+  switch (STGM_ACCESS_MODE(stgm))
+  {
+  case STGM_READ:
+    return GENERIC_READ;
+  case STGM_WRITE:
+  case STGM_READWRITE:
+    return GENERIC_READ | GENERIC_WRITE;
+  }
+  ERR("Invalid access mode!\n");
+  assert(0);
+  return 0;
 }
 
+/************************************************************************
+ *      GetCreationModeFromSTGM
+ *
+ * This method will return a creation mode flag from a STGM value.
+ * The STGM value is assumed valid.
+ */
+static DWORD GetCreationModeFromSTGM(DWORD stgm)
+{
+  switch(STGM_CREATE_MODE(stgm))
+  {
+  case STGM_CREATE:
+    return CREATE_ALWAYS;
+  case STGM_CONVERT:
+    FIXME("STGM_CONVERT not implemented!\n");
+    return CREATE_NEW;
+  case STGM_FAILIFTHERE:
+    return CREATE_NEW;
+  }
+  ERR("Invalid create mode!\n");
+  assert(0);
+  return 0;
+}
 
-static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl* This, DirRef storageDirEntry);
-static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
 
 /************************************************************************
-** Block Functions
-*/
+ * IDirectWriterLock implementation
+ ***********************************************************************/
 
-static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
+static inline StorageBaseImpl *impl_from_IDirectWriterLock( IDirectWriterLock *iface )
 {
-    return (ULONGLONG)(index+1) * This->bigBlockSize;
+    return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
 }
 
-/************************************************************************
-** StorageImpl implementation
-*/
-static HRESULT StorageImpl_ReadAt(StorageImpl* This,
-  ULARGE_INTEGER offset,
-  void*          buffer,
-  ULONG          size,
-  ULONG*         bytesRead)
+static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
 {
-    return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
+  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+  return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
 }
 
-static HRESULT StorageImpl_WriteAt(StorageImpl* This,
-  ULARGE_INTEGER offset,
-  const void*    buffer,
-  const ULONG    size,
-  ULONG*         bytesWritten)
+static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
 {
-    return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
+  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+  return IStorage_AddRef(&This->IStorage_iface);
 }
 
-/************************************************************************
- * StorageBaseImpl_QueryInterface (IUnknown)
- *
- * This method implements the common QueryInterface for all IStorage
- * implementations contained in this file.
- *
- * See Windows documentation for more details on IUnknown methods.
- */
-static HRESULT WINAPI StorageBaseImpl_QueryInterface(
-  IStorage*        iface,
-  REFIID             riid,
-  void**             ppvObject)
+static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
 {
-  StorageBaseImpl *This = impl_from_IStorage(iface);
+  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+  return IStorage_Release(&This->IStorage_iface);
+}
 
-  if (!ppvObject)
-    return E_INVALIDARG;
+static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
+{
+  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+  FIXME("(%p)->(%d): stub\n", This, timeout);
+  return E_NOTIMPL;
+}
 
-  *ppvObject = 0;
+static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
+{
+  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+  FIXME("(%p): stub\n", This);
+  return E_NOTIMPL;
+}
 
-  if (IsEqualGUID(&IID_IUnknown, riid) ||
-      IsEqualGUID(&IID_IStorage, riid))
-  {
-    *ppvObject = &This->IStorage_iface;
-  }
-  else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
-  {
-    *ppvObject = &This->IPropertySetStorage_iface;
-  }
-  /* locking interface is reported for writer only */
-  else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
-  {
-    *ppvObject = &This->IDirectWriterLock_iface;
-  }
-  else
-    return E_NOINTERFACE;
+static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
+{
+  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
+  FIXME("(%p): stub\n", This);
+  return E_NOTIMPL;
+}
 
-  IStorage_AddRef(iface);
+static const IDirectWriterLockVtbl DirectWriterLockVtbl =
+{
+  directwriterlock_QueryInterface,
+  directwriterlock_AddRef,
+  directwriterlock_Release,
+  directwriterlock_WaitForWriteAccess,
+  directwriterlock_ReleaseWriteAccess,
+  directwriterlock_HaveWriteAccess
+};
 
-  return S_OK;
-}
 
 /************************************************************************
- * StorageBaseImpl_AddRef (IUnknown)
+ * StorageBaseImpl implementation : Tree helper functions
+ ***********************************************************************/
+
+/****************************************************************************
  *
- * This method implements the common AddRef for all IStorage
- * implementations contained in this file.
+ * Internal Method
  *
- * See Windows documentation for more details on IUnknown methods.
+ * Case insensitive comparison of DirEntry.name by first considering
+ * their size.
+ *
+ * Returns <0 when name1 < name2
+ *         >0 when name1 > name2
+ *          0 when name1 == name2
  */
-static ULONG WINAPI StorageBaseImpl_AddRef(
-            IStorage* iface)
+static LONG entryNameCmp(
+    const OLECHAR *name1,
+    const OLECHAR *name2)
 {
-  StorageBaseImpl *This = impl_from_IStorage(iface);
-  ULONG ref = InterlockedIncrement(&This->ref);
+  LONG diff      = lstrlenW(name1) - lstrlenW(name2);
 
-  TRACE("(%p) AddRef to %d\n", This, ref);
+  while (diff == 0 && *name1 != 0)
+  {
+    /*
+     * We compare the string themselves only when they are of the same length
+     */
+    diff = toupperW(*name1++) - toupperW(*name2++);
+  }
 
-  return ref;
+  return diff;
 }
 
-/************************************************************************
- * StorageBaseImpl_Release (IUnknown)
+/****************************************************************************
  *
- * This method implements the common Release for all IStorage
- * implementations contained in this file.
+ * Internal Method
  *
- * See Windows documentation for more details on IUnknown methods.
+ * Find and read the element of a storage with the given name.
  */
-static ULONG WINAPI StorageBaseImpl_Release(
-      IStorage* iface)
+static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
+    const OLECHAR *name, DirEntry *data)
 {
-  StorageBaseImpl *This = impl_from_IStorage(iface);
+  DirRef currentEntry;
 
-  ULONG ref = InterlockedDecrement(&This->ref);
+  /* Read the storage entry to find the root of the tree. */
+  StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
 
-  TRACE("(%p) ReleaseRef to %d\n", This, ref);
+  currentEntry = data->dirRootEntry;
 
-  if (ref == 0)
+  while (currentEntry != DIRENTRY_NULL)
   {
-    /*
-     * Since we are using a system of base-classes, we want to call the
-     * destructor of the appropriate derived class. To do this, we are
-     * using virtual functions to implement the destructor.
-     */
-    StorageBaseImpl_Destroy(This);
+    LONG cmp;
+
+    StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
+
+    cmp = entryNameCmp(name, data->name);
+
+    if (cmp == 0)
+      /* found it */
+      break;
+
+    else if (cmp < 0)
+      currentEntry = data->leftChild;
+
+    else if (cmp > 0)
+      currentEntry = data->rightChild;
   }
 
-  return ref;
+  return currentEntry;
 }
 
-/************************************************************************
- * StorageBaseImpl_OpenStream (IStorage)
+/****************************************************************************
  *
- * This method will open the specified stream object from the current storage.
+ * Internal Method
  *
- * See Windows documentation for more details on IStorage methods.
+ * Find and read the binary tree parent of the element with the given name.
+ *
+ * If there is no such element, find a place where it could be inserted and
+ * return STG_E_FILENOTFOUND.
  */
-static HRESULT WINAPI StorageBaseImpl_OpenStream(
-  IStorage*        iface,
-  const OLECHAR*   pwcsName,  /* [string][in] */
-  void*            reserved1, /* [unique][in] */
-  DWORD            grfMode,   /* [in]  */
-  DWORD            reserved2, /* [in]  */
-  IStream**        ppstm)     /* [out] */
+static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
+    const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
+    ULONG *relation)
 {
-  StorageBaseImpl *This = impl_from_IStorage(iface);
-  StgStreamImpl*    newStream;
-  DirEntry          currentEntry;
-  DirRef            streamEntryRef;
-  HRESULT           res = STG_E_UNKNOWN;
+  DirRef childEntry;
+  DirEntry childData;
 
-  TRACE("(%p, %s, %p, %x, %d, %p)\n",
-	iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
+  /* Read the storage entry to find the root of the tree. */
+  StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
 
-  if ( (pwcsName==NULL) || (ppstm==0) )
-  {
-    res = E_INVALIDARG;
-    goto end;
-  }
+  *parentEntry = storageEntry;
+  *relation = DIRENTRY_RELATION_DIR;
 
-  *ppstm = NULL;
+  childEntry = parentData->dirRootEntry;
 
-  if ( FAILED( validateSTGM(grfMode) ) ||
-       STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
+  while (childEntry != DIRENTRY_NULL)
   {
-    res = STG_E_INVALIDFLAG;
-    goto end;
-  }
+    LONG cmp;
 
-  /*
-   * As documented.
-   */
-  if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
-  {
-    res = STG_E_INVALIDFUNCTION;
-    goto end;
-  }
+    StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
 
-  if (This->reverted)
-  {
-    res = STG_E_REVERTED;
-    goto end;
-  }
+    cmp = entryNameCmp(childName, childData.name);
 
-  /*
-   * Check that we're compatible with the parent's storage mode, but
-   * only if we are not in transacted mode
-   */
-  if(!(This->openFlags & STGM_TRANSACTED)) {
-    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
+    if (cmp == 0)
+      /* found it */
+      break;
+
+    else if (cmp < 0)
     {
-      res = STG_E_INVALIDFLAG;
-      goto end;
-    }
-  }
+      *parentData = childData;
+      *parentEntry = childEntry;
+      *relation = DIRENTRY_RELATION_PREVIOUS;
 
-  /*
-   * Search for the element with the given name
-   */
-  streamEntryRef = findElement(
-    This,
-    This->storageDirEntry,
-    pwcsName,
-    &currentEntry);
+      childEntry = parentData->leftChild;
+    }
 
-  /*
-   * If it was found, construct the stream object and return a pointer to it.
-   */
-  if ( (streamEntryRef!=DIRENTRY_NULL) &&
-       (currentEntry.stgType==STGTY_STREAM) )
-  {
-    if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
+    else if (cmp > 0)
     {
-      /* A single stream cannot be opened a second time. */
-      res = STG_E_ACCESSDENIED;
-      goto end;
-    }
-
-    newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
-
-    if (newStream)
-    {
-      newStream->grfMode = grfMode;
-      *ppstm = &newStream->IStream_iface;
-
-      IStream_AddRef(*ppstm);
+      *parentData = childData;
+      *parentEntry = childEntry;
+      *relation = DIRENTRY_RELATION_NEXT;
 
-      res = S_OK;
-      goto end;
+      childEntry = parentData->rightChild;
     }
-
-    res = E_OUTOFMEMORY;
-    goto end;
   }
 
-  res = STG_E_FILENOTFOUND;
+  if (childEntry == DIRENTRY_NULL)
+    return STG_E_FILENOTFOUND;
+  else
+    return S_OK;
+}
 
-end:
-  if (res == S_OK)
-    TRACE("<-- IStream %p\n", *ppstm);
-  TRACE("<-- %08x\n", res);
-  return res;
+static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
+{
+  switch (relation)
+  {
+    case DIRENTRY_RELATION_PREVIOUS:
+      entry->leftChild = new_target;
+      break;
+    case DIRENTRY_RELATION_NEXT:
+      entry->rightChild = new_target;
+      break;
+    case DIRENTRY_RELATION_DIR:
+      entry->dirRootEntry = new_target;
+      break;
+    default:
+      assert(0);
+  }
 }
 
-/************************************************************************
- * StorageBaseImpl_OpenStorage (IStorage)
+/****************************************************************************
  *
- * This method will open a new storage object from the current storage.
+ * Internal Method
  *
- * See Windows documentation for more details on IStorage methods.
+ * Add a directory entry to a storage
  */
-static HRESULT WINAPI StorageBaseImpl_OpenStorage(
-  IStorage*        iface,
-  const OLECHAR*   pwcsName,      /* [string][unique][in] */
-  IStorage*        pstgPriority,  /* [unique][in] */
-  DWORD            grfMode,       /* [in] */
-  SNB              snbExclude,    /* [unique][in] */
-  DWORD            reserved,      /* [in] */
-  IStorage**       ppstg)         /* [out] */
+static HRESULT insertIntoTree(
+  StorageBaseImpl *This,
+  DirRef        parentStorageIndex,
+  DirRef        newEntryIndex)
 {
-  StorageBaseImpl *This = impl_from_IStorage(iface);
-  StorageInternalImpl*   newStorage;
-  StorageBaseImpl*       newTransactedStorage;
-  DirEntry               currentEntry;
-  DirRef                 storageEntryRef;
-  HRESULT                res = STG_E_UNKNOWN;
-
-  TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
-	iface, debugstr_w(pwcsName), pstgPriority,
-	grfMode, snbExclude, reserved, ppstg);
-
-  if ((pwcsName==NULL) || (ppstg==0) )
-  {
-    res = E_INVALIDARG;
-    goto end;
-  }
-
-  if (This->openFlags & STGM_SIMPLE)
-  {
-    res = STG_E_INVALIDFUNCTION;
-    goto end;
-  }
-
-  /* as documented */
-  if (snbExclude != NULL)
-  {
-    res = STG_E_INVALIDPARAMETER;
-    goto end;
-  }
-
-  if ( FAILED( validateSTGM(grfMode) ))
-  {
-    res = STG_E_INVALIDFLAG;
-    goto end;
-  }
+  DirEntry currentEntry;
+  DirEntry newEntry;
 
   /*
-   * As documented.
+   * Read the inserted entry
    */
-  if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
-        (grfMode & STGM_DELETEONRELEASE) ||
-        (grfMode & STGM_PRIORITY) )
-  {
-    res = STG_E_INVALIDFUNCTION;
-    goto end;
-  }
-
-  if (This->reverted)
-    return STG_E_REVERTED;
+  StorageBaseImpl_ReadDirEntry(This,
+                               newEntryIndex,
+                               &newEntry);
 
   /*
-   * Check that we're compatible with the parent's storage mode,
-   * but only if we are not transacted
+   * Read the storage entry
    */
-  if(!(This->openFlags & STGM_TRANSACTED)) {
-    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
-    {
-      res = STG_E_ACCESSDENIED;
-      goto end;
-    }
-  }
+  StorageBaseImpl_ReadDirEntry(This,
+                               parentStorageIndex,
+                               &currentEntry);
 
-  *ppstg = NULL;
+  if (currentEntry.dirRootEntry != DIRENTRY_NULL)
+  {
+    /*
+     * The root storage contains some element, therefore, start the research
+     * for the appropriate location.
+     */
+    BOOL found = FALSE;
+    DirRef current, next, previous, currentEntryId;
 
-  storageEntryRef = findElement(
-                         This,
-                         This->storageDirEntry,
-                         pwcsName,
-                         &currentEntry);
+    /*
+     * Keep a reference to the root of the storage's element tree
+     */
+    currentEntryId = currentEntry.dirRootEntry;
 
-  if ( (storageEntryRef!=DIRENTRY_NULL) &&
-       (currentEntry.stgType==STGTY_STORAGE) )
-  {
-    if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
-    {
-      /* A single storage cannot be opened a second time. */
-      res = STG_E_ACCESSDENIED;
-      goto end;
-    }
+    /*
+     * Read
+     */
+    StorageBaseImpl_ReadDirEntry(This,
+                                 currentEntry.dirRootEntry,
+                                 &currentEntry);
 
-    newStorage = StorageInternalImpl_Construct(
-                   This,
-                   grfMode,
-                   storageEntryRef);
+    previous = currentEntry.leftChild;
+    next     = currentEntry.rightChild;
+    current  = currentEntryId;
 
-    if (newStorage != 0)
+    while (!found)
     {
-      if (grfMode & STGM_TRANSACTED)
-      {
-        res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
+      LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
 
-        if (FAILED(res))
+      if (diff < 0)
+      {
+        if (previous != DIRENTRY_NULL)
         {
-          HeapFree(GetProcessHeap(), 0, newStorage);
-          goto end;
+          StorageBaseImpl_ReadDirEntry(This,
+                                       previous,
+                                       &currentEntry);
+          current = previous;
+        }
+        else
+        {
+          currentEntry.leftChild = newEntryIndex;
+          StorageBaseImpl_WriteDirEntry(This,
+                                        current,
+                                        &currentEntry);
+          found = TRUE;
+        }
+      }
+      else if (diff > 0)
+      {
+        if (next != DIRENTRY_NULL)
+        {
+          StorageBaseImpl_ReadDirEntry(This,
+                                       next,
+                                       &currentEntry);
+          current = next;
+        }
+        else
+        {
+          currentEntry.rightChild = newEntryIndex;
+          StorageBaseImpl_WriteDirEntry(This,
+                                        current,
+                                        &currentEntry);
+          found = TRUE;
         }
-
-        *ppstg = &newTransactedStorage->IStorage_iface;
       }
       else
       {
-        *ppstg = &newStorage->base.IStorage_iface;
+	/*
+	 * Trying to insert an item with the same name in the
+	 * subtree structure.
+	 */
+	return STG_E_FILEALREADYEXISTS;
       }
 
-      list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
-
-      res = S_OK;
-      goto end;
+      previous = currentEntry.leftChild;
+      next     = currentEntry.rightChild;
     }
-
-    res = STG_E_INSUFFICIENTMEMORY;
-    goto end;
+  }
+  else
+  {
+    /*
+     * The storage is empty, make the new entry the root of its element tree
+     */
+    currentEntry.dirRootEntry = newEntryIndex;
+    StorageBaseImpl_WriteDirEntry(This,
+                                  parentStorageIndex,
+                                  &currentEntry);
   }
 
-  res = STG_E_FILENOTFOUND;
-
-end:
-  TRACE("<-- %08x\n", res);
-  return res;
+  return S_OK;
 }
 
-/************************************************************************
- * StorageBaseImpl_EnumElements (IStorage)
+/*************************************************************************
  *
- * This method will create an enumerator object that can be used to
- * retrieve information about all the elements in the storage object.
+ * Internal Method
  *
- * See Windows documentation for more details on IStorage methods.
+ * This method removes a directory entry from its parent storage tree without
+ * freeing any resources attached to it.
  */
-static HRESULT WINAPI StorageBaseImpl_EnumElements(
-  IStorage*       iface,
-  DWORD           reserved1, /* [in] */
-  void*           reserved2, /* [size_is][unique][in] */
-  DWORD           reserved3, /* [in] */
-  IEnumSTATSTG**  ppenum)    /* [out] */
+static HRESULT removeFromTree(
+  StorageBaseImpl *This,
+  DirRef        parentStorageIndex,
+  DirRef        deletedIndex)
 {
-  StorageBaseImpl *This = impl_from_IStorage(iface);
-  IEnumSTATSTGImpl* newEnum;
+  DirEntry   entryToDelete;
+  DirEntry   parentEntry;
+  DirRef parentEntryRef;
+  ULONG typeOfRelation;
+  HRESULT hr;
 
-  TRACE("(%p, %d, %p, %d, %p)\n",
-	iface, reserved1, reserved2, reserved3, ppenum);
+  hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
 
-  if (!ppenum)
-    return E_INVALIDARG;
+  if (hr != S_OK)
+    return hr;
 
-  if (This->reverted)
-    return STG_E_REVERTED;
+  /*
+   * Find the element that links to the one we want to delete.
+   */
+  hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
+    &parentEntry, &parentEntryRef, &typeOfRelation);
 
-  newEnum = IEnumSTATSTGImpl_Construct(
-              This,
-              This->storageDirEntry);
+  if (hr != S_OK)
+    return hr;
 
-  if (newEnum)
+  if (entryToDelete.leftChild != DIRENTRY_NULL)
   {
-    *ppenum = &newEnum->IEnumSTATSTG_iface;
-    return S_OK;
-  }
-
-  return E_OUTOFMEMORY;
-}
+    /*
+     * Replace the deleted entry with its left child
+     */
+    setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
 
-/************************************************************************
- * StorageBaseImpl_Stat (IStorage)
- *
- * This method will retrieve information about this storage object.
- *
- * See Windows documentation for more details on IStorage methods.
- */
-static HRESULT WINAPI StorageBaseImpl_Stat(
-  IStorage*        iface,
-  STATSTG*         pstatstg,     /* [out] */
-  DWORD            grfStatFlag)  /* [in] */
-{
-  StorageBaseImpl *This = impl_from_IStorage(iface);
-  DirEntry       currentEntry;
-  HRESULT        res = STG_E_UNKNOWN;
+    hr = StorageBaseImpl_WriteDirEntry(
+            This,
+            parentEntryRef,
+            &parentEntry);
+    if(FAILED(hr))
+    {
+      return hr;
+    }
 
-  TRACE("(%p, %p, %x)\n",
-	iface, pstatstg, grfStatFlag);
+    if (entryToDelete.rightChild != DIRENTRY_NULL)
+    {
+      /*
+       * We need to reinsert the right child somewhere. We already know it and
+       * its children are greater than everything in the left tree, so we
+       * insert it at the rightmost point in the left tree.
+       */
+      DirRef newRightChildParent = entryToDelete.leftChild;
+      DirEntry newRightChildParentEntry;
 
-  if (!pstatstg)
-  {
-    res = E_INVALIDARG;
-    goto end;
-  }
+      do
+      {
+        hr = StorageBaseImpl_ReadDirEntry(
+                This,
+                newRightChildParent,
+                &newRightChildParentEntry);
+        if (FAILED(hr))
+        {
+          return hr;
+        }
 
-  if (This->reverted)
-  {
-    res = STG_E_REVERTED;
-    goto end;
-  }
+        if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
+          newRightChildParent = newRightChildParentEntry.rightChild;
+      } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
 
-  res = StorageBaseImpl_ReadDirEntry(
-                    This,
-                    This->storageDirEntry,
-                    &currentEntry);
+      newRightChildParentEntry.rightChild = entryToDelete.rightChild;
 
-  if (SUCCEEDED(res))
+      hr = StorageBaseImpl_WriteDirEntry(
+              This,
+              newRightChildParent,
+              &newRightChildParentEntry);
+      if (FAILED(hr))
+      {
+        return hr;
+      }
+    }
+  }
+  else
   {
-    StorageUtl_CopyDirEntryToSTATSTG(
-      This,
-      pstatstg,
-      &currentEntry,
-      grfStatFlag);
+    /*
+     * Replace the deleted entry with its right child
+     */
+    setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
 
-    pstatstg->grfMode = This->openFlags;
-    pstatstg->grfStateBits = This->stateBits;
+    hr = StorageBaseImpl_WriteDirEntry(
+            This,
+            parentEntryRef,
+            &parentEntry);
+    if(FAILED(hr))
+    {
+      return hr;
+    }
   }
 
-end:
-  if (res == S_OK)
-  {
-    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);
-  }
-  TRACE("<-- %08x\n", res);
-  return res;
+  return hr;
 }
 
+
 /************************************************************************
- * StorageBaseImpl_RenameElement (IStorage)
- *
- * This method will rename the specified element.
+ * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
+ ***********************************************************************/
+
+/*
+ * IEnumSTATSTGImpl definitions.
  *
- * See Windows documentation for more details on IStorage methods.
+ * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
+ * This class allows iterating through the content of a storage and to find
+ * specific items inside it.
  */
-static HRESULT WINAPI StorageBaseImpl_RenameElement(
-            IStorage*        iface,
-            const OLECHAR*   pwcsOldName,  /* [in] */
-            const OLECHAR*   pwcsNewName)  /* [in] */
+struct IEnumSTATSTGImpl
 {
-  StorageBaseImpl *This = impl_from_IStorage(iface);
-  DirEntry          currentEntry;
-  DirRef            currentEntryRef;
-
-  TRACE("(%p, %s, %s)\n",
-	iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
-
-  if (This->reverted)
-    return STG_E_REVERTED;
-
-  currentEntryRef = findElement(This,
-                                   This->storageDirEntry,
-                                   pwcsNewName,
-                                   &currentEntry);
+  IEnumSTATSTG   IEnumSTATSTG_iface;
 
-  if (currentEntryRef != DIRENTRY_NULL)
-  {
-    /*
-     * There is already an element with the new name
-     */
-    return STG_E_FILEALREADYEXISTS;
-  }
+  LONG           ref;                   /* Reference count */
+  StorageBaseImpl* parentStorage;         /* Reference to the parent storage */
+  DirRef         storageDirEntry;     /* Directory entry of the storage to enumerate */
 
-  /*
-   * Search for the old element name
-   */
-  currentEntryRef = findElement(This,
-                                   This->storageDirEntry,
-                                   pwcsOldName,
-                                   &currentEntry);
+  WCHAR	         name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
+};
 
-  if (currentEntryRef != DIRENTRY_NULL)
-  {
-    if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
-        StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
-    {
-      WARN("Element is already open; cannot rename.\n");
-      return STG_E_ACCESSDENIED;
-    }
+static inline IEnumSTATSTGImpl *impl_from_IEnumSTATSTG(IEnumSTATSTG *iface)
+{
+  return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
+}
 
-    /* Remove the element from its current position in the tree */
-    removeFromTree(This, This->storageDirEntry,
-        currentEntryRef);
+static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
+{
+  IStorage_Release(&This->parentStorage->IStorage_iface);
+  HeapFree(GetProcessHeap(), 0, This);
+}
 
-    /* Change the name of the element */
-    strcpyW(currentEntry.name, pwcsNewName);
+static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
+  IEnumSTATSTG*     iface,
+  REFIID            riid,
+  void**            ppvObject)
+{
+  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
 
-    /* Delete any sibling links */
-    currentEntry.leftChild = DIRENTRY_NULL;
-    currentEntry.rightChild = DIRENTRY_NULL;
+  if (ppvObject==0)
+    return E_INVALIDARG;
 
-    StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
-        &currentEntry);
+  *ppvObject = 0;
 
-    /* Insert the element in a new position in the tree */
-    insertIntoTree(This, This->storageDirEntry,
-        currentEntryRef);
-  }
-  else
+  if (IsEqualGUID(&IID_IUnknown, riid) ||
+      IsEqualGUID(&IID_IEnumSTATSTG, riid))
   {
-    /*
-     * There is no element with the old name
-     */
-    return STG_E_FILENOTFOUND;
+    *ppvObject = &This->IEnumSTATSTG_iface;
+    IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
+    return S_OK;
   }
 
-  return StorageBaseImpl_Flush(This);
+  return E_NOINTERFACE;
 }
 
-/************************************************************************
- * StorageBaseImpl_CreateStream (IStorage)
- *
- * This method will create a stream object within this storage
- *
- * See Windows documentation for more details on IStorage methods.
- */
-static HRESULT WINAPI StorageBaseImpl_CreateStream(
-            IStorage*        iface,
-            const OLECHAR*   pwcsName,  /* [string][in] */
-            DWORD            grfMode,   /* [in] */
-            DWORD            reserved1, /* [in] */
-            DWORD            reserved2, /* [in] */
-            IStream**        ppstm)     /* [out] */
+static ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
+  IEnumSTATSTG* iface)
 {
-  StorageBaseImpl *This = impl_from_IStorage(iface);
-  StgStreamImpl*    newStream;
-  DirEntry          currentEntry, newStreamEntry;
-  DirRef            currentEntryRef, newStreamEntryRef;
-  HRESULT hr;
-
-  TRACE("(%p, %s, %x, %d, %d, %p)\n",
-	iface, debugstr_w(pwcsName), grfMode,
-	reserved1, reserved2, ppstm);
-
-  if (ppstm == 0)
-    return STG_E_INVALIDPOINTER;
-
-  if (pwcsName == 0)
-    return STG_E_INVALIDNAME;
-
-  if (reserved1 || reserved2)
-    return STG_E_INVALIDPARAMETER;
-
-  if ( FAILED( validateSTGM(grfMode) ))
-    return STG_E_INVALIDFLAG;
-
-  if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE) 
-    return STG_E_INVALIDFLAG;
+  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+  return InterlockedIncrement(&This->ref);
+}
 
-  if (This->reverted)
-    return STG_E_REVERTED;
+static ULONG   WINAPI IEnumSTATSTGImpl_Release(
+  IEnumSTATSTG* iface)
+{
+  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
 
-  /*
-   * As documented.
-   */
-  if ((grfMode & STGM_DELETEONRELEASE) ||
-      (grfMode & STGM_TRANSACTED))
-    return STG_E_INVALIDFUNCTION;
+  ULONG newRef;
 
-  /*
-   * Don't worry about permissions in transacted mode, as we can always write
-   * changes; we just can't always commit them.
-   */
-  if(!(This->openFlags & STGM_TRANSACTED)) {
-    /* Can't create a stream on read-only storage */
-    if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
-      return STG_E_ACCESSDENIED;
+  newRef = InterlockedDecrement(&This->ref);
 
-    /* Can't create a stream with greater access than the parent. */
-    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
-      return STG_E_ACCESSDENIED;
+  if (newRef==0)
+  {
+    IEnumSTATSTGImpl_Destroy(This);
   }
 
-  if(This->openFlags & STGM_SIMPLE)
-    if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
+  return newRef;
+}
 
-  *ppstm = 0;
+static HRESULT IEnumSTATSTGImpl_GetNextRef(
+  IEnumSTATSTGImpl* This,
+  DirRef *ref)
+{
+  DirRef result = DIRENTRY_NULL;
+  DirRef searchNode;
+  DirEntry entry;
+  HRESULT hr;
+  WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
 
-  currentEntryRef = findElement(This,
-                                   This->storageDirEntry,
-                                   pwcsName,
-                                   &currentEntry);
+  hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
+    This->parentStorage->storageDirEntry, &entry);
+  searchNode = entry.dirRootEntry;
 
-  if (currentEntryRef != DIRENTRY_NULL)
+  while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
   {
-    /*
-     * An element with this name already exists
-     */
-    if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
+    hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
+
+    if (SUCCEEDED(hr))
     {
-      IStorage_DestroyElement(iface, pwcsName);
+      LONG diff = entryNameCmp( entry.name, This->name);
+
+      if (diff <= 0)
+      {
+        searchNode = entry.rightChild;
+      }
+      else
+      {
+        result = searchNode;
+        memcpy(result_name, entry.name, sizeof(result_name));
+        searchNode = entry.leftChild;
+      }
     }
-    else
-      return STG_E_FILEALREADYEXISTS;
   }
 
-  /*
-   * memset the empty entry
-   */
-  memset(&newStreamEntry, 0, sizeof(DirEntry));
-
-  newStreamEntry.sizeOfNameString =
-      ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
-
-  if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
-    return STG_E_INVALIDNAME;
+  if (SUCCEEDED(hr))
+  {
+    *ref = result;
+    if (result != DIRENTRY_NULL)
+      memcpy(This->name, result_name, sizeof(result_name));
+  }
 
-  strcpyW(newStreamEntry.name, pwcsName);
+  return hr;
+}
 
-  newStreamEntry.stgType       = STGTY_STREAM;
-  newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
-  newStreamEntry.size.u.LowPart  = 0;
-  newStreamEntry.size.u.HighPart = 0;
+static HRESULT WINAPI IEnumSTATSTGImpl_Next(
+  IEnumSTATSTG* iface,
+  ULONG             celt,
+  STATSTG*          rgelt,
+  ULONG*            pceltFetched)
+{
+  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
 
-  newStreamEntry.leftChild        = DIRENTRY_NULL;
-  newStreamEntry.rightChild       = DIRENTRY_NULL;
-  newStreamEntry.dirRootEntry     = DIRENTRY_NULL;
+  DirEntry    currentEntry;
+  STATSTG*    currentReturnStruct = rgelt;
+  ULONG       objectFetched       = 0;
+  DirRef      currentSearchNode;
+  HRESULT     hr=S_OK;
 
-  /* call CoFileTime to get the current time
-  newStreamEntry.ctime
-  newStreamEntry.mtime
-  */
+  if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
+    return E_INVALIDARG;
 
-  /*  newStreamEntry.clsid */
+  if (This->parentStorage->reverted)
+    return STG_E_REVERTED;
 
   /*
-   * Create an entry with the new data
+   * To avoid the special case, get another pointer to a ULONG value if
+   * the caller didn't supply one.
    */
-  hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
-  if (FAILED(hr))
-    return hr;
+  if (pceltFetched==0)
+    pceltFetched = &objectFetched;
 
   /*
-   * Insert the new entry in the parent storage's tree.
+   * Start the iteration, we will iterate until we hit the end of the
+   * linked list or until we hit the number of items to iterate through
    */
-  hr = insertIntoTree(
-    This,
-    This->storageDirEntry,
-    newStreamEntryRef);
-  if (FAILED(hr))
+  *pceltFetched = 0;
+
+  while ( *pceltFetched < celt )
   {
-    StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
-    return hr;
-  }
+    hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
 
-  /*
-   * Open the stream to return it.
-   */
-  newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
+    if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
+      break;
 
-  if (newStream)
-  {
-    *ppstm = &newStream->IStream_iface;
-    IStream_AddRef(*ppstm);
-  }
-  else
-  {
-    return STG_E_INSUFFICIENTMEMORY;
+    /*
+     * Read the entry from the storage.
+     */
+    StorageBaseImpl_ReadDirEntry(This->parentStorage,
+      currentSearchNode,
+      &currentEntry);
+
+    /*
+     * Copy the information to the return buffer.
+     */
+    StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
+      currentReturnStruct,
+      &currentEntry,
+      STATFLAG_DEFAULT);
+
+    /*
+     * Step to the next item in the iteration
+     */
+    (*pceltFetched)++;
+    currentReturnStruct++;
   }
 
-  return StorageBaseImpl_Flush(This);
+  if (SUCCEEDED(hr) && *pceltFetched != celt)
+    hr = S_FALSE;
+
+  return hr;
 }
 
-/************************************************************************
- * StorageBaseImpl_SetClass (IStorage)
- *
- * This method will write the specified CLSID in the directory entry of this
- * storage.
- *
- * See Windows documentation for more details on IStorage methods.
- */
-static HRESULT WINAPI StorageBaseImpl_SetClass(
-  IStorage*        iface,
-  REFCLSID         clsid) /* [in] */
+
+static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
+  IEnumSTATSTG* iface,
+  ULONG             celt)
 {
-  StorageBaseImpl *This = impl_from_IStorage(iface);
-  HRESULT hRes;
-  DirEntry currentEntry;
+  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
 
-  TRACE("(%p, %p)\n", iface, clsid);
+  ULONG       objectFetched = 0;
+  DirRef      currentSearchNode;
+  HRESULT     hr=S_OK;
 
-  if (This->reverted)
+  if (This->parentStorage->reverted)
     return STG_E_REVERTED;
 
-  hRes = StorageBaseImpl_ReadDirEntry(This,
-                                      This->storageDirEntry,
-                                      &currentEntry);
-  if (SUCCEEDED(hRes))
+  while ( (objectFetched < celt) )
   {
-    currentEntry.clsid = *clsid;
-
-    hRes = StorageBaseImpl_WriteDirEntry(This,
-                                         This->storageDirEntry,
-                                         &currentEntry);
-  }
+    hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
 
-  if (SUCCEEDED(hRes))
-    hRes = StorageBaseImpl_Flush(This);
+    if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
+      break;
 
-  return hRes;
-}
+    objectFetched++;
+  }
 
-/************************************************************************
-** StorageBaseImpl implementation
-*/
+  if (SUCCEEDED(hr) && objectFetched != celt)
+    return S_FALSE;
 
-/************************************************************************
- * StorageBaseImpl_CreateStorage (IStorage)
- *
- * This method will create the storage object within the provided storage.
- *
- * See Windows documentation for more details on IStorage methods.
- */
-static HRESULT WINAPI StorageBaseImpl_CreateStorage(
-  IStorage*      iface,
-  const OLECHAR  *pwcsName, /* [string][in] */
-  DWORD            grfMode,   /* [in] */
-  DWORD            reserved1, /* [in] */
-  DWORD            reserved2, /* [in] */
-  IStorage       **ppstg)   /* [out] */
+  return hr;
+}
+
+static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
+  IEnumSTATSTG* iface)
 {
-  StorageBaseImpl* This = impl_from_IStorage(iface);
+  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
 
-  DirEntry         currentEntry;
-  DirEntry         newEntry;
-  DirRef           currentEntryRef;
-  DirRef           newEntryRef;
-  HRESULT          hr;
+  if (This->parentStorage->reverted)
+    return STG_E_REVERTED;
 
-  TRACE("(%p, %s, %x, %d, %d, %p)\n",
-	iface, debugstr_w(pwcsName), grfMode,
-	reserved1, reserved2, ppstg);
+  This->name[0] = 0;
 
-  if (ppstg == 0)
-    return STG_E_INVALIDPOINTER;
+  return S_OK;
+}
 
-  if (This->openFlags & STGM_SIMPLE)
-  {
-    return STG_E_INVALIDFUNCTION;
-  }
+static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageBaseImpl*,DirRef);
 
-  if (pwcsName == 0)
-    return STG_E_INVALIDNAME;
+static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
+  IEnumSTATSTG* iface,
+  IEnumSTATSTG**    ppenum)
+{
+  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+  IEnumSTATSTGImpl* newClone;
 
-  *ppstg = NULL;
+  if (This->parentStorage->reverted)
+    return STG_E_REVERTED;
 
-  if ( FAILED( validateSTGM(grfMode) ) ||
-       (grfMode & STGM_DELETEONRELEASE) )
+  if (ppenum==0)
+    return E_INVALIDARG;
+
+  newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
+               This->storageDirEntry);
+  if (!newClone)
   {
-    WARN("bad grfMode: 0x%x\n", grfMode);
-    return STG_E_INVALIDFLAG;
+    *ppenum = NULL;
+    return E_OUTOFMEMORY;
   }
 
-  if (This->reverted)
-    return STG_E_REVERTED;
-
   /*
-   * Check that we're compatible with the parent's storage mode
+   * The new clone enumeration must point to the same current node as
+   * the old one.
    */
-  if ( !(This->openFlags & STGM_TRANSACTED) &&
-       STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
-  {
-    WARN("access denied\n");
-    return STG_E_ACCESSDENIED;
-  }
+  memcpy(newClone->name, This->name, sizeof(newClone->name));
 
-  currentEntryRef = findElement(This,
-                                   This->storageDirEntry,
-                                   pwcsName,
-                                   &currentEntry);
+  *ppenum = &newClone->IEnumSTATSTG_iface;
 
-  if (currentEntryRef != DIRENTRY_NULL)
-  {
-    /*
-     * An element with this name already exists
-     */
-    if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
-        ((This->openFlags & STGM_TRANSACTED) ||
-         STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
-    {
-      hr = IStorage_DestroyElement(iface, pwcsName);
-      if (FAILED(hr))
-        return hr;
-    }
-    else
-    {
-      WARN("file already exists\n");
-      return STG_E_FILEALREADYEXISTS;
-    }
-  }
-  else if (!(This->openFlags & STGM_TRANSACTED) &&
-           STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
-  {
-    WARN("read-only storage\n");
-    return STG_E_ACCESSDENIED;
-  }
+  return S_OK;
+}
 
-  memset(&newEntry, 0, sizeof(DirEntry));
+/*
+ * Virtual function table for the IEnumSTATSTGImpl class.
+ */
+static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
+{
+    IEnumSTATSTGImpl_QueryInterface,
+    IEnumSTATSTGImpl_AddRef,
+    IEnumSTATSTGImpl_Release,
+    IEnumSTATSTGImpl_Next,
+    IEnumSTATSTGImpl_Skip,
+    IEnumSTATSTGImpl_Reset,
+    IEnumSTATSTGImpl_Clone
+};
 
-  newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
+static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
+  StorageBaseImpl* parentStorage,
+  DirRef         storageDirEntry)
+{
+  IEnumSTATSTGImpl* newEnumeration;
 
-  if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
+  newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
+
+  if (newEnumeration)
   {
-    FIXME("name too long\n");
-    return STG_E_INVALIDNAME;
+    newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
+    newEnumeration->ref = 1;
+    newEnumeration->name[0] = 0;
+
+    /*
+     * We want to nail-down the reference to the storage in case the
+     * enumeration out-lives the storage in the client application.
+     */
+    newEnumeration->parentStorage = parentStorage;
+    IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
+
+    newEnumeration->storageDirEntry = storageDirEntry;
   }
 
-  strcpyW(newEntry.name, pwcsName);
+  return newEnumeration;
+}
 
-  newEntry.stgType       = STGTY_STORAGE;
-  newEntry.startingBlock = BLOCK_END_OF_CHAIN;
-  newEntry.size.u.LowPart  = 0;
-  newEntry.size.u.HighPart = 0;
 
-  newEntry.leftChild        = DIRENTRY_NULL;
-  newEntry.rightChild       = DIRENTRY_NULL;
-  newEntry.dirRootEntry     = DIRENTRY_NULL;
+/************************************************************************
+ * StorageBaseImpl implementation
+ ***********************************************************************/
 
-  /* call CoFileTime to get the current time
-  newEntry.ctime
-  newEntry.mtime
-  */
+static inline StorageBaseImpl *impl_from_IStorage( IStorage *iface )
+{
+    return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
+}
 
-  /*  newEntry.clsid */
+/************************************************************************
+ * StorageBaseImpl_QueryInterface (IUnknown)
+ *
+ * This method implements the common QueryInterface for all IStorage
+ * implementations contained in this file.
+ *
+ * See Windows documentation for more details on IUnknown methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_QueryInterface(
+  IStorage*        iface,
+  REFIID             riid,
+  void**             ppvObject)
+{
+  StorageBaseImpl *This = impl_from_IStorage(iface);
 
-  /*
-   * Create a new directory entry for the storage
-   */
-  hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
-  if (FAILED(hr))
-    return hr;
+  if (!ppvObject)
+    return E_INVALIDARG;
 
-  /*
-   * Insert the new directory entry into the parent storage's tree
-   */
-  hr = insertIntoTree(
-    This,
-    This->storageDirEntry,
-    newEntryRef);
-  if (FAILED(hr))
+  *ppvObject = 0;
+
+  if (IsEqualGUID(&IID_IUnknown, riid) ||
+      IsEqualGUID(&IID_IStorage, riid))
   {
-    StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
-    return hr;
+    *ppvObject = &This->IStorage_iface;
   }
-
-  /*
-   * Open it to get a pointer to return.
-   */
-  hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
-
-  if( (hr != S_OK) || (*ppstg == NULL))
+  else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
   {
-    return hr;
+    *ppvObject = &This->IPropertySetStorage_iface;
+  }
+  /* locking interface is reported for writer only */
+  else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
+  {
+    *ppvObject = &This->IDirectWriterLock_iface;
   }
+  else
+    return E_NOINTERFACE;
 
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_Flush(This);
+  IStorage_AddRef(iface);
 
   return S_OK;
 }
 
-
-/***************************************************************************
+/************************************************************************
+ * StorageBaseImpl_AddRef (IUnknown)
  *
- * Internal Method
+ * This method implements the common AddRef for all IStorage
+ * implementations contained in this file.
  *
- * Reserve a directory entry in the file and initialize it.
+ * See Windows documentation for more details on IUnknown methods.
  */
-static HRESULT StorageImpl_CreateDirEntry(
-  StorageBaseImpl *base,
-  const DirEntry *newData,
-  DirRef *index)
+static ULONG WINAPI StorageBaseImpl_AddRef(
+            IStorage* iface)
 {
-  StorageImpl *storage = (StorageImpl*)base;
-  ULONG       currentEntryIndex    = 0;
-  ULONG       newEntryIndex        = DIRENTRY_NULL;
-  HRESULT hr = S_OK;
-  BYTE currentData[RAW_DIRENTRY_SIZE];
-  WORD sizeOfNameString;
+  StorageBaseImpl *This = impl_from_IStorage(iface);
+  ULONG ref = InterlockedIncrement(&This->ref);
 
-  do
+  TRACE("(%p) AddRef to %d\n", This, ref);
+
+  return ref;
+}
+
+/************************************************************************
+ * StorageBaseImpl_Release (IUnknown)
+ *
+ * This method implements the common Release for all IStorage
+ * implementations contained in this file.
+ *
+ * See Windows documentation for more details on IUnknown methods.
+ */
+static ULONG WINAPI StorageBaseImpl_Release(
+      IStorage* iface)
+{
+  StorageBaseImpl *This = impl_from_IStorage(iface);
+
+  ULONG ref = InterlockedDecrement(&This->ref);
+
+  TRACE("(%p) ReleaseRef to %d\n", This, ref);
+
+  if (ref == 0)
   {
-    hr = StorageImpl_ReadRawDirEntry(storage,
-                                     currentEntryIndex,
-                                     currentData);
+    /*
+     * Since we are using a system of base-classes, we want to call the
+     * destructor of the appropriate derived class. To do this, we are
+     * using virtual functions to implement the destructor.
+     */
+    StorageBaseImpl_Destroy(This);
+  }
 
-    if (SUCCEEDED(hr))
+  return ref;
+}
+
+static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
+    DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
+    SNB snbExclude, IStorage *pstgDest);
+
+static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
+    DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
+    SNB snbExclude, IStorage *pstgDest)
+{
+  DirEntry data;
+  HRESULT hr;
+  BOOL skip = FALSE;
+  IStorage *pstgTmp;
+  IStream *pstrChild, *pstrTmp;
+  STATSTG strStat;
+
+  if (srcEntry == DIRENTRY_NULL)
+    return S_OK;
+
+  hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
+
+  if (FAILED(hr))
+    return hr;
+
+  if ( snbExclude )
+  {
+    WCHAR **snb = snbExclude;
+
+    while ( *snb != NULL && !skip )
     {
-      StorageUtl_ReadWord(
-        currentData,
-        OFFSET_PS_NAMELENGTH,
-        &sizeOfNameString);
+      if ( lstrcmpW(data.name, *snb) == 0 )
+        skip = TRUE;
+      ++snb;
+    }
+  }
 
-      if (sizeOfNameString == 0)
+  if (!skip)
+  {
+    if (data.stgType == STGTY_STORAGE && !skip_storage)
+    {
+      /*
+       * create a new storage in destination storage
+       */
+      hr = IStorage_CreateStorage( pstgDest, data.name,
+                                   STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
+                                   0, 0,
+                                   &pstgTmp );
+
+      /*
+       * if it already exist, don't create a new one use this one
+       */
+      if (hr == STG_E_FILEALREADYEXISTS)
       {
-        /*
-         * The entry exists and is available, we found it.
-         */
-        newEntryIndex = currentEntryIndex;
+        hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
+                                   STGM_WRITE|STGM_SHARE_EXCLUSIVE,
+                                   NULL, 0, &pstgTmp );
+      }
+
+      if (SUCCEEDED(hr))
+      {
+        hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
+                                                 skip_stream, NULL, pstgTmp );
+
+        IStorage_Release(pstgTmp);
       }
     }
-    else
+    else if (data.stgType == STGTY_STREAM && !skip_stream)
     {
       /*
-       * We exhausted the directory entries, we will create more space below
+       * create a new stream in destination storage. If the stream already
+       * exist, it will be deleted and a new one will be created.
        */
-      newEntryIndex = currentEntryIndex;
-    }
-    currentEntryIndex++;
-
-  } while (newEntryIndex == DIRENTRY_NULL);
+      hr = IStorage_CreateStream( pstgDest, data.name,
+                                  STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
+                                  0, 0, &pstrTmp );
 
-  /*
-   * grow the directory stream
-   */
-  if (FAILED(hr))
-  {
-    BYTE           emptyData[RAW_DIRENTRY_SIZE];
-    ULARGE_INTEGER newSize;
-    ULONG          entryIndex;
-    ULONG          lastEntry     = 0;
-    ULONG          blockCount    = 0;
+      /*
+       * open child stream storage. This operation must succeed even if the
+       * stream is already open, so we use internal functions to do it.
+       */
+      if (hr == S_OK)
+      {
+        StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
 
-    /*
-     * obtain the new count of blocks in the directory stream
-     */
-    blockCount = BlockChainStream_GetCount(
-                   storage->rootBlockChain)+1;
+        if (streamimpl)
+        {
+          pstrChild = &streamimpl->IStream_iface;
+          if (pstrChild)
+            IStream_AddRef(pstrChild);
+        }
+        else
+        {
+          pstrChild = NULL;
+          hr = E_OUTOFMEMORY;
+        }
+      }
 
-    /*
-     * initialize the size used by the directory stream
-     */
-    newSize.QuadPart  = (ULONGLONG)storage->bigBlockSize * blockCount;
+      if (hr == S_OK)
+      {
+        /*
+         * Get the size of the source stream
+         */
+        IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
 
-    /*
-     * add a block to the directory stream
-     */
-    BlockChainStream_SetSize(storage->rootBlockChain, newSize);
+        /*
+         * Set the size of the destination stream.
+         */
+        IStream_SetSize(pstrTmp, strStat.cbSize);
 
-    /*
-     * memset the empty entry in order to initialize the unused newly
-     * created entries
-     */
-    memset(emptyData, 0, RAW_DIRENTRY_SIZE);
+        /*
+         * do the copy
+         */
+        hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
+                             NULL, NULL );
 
-    /*
-     * initialize them
-     */
-    lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
+        IStream_Release( pstrChild );
+      }
 
-    for(
-      entryIndex = newEntryIndex + 1;
-      entryIndex < lastEntry;
-      entryIndex++)
-    {
-      StorageImpl_WriteRawDirEntry(
-        storage,
-        entryIndex,
-        emptyData);
+      IStream_Release( pstrTmp );
     }
-
-    StorageImpl_SaveFileHeader(storage);
   }
 
-  UpdateRawDirEntry(currentData, newData);
-
-  hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
+  /* copy siblings */
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
+                                           skip_stream, snbExclude, pstgDest );
 
   if (SUCCEEDED(hr))
-    *index = newEntryIndex;
+    hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
+                                           skip_stream, snbExclude, pstgDest );
 
   return hr;
 }
 
-/***************************************************************************
- *
- * Internal Method
- *
- * Mark a directory entry in the file as free.
- */
-static HRESULT StorageImpl_DestroyDirEntry(
-  StorageBaseImpl *base,
-  DirRef index)
+static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
 {
-  BYTE emptyData[RAW_DIRENTRY_SIZE];
-  StorageImpl *storage = (StorageImpl*)base;
+  StgStreamImpl *strm;
 
-  memset(emptyData, 0, RAW_DIRENTRY_SIZE);
+  LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
+  {
+    if (strm->dirEntry == streamEntry)
+    {
+      return TRUE;
+    }
+  }
 
-  return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
+  return FALSE;
 }
 
-
-/****************************************************************************
- *
- * Internal Method
- *
- * Case insensitive comparison of DirEntry.name by first considering
- * their size.
- *
- * Returns <0 when name1 < name2
- *         >0 when name1 > name2
- *          0 when name1 == name2
- */
-static LONG entryNameCmp(
-    const OLECHAR *name1,
-    const OLECHAR *name2)
+static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
 {
-  LONG diff      = lstrlenW(name1) - lstrlenW(name2);
+  StorageInternalImpl *childstg;
 
-  while (diff == 0 && *name1 != 0)
+  LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
   {
-    /*
-     * We compare the string themselves only when they are of the same length
-     */
-    diff = toupperW(*name1++) - toupperW(*name2++);
+    if (childstg->base.storageDirEntry == storageEntry)
+    {
+      return TRUE;
+    }
   }
 
-  return diff;
+  return FALSE;
 }
 
-/****************************************************************************
+/************************************************************************
+ * StorageBaseImpl_OpenStream (IStorage)
  *
- * Internal Method
+ * This method will open the specified stream object from the current storage.
  *
- * Add a directory entry to a storage
+ * See Windows documentation for more details on IStorage methods.
  */
-static HRESULT insertIntoTree(
-  StorageBaseImpl *This,
-  DirRef        parentStorageIndex,
-  DirRef        newEntryIndex)
+static HRESULT WINAPI StorageBaseImpl_OpenStream(
+  IStorage*        iface,
+  const OLECHAR*   pwcsName,  /* [string][in] */
+  void*            reserved1, /* [unique][in] */
+  DWORD            grfMode,   /* [in]  */
+  DWORD            reserved2, /* [in]  */
+  IStream**        ppstm)     /* [out] */
 {
-  DirEntry currentEntry;
-  DirEntry newEntry;
+  StorageBaseImpl *This = impl_from_IStorage(iface);
+  StgStreamImpl*    newStream;
+  DirEntry          currentEntry;
+  DirRef            streamEntryRef;
+  HRESULT           res = STG_E_UNKNOWN;
 
-  /*
-   * Read the inserted entry
-   */
-  StorageBaseImpl_ReadDirEntry(This,
-                               newEntryIndex,
-                               &newEntry);
+  TRACE("(%p, %s, %p, %x, %d, %p)\n",
+	iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
+
+  if ( (pwcsName==NULL) || (ppstm==0) )
+  {
+    res = E_INVALIDARG;
+    goto end;
+  }
+
+  *ppstm = NULL;
+
+  if ( FAILED( validateSTGM(grfMode) ) ||
+       STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
+  {
+    res = STG_E_INVALIDFLAG;
+    goto end;
+  }
 
   /*
-   * Read the storage entry
+   * As documented.
    */
-  StorageBaseImpl_ReadDirEntry(This,
-                               parentStorageIndex,
-                               &currentEntry);
+  if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
+  {
+    res = STG_E_INVALIDFUNCTION;
+    goto end;
+  }
 
-  if (currentEntry.dirRootEntry != DIRENTRY_NULL)
+  if (This->reverted)
   {
-    /*
-     * The root storage contains some element, therefore, start the research
-     * for the appropriate location.
-     */
-    BOOL found = FALSE;
-    DirRef current, next, previous, currentEntryId;
+    res = STG_E_REVERTED;
+    goto end;
+  }
 
-    /*
-     * Keep a reference to the root of the storage's element tree
-     */
-    currentEntryId = currentEntry.dirRootEntry;
+  /*
+   * Check that we're compatible with the parent's storage mode, but
+   * only if we are not in transacted mode
+   */
+  if(!(This->openFlags & STGM_TRANSACTED)) {
+    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
+    {
+      res = STG_E_INVALIDFLAG;
+      goto end;
+    }
+  }
 
-    /*
-     * Read
-     */
-    StorageBaseImpl_ReadDirEntry(This,
-                                 currentEntry.dirRootEntry,
-                                 &currentEntry);
+  /*
+   * Search for the element with the given name
+   */
+  streamEntryRef = findElement(
+    This,
+    This->storageDirEntry,
+    pwcsName,
+    &currentEntry);
 
-    previous = currentEntry.leftChild;
-    next     = currentEntry.rightChild;
-    current  = currentEntryId;
+  /*
+   * If it was found, construct the stream object and return a pointer to it.
+   */
+  if ( (streamEntryRef!=DIRENTRY_NULL) &&
+       (currentEntry.stgType==STGTY_STREAM) )
+  {
+    if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
+    {
+      /* A single stream cannot be opened a second time. */
+      res = STG_E_ACCESSDENIED;
+      goto end;
+    }
 
-    while (!found)
+    newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
+
+    if (newStream)
     {
-      LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
+      newStream->grfMode = grfMode;
+      *ppstm = &newStream->IStream_iface;
 
-      if (diff < 0)
-      {
-        if (previous != DIRENTRY_NULL)
-        {
-          StorageBaseImpl_ReadDirEntry(This,
-                                       previous,
-                                       &currentEntry);
-          current = previous;
-        }
-        else
-        {
-          currentEntry.leftChild = newEntryIndex;
-          StorageBaseImpl_WriteDirEntry(This,
-                                        current,
-                                        &currentEntry);
-          found = TRUE;
-        }
-      }
-      else if (diff > 0)
-      {
-        if (next != DIRENTRY_NULL)
-        {
-          StorageBaseImpl_ReadDirEntry(This,
-                                       next,
-                                       &currentEntry);
-          current = next;
-        }
-        else
-        {
-          currentEntry.rightChild = newEntryIndex;
-          StorageBaseImpl_WriteDirEntry(This,
-                                        current,
-                                        &currentEntry);
-          found = TRUE;
-        }
-      }
-      else
-      {
-	/*
-	 * Trying to insert an item with the same name in the
-	 * subtree structure.
-	 */
-	return STG_E_FILEALREADYEXISTS;
-      }
+      IStream_AddRef(*ppstm);
 
-      previous = currentEntry.leftChild;
-      next     = currentEntry.rightChild;
+      res = S_OK;
+      goto end;
     }
-  }
-  else
-  {
-    /*
-     * The storage is empty, make the new entry the root of its element tree
-     */
-    currentEntry.dirRootEntry = newEntryIndex;
-    StorageBaseImpl_WriteDirEntry(This,
-                                  parentStorageIndex,
-                                  &currentEntry);
+
+    res = E_OUTOFMEMORY;
+    goto end;
   }
 
-  return S_OK;
+  res = STG_E_FILENOTFOUND;
+
+end:
+  if (res == S_OK)
+    TRACE("<-- IStream %p\n", *ppstm);
+  TRACE("<-- %08x\n", res);
+  return res;
 }
 
-/****************************************************************************
+/************************************************************************
+ * StorageBaseImpl_OpenStorage (IStorage)
  *
- * Internal Method
+ * This method will open a new storage object from the current storage.
  *
- * Find and read the element of a storage with the given name.
+ * See Windows documentation for more details on IStorage methods.
  */
-static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
-    const OLECHAR *name, DirEntry *data)
+static HRESULT WINAPI StorageBaseImpl_OpenStorage(
+  IStorage*        iface,
+  const OLECHAR*   pwcsName,      /* [string][unique][in] */
+  IStorage*        pstgPriority,  /* [unique][in] */
+  DWORD            grfMode,       /* [in] */
+  SNB              snbExclude,    /* [unique][in] */
+  DWORD            reserved,      /* [in] */
+  IStorage**       ppstg)         /* [out] */
 {
-  DirRef currentEntry;
+  StorageBaseImpl *This = impl_from_IStorage(iface);
+  StorageInternalImpl*   newStorage;
+  StorageBaseImpl*       newTransactedStorage;
+  DirEntry               currentEntry;
+  DirRef                 storageEntryRef;
+  HRESULT                res = STG_E_UNKNOWN;
 
-  /* Read the storage entry to find the root of the tree. */
-  StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
+  TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
+	iface, debugstr_w(pwcsName), pstgPriority,
+	grfMode, snbExclude, reserved, ppstg);
 
-  currentEntry = data->dirRootEntry;
+  if ((pwcsName==NULL) || (ppstg==0) )
+  {
+    res = E_INVALIDARG;
+    goto end;
+  }
 
-  while (currentEntry != DIRENTRY_NULL)
+  if (This->openFlags & STGM_SIMPLE)
   {
-    LONG cmp;
+    res = STG_E_INVALIDFUNCTION;
+    goto end;
+  }
 
-    StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
+  /* as documented */
+  if (snbExclude != NULL)
+  {
+    res = STG_E_INVALIDPARAMETER;
+    goto end;
+  }
 
-    cmp = entryNameCmp(name, data->name);
+  if ( FAILED( validateSTGM(grfMode) ))
+  {
+    res = STG_E_INVALIDFLAG;
+    goto end;
+  }
 
-    if (cmp == 0)
-      /* found it */
-      break;
-
-    else if (cmp < 0)
-      currentEntry = data->leftChild;
-
-    else if (cmp > 0)
-      currentEntry = data->rightChild;
+  /*
+   * As documented.
+   */
+  if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
+        (grfMode & STGM_DELETEONRELEASE) ||
+        (grfMode & STGM_PRIORITY) )
+  {
+    res = STG_E_INVALIDFUNCTION;
+    goto end;
   }
 
-  return currentEntry;
-}
-
-/****************************************************************************
- *
- * Internal Method
- *
- * Find and read the binary tree parent of the element with the given name.
- *
- * If there is no such element, find a place where it could be inserted and
- * return STG_E_FILENOTFOUND.
- */
-static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
-    const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
-    ULONG *relation)
-{
-  DirRef childEntry;
-  DirEntry childData;
+  if (This->reverted)
+    return STG_E_REVERTED;
 
-  /* Read the storage entry to find the root of the tree. */
-  StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
+  /*
+   * Check that we're compatible with the parent's storage mode,
+   * but only if we are not transacted
+   */
+  if(!(This->openFlags & STGM_TRANSACTED)) {
+    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
+    {
+      res = STG_E_ACCESSDENIED;
+      goto end;
+    }
+  }
 
-  *parentEntry = storageEntry;
-  *relation = DIRENTRY_RELATION_DIR;
+  *ppstg = NULL;
 
-  childEntry = parentData->dirRootEntry;
+  storageEntryRef = findElement(
+                         This,
+                         This->storageDirEntry,
+                         pwcsName,
+                         &currentEntry);
 
-  while (childEntry != DIRENTRY_NULL)
+  if ( (storageEntryRef!=DIRENTRY_NULL) &&
+       (currentEntry.stgType==STGTY_STORAGE) )
   {
-    LONG cmp;
-
-    StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
-
-    cmp = entryNameCmp(childName, childData.name);
+    if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
+    {
+      /* A single storage cannot be opened a second time. */
+      res = STG_E_ACCESSDENIED;
+      goto end;
+    }
 
-    if (cmp == 0)
-      /* found it */
-      break;
+    newStorage = StorageInternalImpl_Construct(
+                   This,
+                   grfMode,
+                   storageEntryRef);
 
-    else if (cmp < 0)
+    if (newStorage != 0)
     {
-      *parentData = childData;
-      *parentEntry = childEntry;
-      *relation = DIRENTRY_RELATION_PREVIOUS;
+      if (grfMode & STGM_TRANSACTED)
+      {
+        res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
 
-      childEntry = parentData->leftChild;
-    }
+        if (FAILED(res))
+        {
+          HeapFree(GetProcessHeap(), 0, newStorage);
+          goto end;
+        }
 
-    else if (cmp > 0)
-    {
-      *parentData = childData;
-      *parentEntry = childEntry;
-      *relation = DIRENTRY_RELATION_NEXT;
+        *ppstg = &newTransactedStorage->IStorage_iface;
+      }
+      else
+      {
+        *ppstg = &newStorage->base.IStorage_iface;
+      }
 
-      childEntry = parentData->rightChild;
+      list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
+
+      res = S_OK;
+      goto end;
     }
+
+    res = STG_E_INSUFFICIENTMEMORY;
+    goto end;
   }
 
-  if (childEntry == DIRENTRY_NULL)
-    return STG_E_FILENOTFOUND;
-  else
-    return S_OK;
+  res = STG_E_FILENOTFOUND;
+
+end:
+  TRACE("<-- %08x\n", res);
+  return res;
 }
 
+/************************************************************************
+ * StorageBaseImpl_EnumElements (IStorage)
+ *
+ * This method will create an enumerator object that can be used to
+ * retrieve information about all the elements in the storage object.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_EnumElements(
+  IStorage*       iface,
+  DWORD           reserved1, /* [in] */
+  void*           reserved2, /* [size_is][unique][in] */
+  DWORD           reserved3, /* [in] */
+  IEnumSTATSTG**  ppenum)    /* [out] */
+{
+  StorageBaseImpl *This = impl_from_IStorage(iface);
+  IEnumSTATSTGImpl* newEnum;
+
+  TRACE("(%p, %d, %p, %d, %p)\n",
+	iface, reserved1, reserved2, reserved3, ppenum);
 
-static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
-    DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
-    SNB snbExclude, IStorage *pstgDest);
+  if (!ppenum)
+    return E_INVALIDARG;
 
-static HRESULT StorageBaseImpl_CopyChildEntryTo(StorageBaseImpl *This,
-    DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
-    SNB snbExclude, IStorage *pstgDest)
-{
-  DirEntry data;
-  HRESULT hr;
-  BOOL skip = FALSE;
-  IStorage *pstgTmp;
-  IStream *pstrChild, *pstrTmp;
-  STATSTG strStat;
+  if (This->reverted)
+    return STG_E_REVERTED;
 
-  if (srcEntry == DIRENTRY_NULL)
+  newEnum = IEnumSTATSTGImpl_Construct(
+              This,
+              This->storageDirEntry);
+
+  if (newEnum)
+  {
+    *ppenum = &newEnum->IEnumSTATSTG_iface;
     return S_OK;
+  }
 
-  hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
+  return E_OUTOFMEMORY;
+}
 
-  if (FAILED(hr))
-    return hr;
+/************************************************************************
+ * StorageBaseImpl_Stat (IStorage)
+ *
+ * This method will retrieve information about this storage object.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_Stat(
+  IStorage*        iface,
+  STATSTG*         pstatstg,     /* [out] */
+  DWORD            grfStatFlag)  /* [in] */
+{
+  StorageBaseImpl *This = impl_from_IStorage(iface);
+  DirEntry       currentEntry;
+  HRESULT        res = STG_E_UNKNOWN;
 
-  if ( snbExclude )
-  {
-    WCHAR **snb = snbExclude;
+  TRACE("(%p, %p, %x)\n",
+	iface, pstatstg, grfStatFlag);
 
-    while ( *snb != NULL && !skip )
-    {
-      if ( lstrcmpW(data.name, *snb) == 0 )
-        skip = TRUE;
-      ++snb;
-    }
+  if (!pstatstg)
+  {
+    res = E_INVALIDARG;
+    goto end;
   }
 
-  if (!skip)
+  if (This->reverted)
   {
-    if (data.stgType == STGTY_STORAGE && !skip_storage)
-    {
-      /*
-       * create a new storage in destination storage
-       */
-      hr = IStorage_CreateStorage( pstgDest, data.name,
-                                   STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
-                                   0, 0,
-                                   &pstgTmp );
+    res = STG_E_REVERTED;
+    goto end;
+  }
 
-      /*
-       * if it already exist, don't create a new one use this one
-       */
-      if (hr == STG_E_FILEALREADYEXISTS)
-      {
-        hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
-                                   STGM_WRITE|STGM_SHARE_EXCLUSIVE,
-                                   NULL, 0, &pstgTmp );
-      }
+  res = StorageBaseImpl_ReadDirEntry(
+                    This,
+                    This->storageDirEntry,
+                    &currentEntry);
 
-      if (SUCCEEDED(hr))
-      {
-        hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
-                                                 skip_stream, NULL, pstgTmp );
+  if (SUCCEEDED(res))
+  {
+    StorageUtl_CopyDirEntryToSTATSTG(
+      This,
+      pstatstg,
+      &currentEntry,
+      grfStatFlag);
 
-        IStorage_Release(pstgTmp);
-      }
-    }
-    else if (data.stgType == STGTY_STREAM && !skip_stream)
-    {
-      /*
-       * create a new stream in destination storage. If the stream already
-       * exist, it will be deleted and a new one will be created.
-       */
-      hr = IStorage_CreateStream( pstgDest, data.name,
-                                  STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
-                                  0, 0, &pstrTmp );
+    pstatstg->grfMode = This->openFlags;
+    pstatstg->grfStateBits = This->stateBits;
+  }
 
-      /*
-       * open child stream storage. This operation must succeed even if the
-       * stream is already open, so we use internal functions to do it.
-       */
-      if (hr == S_OK)
-      {
-        StgStreamImpl *streamimpl = StgStreamImpl_Construct(This, STGM_READ|STGM_SHARE_EXCLUSIVE, srcEntry);
-
-        if (streamimpl)
-        {
-          pstrChild = &streamimpl->IStream_iface;
-          if (pstrChild)
-            IStream_AddRef(pstrChild);
-        }
-        else
-        {
-          pstrChild = NULL;
-          hr = E_OUTOFMEMORY;
-        }
-      }
+end:
+  if (res == S_OK)
+  {
+    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);
+  }
+  TRACE("<-- %08x\n", res);
+  return res;
+}
 
-      if (hr == S_OK)
-      {
-        /*
-         * Get the size of the source stream
-         */
-        IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
+/************************************************************************
+ * StorageBaseImpl_RenameElement (IStorage)
+ *
+ * This method will rename the specified element.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_RenameElement(
+            IStorage*        iface,
+            const OLECHAR*   pwcsOldName,  /* [in] */
+            const OLECHAR*   pwcsNewName)  /* [in] */
+{
+  StorageBaseImpl *This = impl_from_IStorage(iface);
+  DirEntry          currentEntry;
+  DirRef            currentEntryRef;
 
-        /*
-         * Set the size of the destination stream.
-         */
-        IStream_SetSize(pstrTmp, strStat.cbSize);
+  TRACE("(%p, %s, %s)\n",
+	iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
 
-        /*
-         * do the copy
-         */
-        hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
-                             NULL, NULL );
+  if (This->reverted)
+    return STG_E_REVERTED;
 
-        IStream_Release( pstrChild );
-      }
+  currentEntryRef = findElement(This,
+                                   This->storageDirEntry,
+                                   pwcsNewName,
+                                   &currentEntry);
 
-      IStream_Release( pstrTmp );
-    }
+  if (currentEntryRef != DIRENTRY_NULL)
+  {
+    /*
+     * There is already an element with the new name
+     */
+    return STG_E_FILEALREADYEXISTS;
   }
 
-  /* copy siblings */
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
-                                           skip_stream, snbExclude, pstgDest );
+  /*
+   * Search for the old element name
+   */
+  currentEntryRef = findElement(This,
+                                   This->storageDirEntry,
+                                   pwcsOldName,
+                                   &currentEntry);
 
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
-                                           skip_stream, snbExclude, pstgDest );
+  if (currentEntryRef != DIRENTRY_NULL)
+  {
+    if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
+        StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
+    {
+      WARN("Element is already open; cannot rename.\n");
+      return STG_E_ACCESSDENIED;
+    }
 
-  return hr;
-}
+    /* Remove the element from its current position in the tree */
+    removeFromTree(This, This->storageDirEntry,
+        currentEntryRef);
 
-static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
-    DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
-    SNB snbExclude, IStorage *pstgDest)
-{
-  DirEntry data;
-  HRESULT hr;
+    /* Change the name of the element */
+    strcpyW(currentEntry.name, pwcsNewName);
 
-  hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
+    /* Delete any sibling links */
+    currentEntry.leftChild = DIRENTRY_NULL;
+    currentEntry.rightChild = DIRENTRY_NULL;
 
-  if (SUCCEEDED(hr))
-    hr = IStorage_SetClass( pstgDest, &data.clsid );
+    StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
+        &currentEntry);
 
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
-      skip_stream, snbExclude, pstgDest );
+    /* Insert the element in a new position in the tree */
+    insertIntoTree(This, This->storageDirEntry,
+        currentEntryRef);
+  }
+  else
+  {
+    /*
+     * There is no element with the old name
+     */
+    return STG_E_FILENOTFOUND;
+  }
 
-  return hr;
+  return StorageBaseImpl_Flush(This);
 }
 
-/*************************************************************************
- * CopyTo (IStorage)
+/************************************************************************
+ * StorageBaseImpl_CreateStream (IStorage)
+ *
+ * This method will create a stream object within this storage
+ *
+ * See Windows documentation for more details on IStorage methods.
  */
-static HRESULT WINAPI StorageBaseImpl_CopyTo(
-  IStorage*   iface,
-  DWORD       ciidExclude,  /* [in] */
-  const IID*  rgiidExclude, /* [size_is][unique][in] */
-  SNB         snbExclude,   /* [unique][in] */
-  IStorage*   pstgDest)     /* [unique][in] */
+static HRESULT WINAPI StorageBaseImpl_CreateStream(
+            IStorage*        iface,
+            const OLECHAR*   pwcsName,  /* [string][in] */
+            DWORD            grfMode,   /* [in] */
+            DWORD            reserved1, /* [in] */
+            DWORD            reserved2, /* [in] */
+            IStream**        ppstm)     /* [out] */
 {
   StorageBaseImpl *This = impl_from_IStorage(iface);
+  StgStreamImpl*    newStream;
+  DirEntry          currentEntry, newStreamEntry;
+  DirRef            currentEntryRef, newStreamEntryRef;
+  HRESULT hr;
 
-  BOOL         skip_storage = FALSE, skip_stream = FALSE;
-  DWORD        i;
-
-  TRACE("(%p, %d, %p, %p, %p)\n",
-	iface, ciidExclude, rgiidExclude,
-	snbExclude, pstgDest);
+  TRACE("(%p, %s, %x, %d, %d, %p)\n",
+	iface, debugstr_w(pwcsName), grfMode,
+	reserved1, reserved2, ppstm);
 
-  if ( pstgDest == 0 )
+  if (ppstm == 0)
     return STG_E_INVALIDPOINTER;
 
-  for(i = 0; i < ciidExclude; ++i)
-  {
-    if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
-        skip_storage = TRUE;
-    else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
-        skip_stream = TRUE;
-    else
-        WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
-  }
+  if (pwcsName == 0)
+    return STG_E_INVALIDNAME;
 
-  if (!skip_storage)
-  {
-    /* Give up early if it looks like this would be infinitely recursive.
-     * Oddly enough, this includes some cases that aren't really recursive, like
-     * copying to a transacted child. */
-    IStorage *pstgDestAncestor = pstgDest;
-    IStorage *pstgDestAncestorChild = NULL;
+  if (reserved1 || reserved2)
+    return STG_E_INVALIDPARAMETER;
 
-    /* Go up the chain from the destination until we find the source storage. */
-    while (pstgDestAncestor != iface) {
-      pstgDestAncestorChild = pstgDest;
+  if ( FAILED( validateSTGM(grfMode) ))
+    return STG_E_INVALIDFLAG;
 
-      if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
-      {
-        TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
+  if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
+    return STG_E_INVALIDFLAG;
 
-        pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
-      }
-      else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
-      {
-        StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
+  if (This->reverted)
+    return STG_E_REVERTED;
 
-        pstgDestAncestor = &internal->parentStorage->IStorage_iface;
-      }
-      else
-        break;
-    }
+  /*
+   * As documented.
+   */
+  if ((grfMode & STGM_DELETEONRELEASE) ||
+      (grfMode & STGM_TRANSACTED))
+    return STG_E_INVALIDFUNCTION;
 
-    if (pstgDestAncestor == iface)
-    {
-      BOOL fail = TRUE;
+  /*
+   * Don't worry about permissions in transacted mode, as we can always write
+   * changes; we just can't always commit them.
+   */
+  if(!(This->openFlags & STGM_TRANSACTED)) {
+    /* Can't create a stream on read-only storage */
+    if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
+      return STG_E_ACCESSDENIED;
 
-      if (pstgDestAncestorChild && snbExclude)
-      {
-        StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
-        DirEntry data;
-        WCHAR **snb = snbExclude;
+    /* Can't create a stream with greater access than the parent. */
+    if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
+      return STG_E_ACCESSDENIED;
+  }
 
-        StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
+  if(This->openFlags & STGM_SIMPLE)
+    if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
 
-        while ( *snb != NULL && fail )
-        {
-          if ( lstrcmpW(data.name, *snb) == 0 )
-            fail = FALSE;
-          ++snb;
-        }
-      }
+  *ppstm = 0;
 
-      if (fail)
-        return STG_E_ACCESSDENIED;
-    }
-  }
+  currentEntryRef = findElement(This,
+                                   This->storageDirEntry,
+                                   pwcsName,
+                                   &currentEntry);
 
-  return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
-    skip_storage, skip_stream, snbExclude, pstgDest );
-}
+  if (currentEntryRef != DIRENTRY_NULL)
+  {
+    /*
+     * An element with this name already exists
+     */
+    if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
+    {
+      IStorage_DestroyElement(iface, pwcsName);
+    }
+    else
+      return STG_E_FILEALREADYEXISTS;
+  }
 
-/*************************************************************************
- * MoveElementTo (IStorage)
- */
-static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
-  IStorage*     iface,
-  const OLECHAR *pwcsName,   /* [string][in] */
-  IStorage      *pstgDest,   /* [unique][in] */
-  const OLECHAR *pwcsNewName,/* [string][in] */
-  DWORD           grfFlags)    /* [in] */
-{
-  FIXME("(%p %s %p %s %u): stub\n", iface,
-         debugstr_w(pwcsName), pstgDest,
-         debugstr_w(pwcsNewName), grfFlags);
-  return E_NOTIMPL;
-}
+  /*
+   * memset the empty entry
+   */
+  memset(&newStreamEntry, 0, sizeof(DirEntry));
 
-/*************************************************************************
- * Commit (IStorage)
- *
- * Ensures that any changes made to a storage object open in transacted mode
- * are reflected in the parent storage
- *
- * In a non-transacted mode, this ensures all cached writes are completed.
- */
-static HRESULT WINAPI StorageImpl_Commit(
-  IStorage*   iface,
-  DWORD         grfCommitFlags)/* [in] */
-{
-  StorageBaseImpl* This = impl_from_IStorage(iface);
-  TRACE("(%p %d)\n", iface, grfCommitFlags);
-  return StorageBaseImpl_Flush(This);
-}
+  newStreamEntry.sizeOfNameString =
+      ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
 
-/*************************************************************************
- * Revert (IStorage)
- *
- * Discard all changes that have been made since the last commit operation
- */
-static HRESULT WINAPI StorageImpl_Revert(
-  IStorage* iface)
-{
-  TRACE("(%p)\n", iface);
-  return S_OK;
-}
+  if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
+    return STG_E_INVALIDNAME;
 
-/*************************************************************************
- * DestroyElement (IStorage)
- *
- * Strategy: This implementation is built this way for simplicity not for speed.
- *          I always delete the topmost element of the enumeration and adjust
- *          the deleted element pointer all the time.  This takes longer to
- *          do but allow to reinvoke DestroyElement whenever we encounter a
- *          storage object.  The optimisation resides in the usage of another
- *          enumeration strategy that would give all the leaves of a storage
- *          first. (postfix order)
- */
-static HRESULT WINAPI StorageBaseImpl_DestroyElement(
-  IStorage*     iface,
-  const OLECHAR *pwcsName)/* [string][in] */
-{
-  StorageBaseImpl *This = impl_from_IStorage(iface);
+  strcpyW(newStreamEntry.name, pwcsName);
 
-  HRESULT           hr = S_OK;
-  DirEntry          entryToDelete;
-  DirRef            entryToDeleteRef;
+  newStreamEntry.stgType       = STGTY_STREAM;
+  newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
+  newStreamEntry.size.u.LowPart  = 0;
+  newStreamEntry.size.u.HighPart = 0;
 
-  TRACE("(%p, %s)\n",
-	iface, debugstr_w(pwcsName));
+  newStreamEntry.leftChild        = DIRENTRY_NULL;
+  newStreamEntry.rightChild       = DIRENTRY_NULL;
+  newStreamEntry.dirRootEntry     = DIRENTRY_NULL;
 
-  if (pwcsName==NULL)
-    return STG_E_INVALIDPOINTER;
+  /* call CoFileTime to get the current time
+  newStreamEntry.ctime
+  newStreamEntry.mtime
+  */
 
-  if (This->reverted)
-    return STG_E_REVERTED;
+  /*  newStreamEntry.clsid */
 
-  if ( !(This->openFlags & STGM_TRANSACTED) &&
-       STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
-    return STG_E_ACCESSDENIED;
+  /*
+   * Create an entry with the new data
+   */
+  hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
+  if (FAILED(hr))
+    return hr;
 
-  entryToDeleteRef = findElement(
+  /*
+   * Insert the new entry in the parent storage's tree.
+   */
+  hr = insertIntoTree(
     This,
     This->storageDirEntry,
-    pwcsName,
-    &entryToDelete);
-
-  if ( entryToDeleteRef == DIRENTRY_NULL )
+    newStreamEntryRef);
+  if (FAILED(hr))
   {
-    return STG_E_FILENOTFOUND;
+    StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
+    return hr;
   }
 
-  if ( entryToDelete.stgType == STGTY_STORAGE )
+  /*
+   * Open the stream to return it.
+   */
+  newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
+
+  if (newStream)
   {
-    hr = deleteStorageContents(
-           This,
-           entryToDeleteRef,
-           entryToDelete);
+    *ppstm = &newStream->IStream_iface;
+    IStream_AddRef(*ppstm);
   }
-  else if ( entryToDelete.stgType == STGTY_STREAM )
+  else
   {
-    hr = deleteStreamContents(
-           This,
-           entryToDeleteRef,
-           entryToDelete);
+    return STG_E_INSUFFICIENTMEMORY;
   }
 
-  if (hr!=S_OK)
-    return hr;
+  return StorageBaseImpl_Flush(This);
+}
 
-  /*
-   * Remove the entry from its parent storage
-   */
-  hr = removeFromTree(
-        This,
-        This->storageDirEntry,
-        entryToDeleteRef);
+/************************************************************************
+ * StorageBaseImpl_SetClass (IStorage)
+ *
+ * This method will write the specified CLSID in the directory entry of this
+ * storage.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_SetClass(
+  IStorage*        iface,
+  REFCLSID         clsid) /* [in] */
+{
+  StorageBaseImpl *This = impl_from_IStorage(iface);
+  HRESULT hRes;
+  DirEntry currentEntry;
 
-  /*
-   * Invalidate the entry
-   */
-  if (SUCCEEDED(hr))
-    StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
+  TRACE("(%p, %p)\n", iface, clsid);
 
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_Flush(This);
+  if (This->reverted)
+    return STG_E_REVERTED;
 
-  return hr;
-}
+  hRes = StorageBaseImpl_ReadDirEntry(This,
+                                      This->storageDirEntry,
+                                      &currentEntry);
+  if (SUCCEEDED(hRes))
+  {
+    currentEntry.clsid = *clsid;
 
+    hRes = StorageBaseImpl_WriteDirEntry(This,
+                                         This->storageDirEntry,
+                                         &currentEntry);
+  }
 
-/******************************************************************************
- * Internal stream list handlers
- */
+  if (SUCCEEDED(hRes))
+    hRes = StorageBaseImpl_Flush(This);
 
-void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
-{
-  TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
-  list_add_tail(&stg->strmHead,&strm->StrmListEntry);
+  return hRes;
 }
 
-void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
+/************************************************************************
+ * StorageBaseImpl_CreateStorage (IStorage)
+ *
+ * This method will create the storage object within the provided storage.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT WINAPI StorageBaseImpl_CreateStorage(
+  IStorage*      iface,
+  const OLECHAR  *pwcsName, /* [string][in] */
+  DWORD            grfMode,   /* [in] */
+  DWORD            reserved1, /* [in] */
+  DWORD            reserved2, /* [in] */
+  IStorage       **ppstg)   /* [out] */
 {
-  TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
-  list_remove(&(strm->StrmListEntry));
-}
+  StorageBaseImpl* This = impl_from_IStorage(iface);
 
-static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
-{
-  StgStreamImpl *strm;
+  DirEntry         currentEntry;
+  DirEntry         newEntry;
+  DirRef           currentEntryRef;
+  DirRef           newEntryRef;
+  HRESULT          hr;
 
-  LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
+  TRACE("(%p, %s, %x, %d, %d, %p)\n",
+	iface, debugstr_w(pwcsName), grfMode,
+	reserved1, reserved2, ppstg);
+
+  if (ppstg == 0)
+    return STG_E_INVALIDPOINTER;
+
+  if (This->openFlags & STGM_SIMPLE)
   {
-    if (strm->dirEntry == streamEntry)
-    {
-      return TRUE;
-    }
+    return STG_E_INVALIDFUNCTION;
   }
 
-  return FALSE;
-}
+  if (pwcsName == 0)
+    return STG_E_INVALIDNAME;
 
-static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
-{
-  StorageInternalImpl *childstg;
+  *ppstg = NULL;
 
-  LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
+  if ( FAILED( validateSTGM(grfMode) ) ||
+       (grfMode & STGM_DELETEONRELEASE) )
   {
-    if (childstg->base.storageDirEntry == storageEntry)
-    {
-      return TRUE;
-    }
+    WARN("bad grfMode: 0x%x\n", grfMode);
+    return STG_E_INVALIDFLAG;
   }
 
-  return FALSE;
-}
-
-static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
-{
-  struct list *cur, *cur2;
-  StgStreamImpl *strm=NULL;
-  StorageInternalImpl *childstg=NULL;
+  if (This->reverted)
+    return STG_E_REVERTED;
 
-  LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
-    strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
-    TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
-    strm->parentStorage = NULL;
-    list_remove(cur);
+  /*
+   * Check that we're compatible with the parent's storage mode
+   */
+  if ( !(This->openFlags & STGM_TRANSACTED) &&
+       STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
+  {
+    WARN("access denied\n");
+    return STG_E_ACCESSDENIED;
   }
 
-  LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
-    childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
-    StorageBaseImpl_Invalidate( &childstg->base );
-  }
+  currentEntryRef = findElement(This,
+                                   This->storageDirEntry,
+                                   pwcsName,
+                                   &currentEntry);
 
-  if (stg->transactedChild)
+  if (currentEntryRef != DIRENTRY_NULL)
   {
-    StorageBaseImpl_Invalidate(stg->transactedChild);
-
-    stg->transactedChild = NULL;
+    /*
+     * An element with this name already exists
+     */
+    if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
+        ((This->openFlags & STGM_TRANSACTED) ||
+         STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
+    {
+      hr = IStorage_DestroyElement(iface, pwcsName);
+      if (FAILED(hr))
+        return hr;
+    }
+    else
+    {
+      WARN("file already exists\n");
+      return STG_E_FILEALREADYEXISTS;
+    }
+  }
+  else if (!(This->openFlags & STGM_TRANSACTED) &&
+           STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
+  {
+    WARN("read-only storage\n");
+    return STG_E_ACCESSDENIED;
   }
-}
 
+  memset(&newEntry, 0, sizeof(DirEntry));
 
-/*********************************************************************
- *
- * Internal Method
- *
- * Delete the contents of a storage entry.
- *
- */
-static HRESULT deleteStorageContents(
-  StorageBaseImpl *parentStorage,
-  DirRef       indexToDelete,
-  DirEntry     entryDataToDelete)
-{
-  IEnumSTATSTG *elements     = 0;
-  IStorage   *childStorage = 0;
-  STATSTG      currentElement;
-  HRESULT      hr;
-  HRESULT      destroyHr = S_OK;
-  StorageInternalImpl *stg, *stg2;
+  newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
 
-  /* Invalidate any open storage objects. */
-  LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
+  if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
   {
-    if (stg->base.storageDirEntry == indexToDelete)
-    {
-      StorageBaseImpl_Invalidate(&stg->base);
-    }
+    FIXME("name too long\n");
+    return STG_E_INVALIDNAME;
   }
 
+  strcpyW(newEntry.name, pwcsName);
+
+  newEntry.stgType       = STGTY_STORAGE;
+  newEntry.startingBlock = BLOCK_END_OF_CHAIN;
+  newEntry.size.u.LowPart  = 0;
+  newEntry.size.u.HighPart = 0;
+
+  newEntry.leftChild        = DIRENTRY_NULL;
+  newEntry.rightChild       = DIRENTRY_NULL;
+  newEntry.dirRootEntry     = DIRENTRY_NULL;
+
+  /* call CoFileTime to get the current time
+  newEntry.ctime
+  newEntry.mtime
+  */
+
+  /*  newEntry.clsid */
+
   /*
-   * Open the storage and enumerate it
+   * Create a new directory entry for the storage
    */
-  hr = IStorage_OpenStorage(
-        &parentStorage->IStorage_iface,
-        entryDataToDelete.name,
-        0,
-        STGM_WRITE | STGM_SHARE_EXCLUSIVE,
-        0,
-        0,
-        &childStorage);
+  hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
+  if (FAILED(hr))
+    return hr;
 
-  if (hr != S_OK)
+  /*
+   * Insert the new directory entry into the parent storage's tree
+   */
+  hr = insertIntoTree(
+    This,
+    This->storageDirEntry,
+    newEntryRef);
+  if (FAILED(hr))
   {
+    StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
     return hr;
   }
 
   /*
-   * Enumerate the elements
+   * Open it to get a pointer to return.
    */
-  IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
+  hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
 
-  do
+  if( (hr != S_OK) || (*ppstg == NULL))
   {
-    /*
-     * Obtain the next element
-     */
-    hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
-    if (hr==S_OK)
-    {
-      destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
+    return hr;
+  }
 
-      CoTaskMemFree(currentElement.pwcsName);
-    }
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_Flush(This);
 
-    /*
-     * We need to Reset the enumeration every time because we delete elements
-     * and the enumeration could be invalid
-     */
-    IEnumSTATSTG_Reset(elements);
+  return S_OK;
+}
 
-  } while ((hr == S_OK) && (destroyHr == S_OK));
+static HRESULT StorageBaseImpl_CopyStorageEntryTo(StorageBaseImpl *This,
+    DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
+    SNB snbExclude, IStorage *pstgDest)
+{
+  DirEntry data;
+  HRESULT hr;
 
-  IStorage_Release(childStorage);
-  IEnumSTATSTG_Release(elements);
+  hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
 
-  return destroyHr;
+  if (SUCCEEDED(hr))
+    hr = IStorage_SetClass( pstgDest, &data.clsid );
+
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
+      skip_stream, snbExclude, pstgDest );
+
+  return hr;
 }
 
-/*********************************************************************
- *
- * Internal Method
- *
- * Perform the deletion of a stream's data
- *
+/*************************************************************************
+ * CopyTo (IStorage)
  */
-static HRESULT deleteStreamContents(
-  StorageBaseImpl *parentStorage,
-  DirRef        indexToDelete,
-  DirEntry      entryDataToDelete)
+static HRESULT WINAPI StorageBaseImpl_CopyTo(
+  IStorage*   iface,
+  DWORD       ciidExclude,  /* [in] */
+  const IID*  rgiidExclude, /* [size_is][unique][in] */
+  SNB         snbExclude,   /* [unique][in] */
+  IStorage*   pstgDest)     /* [unique][in] */
 {
-  IStream      *pis;
-  HRESULT        hr;
-  ULARGE_INTEGER size;
-  StgStreamImpl *strm, *strm2;
+  StorageBaseImpl *This = impl_from_IStorage(iface);
 
-  /* Invalidate any open stream objects. */
-  LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
-  {
-    if (strm->dirEntry == indexToDelete)
-    {
-      TRACE("Stream deleted %p\n", strm);
-      strm->parentStorage = NULL;
-      list_remove(&strm->StrmListEntry);
-    }
-  }
+  BOOL         skip_storage = FALSE, skip_stream = FALSE;
+  DWORD        i;
 
-  size.u.HighPart = 0;
-  size.u.LowPart = 0;
+  TRACE("(%p, %d, %p, %p, %p)\n",
+	iface, ciidExclude, rgiidExclude,
+	snbExclude, pstgDest);
 
-  hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
-        entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
+  if ( pstgDest == 0 )
+    return STG_E_INVALIDPOINTER;
 
-  if (hr!=S_OK)
+  for(i = 0; i < ciidExclude; ++i)
   {
-    return(hr);
+    if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
+        skip_storage = TRUE;
+    else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
+        skip_stream = TRUE;
+    else
+        WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
   }
 
-  /*
-   * Zap the stream
-   */
-  hr = IStream_SetSize(pis, size);
-
-  if(hr != S_OK)
+  if (!skip_storage)
   {
-    return hr;
-  }
-
-  /*
-   * Release the stream object.
-   */
-  IStream_Release(pis);
+    /* Give up early if it looks like this would be infinitely recursive.
+     * Oddly enough, this includes some cases that aren't really recursive, like
+     * copying to a transacted child. */
+    IStorage *pstgDestAncestor = pstgDest;
+    IStorage *pstgDestAncestorChild = NULL;
 
-  return S_OK;
-}
+    /* Go up the chain from the destination until we find the source storage. */
+    while (pstgDestAncestor != iface) {
+      pstgDestAncestorChild = pstgDest;
 
-static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
-{
-  switch (relation)
-  {
-    case DIRENTRY_RELATION_PREVIOUS:
-      entry->leftChild = new_target;
-      break;
-    case DIRENTRY_RELATION_NEXT:
-      entry->rightChild = new_target;
-      break;
-    case DIRENTRY_RELATION_DIR:
-      entry->dirRootEntry = new_target;
-      break;
-    default:
-      assert(0);
-  }
-}
-
-/*************************************************************************
- *
- * Internal Method
- *
- * This method removes a directory entry from its parent storage tree without
- * freeing any resources attached to it.
- */
-static HRESULT removeFromTree(
-  StorageBaseImpl *This,
-  DirRef        parentStorageIndex,
-  DirRef        deletedIndex)
-{
-  DirEntry   entryToDelete;
-  DirEntry   parentEntry;
-  DirRef parentEntryRef;
-  ULONG typeOfRelation;
-  HRESULT hr;
-
-  hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
-
-  if (hr != S_OK)
-    return hr;
-
-  /*
-   * Find the element that links to the one we want to delete.
-   */
-  hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
-    &parentEntry, &parentEntryRef, &typeOfRelation);
-
-  if (hr != S_OK)
-    return hr;
+      if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
+      {
+        TransactedSnapshotImpl *snapshot = (TransactedSnapshotImpl*) pstgDestAncestor;
 
-  if (entryToDelete.leftChild != DIRENTRY_NULL)
-  {
-    /*
-     * Replace the deleted entry with its left child
-     */
-    setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
+        pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
+      }
+      else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
+      {
+        StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
 
-    hr = StorageBaseImpl_WriteDirEntry(
-            This,
-            parentEntryRef,
-            &parentEntry);
-    if(FAILED(hr))
-    {
-      return hr;
+        pstgDestAncestor = &internal->parentStorage->IStorage_iface;
+      }
+      else
+        break;
     }
 
-    if (entryToDelete.rightChild != DIRENTRY_NULL)
+    if (pstgDestAncestor == iface)
     {
-      /*
-       * We need to reinsert the right child somewhere. We already know it and
-       * its children are greater than everything in the left tree, so we
-       * insert it at the rightmost point in the left tree.
-       */
-      DirRef newRightChildParent = entryToDelete.leftChild;
-      DirEntry newRightChildParentEntry;
+      BOOL fail = TRUE;
 
-      do
+      if (pstgDestAncestorChild && snbExclude)
       {
-        hr = StorageBaseImpl_ReadDirEntry(
-                This,
-                newRightChildParent,
-                &newRightChildParentEntry);
-        if (FAILED(hr))
-        {
-          return hr;
-        }
-
-        if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
-          newRightChildParent = newRightChildParentEntry.rightChild;
-      } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
+        StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
+        DirEntry data;
+        WCHAR **snb = snbExclude;
 
-      newRightChildParentEntry.rightChild = entryToDelete.rightChild;
+        StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
 
-      hr = StorageBaseImpl_WriteDirEntry(
-              This,
-              newRightChildParent,
-              &newRightChildParentEntry);
-      if (FAILED(hr))
-      {
-        return hr;
+        while ( *snb != NULL && fail )
+        {
+          if ( lstrcmpW(data.name, *snb) == 0 )
+            fail = FALSE;
+          ++snb;
+        }
       }
-    }
-  }
-  else
-  {
-    /*
-     * Replace the deleted entry with its right child
-     */
-    setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
 
-    hr = StorageBaseImpl_WriteDirEntry(
-            This,
-            parentEntryRef,
-            &parentEntry);
-    if(FAILED(hr))
-    {
-      return hr;
+      if (fail)
+        return STG_E_ACCESSDENIED;
     }
   }
 
-  return hr;
+  return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
+    skip_storage, skip_stream, snbExclude, pstgDest );
 }
 
-
-/******************************************************************************
- * SetElementTimes (IStorage)
+/*************************************************************************
+ * MoveElementTo (IStorage)
  */
-static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
+static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
   IStorage*     iface,
-  const OLECHAR *pwcsName,/* [string][in] */
-  const FILETIME  *pctime,  /* [in] */
-  const FILETIME  *patime,  /* [in] */
-  const FILETIME  *pmtime)  /* [in] */
+  const OLECHAR *pwcsName,   /* [string][in] */
+  IStorage      *pstgDest,   /* [unique][in] */
+  const OLECHAR *pwcsNewName,/* [string][in] */
+  DWORD           grfFlags)    /* [in] */
 {
-  FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
-  return S_OK;
+  FIXME("(%p %s %p %s %u): stub\n", iface,
+         debugstr_w(pwcsName), pstgDest,
+         debugstr_w(pwcsNewName), grfFlags);
+  return E_NOTIMPL;
 }
 
-/******************************************************************************
- * SetStateBits (IStorage)
+/*************************************************************************
+ * Commit (IStorage)
+ *
+ * Ensures that any changes made to a storage object open in transacted mode
+ * are reflected in the parent storage
+ *
+ * In a non-transacted mode, this ensures all cached writes are completed.
  */
-static HRESULT WINAPI StorageBaseImpl_SetStateBits(
+static HRESULT WINAPI StorageBaseImpl_Commit(
   IStorage*   iface,
-  DWORD         grfStateBits,/* [in] */
-  DWORD         grfMask)     /* [in] */
-{
-  StorageBaseImpl *This = impl_from_IStorage(iface);
-
-  if (This->reverted)
-    return STG_E_REVERTED;
-
-  This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
-  return S_OK;
-}
-
-static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
-  DirRef index, const DirEntry *data)
+  DWORD         grfCommitFlags)/* [in] */
 {
-  StorageImpl *This = (StorageImpl*)base;
-  return StorageImpl_WriteDirEntry(This, index, data);
+  StorageBaseImpl* This = impl_from_IStorage(iface);
+  TRACE("(%p %d)\n", iface, grfCommitFlags);
+  return StorageBaseImpl_Flush(This);
 }
 
-static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
-  DirRef index, DirEntry *data)
+/*************************************************************************
+ * Revert (IStorage)
+ *
+ * Discard all changes that have been made since the last commit operation
+ */
+static HRESULT WINAPI StorageBaseImpl_Revert(
+  IStorage* iface)
 {
-  StorageImpl *This = (StorageImpl*)base;
-  return StorageImpl_ReadDirEntry(This, index, data);
+  TRACE("(%p)\n", iface);
+  return S_OK;
 }
 
-static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
+/*********************************************************************
+ *
+ * Internal helper function for StorageBaseImpl_DestroyElement()
+ *
+ * Delete the contents of a storage entry.
+ *
+ */
+static HRESULT deleteStorageContents(
+  StorageBaseImpl *parentStorage,
+  DirRef       indexToDelete,
+  DirEntry     entryDataToDelete)
 {
-  int i;
+  IEnumSTATSTG *elements     = 0;
+  IStorage   *childStorage = 0;
+  STATSTG      currentElement;
+  HRESULT      hr;
+  HRESULT      destroyHr = S_OK;
+  StorageInternalImpl *stg, *stg2;
 
-  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+  /* Invalidate any open storage objects. */
+  LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
   {
-    if (!This->blockChainCache[i])
+    if (stg->base.storageDirEntry == indexToDelete)
     {
-      return &This->blockChainCache[i];
+      StorageBaseImpl_Invalidate(&stg->base);
     }
   }
 
-  i = This->blockChainToEvict;
-
-  BlockChainStream_Destroy(This->blockChainCache[i]);
-  This->blockChainCache[i] = NULL;
-
-  This->blockChainToEvict++;
-  if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
-    This->blockChainToEvict = 0;
+  /*
+   * Open the storage and enumerate it
+   */
+  hr = IStorage_OpenStorage(
+        &parentStorage->IStorage_iface,
+        entryDataToDelete.name,
+        0,
+        STGM_WRITE | STGM_SHARE_EXCLUSIVE,
+        0,
+        0,
+        &childStorage);
 
-  return &This->blockChainCache[i];
-}
+  if (hr != S_OK)
+  {
+    return hr;
+  }
 
-static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
-    DirRef index)
-{
-  int i, free_index=-1;
+  /*
+   * Enumerate the elements
+   */
+  IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
 
-  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+  do
   {
-    if (!This->blockChainCache[i])
-    {
-      if (free_index == -1) free_index = i;
-    }
-    else if (This->blockChainCache[i]->ownerDirEntry == index)
+    /*
+     * Obtain the next element
+     */
+    hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
+    if (hr==S_OK)
     {
-      return &This->blockChainCache[i];
+      destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
+
+      CoTaskMemFree(currentElement.pwcsName);
     }
-  }
 
-  if (free_index == -1)
-  {
-    free_index = This->blockChainToEvict;
+    /*
+     * We need to Reset the enumeration every time because we delete elements
+     * and the enumeration could be invalid
+     */
+    IEnumSTATSTG_Reset(elements);
 
-    BlockChainStream_Destroy(This->blockChainCache[free_index]);
-    This->blockChainCache[free_index] = NULL;
+  } while ((hr == S_OK) && (destroyHr == S_OK));
 
-    This->blockChainToEvict++;
-    if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
-      This->blockChainToEvict = 0;
-  }
+  IStorage_Release(childStorage);
+  IEnumSTATSTG_Release(elements);
 
-  This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
-  return &This->blockChainCache[free_index];
+  return destroyHr;
 }
 
-static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
+/*********************************************************************
+ *
+ * Internal helper function for StorageBaseImpl_DestroyElement()
+ *
+ * Perform the deletion of a stream's data
+ *
+ */
+static HRESULT deleteStreamContents(
+  StorageBaseImpl *parentStorage,
+  DirRef        indexToDelete,
+  DirEntry      entryDataToDelete)
 {
-  int i;
+  IStream      *pis;
+  HRESULT        hr;
+  ULARGE_INTEGER size;
+  StgStreamImpl *strm, *strm2;
 
-  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+  /* Invalidate any open stream objects. */
+  LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
   {
-    if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
+    if (strm->dirEntry == indexToDelete)
     {
-      BlockChainStream_Destroy(This->blockChainCache[i]);
-      This->blockChainCache[i] = NULL;
-      return;
+      TRACE("Stream deleted %p\n", strm);
+      strm->parentStorage = NULL;
+      list_remove(&strm->StrmListEntry);
     }
   }
-}
 
-static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
-  ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
-{
-  StorageImpl *This = (StorageImpl*)base;
-  DirEntry data;
-  HRESULT hr;
-  ULONG bytesToRead;
+  size.u.HighPart = 0;
+  size.u.LowPart = 0;
 
-  hr = StorageImpl_ReadDirEntry(This, index, &data);
-  if (FAILED(hr)) return hr;
+  hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
+        entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
 
-  if (data.size.QuadPart == 0)
+  if (hr!=S_OK)
   {
-    *bytesRead = 0;
-    return S_OK;
+    return(hr);
   }
 
-  if (offset.QuadPart + size > data.size.QuadPart)
-  {
-    bytesToRead = data.size.QuadPart - offset.QuadPart;
-  }
-  else
-  {
-    bytesToRead = size;
-  }
+  /*
+   * Zap the stream
+   */
+  hr = IStream_SetSize(pis, size);
 
-  if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+  if(hr != S_OK)
   {
-    SmallBlockChainStream *stream;
-
-    stream = SmallBlockChainStream_Construct(This, NULL, index);
-    if (!stream) return E_OUTOFMEMORY;
-
-    hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
-
-    SmallBlockChainStream_Destroy(stream);
-
     return hr;
   }
-  else
-  {
-    BlockChainStream *stream = NULL;
-
-    stream = *StorageImpl_GetCachedBlockChainStream(This, index);
-    if (!stream) return E_OUTOFMEMORY;
 
-    hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
+  /*
+   * Release the stream object.
+   */
+  IStream_Release(pis);
 
-    return hr;
-  }
+  return S_OK;
 }
 
-static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
-  ULARGE_INTEGER newsize)
+/*************************************************************************
+ * DestroyElement (IStorage)
+ *
+ * Strategy: This implementation is built this way for simplicity not for speed.
+ *          I always delete the topmost element of the enumeration and adjust
+ *          the deleted element pointer all the time.  This takes longer to
+ *          do but allow to reinvoke DestroyElement whenever we encounter a
+ *          storage object.  The optimisation resides in the usage of another
+ *          enumeration strategy that would give all the leaves of a storage
+ *          first. (postfix order)
+ */
+static HRESULT WINAPI StorageBaseImpl_DestroyElement(
+  IStorage*     iface,
+  const OLECHAR *pwcsName)/* [string][in] */
 {
-  StorageImpl *This = (StorageImpl*)base;
-  DirEntry data;
-  HRESULT hr;
-  SmallBlockChainStream *smallblock=NULL;
-  BlockChainStream **pbigblock=NULL, *bigblock=NULL;
+  StorageBaseImpl *This = impl_from_IStorage(iface);
 
-  hr = StorageImpl_ReadDirEntry(This, index, &data);
-  if (FAILED(hr)) return hr;
+  HRESULT           hr = S_OK;
+  DirEntry          entryToDelete;
+  DirRef            entryToDeleteRef;
 
-  /* In simple mode keep the stream size above the small block limit */
-  if (This->base.openFlags & STGM_SIMPLE)
-    newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
+  TRACE("(%p, %s)\n",
+	iface, debugstr_w(pwcsName));
 
-  if (data.size.QuadPart == newsize.QuadPart)
-    return S_OK;
+  if (pwcsName==NULL)
+    return STG_E_INVALIDPOINTER;
 
-  /* Create a block chain object of the appropriate type */
-  if (data.size.QuadPart == 0)
-  {
-    if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
-    {
-      smallblock = SmallBlockChainStream_Construct(This, NULL, index);
-      if (!smallblock) return E_OUTOFMEMORY;
-    }
-    else
-    {
-      pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
-      bigblock = *pbigblock;
-      if (!bigblock) return E_OUTOFMEMORY;
-    }
-  }
-  else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
-  {
-    smallblock = SmallBlockChainStream_Construct(This, NULL, index);
-    if (!smallblock) return E_OUTOFMEMORY;
-  }
-  else
-  {
-    pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
-    bigblock = *pbigblock;
-    if (!bigblock) return E_OUTOFMEMORY;
-  }
+  if (This->reverted)
+    return STG_E_REVERTED;
 
-  /* Change the block chain type if necessary. */
-  if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
-  {
-    bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
-    if (!bigblock)
-    {
-      SmallBlockChainStream_Destroy(smallblock);
-      return E_FAIL;
-    }
+  if ( !(This->openFlags & STGM_TRANSACTED) &&
+       STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
+    return STG_E_ACCESSDENIED;
 
-    pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
-    *pbigblock = bigblock;
-  }
-  else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+  entryToDeleteRef = findElement(
+    This,
+    This->storageDirEntry,
+    pwcsName,
+    &entryToDelete);
+
+  if ( entryToDeleteRef == DIRENTRY_NULL )
   {
-    smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
-    if (!smallblock)
-      return E_FAIL;
+    return STG_E_FILENOTFOUND;
   }
 
-  /* Set the size of the block chain. */
-  if (smallblock)
+  if ( entryToDelete.stgType == STGTY_STORAGE )
   {
-    SmallBlockChainStream_SetSize(smallblock, newsize);
-    SmallBlockChainStream_Destroy(smallblock);
+    hr = deleteStorageContents(
+           This,
+           entryToDeleteRef,
+           entryToDelete);
   }
-  else
+  else if ( entryToDelete.stgType == STGTY_STREAM )
   {
-    BlockChainStream_SetSize(bigblock, newsize);
+    hr = deleteStreamContents(
+           This,
+           entryToDeleteRef,
+           entryToDelete);
   }
 
-  /* Set the size in the directory entry. */
-  hr = StorageImpl_ReadDirEntry(This, index, &data);
+  if (hr!=S_OK)
+    return hr;
+
+  /*
+   * Remove the entry from its parent storage
+   */
+  hr = removeFromTree(
+        This,
+        This->storageDirEntry,
+        entryToDeleteRef);
+
+  /*
+   * Invalidate the entry
+   */
   if (SUCCEEDED(hr))
-  {
-    data.size = newsize;
+    StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
+
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_Flush(This);
 
-    hr = StorageImpl_WriteDirEntry(This, index, &data);
-  }
   return hr;
 }
 
-static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
-  ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
+static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
 {
-  StorageImpl *This = (StorageImpl*)base;
-  DirEntry data;
-  HRESULT hr;
-  ULARGE_INTEGER newSize;
-
-  hr = StorageImpl_ReadDirEntry(This, index, &data);
-  if (FAILED(hr)) return hr;
-
-  /* Grow the stream if necessary */
-  newSize.QuadPart = offset.QuadPart + size;
+  struct list *cur, *cur2;
+  StgStreamImpl *strm=NULL;
+  StorageInternalImpl *childstg=NULL;
 
-  if (newSize.QuadPart > data.size.QuadPart)
-  {
-    hr = StorageImpl_StreamSetSize(base, index, newSize);
-    if (FAILED(hr))
-      return hr;
+  LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
+    strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
+    TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
+    strm->parentStorage = NULL;
+    list_remove(cur);
+  }
 
-    hr = StorageImpl_ReadDirEntry(This, index, &data);
-    if (FAILED(hr)) return hr;
+  LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
+    childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
+    StorageBaseImpl_Invalidate( &childstg->base );
   }
 
-  if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+  if (stg->transactedChild)
   {
-    SmallBlockChainStream *stream;
-
-    stream = SmallBlockChainStream_Construct(This, NULL, index);
-    if (!stream) return E_OUTOFMEMORY;
-
-    hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
-
-    SmallBlockChainStream_Destroy(stream);
+    StorageBaseImpl_Invalidate(stg->transactedChild);
 
-    return hr;
+    stg->transactedChild = NULL;
   }
-  else
-  {
-    BlockChainStream *stream;
-
-    stream = *StorageImpl_GetCachedBlockChainStream(This, index);
-    if (!stream) return E_OUTOFMEMORY;
+}
 
-    return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
-  }
+/******************************************************************************
+ * SetElementTimes (IStorage)
+ */
+static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
+  IStorage*     iface,
+  const OLECHAR *pwcsName,/* [string][in] */
+  const FILETIME  *pctime,  /* [in] */
+  const FILETIME  *patime,  /* [in] */
+  const FILETIME  *pmtime)  /* [in] */
+{
+  FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
+  return S_OK;
 }
 
-static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
-  DirRef src)
+/******************************************************************************
+ * SetStateBits (IStorage)
+ */
+static HRESULT WINAPI StorageBaseImpl_SetStateBits(
+  IStorage*   iface,
+  DWORD         grfStateBits,/* [in] */
+  DWORD         grfMask)     /* [in] */
 {
-  StorageImpl *This = (StorageImpl*)base;
-  DirEntry dst_data, src_data;
-  HRESULT hr;
+  StorageBaseImpl *This = impl_from_IStorage(iface);
 
-  hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
+  if (This->reverted)
+    return STG_E_REVERTED;
 
-  if (SUCCEEDED(hr))
-    hr = StorageImpl_ReadDirEntry(This, src, &src_data);
+  This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
+  return S_OK;
+}
 
-  if (SUCCEEDED(hr))
-  {
-    StorageImpl_DeleteCachedBlockChainStream(This, src);
-    dst_data.startingBlock = src_data.startingBlock;
-    dst_data.size = src_data.size;
+/******************************************************************************
+ * Internal stream list handlers
+ */
 
-    hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
-  }
+void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
+{
+  TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
+  list_add_tail(&stg->strmHead,&strm->StrmListEntry);
+}
 
-  return hr;
+void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
+{
+  TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
+  list_remove(&(strm->StrmListEntry));
 }
 
-static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
-  ULONG* result, BOOL refresh)
+static HRESULT StorageBaseImpl_CopyStream(
+  StorageBaseImpl *dst, DirRef dst_entry,
+  StorageBaseImpl *src, DirRef src_entry)
 {
-  StorageImpl *This = (StorageImpl*)base;
-  HRESULT hr=S_OK;
-  DWORD oldTransactionSig = This->transactionSig;
+  HRESULT hr;
+  BYTE data[4096];
+  DirEntry srcdata;
+  ULARGE_INTEGER bytes_copied;
+  ULONG bytestocopy, bytesread, byteswritten;
 
-  if (refresh)
-  {
-    ULARGE_INTEGER offset;
-    ULONG bytes_read;
-    BYTE data[4];
+  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
 
-    offset.u.HighPart = 0;
-    offset.u.LowPart = OFFSET_TRANSACTIONSIG;
-    hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
+  if (SUCCEEDED(hr))
+  {
+    hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
 
-    if (SUCCEEDED(hr))
+    bytes_copied.QuadPart = 0;
+    while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
     {
-      StorageUtl_ReadDWord(data, 0, &This->transactionSig);
+      bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
 
-      if (oldTransactionSig != This->transactionSig)
-      {
-        /* Someone else wrote to this, so toss all cached information. */
-        TRACE("signature changed\n");
+      hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
+        data, &bytesread);
+      if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
 
-        hr = StorageImpl_Refresh(This, FALSE, FALSE);
+      if (SUCCEEDED(hr))
+        hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
+          data, &byteswritten);
+      if (SUCCEEDED(hr))
+      {
+        if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
+        bytes_copied.QuadPart += byteswritten;
       }
-
-      if (FAILED(hr))
-        This->transactionSig = oldTransactionSig;
     }
   }
 
-  *result = This->transactionSig;
-
   return hr;
 }
 
-static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
-  ULONG value)
-{
-  StorageImpl *This = (StorageImpl*)base;
-
-  This->transactionSig = value;
-  StorageImpl_SaveFileHeader(This);
-
-  return S_OK;
-}
-
-static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
+static HRESULT StorageBaseImpl_DupStorageTree(
+  StorageBaseImpl *dst, DirRef *dst_entry,
+  StorageBaseImpl *src, DirRef src_entry)
 {
-  StorageImpl *This = (StorageImpl*)base;
   HRESULT hr;
-  ULARGE_INTEGER offset, cb;
+  DirEntry data;
+  BOOL has_stream=FALSE;
 
-  if (write)
+  if (src_entry == DIRENTRY_NULL)
   {
-    /* Synchronous grab of second priority range, the commit lock, and the
-     * lock-checking lock. */
-    offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
-    cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
+    *dst_entry = DIRENTRY_NULL;
+    return S_OK;
   }
-  else
+
+  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
+  if (SUCCEEDED(hr))
   {
-    offset.QuadPart = RANGELOCK_COMMIT;
-    cb.QuadPart = 1;
+    has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
+    data.startingBlock = BLOCK_END_OF_CHAIN;
+    data.size.QuadPart = 0;
+
+    hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
   }
 
-  hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
 
-  if (hr == STG_E_INVALIDFUNCTION)
-    hr = S_OK;
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
+
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
+
+  if (SUCCEEDED(hr) && has_stream)
+    hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
 
   return hr;
 }
 
-static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
+static HRESULT StorageBaseImpl_CopyStorageTree(
+  StorageBaseImpl *dst, DirRef dst_entry,
+  StorageBaseImpl *src, DirRef src_entry)
 {
-  StorageImpl *This = (StorageImpl*)base;
   HRESULT hr;
-  ULARGE_INTEGER offset, cb;
+  DirEntry src_data, dst_data;
+  DirRef new_root_entry;
 
-  if (write)
+  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
+
+  if (SUCCEEDED(hr))
   {
-    offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
-    cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
+    hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
   }
-  else
+
+  if (SUCCEEDED(hr))
   {
-    offset.QuadPart = RANGELOCK_COMMIT;
-    cb.QuadPart = 1;
+    hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
+    dst_data.clsid = src_data.clsid;
+    dst_data.ctime = src_data.ctime;
+    dst_data.mtime = src_data.mtime;
+    dst_data.dirRootEntry = new_root_entry;
   }
 
-  hr = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
-
-  if (hr == STG_E_INVALIDFUNCTION)
-    hr = S_OK;
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
 
   return hr;
 }
 
-static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
+static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
 {
-  StorageImpl *This = (StorageImpl*) iface;
-  STATSTG statstg;
   HRESULT hr;
+  DirEntry data;
+  ULARGE_INTEGER zero;
 
-  hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
+  if (entry == DIRENTRY_NULL)
+    return S_OK;
 
-  *result = statstg.pwcsName;
+  zero.QuadPart = 0;
 
-  return hr;
-}
+  hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
 
-static HRESULT WINAPI directwriterlock_QueryInterface(IDirectWriterLock *iface, REFIID riid, void **obj)
-{
-  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
-  return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
-}
+  if (SUCCEEDED(hr) && include_siblings)
+    hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
 
-static ULONG WINAPI directwriterlock_AddRef(IDirectWriterLock *iface)
-{
-  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
-  return IStorage_AddRef(&This->IStorage_iface);
-}
+  if (SUCCEEDED(hr) && include_siblings)
+    hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
 
-static ULONG WINAPI directwriterlock_Release(IDirectWriterLock *iface)
-{
-  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
-  return IStorage_Release(&This->IStorage_iface);
-}
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
 
-static HRESULT WINAPI directwriterlock_WaitForWriteAccess(IDirectWriterLock *iface, DWORD timeout)
-{
-  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
-  FIXME("(%p)->(%d): stub\n", This, timeout);
-  return E_NOTIMPL;
-}
+  if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
+    hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
 
-static HRESULT WINAPI directwriterlock_ReleaseWriteAccess(IDirectWriterLock *iface)
-{
-  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
-  FIXME("(%p): stub\n", This);
-  return E_NOTIMPL;
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_DestroyDirEntry(This, entry);
+
+  return hr;
 }
 
-static HRESULT WINAPI directwriterlock_HaveWriteAccess(IDirectWriterLock *iface)
+
+/************************************************************************
+ * StorageImpl implementation
+ ***********************************************************************/
+
+static HRESULT StorageImpl_ReadAt(StorageImpl* This,
+  ULARGE_INTEGER offset,
+  void*          buffer,
+  ULONG          size,
+  ULONG*         bytesRead)
 {
-  StorageBaseImpl *This = impl_from_IDirectWriterLock(iface);
-  FIXME("(%p): stub\n", This);
-  return E_NOTIMPL;
+    return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
 }
 
-static const IDirectWriterLockVtbl DirectWriterLockVtbl =
+static HRESULT StorageImpl_WriteAt(StorageImpl* This,
+  ULARGE_INTEGER offset,
+  const void*    buffer,
+  const ULONG    size,
+  ULONG*         bytesWritten)
 {
-  directwriterlock_QueryInterface,
-  directwriterlock_AddRef,
-  directwriterlock_Release,
-  directwriterlock_WaitForWriteAccess,
-  directwriterlock_ReleaseWriteAccess,
-  directwriterlock_HaveWriteAccess
-};
+    return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
+}
 
-/*
- * Virtual function table for the IStorageImpl class.
+/******************************************************************************
+ *      StorageImpl_LoadFileHeader
+ *
+ * This method will read in the file header
  */
-static const IStorageVtbl StorageImpl_Vtbl =
-{
-    StorageBaseImpl_QueryInterface,
-    StorageBaseImpl_AddRef,
-    StorageBaseImpl_Release,
-    StorageBaseImpl_CreateStream,
-    StorageBaseImpl_OpenStream,
-    StorageBaseImpl_CreateStorage,
-    StorageBaseImpl_OpenStorage,
-    StorageBaseImpl_CopyTo,
-    StorageBaseImpl_MoveElementTo,
-    StorageImpl_Commit,
-    StorageImpl_Revert,
-    StorageBaseImpl_EnumElements,
-    StorageBaseImpl_DestroyElement,
-    StorageBaseImpl_RenameElement,
-    StorageBaseImpl_SetElementTimes,
-    StorageBaseImpl_SetClass,
-    StorageBaseImpl_SetStateBits,
-    StorageBaseImpl_Stat
-};
-
-static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
+static HRESULT StorageImpl_LoadFileHeader(
+          StorageImpl* This)
 {
-  StorageImpl_Destroy,
-  StorageImpl_Invalidate,
-  StorageImpl_Flush,
-  StorageImpl_GetFilename,
-  StorageImpl_CreateDirEntry,
-  StorageImpl_BaseWriteDirEntry,
-  StorageImpl_BaseReadDirEntry,
-  StorageImpl_DestroyDirEntry,
-  StorageImpl_StreamReadAt,
-  StorageImpl_StreamWriteAt,
-  StorageImpl_StreamSetSize,
-  StorageImpl_StreamLink,
-  StorageImpl_GetTransactionSig,
-  StorageImpl_SetTransactionSig,
-  StorageImpl_LockTransaction,
-  StorageImpl_UnlockTransaction
-};
+  HRESULT hr;
+  BYTE    headerBigBlock[HEADER_SIZE];
+  int     index;
+  ULARGE_INTEGER offset;
+  DWORD bytes_read;
 
-static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
-    ULARGE_INTEGER cb, DWORD dwLockType)
-{
-    HRESULT hr;
-    int delay = 0;
-    DWORD start_time = GetTickCount();
-    DWORD last_sanity_check = start_time;
-    ULARGE_INTEGER sanity_offset, sanity_cb;
+  TRACE("\n");
+  /*
+   * Get a pointer to the big block of data containing the header.
+   */
+  offset.u.HighPart = 0;
+  offset.u.LowPart = 0;
+  hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
+  if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
+    hr = STG_E_FILENOTFOUND;
 
-    sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
-    sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
+  /*
+   * Extract the information from the header.
+   */
+  if (SUCCEEDED(hr))
+  {
+    /*
+     * Check for the "magic number" signature and return an error if it is not
+     * found.
+     */
+    if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
+    {
+      return STG_E_OLDFORMAT;
+    }
 
-    do
+    if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
     {
-        hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
+      return STG_E_INVALIDHEADER;
+    }
 
-        if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
-        {
-            DWORD current_time = GetTickCount();
-            if (current_time - start_time >= 20000)
-            {
-                /* timeout */
-                break;
-            }
-            if (current_time - last_sanity_check >= 500)
-            {
-                /* Any storage implementation with the file open in a
-                 * shared mode should not lock these bytes for writing. However,
-                 * some programs (LibreOffice Writer) will keep ALL bytes locked
-                 * when opening in exclusive mode. We can use a read lock to
-                 * detect this case early, and not hang a full 20 seconds.
-                 *
-                 * This can collide with another attempt to open the file in
-                 * exclusive mode, but it's unlikely, and someone would fail anyway. */
-                hr = ILockBytes_LockRegion(This->lockBytes, sanity_offset, sanity_cb, 0);
-                if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
-                    break;
-                if (hr == STG_E_INVALIDFUNCTION)
-                {
-                    /* ignore this, lockbytes might support dwLockType but not 0 */
-                    hr = STG_E_ACCESSDENIED;
-                }
-                if (SUCCEEDED(hr))
-                {
-                    ILockBytes_UnlockRegion(This->lockBytes, sanity_offset, sanity_cb, 0);
-                    hr = STG_E_ACCESSDENIED;
-                }
+    StorageUtl_ReadWord(
+      headerBigBlock,
+      OFFSET_BIGBLOCKSIZEBITS,
+      &This->bigBlockSizeBits);
 
-                last_sanity_check = current_time;
-            }
-            Sleep(delay);
-            if (delay < 150) delay++;
-        }
-    } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
+    StorageUtl_ReadWord(
+      headerBigBlock,
+      OFFSET_SMALLBLOCKSIZEBITS,
+      &This->smallBlockSizeBits);
 
-    return hr;
-}
+    StorageUtl_ReadDWord(
+      headerBigBlock,
+      OFFSET_BBDEPOTCOUNT,
+      &This->bigBlockDepotCount);
 
-static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
-    ULONG end, HRESULT fail_hr)
-{
-    HRESULT hr;
-    ULARGE_INTEGER offset, cb;
+    StorageUtl_ReadDWord(
+      headerBigBlock,
+      OFFSET_ROOTSTARTBLOCK,
+      &This->rootStartBlock);
 
-    offset.QuadPart = start;
-    cb.QuadPart = 1 + end - start;
+    StorageUtl_ReadDWord(
+      headerBigBlock,
+      OFFSET_TRANSACTIONSIG,
+      &This->transactionSig);
 
-    hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
-    if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+    StorageUtl_ReadDWord(
+      headerBigBlock,
+      OFFSET_SMALLBLOCKLIMIT,
+      &This->smallBlockLimit);
 
-    if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
-        return fail_hr;
-    else
-        return S_OK;
-}
+    StorageUtl_ReadDWord(
+      headerBigBlock,
+      OFFSET_SBDEPOTSTART,
+      &This->smallBlockDepotStart);
 
-static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
-{
-    HRESULT hr=S_OK;
-    int i, j;
-    ULARGE_INTEGER offset, cb;
+    StorageUtl_ReadDWord(
+      headerBigBlock,
+      OFFSET_EXTBBDEPOTSTART,
+      &This->extBigBlockDepotStart);
 
-    cb.QuadPart = 1;
+    StorageUtl_ReadDWord(
+      headerBigBlock,
+      OFFSET_EXTBBDEPOTCOUNT,
+      &This->extBigBlockDepotCount);
 
-    for (i=start; i<=end; i++)
+    for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
     {
-        offset.QuadPart = i;
-        hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
-        if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
-            break;
+      StorageUtl_ReadDWord(
+        headerBigBlock,
+        OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
+        &(This->bigBlockDepotStart[index]));
     }
 
-    if (SUCCEEDED(hr))
-    {
-        for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
-        {
-            if (This->locked_bytes[j] == 0)
-            {
-                This->locked_bytes[j] = i;
-                break;
-            }
-        }
+    /*
+     * Make the bitwise arithmetic to get the size of the blocks in bytes.
+     */
+    This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
+    This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
+
+    /*
+     * Right now, the code is making some assumptions about the size of the
+     * blocks, just make sure they are what we're expecting.
+     */
+    if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
+	This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
+	This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
+    {
+	FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
+	    This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
+	hr = STG_E_INVALIDHEADER;
     }
+    else
+	hr = S_OK;
+  }
 
-    return hr;
+  return hr;
 }
 
-static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
+/******************************************************************************
+ *      StorageImpl_SaveFileHeader
+ *
+ * This method will save to the file the header
+ */
+static void StorageImpl_SaveFileHeader(
+          StorageImpl* This)
 {
-    HRESULT hr;
-    ULARGE_INTEGER offset;
-    ULARGE_INTEGER cb;
-    DWORD share_mode = STGM_SHARE_MODE(openFlags);
+  BYTE   headerBigBlock[HEADER_SIZE];
+  int    index;
+  HRESULT hr;
+  ULARGE_INTEGER offset;
+  DWORD bytes_read, bytes_written;
+  DWORD major_version, dirsectorcount;
 
-    if (openFlags & STGM_NOSNAPSHOT)
-    {
-        /* STGM_NOSNAPSHOT implies deny write */
-        if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
-        else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
-    }
+  /*
+   * Get a pointer to the big block of data containing the header.
+   */
+  offset.u.HighPart = 0;
+  offset.u.LowPart = 0;
+  hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
+  if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
+    hr = STG_E_FILENOTFOUND;
 
-    /* Wrap all other locking inside a single lock so we can check ranges safely */
-    offset.QuadPart = RANGELOCK_CHECKLOCKS;
-    cb.QuadPart = 1;
-    hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
+  if (This->bigBlockSizeBits == 0x9)
+    major_version = 3;
+  else if (This->bigBlockSizeBits == 0xc)
+    major_version = 4;
+  else
+  {
+    ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
+    major_version = 4;
+  }
 
-    /* If the ILockBytes doesn't support locking that's ok. */
-    if (hr == STG_E_INVALIDFUNCTION) return S_OK;
-    else if (FAILED(hr)) return hr;
+  /*
+   * If the block read failed, the file is probably new.
+   */
+  if (FAILED(hr))
+  {
+    /*
+     * Initialize for all unknown fields.
+     */
+    memset(headerBigBlock, 0, HEADER_SIZE);
 
-    hr = S_OK;
+    /*
+     * Initialize the magic number.
+     */
+    memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
+  }
 
-    /* First check for any conflicting locks. */
-    if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
-        hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
+  /*
+   * Write the information to the header.
+   */
+  StorageUtl_WriteWord(
+    headerBigBlock,
+    OFFSET_MINORVERSION,
+    0x3e);
 
-    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
-        hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
+  StorageUtl_WriteWord(
+    headerBigBlock,
+    OFFSET_MAJORVERSION,
+    major_version);
 
-    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
-        hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
+  StorageUtl_WriteWord(
+    headerBigBlock,
+    OFFSET_BYTEORDERMARKER,
+    (WORD)-2);
 
-    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
-        hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
+  StorageUtl_WriteWord(
+    headerBigBlock,
+    OFFSET_BIGBLOCKSIZEBITS,
+    This->bigBlockSizeBits);
 
-    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
-        hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
+  StorageUtl_WriteWord(
+    headerBigBlock,
+    OFFSET_SMALLBLOCKSIZEBITS,
+    This->smallBlockSizeBits);
 
-    /* Then grab our locks. */
-    if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
-    {
-        hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
-        if (SUCCEEDED(hr))
-            hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
-    }
+  if (major_version >= 4)
+  {
+    if (This->rootBlockChain)
+      dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
+    else
+      /* This file is being created, and it will start out with one block. */
+      dirsectorcount = 1;
+  }
+  else
+    /* This field must be 0 in versions older than 4 */
+    dirsectorcount = 0;
 
-    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
-        hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
+  StorageUtl_WriteDWord(
+    headerBigBlock,
+    OFFSET_DIRSECTORCOUNT,
+    dirsectorcount);
 
-    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
-        hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
+  StorageUtl_WriteDWord(
+    headerBigBlock,
+    OFFSET_BBDEPOTCOUNT,
+    This->bigBlockDepotCount);
 
-    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
-        hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
+  StorageUtl_WriteDWord(
+    headerBigBlock,
+    OFFSET_ROOTSTARTBLOCK,
+    This->rootStartBlock);
 
-    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
-        hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
+  StorageUtl_WriteDWord(
+    headerBigBlock,
+    OFFSET_TRANSACTIONSIG,
+    This->transactionSig);
 
-    if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
-        hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
+  StorageUtl_WriteDWord(
+    headerBigBlock,
+    OFFSET_SMALLBLOCKLIMIT,
+    This->smallBlockLimit);
 
-    offset.QuadPart = RANGELOCK_CHECKLOCKS;
-    cb.QuadPart = 1;
-    ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+  StorageUtl_WriteDWord(
+    headerBigBlock,
+    OFFSET_SBDEPOTSTART,
+    This->smallBlockDepotStart);
 
-    return hr;
-}
+  StorageUtl_WriteDWord(
+    headerBigBlock,
+    OFFSET_SBDEPOTCOUNT,
+    This->smallBlockDepotChain ?
+     BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
 
-static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
-{
-  HRESULT hr=S_OK;
-  DirEntry currentEntry;
-  DirRef      currentEntryRef;
-  BlockChainStream *blockChainStream;
+  StorageUtl_WriteDWord(
+    headerBigBlock,
+    OFFSET_EXTBBDEPOTSTART,
+    This->extBigBlockDepotStart);
 
-  if (create)
+  StorageUtl_WriteDWord(
+    headerBigBlock,
+    OFFSET_EXTBBDEPOTCOUNT,
+    This->extBigBlockDepotCount);
+
+  for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
   {
-    ULARGE_INTEGER size;
-    BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
+    StorageUtl_WriteDWord(
+      headerBigBlock,
+      OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
+      (This->bigBlockDepotStart[index]));
+  }
 
-    /* Discard any existing data. */
-    size.QuadPart = 0;
-    ILockBytes_SetSize(This->lockBytes, size);
+  /*
+   * Write the big block back to the file.
+   */
+  StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
+}
 
-    /*
-     * Initialize all header variables:
-     * - The big block depot consists of one block and it is at block 0
-     * - The directory table starts at block 1
-     * - There is no small block depot
-     */
-    memset( This->bigBlockDepotStart,
-            BLOCK_UNUSED,
-            sizeof(This->bigBlockDepotStart));
 
-    This->bigBlockDepotCount    = 1;
-    This->bigBlockDepotStart[0] = 0;
-    This->rootStartBlock        = 1;
-    This->smallBlockLimit       = LIMIT_TO_USE_SMALL_BLOCK;
-    This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
-    if (This->bigBlockSize == 4096)
-      This->bigBlockSizeBits      = MAX_BIG_BLOCK_SIZE_BITS;
-    else
-      This->bigBlockSizeBits      = MIN_BIG_BLOCK_SIZE_BITS;
-    This->smallBlockSizeBits    = DEF_SMALL_BLOCK_SIZE_BITS;
-    This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
-    This->extBigBlockDepotCount = 0;
+/************************************************************************
+ * StorageImpl implementation : DirEntry methods
+ ***********************************************************************/
 
-    StorageImpl_SaveFileHeader(This);
+/******************************************************************************
+ *      StorageImpl_ReadRawDirEntry
+ *
+ * This method will read the raw data from a directory entry in the file.
+ *
+ * buffer must be RAW_DIRENTRY_SIZE bytes long.
+ */
+HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
+{
+  ULARGE_INTEGER offset;
+  HRESULT hr;
+  ULONG bytesRead;
 
-    /*
-     * Add one block for the big block depot and one block for the directory table
-     */
-    size.u.HighPart = 0;
-    size.u.LowPart  = This->bigBlockSize * 3;
-    ILockBytes_SetSize(This->lockBytes, size);
+  offset.QuadPart  = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
 
-    /*
-     * Initialize the big block depot
-     */
-    memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
-    StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
-    StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
-    StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
-  }
-  else
-  {
-    /*
-     * Load the header for the file.
-     */
-    hr = StorageImpl_LoadFileHeader(This);
+  hr = BlockChainStream_ReadAt(
+                    This->rootBlockChain,
+                    offset,
+                    RAW_DIRENTRY_SIZE,
+                    buffer,
+                    &bytesRead);
 
-    if (FAILED(hr))
-    {
-      return hr;
-    }
-  }
+  if (bytesRead != RAW_DIRENTRY_SIZE)
+    return STG_E_READFAULT;
 
-  /*
-   * There is no block depot cached yet.
-   */
-  This->indexBlockDepotCached = 0xFFFFFFFF;
-  This->indexExtBlockDepotCached = 0xFFFFFFFF;
+  return hr;
+}
 
-  /*
-   * Start searching for free blocks with block 0.
-   */
-  This->prevFreeBlock = 0;
+/******************************************************************************
+ *      StorageImpl_WriteRawDirEntry
+ *
+ * This method will write the raw data from a directory entry in the file.
+ *
+ * buffer must be RAW_DIRENTRY_SIZE bytes long.
+ */
+HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
+{
+  ULARGE_INTEGER offset;
+  ULONG bytesRead;
 
-  This->firstFreeSmallBlock = 0;
+  offset.QuadPart  = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
 
-  /* Read the extended big block depot locations. */
-  if (This->extBigBlockDepotCount != 0)
-  {
-    ULONG current_block = This->extBigBlockDepotStart;
-    ULONG cache_size = This->extBigBlockDepotCount * 2;
-    ULONG i;
+  return BlockChainStream_WriteAt(
+                    This->rootBlockChain,
+                    offset,
+                    RAW_DIRENTRY_SIZE,
+                    buffer,
+                    &bytesRead);
+}
 
-    This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
-    if (!This->extBigBlockDepotLocations)
-    {
-      return E_OUTOFMEMORY;
-    }
+/***************************************************************************
+ *
+ * Internal Method
+ *
+ * Mark a directory entry in the file as free.
+ */
+static HRESULT StorageImpl_DestroyDirEntry(
+  StorageBaseImpl *base,
+  DirRef index)
+{
+  BYTE emptyData[RAW_DIRENTRY_SIZE];
+  StorageImpl *storage = (StorageImpl*)base;
 
-    This->extBigBlockDepotLocationsSize = cache_size;
+  memset(emptyData, 0, RAW_DIRENTRY_SIZE);
 
-    for (i=0; i<This->extBigBlockDepotCount; i++)
-    {
-      if (current_block == BLOCK_END_OF_CHAIN)
-      {
-        WARN("File has too few extended big block depot blocks.\n");
-        return STG_E_DOCFILECORRUPT;
-      }
-      This->extBigBlockDepotLocations[i] = current_block;
-      current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
-    }
-  }
-  else
-  {
-    This->extBigBlockDepotLocations = NULL;
-    This->extBigBlockDepotLocationsSize = 0;
-  }
+  return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
+}
 
-  /*
-   * Create the block chain abstractions.
-   */
-  if(!(blockChainStream =
-       BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
-  {
-    return STG_E_READFAULT;
-  }
-  if (!new_object)
-    BlockChainStream_Destroy(This->rootBlockChain);
-  This->rootBlockChain = blockChainStream;
+/******************************************************************************
+ *      UpdateRawDirEntry
+ *
+ * Update raw directory entry data from the fields in newData.
+ *
+ * buffer must be RAW_DIRENTRY_SIZE bytes long.
+ */
+void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
+{
+  memset(buffer, 0, RAW_DIRENTRY_SIZE);
 
-  if(!(blockChainStream =
-       BlockChainStream_Construct(This, &This->smallBlockDepotStart,
-				  DIRENTRY_NULL)))
-  {
-    return STG_E_READFAULT;
-  }
-  if (!new_object)
-    BlockChainStream_Destroy(This->smallBlockDepotChain);
-  This->smallBlockDepotChain = blockChainStream;
+  memcpy(
+    buffer + OFFSET_PS_NAME,
+    newData->name,
+    DIRENTRY_NAME_BUFFER_LEN );
 
-  /*
-   * Write the root storage entry (memory only)
-   */
-  if (create)
-  {
-    static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
-    DirEntry rootEntry;
-    /*
-     * Initialize the directory table
-     */
-    memset(&rootEntry, 0, sizeof(rootEntry));
-    strcpyW(rootEntry.name, rootentryW);
-    rootEntry.sizeOfNameString = sizeof(rootentryW);
-    rootEntry.stgType          = STGTY_ROOT;
-    rootEntry.leftChild        = DIRENTRY_NULL;
-    rootEntry.rightChild       = DIRENTRY_NULL;
-    rootEntry.dirRootEntry     = DIRENTRY_NULL;
-    rootEntry.startingBlock    = BLOCK_END_OF_CHAIN;
-    rootEntry.size.u.HighPart  = 0;
-    rootEntry.size.u.LowPart   = 0;
+  memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
 
-    StorageImpl_WriteDirEntry(This, 0, &rootEntry);
-  }
+  StorageUtl_WriteWord(
+    buffer,
+      OFFSET_PS_NAMELENGTH,
+      newData->sizeOfNameString);
 
-  /*
-   * Find the ID of the root storage.
-   */
-  currentEntryRef = 0;
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_LEFTCHILD,
+      newData->leftChild);
 
-  do
-  {
-    hr = StorageImpl_ReadDirEntry(
-                      This,
-                      currentEntryRef,
-                      &currentEntry);
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_RIGHTCHILD,
+      newData->rightChild);
 
-    if (SUCCEEDED(hr))
-    {
-      if ( (currentEntry.sizeOfNameString != 0 ) &&
-           (currentEntry.stgType          == STGTY_ROOT) )
-      {
-        This->base.storageDirEntry = currentEntryRef;
-      }
-    }
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_DIRROOT,
+      newData->dirRootEntry);
 
-    currentEntryRef++;
+  StorageUtl_WriteGUID(
+    buffer,
+      OFFSET_PS_GUID,
+      &newData->clsid);
 
-  } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_CTIMELOW,
+      newData->ctime.dwLowDateTime);
 
-  if (FAILED(hr))
-  {
-    return STG_E_READFAULT;
-  }
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_CTIMEHIGH,
+      newData->ctime.dwHighDateTime);
 
-  /*
-   * Create the block chain abstraction for the small block root chain.
-   */
-  if(!(blockChainStream =
-       BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
-  {
-    return STG_E_READFAULT;
-  }
-  if (!new_object)
-    BlockChainStream_Destroy(This->smallBlockRootChain);
-  This->smallBlockRootChain = blockChainStream;
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_MTIMELOW,
+      newData->mtime.dwLowDateTime);
 
-  if (!new_object)
-  {
-    int i;
-    for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
-    {
-      BlockChainStream_Destroy(This->blockChainCache[i]);
-      This->blockChainCache[i] = NULL;
-    }
-  }
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_MTIMEHIGH,
+      newData->ctime.dwHighDateTime);
 
-  return hr;
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_STARTBLOCK,
+      newData->startingBlock);
+
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_SIZE,
+      newData->size.u.LowPart);
+
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_SIZE_HIGH,
+      newData->size.u.HighPart);
 }
 
-static HRESULT StorageImpl_Construct(
-  HANDLE       hFile,
-  LPCOLESTR    pwcsName,
-  ILockBytes*  pLkbyt,
-  DWORD        openFlags,
-  BOOL         fileBased,
-  BOOL         create,
-  ULONG        sector_size,
-  StorageImpl** result)
+/***************************************************************************
+ *
+ * Internal Method
+ *
+ * Reserve a directory entry in the file and initialize it.
+ */
+static HRESULT StorageImpl_CreateDirEntry(
+  StorageBaseImpl *base,
+  const DirEntry *newData,
+  DirRef *index)
 {
-  StorageImpl* This;
-  HRESULT     hr = S_OK;
-
-  if ( FAILED( validateSTGM(openFlags) ))
-    return STG_E_INVALIDFLAG;
-
-  This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
-  if (!This)
-    return E_OUTOFMEMORY;
-
-  memset(This, 0, sizeof(StorageImpl));
-
-  list_init(&This->base.strmHead);
+  StorageImpl *storage = (StorageImpl*)base;
+  ULONG       currentEntryIndex    = 0;
+  ULONG       newEntryIndex        = DIRENTRY_NULL;
+  HRESULT hr = S_OK;
+  BYTE currentData[RAW_DIRENTRY_SIZE];
+  WORD sizeOfNameString;
 
-  list_init(&This->base.storageHead);
+  do
+  {
+    hr = StorageImpl_ReadRawDirEntry(storage,
+                                     currentEntryIndex,
+                                     currentData);
 
-  This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
-  This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
-  This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
-  This->base.baseVtbl = &StorageImpl_BaseVtbl;
-  This->base.openFlags = (openFlags & ~STGM_CREATE);
-  This->base.ref = 1;
-  This->base.create = create;
+    if (SUCCEEDED(hr))
+    {
+      StorageUtl_ReadWord(
+        currentData,
+        OFFSET_PS_NAMELENGTH,
+        &sizeOfNameString);
 
-  if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
-    This->base.lockingrole = SWMR_Writer;
-  else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
-    This->base.lockingrole = SWMR_Reader;
-  else
-    This->base.lockingrole = SWMR_None;
+      if (sizeOfNameString == 0)
+      {
+        /*
+         * The entry exists and is available, we found it.
+         */
+        newEntryIndex = currentEntryIndex;
+      }
+    }
+    else
+    {
+      /*
+       * We exhausted the directory entries, we will create more space below
+       */
+      newEntryIndex = currentEntryIndex;
+    }
+    currentEntryIndex++;
 
-  This->base.reverted = FALSE;
+  } while (newEntryIndex == DIRENTRY_NULL);
 
   /*
-   * Initialize the big block cache.
+   * grow the directory stream
    */
-  This->bigBlockSize   = sector_size;
-  This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
-  if (hFile)
-    hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
-  else
-  {
-    This->lockBytes = pLkbyt;
-    ILockBytes_AddRef(pLkbyt);
-  }
-
-  if (SUCCEEDED(hr))
-    hr = StorageImpl_GrabLocks(This, openFlags);
-
-  if (SUCCEEDED(hr))
-    hr = StorageImpl_Refresh(This, TRUE, create);
-
   if (FAILED(hr))
   {
-    IStorage_Release(&This->base.IStorage_iface);
-    *result = NULL;
-  }
-  else
-  {
-    StorageImpl_Flush(&This->base);
-    *result = This;
-  }
-
-  return hr;
-}
-
-static void StorageImpl_Invalidate(StorageBaseImpl* iface)
-{
-  StorageImpl *This = (StorageImpl*) iface;
-
-  StorageBaseImpl_DeleteAll(&This->base);
-
-  This->base.reverted = TRUE;
-}
-
-static void StorageImpl_Destroy(StorageBaseImpl* iface)
-{
-  StorageImpl *This = (StorageImpl*) iface;
-  int i;
-  TRACE("(%p)\n", This);
+    BYTE           emptyData[RAW_DIRENTRY_SIZE];
+    ULARGE_INTEGER newSize;
+    ULONG          entryIndex;
+    ULONG          lastEntry     = 0;
+    ULONG          blockCount    = 0;
 
-  StorageImpl_Flush(iface);
+    /*
+     * obtain the new count of blocks in the directory stream
+     */
+    blockCount = BlockChainStream_GetCount(
+                   storage->rootBlockChain)+1;
 
-  StorageImpl_Invalidate(iface);
+    /*
+     * initialize the size used by the directory stream
+     */
+    newSize.QuadPart  = (ULONGLONG)storage->bigBlockSize * blockCount;
 
-  HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
+    /*
+     * add a block to the directory stream
+     */
+    BlockChainStream_SetSize(storage->rootBlockChain, newSize);
 
-  BlockChainStream_Destroy(This->smallBlockRootChain);
-  BlockChainStream_Destroy(This->rootBlockChain);
-  BlockChainStream_Destroy(This->smallBlockDepotChain);
+    /*
+     * memset the empty entry in order to initialize the unused newly
+     * created entries
+     */
+    memset(emptyData, 0, RAW_DIRENTRY_SIZE);
 
-  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
-    BlockChainStream_Destroy(This->blockChainCache[i]);
+    /*
+     * initialize them
+     */
+    lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
 
-  for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
-  {
-    ULARGE_INTEGER offset, cb;
-    cb.QuadPart = 1;
-    if (This->locked_bytes[i] != 0)
+    for(
+      entryIndex = newEntryIndex + 1;
+      entryIndex < lastEntry;
+      entryIndex++)
     {
-      offset.QuadPart = This->locked_bytes[i];
-      ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+      StorageImpl_WriteRawDirEntry(
+        storage,
+        entryIndex,
+        emptyData);
     }
-  }
-
-  if (This->lockBytes)
-    ILockBytes_Release(This->lockBytes);
-  HeapFree(GetProcessHeap(), 0, This);
-}
-
-static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
-{
-  StorageImpl *This = (StorageImpl*)storage;
-  int i;
-  HRESULT hr;
-  TRACE("(%p)\n", This);
-
-  hr = BlockChainStream_Flush(This->smallBlockRootChain);
 
-  if (SUCCEEDED(hr))
-    hr = BlockChainStream_Flush(This->rootBlockChain);
+    StorageImpl_SaveFileHeader(storage);
+  }
 
-  if (SUCCEEDED(hr))
-    hr = BlockChainStream_Flush(This->smallBlockDepotChain);
+  UpdateRawDirEntry(currentData, newData);
 
-  for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
-    if (This->blockChainCache[i])
-      hr = BlockChainStream_Flush(This->blockChainCache[i]);
+  hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
 
   if (SUCCEEDED(hr))
-    hr = ILockBytes_Flush(This->lockBytes);
+    *index = newEntryIndex;
 
   return hr;
 }
 
 /******************************************************************************
- *      StorageImpl_GetNextFreeBigBlock
- *
- * Returns the index of the next free big block.
- * If the big block depot is filled, this method will enlarge it.
+ *      StorageImpl_ReadDirEntry
  *
+ * This method will read the specified directory entry.
  */
-static ULONG StorageImpl_GetNextFreeBigBlock(
-  StorageImpl* This)
+HRESULT StorageImpl_ReadDirEntry(
+  StorageImpl* This,
+  DirRef         index,
+  DirEntry*      buffer)
 {
-  ULONG depotBlockIndexPos;
-  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
-  ULONG depotBlockOffset;
-  ULONG blocksPerDepot    = This->bigBlockSize / sizeof(ULONG);
-  ULONG nextBlockIndex    = BLOCK_SPECIAL;
-  int   depotIndex        = 0;
-  ULONG freeBlock         = BLOCK_UNUSED;
-  ULONG read;
-  ULARGE_INTEGER neededSize;
-  STATSTG statstg;
+  BYTE           currentEntry[RAW_DIRENTRY_SIZE];
+  HRESULT        readRes;
 
-  depotIndex = This->prevFreeBlock / blocksPerDepot;
-  depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
+  readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
 
-  /*
-   * Scan the entire big block depot until we find a block marked free
-   */
-  while (nextBlockIndex != BLOCK_UNUSED)
+  if (SUCCEEDED(readRes))
   {
-    if (depotIndex < COUNT_BBDEPOTINHEADER)
-    {
-      depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
-
-      /*
-       * Grow the primary depot.
-       */
-      if (depotBlockIndexPos == BLOCK_UNUSED)
-      {
-        depotBlockIndexPos = depotIndex*blocksPerDepot;
+    memset(buffer->name, 0, sizeof(buffer->name));
+    memcpy(
+      buffer->name,
+      (WCHAR *)currentEntry+OFFSET_PS_NAME,
+      DIRENTRY_NAME_BUFFER_LEN );
+    TRACE("storage name: %s\n", debugstr_w(buffer->name));
 
-        /*
-         * Add a block depot.
-         */
-        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
-        This->bigBlockDepotCount++;
-        This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
+    memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
 
-        /*
-         * Flag it as a block depot.
-         */
-        StorageImpl_SetNextBlockInChain(This,
-                                          depotBlockIndexPos,
-                                          BLOCK_SPECIAL);
-
-        /* Save new header information.
-         */
-        StorageImpl_SaveFileHeader(This);
-      }
-    }
-    else
-    {
-      depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
+    StorageUtl_ReadWord(
+      currentEntry,
+      OFFSET_PS_NAMELENGTH,
+      &buffer->sizeOfNameString);
 
-      if (depotBlockIndexPos == BLOCK_UNUSED)
-      {
-        /*
-         * Grow the extended depot.
-         */
-        ULONG extIndex       = BLOCK_UNUSED;
-        ULONG numExtBlocks   = depotIndex - COUNT_BBDEPOTINHEADER;
-        ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_LEFTCHILD,
+      &buffer->leftChild);
 
-        if (extBlockOffset == 0)
-        {
-          /* We need an extended block.
-           */
-          extIndex = Storage32Impl_AddExtBlockDepot(This);
-          This->extBigBlockDepotCount++;
-          depotBlockIndexPos = extIndex + 1;
-        }
-        else
-          depotBlockIndexPos = depotIndex * blocksPerDepot;
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_RIGHTCHILD,
+      &buffer->rightChild);
 
-        /*
-         * Add a block depot and mark it in the extended block.
-         */
-        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
-        This->bigBlockDepotCount++;
-        Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_DIRROOT,
+      &buffer->dirRootEntry);
 
-        /* Flag the block depot.
-         */
-        StorageImpl_SetNextBlockInChain(This,
-                                          depotBlockIndexPos,
-                                          BLOCK_SPECIAL);
+    StorageUtl_ReadGUID(
+      currentEntry,
+      OFFSET_PS_GUID,
+      &buffer->clsid);
 
-        /* If necessary, flag the extended depot block.
-         */
-        if (extIndex != BLOCK_UNUSED)
-          StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_CTIMELOW,
+      &buffer->ctime.dwLowDateTime);
 
-        /* Save header information.
-         */
-        StorageImpl_SaveFileHeader(This);
-      }
-    }
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_CTIMEHIGH,
+      &buffer->ctime.dwHighDateTime);
 
-    StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_MTIMELOW,
+      &buffer->mtime.dwLowDateTime);
 
-    if (read)
-    {
-      while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
-              ( nextBlockIndex != BLOCK_UNUSED))
-      {
-        StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_MTIMEHIGH,
+      &buffer->mtime.dwHighDateTime);
 
-        if (nextBlockIndex == BLOCK_UNUSED)
-        {
-          freeBlock = (depotIndex * blocksPerDepot) +
-                      (depotBlockOffset/sizeof(ULONG));
-        }
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_STARTBLOCK,
+      &buffer->startingBlock);
 
-        depotBlockOffset += sizeof(ULONG);
-      }
-    }
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_SIZE,
+      &buffer->size.u.LowPart);
 
-    depotIndex++;
-    depotBlockOffset = 0;
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_SIZE_HIGH,
+      &buffer->size.u.HighPart);
   }
 
-  /*
-   * make sure that the block physically exists before using it
-   */
-  neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
+  return readRes;
+}
 
-  ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
+/*********************************************************************
+ * Write the specified directory entry to the file
+ */
+HRESULT StorageImpl_WriteDirEntry(
+  StorageImpl*          This,
+  DirRef                index,
+  const DirEntry*       buffer)
+{
+  BYTE currentEntry[RAW_DIRENTRY_SIZE];
 
-  if (neededSize.QuadPart > statstg.cbSize.QuadPart)
-    ILockBytes_SetSize(This->lockBytes, neededSize);
+  UpdateRawDirEntry(currentEntry, buffer);
 
-  This->prevFreeBlock = freeBlock;
+  return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
+}
 
-  return freeBlock;
+
+/************************************************************************
+ * StorageImpl implementation : Block methods
+ ***********************************************************************/
+
+static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
+{
+    return (ULONGLONG)(index+1) * This->bigBlockSize;
 }
 
-/******************************************************************************
- *      Storage32Impl_AddBlockDepot
- *
- * This will create a depot block, essentially it is a block initialized
- * to BLOCK_UNUSEDs.
- */
-static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
+static HRESULT StorageImpl_ReadBigBlock(
+  StorageImpl* This,
+  ULONG          blockIndex,
+  void*          buffer,
+  ULONG*         out_read)
 {
-  BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
-  ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
-  ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
-  ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
+  ULARGE_INTEGER ulOffset;
+  DWORD  read=0;
+  HRESULT hr;
 
-  /*
-   * Initialize blocks as free
-   */
-  memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
+  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
 
-  /* Reserve the range lock sector */
-  if (depotIndex == rangeLockDepot)
+  hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
+
+  if (SUCCEEDED(hr) &&  read < This->bigBlockSize)
   {
-    ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
+    /* File ends during this block; fill the rest with 0's. */
+    memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
   }
 
-  StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
+  if (out_read) *out_read = read;
+
+  return hr;
 }
 
-/******************************************************************************
- *      Storage32Impl_GetExtDepotBlock
- *
- * Returns the index of the block that corresponds to the specified depot
- * index. This method is only for depot indexes equal or greater than
- * COUNT_BBDEPOTINHEADER.
- */
-static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
+static BOOL StorageImpl_ReadDWordFromBigBlock(
+  StorageImpl*  This,
+  ULONG         blockIndex,
+  ULONG         offset,
+  DWORD*        value)
 {
-  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
-  ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
-  ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
-  ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
-  ULONG blockIndex             = BLOCK_UNUSED;
-  ULONG extBlockIndex;
-  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
-  int index, num_blocks;
-
-  assert(depotIndex >= COUNT_BBDEPOTINHEADER);
+  ULARGE_INTEGER ulOffset;
+  DWORD  read;
+  DWORD  tmp;
 
-  if (extBlockCount >= This->extBigBlockDepotCount)
-    return BLOCK_UNUSED;
+  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+  ulOffset.QuadPart += offset;
 
-  if (This->indexExtBlockDepotCached != extBlockCount)
-  {
-    extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
+  StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
+  *value = lendian32toh(tmp);
+  return (read == sizeof(DWORD));
+}
 
-    StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
+static BOOL StorageImpl_WriteBigBlock(
+  StorageImpl*  This,
+  ULONG         blockIndex,
+  const void*   buffer)
+{
+  ULARGE_INTEGER ulOffset;
+  DWORD  wrote;
 
-    num_blocks = This->bigBlockSize / 4;
+  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
 
-    for (index = 0; index < num_blocks; index++)
-    {
-      StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
-      This->extBlockDepotCached[index] = blockIndex;
-    }
+  StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
+  return (wrote == This->bigBlockSize);
+}
 
-    This->indexExtBlockDepotCached = extBlockCount;
-  }
+static BOOL StorageImpl_WriteDWordToBigBlock(
+  StorageImpl* This,
+  ULONG         blockIndex,
+  ULONG         offset,
+  DWORD         value)
+{
+  ULARGE_INTEGER ulOffset;
+  DWORD  wrote;
 
-  blockIndex = This->extBlockDepotCached[extBlockOffset];
+  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+  ulOffset.QuadPart += offset;
 
-  return blockIndex;
+  value = htole32(value);
+  StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
+  return (wrote == sizeof(DWORD));
 }
 
 /******************************************************************************
- *      Storage32Impl_SetExtDepotBlock
+ *              Storage32Impl_SmallBlocksToBigBlocks
  *
- * Associates the specified block index to the specified depot index.
- * This method is only for depot indexes equal or greater than
- * COUNT_BBDEPOTINHEADER.
+ * This method will convert a small block chain to a big block chain.
+ * The small block chain will be destroyed.
  */
-static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
+BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
+                      StorageImpl* This,
+                      SmallBlockChainStream** ppsbChain)
 {
-  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
-  ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
-  ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
-  ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
-  ULONG extBlockIndex;
-
-  assert(depotIndex >= COUNT_BBDEPOTINHEADER);
+  ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
+  ULARGE_INTEGER size, offset;
+  ULONG cbRead, cbWritten;
+  ULARGE_INTEGER cbTotalRead;
+  DirRef streamEntryRef;
+  HRESULT resWrite = S_OK;
+  HRESULT resRead;
+  DirEntry streamEntry;
+  BYTE *buffer;
+  BlockChainStream *bbTempChain = NULL;
+  BlockChainStream *bigBlockChain = NULL;
 
-  assert(extBlockCount < This->extBigBlockDepotCount);
+  /*
+   * Create a temporary big block chain that doesn't have
+   * an associated directory entry. This temporary chain will be
+   * used to copy data from small blocks to big blocks.
+   */
+  bbTempChain = BlockChainStream_Construct(This,
+                                           &bbHeadOfChain,
+                                           DIRENTRY_NULL);
+  if(!bbTempChain) return NULL;
+  /*
+   * Grow the big block chain.
+   */
+  size = SmallBlockChainStream_GetSize(*ppsbChain);
+  BlockChainStream_SetSize(bbTempChain, size);
 
-  extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
+  /*
+   * Copy the contents of the small block chain to the big block chain
+   * by small block size increments.
+   */
+  offset.u.LowPart = 0;
+  offset.u.HighPart = 0;
+  cbTotalRead.QuadPart = 0;
 
-  if (extBlockIndex != BLOCK_UNUSED)
+  buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
+  do
   {
-    StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
-                        extBlockOffset * sizeof(ULONG),
-                        blockIndex);
-  }
+    resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
+                                           offset,
+                                           min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
+                                           buffer,
+                                           &cbRead);
+    if (FAILED(resRead))
+        break;
 
-  if (This->indexExtBlockDepotCached == extBlockCount)
-  {
-    This->extBlockDepotCached[extBlockOffset] = blockIndex;
-  }
-}
+    if (cbRead > 0)
+    {
+        cbTotalRead.QuadPart += cbRead;
 
-/******************************************************************************
- *      Storage32Impl_AddExtBlockDepot
- *
- * Creates an extended depot block.
- */
-static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
-{
-  ULONG numExtBlocks           = This->extBigBlockDepotCount;
-  ULONG nextExtBlock           = This->extBigBlockDepotStart;
-  BYTE  depotBuffer[MAX_BIG_BLOCK_SIZE];
-  ULONG index                  = BLOCK_UNUSED;
-  ULONG nextBlockOffset        = This->bigBlockSize - sizeof(ULONG);
-  ULONG blocksPerDepotBlock    = This->bigBlockSize / sizeof(ULONG);
-  ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
+        resWrite = BlockChainStream_WriteAt(bbTempChain,
+                                            offset,
+                                            cbRead,
+                                            buffer,
+                                            &cbWritten);
 
-  index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
-          blocksPerDepotBlock;
+        if (FAILED(resWrite))
+            break;
 
-  if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
+        offset.u.LowPart += cbRead;
+    }
+    else
+    {
+        resRead = STG_E_READFAULT;
+        break;
+    }
+  } while (cbTotalRead.QuadPart < size.QuadPart);
+  HeapFree(GetProcessHeap(),0,buffer);
+
+  size.u.HighPart = 0;
+  size.u.LowPart  = 0;
+
+  if (FAILED(resRead) || FAILED(resWrite))
   {
-    /*
-     * The first extended block.
-     */
-    This->extBigBlockDepotStart = index;
+    ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
+    BlockChainStream_SetSize(bbTempChain, size);
+    BlockChainStream_Destroy(bbTempChain);
+    return NULL;
   }
-  else
-  {
-    /*
-     * Find the last existing extended block.
-     */
-    nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
 
-    /*
-     * Add the new extended block to the chain.
-     */
-    StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
-                                     index);
-  }
+  /*
+   * Destroy the small block chain.
+   */
+  streamEntryRef = (*ppsbChain)->ownerDirEntry;
+  SmallBlockChainStream_SetSize(*ppsbChain, size);
+  SmallBlockChainStream_Destroy(*ppsbChain);
+  *ppsbChain = 0;
 
   /*
-   * Initialize this block.
+   * Change the directory entry. This chain is now a big block chain
+   * and it doesn't reside in the small blocks chain anymore.
    */
-  memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
-  StorageImpl_WriteBigBlock(This, index, depotBuffer);
+  StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
 
-  /* Add the block to our cache. */
-  if (This->extBigBlockDepotLocationsSize == numExtBlocks)
-  {
-    ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
-    ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
+  streamEntry.startingBlock = bbHeadOfChain;
 
-    memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
-    HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
+  StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
 
-    This->extBigBlockDepotLocations = new_cache;
-    This->extBigBlockDepotLocationsSize = new_cache_size;
-  }
-  This->extBigBlockDepotLocations[numExtBlocks] = index;
+  /*
+   * Destroy the temporary entryless big block chain.
+   * Create a new big block chain associated with this entry.
+   */
+  BlockChainStream_Destroy(bbTempChain);
+  bigBlockChain = BlockChainStream_Construct(This,
+                                             NULL,
+                                             streamEntryRef);
 
-  return index;
+  return bigBlockChain;
 }
 
 /******************************************************************************
- *      StorageImpl_FreeBigBlock
+ *              Storage32Impl_BigBlocksToSmallBlocks
  *
- * This method will flag the specified block as free in the big block depot.
+ * This method will convert a big block chain to a small block chain.
+ * The big block chain will be destroyed on success.
  */
-static void StorageImpl_FreeBigBlock(
-  StorageImpl* This,
-  ULONG          blockIndex)
+SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
+                           StorageImpl* This,
+                           BlockChainStream** ppbbChain,
+                           ULARGE_INTEGER newSize)
 {
-  StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
-
-  if (blockIndex < This->prevFreeBlock)
-    This->prevFreeBlock = blockIndex;
-}
+    ULARGE_INTEGER size, offset, cbTotalRead;
+    ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
+    DirRef streamEntryRef;
+    HRESULT resWrite = S_OK, resRead = S_OK;
+    DirEntry streamEntry;
+    BYTE* buffer;
+    SmallBlockChainStream* sbTempChain;
 
-/************************************************************************
- * StorageImpl_GetNextBlockInChain
- *
- * This method will retrieve the block index of the next big block in
- * in the chain.
- *
- * Params:  This       - Pointer to the Storage object.
- *          blockIndex - Index of the block to retrieve the chain
- *                       for.
- *          nextBlockIndex - receives the return value.
- *
- * Returns: This method returns the index of the next block in the chain.
- *          It will return the constants:
- *              BLOCK_SPECIAL - If the block given was not part of a
- *                              chain.
- *              BLOCK_END_OF_CHAIN - If the block given was the last in
- *                                   a chain.
- *              BLOCK_UNUSED - If the block given was not past of a chain
- *                             and is available.
- *              BLOCK_EXTBBDEPOT - This block is part of the extended
- *                                 big block depot.
- *
- * See Windows documentation for more details on IStorage methods.
- */
-static HRESULT StorageImpl_GetNextBlockInChain(
-  StorageImpl* This,
-  ULONG        blockIndex,
-  ULONG*       nextBlockIndex)
-{
-  ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
-  ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
-  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
-  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
-  ULONG read;
-  ULONG depotBlockIndexPos;
-  int index, num_blocks;
+    TRACE("%p %p\n", This, ppbbChain);
 
-  *nextBlockIndex   = BLOCK_SPECIAL;
+    sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
+            DIRENTRY_NULL);
 
-  if(depotBlockCount >= This->bigBlockDepotCount)
-  {
-    WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
-	 This->bigBlockDepotCount);
-    return STG_E_READFAULT;
-  }
+    if(!sbTempChain)
+        return NULL;
 
-  /*
-   * Cache the currently accessed depot block.
-   */
-  if (depotBlockCount != This->indexBlockDepotCached)
-  {
-    This->indexBlockDepotCached = depotBlockCount;
+    SmallBlockChainStream_SetSize(sbTempChain, newSize);
+    size = BlockChainStream_GetSize(*ppbbChain);
+    size.QuadPart = min(size.QuadPart, newSize.QuadPart);
 
-    if (depotBlockCount < COUNT_BBDEPOTINHEADER)
+    offset.u.HighPart = 0;
+    offset.u.LowPart = 0;
+    cbTotalRead.QuadPart = 0;
+    buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
+    while(cbTotalRead.QuadPart < size.QuadPart)
     {
-      depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
-    }
-    else
-    {
-      /*
-       * We have to look in the extended depot.
-       */
-      depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
-    }
+        resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
+                min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
+                buffer, &cbRead);
 
-    StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
+        if(FAILED(resRead))
+            break;
 
-    if (!read)
-      return STG_E_READFAULT;
+        if(cbRead > 0)
+        {
+            cbTotalRead.QuadPart += cbRead;
 
-    num_blocks = This->bigBlockSize / 4;
+            resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
+                    cbRead, buffer, &cbWritten);
 
-    for (index = 0; index < num_blocks; index++)
+            if(FAILED(resWrite))
+                break;
+
+            offset.u.LowPart += cbRead;
+        }
+        else
+        {
+            resRead = STG_E_READFAULT;
+            break;
+        }
+    }
+    HeapFree(GetProcessHeap(), 0, buffer);
+
+    size.u.HighPart = 0;
+    size.u.LowPart = 0;
+
+    if(FAILED(resRead) || FAILED(resWrite))
     {
-      StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
-      This->blockDepotCached[index] = *nextBlockIndex;
+        ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
+        SmallBlockChainStream_SetSize(sbTempChain, size);
+        SmallBlockChainStream_Destroy(sbTempChain);
+        return NULL;
     }
-  }
 
-  *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
+    /* destroy the original big block chain */
+    streamEntryRef = (*ppbbChain)->ownerDirEntry;
+    BlockChainStream_SetSize(*ppbbChain, size);
+    BlockChainStream_Destroy(*ppbbChain);
+    *ppbbChain = NULL;
 
-  return S_OK;
+    StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
+    streamEntry.startingBlock = sbHeadOfChain;
+    StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
+
+    SmallBlockChainStream_Destroy(sbTempChain);
+    return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
 }
 
 /******************************************************************************
- *      Storage32Impl_GetNextExtendedBlock
- *
- * Given an extended block this method will return the next extended block.
- *
- * NOTES:
- * The last ULONG of an extended block is the block index of the next
- * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
- * depot.
+ *      Storage32Impl_AddBlockDepot
  *
- * Return values:
- *    - The index of the next extended block
- *    - BLOCK_UNUSED: there is no next extended block.
- *    - Any other return values denotes failure.
+ * This will create a depot block, essentially it is a block initialized
+ * to BLOCK_UNUSEDs.
  */
-static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
+static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
 {
-  ULONG nextBlockIndex   = BLOCK_SPECIAL;
-  ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
+  BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
+  ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
+  ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
+  ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
 
-  StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
-                        &nextBlockIndex);
+  /*
+   * Initialize blocks as free
+   */
+  memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
 
-  return nextBlockIndex;
+  /* Reserve the range lock sector */
+  if (depotIndex == rangeLockDepot)
+  {
+    ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
+  }
+
+  StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
 }
 
 /******************************************************************************
- *      StorageImpl_SetNextBlockInChain
- *
- * This method will write the index of the specified block's next block
- * in the big block depot.
- *
- * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
- *              do the following
- *
- * StorageImpl_SetNextBlockInChain(This, 3, 1);
- * StorageImpl_SetNextBlockInChain(This, 1, 7);
- * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
+ *      Storage32Impl_GetExtDepotBlock
  *
+ * Returns the index of the block that corresponds to the specified depot
+ * index. This method is only for depot indexes equal or greater than
+ * COUNT_BBDEPOTINHEADER.
  */
-static void StorageImpl_SetNextBlockInChain(
-          StorageImpl* This,
-          ULONG          blockIndex,
-          ULONG          nextBlock)
+static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
 {
-  ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
-  ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
-  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
-  ULONG depotBlockIndexPos;
+  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
+  ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
+  ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
+  ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
+  ULONG blockIndex             = BLOCK_UNUSED;
+  ULONG extBlockIndex;
+  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
+  int index, num_blocks;
 
-  assert(depotBlockCount < This->bigBlockDepotCount);
-  assert(blockIndex != nextBlock);
+  assert(depotIndex >= COUNT_BBDEPOTINHEADER);
 
-  if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
-    /* This should never happen (storage file format spec forbids it), but
-     * older versions of Wine may have generated broken files. We don't want to
-     * assert and potentially lose data, but we do want to know if this ever
-     * happens in a newly-created file. */
-    ERR("Using range lock page\n");
+  if (extBlockCount >= This->extBigBlockDepotCount)
+    return BLOCK_UNUSED;
 
-  if (depotBlockCount < COUNT_BBDEPOTINHEADER)
+  if (This->indexExtBlockDepotCached != extBlockCount)
   {
-    depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
+    extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
+
+    StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
+
+    num_blocks = This->bigBlockSize / 4;
+
+    for (index = 0; index < num_blocks; index++)
+    {
+      StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
+      This->extBlockDepotCached[index] = blockIndex;
+    }
+
+    This->indexExtBlockDepotCached = extBlockCount;
   }
-  else
+
+  blockIndex = This->extBlockDepotCached[extBlockOffset];
+
+  return blockIndex;
+}
+
+/******************************************************************************
+ *      Storage32Impl_SetExtDepotBlock
+ *
+ * Associates the specified block index to the specified depot index.
+ * This method is only for depot indexes equal or greater than
+ * COUNT_BBDEPOTINHEADER.
+ */
+static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
+{
+  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
+  ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
+  ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
+  ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
+  ULONG extBlockIndex;
+
+  assert(depotIndex >= COUNT_BBDEPOTINHEADER);
+
+  assert(extBlockCount < This->extBigBlockDepotCount);
+
+  extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
+
+  if (extBlockIndex != BLOCK_UNUSED)
   {
-    /*
-     * We have to look in the extended depot.
-     */
-    depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
+    StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
+                        extBlockOffset * sizeof(ULONG),
+                        blockIndex);
   }
 
-  StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
-                        nextBlock);
-  /*
-   * Update the cached block depot, if necessary.
-   */
-  if (depotBlockCount == This->indexBlockDepotCached)
+  if (This->indexExtBlockDepotCached == extBlockCount)
   {
-    This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
+    This->extBlockDepotCached[extBlockOffset] = blockIndex;
   }
 }
 
 /******************************************************************************
- *      StorageImpl_LoadFileHeader
+ *      Storage32Impl_AddExtBlockDepot
  *
- * This method will read in the file header
+ * Creates an extended depot block.
  */
-static HRESULT StorageImpl_LoadFileHeader(
-          StorageImpl* This)
+static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
 {
-  HRESULT hr;
-  BYTE    headerBigBlock[HEADER_SIZE];
-  int     index;
-  ULARGE_INTEGER offset;
-  DWORD bytes_read;
+  ULONG numExtBlocks           = This->extBigBlockDepotCount;
+  ULONG nextExtBlock           = This->extBigBlockDepotStart;
+  BYTE  depotBuffer[MAX_BIG_BLOCK_SIZE];
+  ULONG index                  = BLOCK_UNUSED;
+  ULONG nextBlockOffset        = This->bigBlockSize - sizeof(ULONG);
+  ULONG blocksPerDepotBlock    = This->bigBlockSize / sizeof(ULONG);
+  ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
 
-  TRACE("\n");
-  /*
-   * Get a pointer to the big block of data containing the header.
-   */
-  offset.u.HighPart = 0;
-  offset.u.LowPart = 0;
-  hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
-  if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
-    hr = STG_E_FILENOTFOUND;
+  index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
+          blocksPerDepotBlock;
 
-  /*
-   * Extract the information from the header.
-   */
-  if (SUCCEEDED(hr))
+  if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
   {
     /*
-     * Check for the "magic number" signature and return an error if it is not
-     * found.
+     * The first extended block.
      */
-    if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
-    {
-      return STG_E_OLDFORMAT;
-    }
-
-    if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
-    {
-      return STG_E_INVALIDHEADER;
-    }
+    This->extBigBlockDepotStart = index;
+  }
+  else
+  {
+    /*
+     * Find the last existing extended block.
+     */
+    nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
 
-    StorageUtl_ReadWord(
-      headerBigBlock,
-      OFFSET_BIGBLOCKSIZEBITS,
-      &This->bigBlockSizeBits);
+    /*
+     * Add the new extended block to the chain.
+     */
+    StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
+                                     index);
+  }
 
-    StorageUtl_ReadWord(
-      headerBigBlock,
-      OFFSET_SMALLBLOCKSIZEBITS,
-      &This->smallBlockSizeBits);
+  /*
+   * Initialize this block.
+   */
+  memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
+  StorageImpl_WriteBigBlock(This, index, depotBuffer);
 
-    StorageUtl_ReadDWord(
-      headerBigBlock,
-      OFFSET_BBDEPOTCOUNT,
-      &This->bigBlockDepotCount);
+  /* Add the block to our cache. */
+  if (This->extBigBlockDepotLocationsSize == numExtBlocks)
+  {
+    ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
+    ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
 
-    StorageUtl_ReadDWord(
-      headerBigBlock,
-      OFFSET_ROOTSTARTBLOCK,
-      &This->rootStartBlock);
+    memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
+    HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
 
-    StorageUtl_ReadDWord(
-      headerBigBlock,
-      OFFSET_TRANSACTIONSIG,
-      &This->transactionSig);
+    This->extBigBlockDepotLocations = new_cache;
+    This->extBigBlockDepotLocationsSize = new_cache_size;
+  }
+  This->extBigBlockDepotLocations[numExtBlocks] = index;
 
-    StorageUtl_ReadDWord(
-      headerBigBlock,
-      OFFSET_SMALLBLOCKLIMIT,
-      &This->smallBlockLimit);
+  return index;
+}
 
-    StorageUtl_ReadDWord(
-      headerBigBlock,
-      OFFSET_SBDEPOTSTART,
-      &This->smallBlockDepotStart);
+/************************************************************************
+ * StorageImpl_GetNextBlockInChain
+ *
+ * This method will retrieve the block index of the next big block in
+ * in the chain.
+ *
+ * Params:  This       - Pointer to the Storage object.
+ *          blockIndex - Index of the block to retrieve the chain
+ *                       for.
+ *          nextBlockIndex - receives the return value.
+ *
+ * Returns: This method returns the index of the next block in the chain.
+ *          It will return the constants:
+ *              BLOCK_SPECIAL - If the block given was not part of a
+ *                              chain.
+ *              BLOCK_END_OF_CHAIN - If the block given was the last in
+ *                                   a chain.
+ *              BLOCK_UNUSED - If the block given was not past of a chain
+ *                             and is available.
+ *              BLOCK_EXTBBDEPOT - This block is part of the extended
+ *                                 big block depot.
+ *
+ * See Windows documentation for more details on IStorage methods.
+ */
+static HRESULT StorageImpl_GetNextBlockInChain(
+  StorageImpl* This,
+  ULONG        blockIndex,
+  ULONG*       nextBlockIndex)
+{
+  ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
+  ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
+  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
+  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
+  ULONG read;
+  ULONG depotBlockIndexPos;
+  int index, num_blocks;
 
-    StorageUtl_ReadDWord(
-      headerBigBlock,
-      OFFSET_EXTBBDEPOTSTART,
-      &This->extBigBlockDepotStart);
+  *nextBlockIndex   = BLOCK_SPECIAL;
 
-    StorageUtl_ReadDWord(
-      headerBigBlock,
-      OFFSET_EXTBBDEPOTCOUNT,
-      &This->extBigBlockDepotCount);
+  if(depotBlockCount >= This->bigBlockDepotCount)
+  {
+    WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
+	 This->bigBlockDepotCount);
+    return STG_E_READFAULT;
+  }
 
-    for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
+  /*
+   * Cache the currently accessed depot block.
+   */
+  if (depotBlockCount != This->indexBlockDepotCached)
+  {
+    This->indexBlockDepotCached = depotBlockCount;
+
+    if (depotBlockCount < COUNT_BBDEPOTINHEADER)
     {
-      StorageUtl_ReadDWord(
-        headerBigBlock,
-        OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
-        &(This->bigBlockDepotStart[index]));
+      depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
+    }
+    else
+    {
+      /*
+       * We have to look in the extended depot.
+       */
+      depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
     }
 
-    /*
-     * Make the bitwise arithmetic to get the size of the blocks in bytes.
-     */
-    This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
-    This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
+    StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
 
-    /*
-     * Right now, the code is making some assumptions about the size of the
-     * blocks, just make sure they are what we're expecting.
-     */
-    if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
-	This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
-	This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
+    if (!read)
+      return STG_E_READFAULT;
+
+    num_blocks = This->bigBlockSize / 4;
+
+    for (index = 0; index < num_blocks; index++)
     {
-	FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
-	    This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
-	hr = STG_E_INVALIDHEADER;
+      StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
+      This->blockDepotCached[index] = *nextBlockIndex;
     }
-    else
-	hr = S_OK;
   }
 
-  return hr;
+  *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
+
+  return S_OK;
 }
 
 /******************************************************************************
- *      StorageImpl_SaveFileHeader
+ *      Storage32Impl_GetNextExtendedBlock
  *
- * This method will save to the file the header
+ * Given an extended block this method will return the next extended block.
+ *
+ * NOTES:
+ * The last ULONG of an extended block is the block index of the next
+ * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
+ * depot.
+ *
+ * Return values:
+ *    - The index of the next extended block
+ *    - BLOCK_UNUSED: there is no next extended block.
+ *    - Any other return values denotes failure.
  */
-static void StorageImpl_SaveFileHeader(
-          StorageImpl* This)
+static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
 {
-  BYTE   headerBigBlock[HEADER_SIZE];
-  int    index;
-  HRESULT hr;
-  ULARGE_INTEGER offset;
-  DWORD bytes_read, bytes_written;
-  DWORD major_version, dirsectorcount;
+  ULONG nextBlockIndex   = BLOCK_SPECIAL;
+  ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
 
-  /*
-   * Get a pointer to the big block of data containing the header.
-   */
-  offset.u.HighPart = 0;
-  offset.u.LowPart = 0;
-  hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
-  if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
-    hr = STG_E_FILENOTFOUND;
+  StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
+                        &nextBlockIndex);
 
-  if (This->bigBlockSizeBits == 0x9)
-    major_version = 3;
-  else if (This->bigBlockSizeBits == 0xc)
-    major_version = 4;
-  else
+  return nextBlockIndex;
+}
+
+/******************************************************************************
+ *      StorageImpl_SetNextBlockInChain
+ *
+ * This method will write the index of the specified block's next block
+ * in the big block depot.
+ *
+ * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
+ *              do the following
+ *
+ * StorageImpl_SetNextBlockInChain(This, 3, 1);
+ * StorageImpl_SetNextBlockInChain(This, 1, 7);
+ * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
+ *
+ */
+static void StorageImpl_SetNextBlockInChain(
+          StorageImpl* This,
+          ULONG          blockIndex,
+          ULONG          nextBlock)
+{
+  ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
+  ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
+  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
+  ULONG depotBlockIndexPos;
+
+  assert(depotBlockCount < This->bigBlockDepotCount);
+  assert(blockIndex != nextBlock);
+
+  if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
+    /* This should never happen (storage file format spec forbids it), but
+     * older versions of Wine may have generated broken files. We don't want to
+     * assert and potentially lose data, but we do want to know if this ever
+     * happens in a newly-created file. */
+    ERR("Using range lock page\n");
+
+  if (depotBlockCount < COUNT_BBDEPOTINHEADER)
   {
-    ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
-    major_version = 4;
+    depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
   }
-
-  /*
-   * If the block read failed, the file is probably new.
-   */
-  if (FAILED(hr))
+  else
   {
     /*
-     * Initialize for all unknown fields.
-     */
-    memset(headerBigBlock, 0, HEADER_SIZE);
-
-    /*
-     * Initialize the magic number.
+     * We have to look in the extended depot.
      */
-    memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
+    depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
   }
 
+  StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
+                        nextBlock);
   /*
-   * Write the information to the header.
+   * Update the cached block depot, if necessary.
    */
-  StorageUtl_WriteWord(
-    headerBigBlock,
-    OFFSET_MINORVERSION,
-    0x3e);
+  if (depotBlockCount == This->indexBlockDepotCached)
+  {
+    This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
+  }
+}
 
-  StorageUtl_WriteWord(
-    headerBigBlock,
-    OFFSET_MAJORVERSION,
-    major_version);
+/******************************************************************************
+ *      StorageImpl_GetNextFreeBigBlock
+ *
+ * Returns the index of the next free big block.
+ * If the big block depot is filled, this method will enlarge it.
+ *
+ */
+static ULONG StorageImpl_GetNextFreeBigBlock(
+  StorageImpl* This)
+{
+  ULONG depotBlockIndexPos;
+  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
+  ULONG depotBlockOffset;
+  ULONG blocksPerDepot    = This->bigBlockSize / sizeof(ULONG);
+  ULONG nextBlockIndex    = BLOCK_SPECIAL;
+  int   depotIndex        = 0;
+  ULONG freeBlock         = BLOCK_UNUSED;
+  ULONG read;
+  ULARGE_INTEGER neededSize;
+  STATSTG statstg;
 
-  StorageUtl_WriteWord(
-    headerBigBlock,
-    OFFSET_BYTEORDERMARKER,
-    (WORD)-2);
+  depotIndex = This->prevFreeBlock / blocksPerDepot;
+  depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
 
-  StorageUtl_WriteWord(
-    headerBigBlock,
-    OFFSET_BIGBLOCKSIZEBITS,
-    This->bigBlockSizeBits);
+  /*
+   * Scan the entire big block depot until we find a block marked free
+   */
+  while (nextBlockIndex != BLOCK_UNUSED)
+  {
+    if (depotIndex < COUNT_BBDEPOTINHEADER)
+    {
+      depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
 
-  StorageUtl_WriteWord(
-    headerBigBlock,
-    OFFSET_SMALLBLOCKSIZEBITS,
-    This->smallBlockSizeBits);
+      /*
+       * Grow the primary depot.
+       */
+      if (depotBlockIndexPos == BLOCK_UNUSED)
+      {
+        depotBlockIndexPos = depotIndex*blocksPerDepot;
 
-  if (major_version >= 4)
-  {
-    if (This->rootBlockChain)
-      dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
+        /*
+         * Add a block depot.
+         */
+        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
+        This->bigBlockDepotCount++;
+        This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
+
+        /*
+         * Flag it as a block depot.
+         */
+        StorageImpl_SetNextBlockInChain(This,
+                                          depotBlockIndexPos,
+                                          BLOCK_SPECIAL);
+
+        /* Save new header information.
+         */
+        StorageImpl_SaveFileHeader(This);
+      }
+    }
     else
-      /* This file is being created, and it will start out with one block. */
-      dirsectorcount = 1;
-  }
-  else
-    /* This field must be 0 in versions older than 4 */
-    dirsectorcount = 0;
+    {
+      depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
 
-  StorageUtl_WriteDWord(
-    headerBigBlock,
-    OFFSET_DIRSECTORCOUNT,
-    dirsectorcount);
+      if (depotBlockIndexPos == BLOCK_UNUSED)
+      {
+        /*
+         * Grow the extended depot.
+         */
+        ULONG extIndex       = BLOCK_UNUSED;
+        ULONG numExtBlocks   = depotIndex - COUNT_BBDEPOTINHEADER;
+        ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
 
-  StorageUtl_WriteDWord(
-    headerBigBlock,
-    OFFSET_BBDEPOTCOUNT,
-    This->bigBlockDepotCount);
+        if (extBlockOffset == 0)
+        {
+          /* We need an extended block.
+           */
+          extIndex = Storage32Impl_AddExtBlockDepot(This);
+          This->extBigBlockDepotCount++;
+          depotBlockIndexPos = extIndex + 1;
+        }
+        else
+          depotBlockIndexPos = depotIndex * blocksPerDepot;
 
-  StorageUtl_WriteDWord(
-    headerBigBlock,
-    OFFSET_ROOTSTARTBLOCK,
-    This->rootStartBlock);
+        /*
+         * Add a block depot and mark it in the extended block.
+         */
+        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
+        This->bigBlockDepotCount++;
+        Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
 
-  StorageUtl_WriteDWord(
-    headerBigBlock,
-    OFFSET_TRANSACTIONSIG,
-    This->transactionSig);
+        /* Flag the block depot.
+         */
+        StorageImpl_SetNextBlockInChain(This,
+                                          depotBlockIndexPos,
+                                          BLOCK_SPECIAL);
 
-  StorageUtl_WriteDWord(
-    headerBigBlock,
-    OFFSET_SMALLBLOCKLIMIT,
-    This->smallBlockLimit);
+        /* If necessary, flag the extended depot block.
+         */
+        if (extIndex != BLOCK_UNUSED)
+          StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
 
-  StorageUtl_WriteDWord(
-    headerBigBlock,
-    OFFSET_SBDEPOTSTART,
-    This->smallBlockDepotStart);
+        /* Save header information.
+         */
+        StorageImpl_SaveFileHeader(This);
+      }
+    }
 
-  StorageUtl_WriteDWord(
-    headerBigBlock,
-    OFFSET_SBDEPOTCOUNT,
-    This->smallBlockDepotChain ?
-     BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
+    StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
 
-  StorageUtl_WriteDWord(
-    headerBigBlock,
-    OFFSET_EXTBBDEPOTSTART,
-    This->extBigBlockDepotStart);
+    if (read)
+    {
+      while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
+              ( nextBlockIndex != BLOCK_UNUSED))
+      {
+        StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
 
-  StorageUtl_WriteDWord(
-    headerBigBlock,
-    OFFSET_EXTBBDEPOTCOUNT,
-    This->extBigBlockDepotCount);
+        if (nextBlockIndex == BLOCK_UNUSED)
+        {
+          freeBlock = (depotIndex * blocksPerDepot) +
+                      (depotBlockOffset/sizeof(ULONG));
+        }
 
-  for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
-  {
-    StorageUtl_WriteDWord(
-      headerBigBlock,
-      OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
-      (This->bigBlockDepotStart[index]));
+        depotBlockOffset += sizeof(ULONG);
+      }
+    }
+
+    depotIndex++;
+    depotBlockOffset = 0;
   }
 
   /*
-   * Write the big block back to the file.
+   * make sure that the block physically exists before using it
    */
-  StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
-}
-
-/******************************************************************************
- *      StorageImpl_ReadRawDirEntry
- *
- * This method will read the raw data from a directory entry in the file.
- *
- * buffer must be RAW_DIRENTRY_SIZE bytes long.
- */
-HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
-{
-  ULARGE_INTEGER offset;
-  HRESULT hr;
-  ULONG bytesRead;
+  neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
 
-  offset.QuadPart  = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
+  ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
 
-  hr = BlockChainStream_ReadAt(
-                    This->rootBlockChain,
-                    offset,
-                    RAW_DIRENTRY_SIZE,
-                    buffer,
-                    &bytesRead);
+  if (neededSize.QuadPart > statstg.cbSize.QuadPart)
+    ILockBytes_SetSize(This->lockBytes, neededSize);
 
-  if (bytesRead != RAW_DIRENTRY_SIZE)
-    return STG_E_READFAULT;
+  This->prevFreeBlock = freeBlock;
 
-  return hr;
+  return freeBlock;
 }
 
 /******************************************************************************
- *      StorageImpl_WriteRawDirEntry
- *
- * This method will write the raw data from a directory entry in the file.
+ *      StorageImpl_FreeBigBlock
  *
- * buffer must be RAW_DIRENTRY_SIZE bytes long.
+ * This method will flag the specified block as free in the big block depot.
  */
-HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
+static void StorageImpl_FreeBigBlock(
+  StorageImpl* This,
+  ULONG          blockIndex)
 {
-  ULARGE_INTEGER offset;
-  ULONG bytesRead;
-
-  offset.QuadPart  = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
+  StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
 
-  return BlockChainStream_WriteAt(
-                    This->rootBlockChain,
-                    offset,
-                    RAW_DIRENTRY_SIZE,
-                    buffer,
-                    &bytesRead);
+  if (blockIndex < This->prevFreeBlock)
+    This->prevFreeBlock = blockIndex;
 }
 
-/******************************************************************************
- *      UpdateRawDirEntry
- *
- * Update raw directory entry data from the fields in newData.
- *
- * buffer must be RAW_DIRENTRY_SIZE bytes long.
- */
-void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
-{
-  memset(buffer, 0, RAW_DIRENTRY_SIZE);
 
-  memcpy(
-    buffer + OFFSET_PS_NAME,
-    newData->name,
-    DIRENTRY_NAME_BUFFER_LEN );
+static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
+  DirRef index, const DirEntry *data)
+{
+  StorageImpl *This = (StorageImpl*)base;
+  return StorageImpl_WriteDirEntry(This, index, data);
+}
 
-  memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
+static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
+  DirRef index, DirEntry *data)
+{
+  StorageImpl *This = (StorageImpl*)base;
+  return StorageImpl_ReadDirEntry(This, index, data);
+}
 
-  StorageUtl_WriteWord(
-    buffer,
-      OFFSET_PS_NAMELENGTH,
-      newData->sizeOfNameString);
+static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
+{
+  int i;
 
-  StorageUtl_WriteDWord(
-    buffer,
-      OFFSET_PS_LEFTCHILD,
-      newData->leftChild);
+  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+  {
+    if (!This->blockChainCache[i])
+    {
+      return &This->blockChainCache[i];
+    }
+  }
 
-  StorageUtl_WriteDWord(
-    buffer,
-      OFFSET_PS_RIGHTCHILD,
-      newData->rightChild);
+  i = This->blockChainToEvict;
 
-  StorageUtl_WriteDWord(
-    buffer,
-      OFFSET_PS_DIRROOT,
-      newData->dirRootEntry);
+  BlockChainStream_Destroy(This->blockChainCache[i]);
+  This->blockChainCache[i] = NULL;
 
-  StorageUtl_WriteGUID(
-    buffer,
-      OFFSET_PS_GUID,
-      &newData->clsid);
+  This->blockChainToEvict++;
+  if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
+    This->blockChainToEvict = 0;
 
-  StorageUtl_WriteDWord(
-    buffer,
-      OFFSET_PS_CTIMELOW,
-      newData->ctime.dwLowDateTime);
+  return &This->blockChainCache[i];
+}
 
-  StorageUtl_WriteDWord(
-    buffer,
-      OFFSET_PS_CTIMEHIGH,
-      newData->ctime.dwHighDateTime);
+static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
+    DirRef index)
+{
+  int i, free_index=-1;
 
-  StorageUtl_WriteDWord(
-    buffer,
-      OFFSET_PS_MTIMELOW,
-      newData->mtime.dwLowDateTime);
+  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+  {
+    if (!This->blockChainCache[i])
+    {
+      if (free_index == -1) free_index = i;
+    }
+    else if (This->blockChainCache[i]->ownerDirEntry == index)
+    {
+      return &This->blockChainCache[i];
+    }
+  }
 
-  StorageUtl_WriteDWord(
-    buffer,
-      OFFSET_PS_MTIMEHIGH,
-      newData->ctime.dwHighDateTime);
+  if (free_index == -1)
+  {
+    free_index = This->blockChainToEvict;
 
-  StorageUtl_WriteDWord(
-    buffer,
-      OFFSET_PS_STARTBLOCK,
-      newData->startingBlock);
+    BlockChainStream_Destroy(This->blockChainCache[free_index]);
+    This->blockChainCache[free_index] = NULL;
 
-  StorageUtl_WriteDWord(
-    buffer,
-      OFFSET_PS_SIZE,
-      newData->size.u.LowPart);
+    This->blockChainToEvict++;
+    if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
+      This->blockChainToEvict = 0;
+  }
 
-  StorageUtl_WriteDWord(
-    buffer,
-      OFFSET_PS_SIZE_HIGH,
-      newData->size.u.HighPart);
+  This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
+  return &This->blockChainCache[free_index];
 }
 
-/******************************************************************************
- *      StorageImpl_ReadDirEntry
- *
- * This method will read the specified directory entry.
- */
-HRESULT StorageImpl_ReadDirEntry(
-  StorageImpl* This,
-  DirRef         index,
-  DirEntry*      buffer)
+static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
 {
-  BYTE           currentEntry[RAW_DIRENTRY_SIZE];
-  HRESULT        readRes;
-
-  readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
+  int i;
 
-  if (SUCCEEDED(readRes))
+  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
   {
-    memset(buffer->name, 0, sizeof(buffer->name));
-    memcpy(
-      buffer->name,
-      (WCHAR *)currentEntry+OFFSET_PS_NAME,
-      DIRENTRY_NAME_BUFFER_LEN );
-    TRACE("storage name: %s\n", debugstr_w(buffer->name));
-
-    memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
+    if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
+    {
+      BlockChainStream_Destroy(This->blockChainCache[i]);
+      This->blockChainCache[i] = NULL;
+      return;
+    }
+  }
+}
 
-    StorageUtl_ReadWord(
-      currentEntry,
-      OFFSET_PS_NAMELENGTH,
-      &buffer->sizeOfNameString);
+static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
+  ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
+{
+  StorageImpl *This = (StorageImpl*)base;
+  DirEntry data;
+  HRESULT hr;
+  ULONG bytesToRead;
 
-    StorageUtl_ReadDWord(
-      currentEntry,
-      OFFSET_PS_LEFTCHILD,
-      &buffer->leftChild);
+  hr = StorageImpl_ReadDirEntry(This, index, &data);
+  if (FAILED(hr)) return hr;
 
-    StorageUtl_ReadDWord(
-      currentEntry,
-      OFFSET_PS_RIGHTCHILD,
-      &buffer->rightChild);
+  if (data.size.QuadPart == 0)
+  {
+    *bytesRead = 0;
+    return S_OK;
+  }
 
-    StorageUtl_ReadDWord(
-      currentEntry,
-      OFFSET_PS_DIRROOT,
-      &buffer->dirRootEntry);
+  if (offset.QuadPart + size > data.size.QuadPart)
+  {
+    bytesToRead = data.size.QuadPart - offset.QuadPart;
+  }
+  else
+  {
+    bytesToRead = size;
+  }
 
-    StorageUtl_ReadGUID(
-      currentEntry,
-      OFFSET_PS_GUID,
-      &buffer->clsid);
+  if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+  {
+    SmallBlockChainStream *stream;
 
-    StorageUtl_ReadDWord(
-      currentEntry,
-      OFFSET_PS_CTIMELOW,
-      &buffer->ctime.dwLowDateTime);
+    stream = SmallBlockChainStream_Construct(This, NULL, index);
+    if (!stream) return E_OUTOFMEMORY;
 
-    StorageUtl_ReadDWord(
-      currentEntry,
-      OFFSET_PS_CTIMEHIGH,
-      &buffer->ctime.dwHighDateTime);
+    hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
 
-    StorageUtl_ReadDWord(
-      currentEntry,
-      OFFSET_PS_MTIMELOW,
-      &buffer->mtime.dwLowDateTime);
+    SmallBlockChainStream_Destroy(stream);
 
-    StorageUtl_ReadDWord(
-      currentEntry,
-      OFFSET_PS_MTIMEHIGH,
-      &buffer->mtime.dwHighDateTime);
+    return hr;
+  }
+  else
+  {
+    BlockChainStream *stream = NULL;
 
-    StorageUtl_ReadDWord(
-      currentEntry,
-      OFFSET_PS_STARTBLOCK,
-      &buffer->startingBlock);
+    stream = *StorageImpl_GetCachedBlockChainStream(This, index);
+    if (!stream) return E_OUTOFMEMORY;
 
-    StorageUtl_ReadDWord(
-      currentEntry,
-      OFFSET_PS_SIZE,
-      &buffer->size.u.LowPart);
+    hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
 
-    StorageUtl_ReadDWord(
-      currentEntry,
-      OFFSET_PS_SIZE_HIGH,
-      &buffer->size.u.HighPart);
+    return hr;
   }
-
-  return readRes;
 }
 
-/*********************************************************************
- * Write the specified directory entry to the file
- */
-HRESULT StorageImpl_WriteDirEntry(
-  StorageImpl*          This,
-  DirRef                index,
-  const DirEntry*       buffer)
+static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
+  ULARGE_INTEGER newsize)
 {
-  BYTE currentEntry[RAW_DIRENTRY_SIZE];
+  StorageImpl *This = (StorageImpl*)base;
+  DirEntry data;
+  HRESULT hr;
+  SmallBlockChainStream *smallblock=NULL;
+  BlockChainStream **pbigblock=NULL, *bigblock=NULL;
 
-  UpdateRawDirEntry(currentEntry, buffer);
+  hr = StorageImpl_ReadDirEntry(This, index, &data);
+  if (FAILED(hr)) return hr;
 
-  return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
-}
+  /* In simple mode keep the stream size above the small block limit */
+  if (This->base.openFlags & STGM_SIMPLE)
+    newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
 
-static HRESULT StorageImpl_ReadBigBlock(
-  StorageImpl* This,
-  ULONG          blockIndex,
-  void*          buffer,
-  ULONG*         out_read)
-{
-  ULARGE_INTEGER ulOffset;
-  DWORD  read=0;
-  HRESULT hr;
+  if (data.size.QuadPart == newsize.QuadPart)
+    return S_OK;
 
-  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+  /* Create a block chain object of the appropriate type */
+  if (data.size.QuadPart == 0)
+  {
+    if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+    {
+      smallblock = SmallBlockChainStream_Construct(This, NULL, index);
+      if (!smallblock) return E_OUTOFMEMORY;
+    }
+    else
+    {
+      pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
+      bigblock = *pbigblock;
+      if (!bigblock) return E_OUTOFMEMORY;
+    }
+  }
+  else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+  {
+    smallblock = SmallBlockChainStream_Construct(This, NULL, index);
+    if (!smallblock) return E_OUTOFMEMORY;
+  }
+  else
+  {
+    pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
+    bigblock = *pbigblock;
+    if (!bigblock) return E_OUTOFMEMORY;
+  }
 
-  hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
+  /* Change the block chain type if necessary. */
+  if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
+  {
+    bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
+    if (!bigblock)
+    {
+      SmallBlockChainStream_Destroy(smallblock);
+      return E_FAIL;
+    }
 
-  if (SUCCEEDED(hr) &&  read < This->bigBlockSize)
+    pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
+    *pbigblock = bigblock;
+  }
+  else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
   {
-    /* File ends during this block; fill the rest with 0's. */
-    memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
+    smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
+    if (!smallblock)
+      return E_FAIL;
   }
 
-  if (out_read) *out_read = read;
+  /* Set the size of the block chain. */
+  if (smallblock)
+  {
+    SmallBlockChainStream_SetSize(smallblock, newsize);
+    SmallBlockChainStream_Destroy(smallblock);
+  }
+  else
+  {
+    BlockChainStream_SetSize(bigblock, newsize);
+  }
 
+  /* Set the size in the directory entry. */
+  hr = StorageImpl_ReadDirEntry(This, index, &data);
+  if (SUCCEEDED(hr))
+  {
+    data.size = newsize;
+
+    hr = StorageImpl_WriteDirEntry(This, index, &data);
+  }
   return hr;
 }
 
-static BOOL StorageImpl_ReadDWordFromBigBlock(
-  StorageImpl*  This,
-  ULONG         blockIndex,
-  ULONG         offset,
-  DWORD*        value)
+static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
+  ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
 {
-  ULARGE_INTEGER ulOffset;
-  DWORD  read;
-  DWORD  tmp;
+  StorageImpl *This = (StorageImpl*)base;
+  DirEntry data;
+  HRESULT hr;
+  ULARGE_INTEGER newSize;
 
-  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
-  ulOffset.QuadPart += offset;
+  hr = StorageImpl_ReadDirEntry(This, index, &data);
+  if (FAILED(hr)) return hr;
 
-  StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
-  *value = lendian32toh(tmp);
-  return (read == sizeof(DWORD));
-}
+  /* Grow the stream if necessary */
+  newSize.QuadPart = offset.QuadPart + size;
 
-static BOOL StorageImpl_WriteBigBlock(
-  StorageImpl*  This,
-  ULONG         blockIndex,
-  const void*   buffer)
-{
-  ULARGE_INTEGER ulOffset;
-  DWORD  wrote;
+  if (newSize.QuadPart > data.size.QuadPart)
+  {
+    hr = StorageImpl_StreamSetSize(base, index, newSize);
+    if (FAILED(hr))
+      return hr;
 
-  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+    hr = StorageImpl_ReadDirEntry(This, index, &data);
+    if (FAILED(hr)) return hr;
+  }
 
-  StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
-  return (wrote == This->bigBlockSize);
-}
+  if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
+  {
+    SmallBlockChainStream *stream;
 
-static BOOL StorageImpl_WriteDWordToBigBlock(
-  StorageImpl* This,
-  ULONG         blockIndex,
-  ULONG         offset,
-  DWORD         value)
-{
-  ULARGE_INTEGER ulOffset;
-  DWORD  wrote;
+    stream = SmallBlockChainStream_Construct(This, NULL, index);
+    if (!stream) return E_OUTOFMEMORY;
 
-  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
-  ulOffset.QuadPart += offset;
+    hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
 
-  value = htole32(value);
-  StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
-  return (wrote == sizeof(DWORD));
+    SmallBlockChainStream_Destroy(stream);
+
+    return hr;
+  }
+  else
+  {
+    BlockChainStream *stream;
+
+    stream = *StorageImpl_GetCachedBlockChainStream(This, index);
+    if (!stream) return E_OUTOFMEMORY;
+
+    return BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
+  }
 }
 
-/******************************************************************************
- *              Storage32Impl_SmallBlocksToBigBlocks
- *
- * This method will convert a small block chain to a big block chain.
- * The small block chain will be destroyed.
- */
-BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
-                      StorageImpl* This,
-                      SmallBlockChainStream** ppsbChain)
+static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
+  DirRef src)
 {
-  ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
-  ULARGE_INTEGER size, offset;
-  ULONG cbRead, cbWritten;
-  ULARGE_INTEGER cbTotalRead;
-  DirRef streamEntryRef;
-  HRESULT resWrite = S_OK;
-  HRESULT resRead;
-  DirEntry streamEntry;
-  BYTE *buffer;
-  BlockChainStream *bbTempChain = NULL;
-  BlockChainStream *bigBlockChain = NULL;
+  StorageImpl *This = (StorageImpl*)base;
+  DirEntry dst_data, src_data;
+  HRESULT hr;
 
-  /*
-   * Create a temporary big block chain that doesn't have
-   * an associated directory entry. This temporary chain will be
-   * used to copy data from small blocks to big blocks.
-   */
-  bbTempChain = BlockChainStream_Construct(This,
-                                           &bbHeadOfChain,
-                                           DIRENTRY_NULL);
-  if(!bbTempChain) return NULL;
-  /*
-   * Grow the big block chain.
-   */
-  size = SmallBlockChainStream_GetSize(*ppsbChain);
-  BlockChainStream_SetSize(bbTempChain, size);
+  hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
 
-  /*
-   * Copy the contents of the small block chain to the big block chain
-   * by small block size increments.
-   */
-  offset.u.LowPart = 0;
-  offset.u.HighPart = 0;
-  cbTotalRead.QuadPart = 0;
+  if (SUCCEEDED(hr))
+    hr = StorageImpl_ReadDirEntry(This, src, &src_data);
 
-  buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
-  do
+  if (SUCCEEDED(hr))
   {
-    resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
-                                           offset,
-                                           min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
-                                           buffer,
-                                           &cbRead);
-    if (FAILED(resRead))
-        break;
-
-    if (cbRead > 0)
-    {
-        cbTotalRead.QuadPart += cbRead;
-
-        resWrite = BlockChainStream_WriteAt(bbTempChain,
-                                            offset,
-                                            cbRead,
-                                            buffer,
-                                            &cbWritten);
+    StorageImpl_DeleteCachedBlockChainStream(This, src);
+    dst_data.startingBlock = src_data.startingBlock;
+    dst_data.size = src_data.size;
 
-        if (FAILED(resWrite))
-            break;
+    hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
+  }
 
-        offset.u.LowPart += cbRead;
-    }
-    else
-    {
-        resRead = STG_E_READFAULT;
-        break;
-    }
-  } while (cbTotalRead.QuadPart < size.QuadPart);
-  HeapFree(GetProcessHeap(),0,buffer);
+  return hr;
+}
 
-  size.u.HighPart = 0;
-  size.u.LowPart  = 0;
+static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
+{
+  HRESULT hr=S_OK;
+  DirEntry currentEntry;
+  DirRef      currentEntryRef;
+  BlockChainStream *blockChainStream;
 
-  if (FAILED(resRead) || FAILED(resWrite))
+  if (create)
   {
-    ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
-    BlockChainStream_SetSize(bbTempChain, size);
-    BlockChainStream_Destroy(bbTempChain);
-    return NULL;
-  }
+    ULARGE_INTEGER size;
+    BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
 
-  /*
-   * Destroy the small block chain.
-   */
-  streamEntryRef = (*ppsbChain)->ownerDirEntry;
-  SmallBlockChainStream_SetSize(*ppsbChain, size);
-  SmallBlockChainStream_Destroy(*ppsbChain);
-  *ppsbChain = 0;
-
-  /*
-   * Change the directory entry. This chain is now a big block chain
-   * and it doesn't reside in the small blocks chain anymore.
-   */
-  StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
-
-  streamEntry.startingBlock = bbHeadOfChain;
-
-  StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
-
-  /*
-   * Destroy the temporary entryless big block chain.
-   * Create a new big block chain associated with this entry.
-   */
-  BlockChainStream_Destroy(bbTempChain);
-  bigBlockChain = BlockChainStream_Construct(This,
-                                             NULL,
-                                             streamEntryRef);
-
-  return bigBlockChain;
-}
+    /* Discard any existing data. */
+    size.QuadPart = 0;
+    ILockBytes_SetSize(This->lockBytes, size);
 
-/******************************************************************************
- *              Storage32Impl_BigBlocksToSmallBlocks
- *
- * This method will convert a big block chain to a small block chain.
- * The big block chain will be destroyed on success.
- */
-SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
-                           StorageImpl* This,
-                           BlockChainStream** ppbbChain,
-                           ULARGE_INTEGER newSize)
-{
-    ULARGE_INTEGER size, offset, cbTotalRead;
-    ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
-    DirRef streamEntryRef;
-    HRESULT resWrite = S_OK, resRead = S_OK;
-    DirEntry streamEntry;
-    BYTE* buffer;
-    SmallBlockChainStream* sbTempChain;
+    /*
+     * Initialize all header variables:
+     * - The big block depot consists of one block and it is at block 0
+     * - The directory table starts at block 1
+     * - There is no small block depot
+     */
+    memset( This->bigBlockDepotStart,
+            BLOCK_UNUSED,
+            sizeof(This->bigBlockDepotStart));
 
-    TRACE("%p %p\n", This, ppbbChain);
+    This->bigBlockDepotCount    = 1;
+    This->bigBlockDepotStart[0] = 0;
+    This->rootStartBlock        = 1;
+    This->smallBlockLimit       = LIMIT_TO_USE_SMALL_BLOCK;
+    This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
+    if (This->bigBlockSize == 4096)
+      This->bigBlockSizeBits    = MAX_BIG_BLOCK_SIZE_BITS;
+    else
+      This->bigBlockSizeBits    = MIN_BIG_BLOCK_SIZE_BITS;
+    This->smallBlockSizeBits    = DEF_SMALL_BLOCK_SIZE_BITS;
+    This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
+    This->extBigBlockDepotCount = 0;
 
-    sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
-            DIRENTRY_NULL);
+    StorageImpl_SaveFileHeader(This);
 
-    if(!sbTempChain)
-        return NULL;
+    /*
+     * Add one block for the big block depot and one block for the directory table
+     */
+    size.u.HighPart = 0;
+    size.u.LowPart  = This->bigBlockSize * 3;
+    ILockBytes_SetSize(This->lockBytes, size);
 
-    SmallBlockChainStream_SetSize(sbTempChain, newSize);
-    size = BlockChainStream_GetSize(*ppbbChain);
-    size.QuadPart = min(size.QuadPart, newSize.QuadPart);
+    /*
+     * Initialize the big block depot
+     */
+    memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
+    StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
+    StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
+    StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
+  }
+  else
+  {
+    /*
+     * Load the header for the file.
+     */
+    hr = StorageImpl_LoadFileHeader(This);
 
-    offset.u.HighPart = 0;
-    offset.u.LowPart = 0;
-    cbTotalRead.QuadPart = 0;
-    buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
-    while(cbTotalRead.QuadPart < size.QuadPart)
+    if (FAILED(hr))
     {
-        resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
-                min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
-                buffer, &cbRead);
+      return hr;
+    }
+  }
 
-        if(FAILED(resRead))
-            break;
+  /*
+   * There is no block depot cached yet.
+   */
+  This->indexBlockDepotCached = 0xFFFFFFFF;
+  This->indexExtBlockDepotCached = 0xFFFFFFFF;
 
-        if(cbRead > 0)
-        {
-            cbTotalRead.QuadPart += cbRead;
+  /*
+   * Start searching for free blocks with block 0.
+   */
+  This->prevFreeBlock = 0;
 
-            resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
-                    cbRead, buffer, &cbWritten);
+  This->firstFreeSmallBlock = 0;
 
-            if(FAILED(resWrite))
-                break;
+  /* Read the extended big block depot locations. */
+  if (This->extBigBlockDepotCount != 0)
+  {
+    ULONG current_block = This->extBigBlockDepotStart;
+    ULONG cache_size = This->extBigBlockDepotCount * 2;
+    ULONG i;
 
-            offset.u.LowPart += cbRead;
-        }
-        else
-        {
-            resRead = STG_E_READFAULT;
-            break;
-        }
+    This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
+    if (!This->extBigBlockDepotLocations)
+    {
+      return E_OUTOFMEMORY;
     }
-    HeapFree(GetProcessHeap(), 0, buffer);
 
-    size.u.HighPart = 0;
-    size.u.LowPart = 0;
+    This->extBigBlockDepotLocationsSize = cache_size;
 
-    if(FAILED(resRead) || FAILED(resWrite))
+    for (i=0; i<This->extBigBlockDepotCount; i++)
     {
-        ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
-        SmallBlockChainStream_SetSize(sbTempChain, size);
-        SmallBlockChainStream_Destroy(sbTempChain);
-        return NULL;
+      if (current_block == BLOCK_END_OF_CHAIN)
+      {
+        WARN("File has too few extended big block depot blocks.\n");
+        return STG_E_DOCFILECORRUPT;
+      }
+      This->extBigBlockDepotLocations[i] = current_block;
+      current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
     }
+  }
+  else
+  {
+    This->extBigBlockDepotLocations = NULL;
+    This->extBigBlockDepotLocationsSize = 0;
+  }
 
-    /* destroy the original big block chain */
-    streamEntryRef = (*ppbbChain)->ownerDirEntry;
-    BlockChainStream_SetSize(*ppbbChain, size);
-    BlockChainStream_Destroy(*ppbbChain);
-    *ppbbChain = NULL;
+  /*
+   * Create the block chain abstractions.
+   */
+  if(!(blockChainStream =
+       BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
+  {
+    return STG_E_READFAULT;
+  }
+  if (!new_object)
+    BlockChainStream_Destroy(This->rootBlockChain);
+  This->rootBlockChain = blockChainStream;
 
-    StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
-    streamEntry.startingBlock = sbHeadOfChain;
-    StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
+  if(!(blockChainStream =
+       BlockChainStream_Construct(This, &This->smallBlockDepotStart,
+				  DIRENTRY_NULL)))
+  {
+    return STG_E_READFAULT;
+  }
+  if (!new_object)
+    BlockChainStream_Destroy(This->smallBlockDepotChain);
+  This->smallBlockDepotChain = blockChainStream;
 
-    SmallBlockChainStream_Destroy(sbTempChain);
-    return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
-}
+  /*
+   * Write the root storage entry (memory only)
+   */
+  if (create)
+  {
+    static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
+    DirEntry rootEntry;
+    /*
+     * Initialize the directory table
+     */
+    memset(&rootEntry, 0, sizeof(rootEntry));
+    strcpyW(rootEntry.name, rootentryW);
+    rootEntry.sizeOfNameString = sizeof(rootentryW);
+    rootEntry.stgType          = STGTY_ROOT;
+    rootEntry.leftChild        = DIRENTRY_NULL;
+    rootEntry.rightChild       = DIRENTRY_NULL;
+    rootEntry.dirRootEntry     = DIRENTRY_NULL;
+    rootEntry.startingBlock    = BLOCK_END_OF_CHAIN;
+    rootEntry.size.u.HighPart  = 0;
+    rootEntry.size.u.LowPart   = 0;
 
-static HRESULT StorageBaseImpl_CopyStream(
-  StorageBaseImpl *dst, DirRef dst_entry,
-  StorageBaseImpl *src, DirRef src_entry)
-{
-  HRESULT hr;
-  BYTE data[4096];
-  DirEntry srcdata;
-  ULARGE_INTEGER bytes_copied;
-  ULONG bytestocopy, bytesread, byteswritten;
+    StorageImpl_WriteDirEntry(This, 0, &rootEntry);
+  }
 
-  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
+  /*
+   * Find the ID of the root storage.
+   */
+  currentEntryRef = 0;
 
-  if (SUCCEEDED(hr))
+  do
   {
-    hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
+    hr = StorageImpl_ReadDirEntry(
+                      This,
+                      currentEntryRef,
+                      &currentEntry);
 
-    bytes_copied.QuadPart = 0;
-    while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
+    if (SUCCEEDED(hr))
     {
-      bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
-
-      hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
-        data, &bytesread);
-      if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
-
-      if (SUCCEEDED(hr))
-        hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
-          data, &byteswritten);
-      if (SUCCEEDED(hr))
+      if ( (currentEntry.sizeOfNameString != 0 ) &&
+           (currentEntry.stgType          == STGTY_ROOT) )
       {
-        if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
-        bytes_copied.QuadPart += byteswritten;
+        This->base.storageDirEntry = currentEntryRef;
       }
     }
-  }
 
-  return hr;
-}
+    currentEntryRef++;
 
-static HRESULT StorageBaseImpl_DupStorageTree(
-  StorageBaseImpl *dst, DirRef *dst_entry,
-  StorageBaseImpl *src, DirRef src_entry)
-{
-  HRESULT hr;
-  DirEntry data;
-  BOOL has_stream=FALSE;
+  } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
 
-  if (src_entry == DIRENTRY_NULL)
+  if (FAILED(hr))
   {
-    *dst_entry = DIRENTRY_NULL;
-    return S_OK;
+    return STG_E_READFAULT;
   }
 
-  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
-  if (SUCCEEDED(hr))
+  /*
+   * Create the block chain abstraction for the small block root chain.
+   */
+  if(!(blockChainStream =
+       BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
   {
-    has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
-    data.startingBlock = BLOCK_END_OF_CHAIN;
-    data.size.QuadPart = 0;
-
-    hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
+    return STG_E_READFAULT;
   }
+  if (!new_object)
+    BlockChainStream_Destroy(This->smallBlockRootChain);
+  This->smallBlockRootChain = blockChainStream;
 
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
-
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
-
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_CreateDirEntry(dst, &data, dst_entry);
-
-  if (SUCCEEDED(hr) && has_stream)
-    hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
+  if (!new_object)
+  {
+    int i;
+    for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+    {
+      BlockChainStream_Destroy(This->blockChainCache[i]);
+      This->blockChainCache[i] = NULL;
+    }
+  }
 
   return hr;
 }
 
-static HRESULT StorageBaseImpl_CopyStorageTree(
-  StorageBaseImpl *dst, DirRef dst_entry,
-  StorageBaseImpl *src, DirRef src_entry)
+static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
+  ULONG* result, BOOL refresh)
 {
-  HRESULT hr;
-  DirEntry src_data, dst_data;
-  DirRef new_root_entry;
-
-  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
+  StorageImpl *This = (StorageImpl*)base;
+  HRESULT hr=S_OK;
+  DWORD oldTransactionSig = This->transactionSig;
 
-  if (SUCCEEDED(hr))
+  if (refresh)
   {
-    hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
-  }
+    ULARGE_INTEGER offset;
+    ULONG bytes_read;
+    BYTE data[4];
 
-  if (SUCCEEDED(hr))
-  {
-    hr = StorageBaseImpl_ReadDirEntry(dst, dst_entry, &dst_data);
-    dst_data.clsid = src_data.clsid;
-    dst_data.ctime = src_data.ctime;
-    dst_data.mtime = src_data.mtime;
-    dst_data.dirRootEntry = new_root_entry;
+    offset.u.HighPart = 0;
+    offset.u.LowPart = OFFSET_TRANSACTIONSIG;
+    hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
+
+    if (SUCCEEDED(hr))
+    {
+      StorageUtl_ReadDWord(data, 0, &This->transactionSig);
+
+      if (oldTransactionSig != This->transactionSig)
+      {
+        /* Someone else wrote to this, so toss all cached information. */
+        TRACE("signature changed\n");
+
+        hr = StorageImpl_Refresh(This, FALSE, FALSE);
+      }
+
+      if (FAILED(hr))
+        This->transactionSig = oldTransactionSig;
+    }
   }
 
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
+  *result = This->transactionSig;
 
   return hr;
 }
 
-static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
+static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base,
+  ULONG value)
 {
-  HRESULT hr;
-  DirEntry data;
-  ULARGE_INTEGER zero;
-
-  if (entry == DIRENTRY_NULL)
-    return S_OK;
+  StorageImpl *This = (StorageImpl*)base;
 
-  zero.QuadPart = 0;
+  This->transactionSig = value;
+  StorageImpl_SaveFileHeader(This);
 
-  hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
+  return S_OK;
+}
 
-  if (SUCCEEDED(hr) && include_siblings)
-    hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
+/* Internal function */
+static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
+    ULARGE_INTEGER cb, DWORD dwLockType)
+{
+    HRESULT hr;
+    int delay = 0;
+    DWORD start_time = GetTickCount();
+    DWORD last_sanity_check = start_time;
+    ULARGE_INTEGER sanity_offset, sanity_cb;
 
-  if (SUCCEEDED(hr) && include_siblings)
-    hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
+    sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
+    sanity_cb.QuadPart = RANGELOCK_UNK1_LAST - RANGELOCK_UNK1_FIRST + 1;
 
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
+    do
+    {
+        hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
 
-  if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
-    hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
+        if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
+        {
+            DWORD current_time = GetTickCount();
+            if (current_time - start_time >= 20000)
+            {
+                /* timeout */
+                break;
+            }
+            if (current_time - last_sanity_check >= 500)
+            {
+                /* Any storage implementation with the file open in a
+                 * shared mode should not lock these bytes for writing. However,
+                 * some programs (LibreOffice Writer) will keep ALL bytes locked
+                 * when opening in exclusive mode. We can use a read lock to
+                 * detect this case early, and not hang a full 20 seconds.
+                 *
+                 * This can collide with another attempt to open the file in
+                 * exclusive mode, but it's unlikely, and someone would fail anyway. */
+                hr = ILockBytes_LockRegion(This->lockBytes, sanity_offset, sanity_cb, 0);
+                if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
+                    break;
+                if (hr == STG_E_INVALIDFUNCTION)
+                {
+                    /* ignore this, lockbytes might support dwLockType but not 0 */
+                    hr = STG_E_ACCESSDENIED;
+                }
+                if (SUCCEEDED(hr))
+                {
+                    ILockBytes_UnlockRegion(This->lockBytes, sanity_offset, sanity_cb, 0);
+                    hr = STG_E_ACCESSDENIED;
+                }
 
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_DestroyDirEntry(This, entry);
+                last_sanity_check = current_time;
+            }
+            Sleep(delay);
+            if (delay < 150) delay++;
+        }
+    } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
 
-  return hr;
+    return hr;
 }
 
-static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
+static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
 {
-  DirRef result=This->firstFreeEntry;
-
-  while (result < This->entries_size && This->entries[result].inuse)
-    result++;
+  StorageImpl *This = (StorageImpl*)base;
+  HRESULT hr;
+  ULARGE_INTEGER offset, cb;
 
-  if (result == This->entries_size)
+  if (write)
   {
-    ULONG new_size = This->entries_size * 2;
-    TransactedDirEntry *new_entries;
-
-    new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
-    if (!new_entries) return DIRENTRY_NULL;
-
-    memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
-    HeapFree(GetProcessHeap(), 0, This->entries);
-
-    This->entries = new_entries;
-    This->entries_size = new_size;
+    /* Synchronous grab of second priority range, the commit lock, and the
+     * lock-checking lock. */
+    offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
+    cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
+  }
+  else
+  {
+    offset.QuadPart = RANGELOCK_COMMIT;
+    cb.QuadPart = 1;
   }
 
-  This->entries[result].inuse = TRUE;
+  hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
 
-  This->firstFreeEntry = result+1;
+  if (hr == STG_E_INVALIDFUNCTION)
+    hr = S_OK;
 
-  return result;
+  return hr;
 }
 
-static DirRef TransactedSnapshotImpl_CreateStubEntry(
-  TransactedSnapshotImpl *This, DirRef parentEntryRef)
+static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
 {
-  DirRef stubEntryRef;
-  TransactedDirEntry *entry;
-
-  stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
+  StorageImpl *This = (StorageImpl*)base;
+  HRESULT hr;
+  ULARGE_INTEGER offset, cb;
 
-  if (stubEntryRef != DIRENTRY_NULL)
+  if (write)
   {
-    entry = &This->entries[stubEntryRef];
+    offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
+    cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
+  }
+  else
+  {
+    offset.QuadPart = RANGELOCK_COMMIT;
+    cb.QuadPart = 1;
+  }
 
-    entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
+  hr = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
 
-    entry->read = FALSE;
-  }
+  if (hr == STG_E_INVALIDFUNCTION)
+    hr = S_OK;
 
-  return stubEntryRef;
+  return hr;
 }
 
-static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
-  TransactedSnapshotImpl *This, DirRef entry)
+static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
 {
-  HRESULT hr=S_OK;
-  DirEntry data;
+  StorageImpl *This = (StorageImpl*) iface;
+  STATSTG statstg;
+  HRESULT hr;
 
-  if (!This->entries[entry].read)
-  {
-    hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
-        This->entries[entry].transactedParentEntry,
-        &data);
+  hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
 
-    if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
-    {
-      data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
+  *result = statstg.pwcsName;
 
-      if (data.leftChild == DIRENTRY_NULL)
-        hr = E_OUTOFMEMORY;
-    }
+  return hr;
+}
 
-    if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
-    {
-      data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
+static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
+    ULONG end, HRESULT fail_hr)
+{
+    HRESULT hr;
+    ULARGE_INTEGER offset, cb;
 
-      if (data.rightChild == DIRENTRY_NULL)
-        hr = E_OUTOFMEMORY;
-    }
+    offset.QuadPart = start;
+    cb.QuadPart = 1 + end - start;
 
-    if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
-    {
-      data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
+    hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+    if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
 
-      if (data.dirRootEntry == DIRENTRY_NULL)
-        hr = E_OUTOFMEMORY;
+    if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
+        return fail_hr;
+    else
+        return S_OK;
+}
+
+static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
+{
+    HRESULT hr=S_OK;
+    int i, j;
+    ULARGE_INTEGER offset, cb;
+
+    cb.QuadPart = 1;
+
+    for (i=start; i<=end; i++)
+    {
+        offset.QuadPart = i;
+        hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+        if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION)
+            break;
     }
 
     if (SUCCEEDED(hr))
     {
-      memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
-      This->entries[entry].read = TRUE;
+        for (j=0; j<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); j++)
+        {
+            if (This->locked_bytes[j] == 0)
+            {
+                This->locked_bytes[j] = i;
+                break;
+            }
+        }
     }
-  }
 
-  return hr;
+    return hr;
 }
 
-static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
-  TransactedSnapshotImpl *This, DirRef entry)
+static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
 {
-  HRESULT hr = S_OK;
-
-  if (!This->entries[entry].stream_dirty)
-  {
-    DirEntry new_entrydata;
-
-    memset(&new_entrydata, 0, sizeof(DirEntry));
-    new_entrydata.name[0] = 'S';
-    new_entrydata.sizeOfNameString = 1;
-    new_entrydata.stgType = STGTY_STREAM;
-    new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
-    new_entrydata.leftChild = DIRENTRY_NULL;
-    new_entrydata.rightChild = DIRENTRY_NULL;
-    new_entrydata.dirRootEntry = DIRENTRY_NULL;
-
-    hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
-      &This->entries[entry].stream_entry);
+    HRESULT hr;
+    ULARGE_INTEGER offset;
+    ULARGE_INTEGER cb;
+    DWORD share_mode = STGM_SHARE_MODE(openFlags);
 
-    if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
+    if (openFlags & STGM_NOSNAPSHOT)
     {
-      hr = StorageBaseImpl_CopyStream(
-        This->scratch, This->entries[entry].stream_entry,
-        This->transactedParent, This->entries[entry].transactedParentEntry);
-
-      if (FAILED(hr))
-        StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
+        /* STGM_NOSNAPSHOT implies deny write */
+        if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
+        else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
     }
 
-    if (SUCCEEDED(hr))
-      This->entries[entry].stream_dirty = TRUE;
+    /* Wrap all other locking inside a single lock so we can check ranges safely */
+    offset.QuadPart = RANGELOCK_CHECKLOCKS;
+    cb.QuadPart = 1;
+    hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
 
-    if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
-    {
-      /* Since this entry is modified, and we aren't using its stream data, we
-       * no longer care about the original entry. */
-      DirRef delete_ref;
-      delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
+    /* If the ILockBytes doesn't support locking that's ok. */
+    if (hr == STG_E_INVALIDFUNCTION) return S_OK;
+    else if (FAILED(hr)) return hr;
 
-      if (delete_ref != DIRENTRY_NULL)
-        This->entries[delete_ref].deleted = TRUE;
+    hr = S_OK;
 
-      This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
-    }
-  }
+    /* First check for any conflicting locks. */
+    if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
+        hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
 
-  return hr;
-}
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
+        hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
 
-/* Find the first entry in a depth-first traversal. */
-static DirRef TransactedSnapshotImpl_FindFirstChild(
-  TransactedSnapshotImpl* This, DirRef parent)
-{
-  DirRef cursor, prev;
-  TransactedDirEntry *entry;
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
+        hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
 
-  cursor = parent;
-  entry = &This->entries[cursor];
-  while (entry->read)
-  {
-    if (entry->data.leftChild != DIRENTRY_NULL)
-    {
-      prev = cursor;
-      cursor = entry->data.leftChild;
-      entry = &This->entries[cursor];
-      entry->parent = prev;
-    }
-    else if (entry->data.rightChild != DIRENTRY_NULL)
-    {
-      prev = cursor;
-      cursor = entry->data.rightChild;
-      entry = &This->entries[cursor];
-      entry->parent = prev;
-    }
-    else if (entry->data.dirRootEntry != DIRENTRY_NULL)
+    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
+        hr = StorageImpl_CheckLockRange(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST, STG_E_LOCKVIOLATION);
+
+    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
+        hr = StorageImpl_CheckLockRange(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST, STG_E_LOCKVIOLATION);
+
+    /* Then grab our locks. */
+    if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
     {
-      prev = cursor;
-      cursor = entry->data.dirRootEntry;
-      entry = &This->entries[cursor];
-      entry->parent = prev;
+        hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
+        if (SUCCEEDED(hr))
+            hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
     }
-    else
-      break;
-  }
 
-  return cursor;
-}
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
+        hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
 
-/* Find the next entry in a depth-first traversal. */
-static DirRef TransactedSnapshotImpl_FindNextChild(
-  TransactedSnapshotImpl* This, DirRef current)
-{
-  DirRef parent;
-  TransactedDirEntry *parent_entry;
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
+        hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
 
-  parent = This->entries[current].parent;
-  parent_entry = &This->entries[parent];
+    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
+        hr = StorageImpl_LockOne(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST);
 
-  if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
-  {
-    if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
-    {
-      This->entries[parent_entry->data.rightChild].parent = parent;
-      return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
-    }
+    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
+        hr = StorageImpl_LockOne(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST);
 
-    if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
-    {
-      This->entries[parent_entry->data.dirRootEntry].parent = parent;
-      return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
-    }
-  }
+    if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
+        hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
 
-  return parent;
-}
+    offset.QuadPart = RANGELOCK_CHECKLOCKS;
+    cb.QuadPart = 1;
+    ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
 
-/* Return TRUE if we've made a copy of this entry for committing to the parent. */
-static inline BOOL TransactedSnapshotImpl_MadeCopy(
-  TransactedSnapshotImpl* This, DirRef entry)
-{
-  return entry != DIRENTRY_NULL &&
-    This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
+    return hr;
 }
 
-/* Destroy the entries created by CopyTree. */
-static void TransactedSnapshotImpl_DestroyTemporaryCopy(
-  TransactedSnapshotImpl* This, DirRef stop)
+static HRESULT StorageImpl_Flush(StorageBaseImpl *storage)
 {
-  DirRef cursor;
-  TransactedDirEntry *entry;
-  ULARGE_INTEGER zero;
+  StorageImpl *This = (StorageImpl*)storage;
+  int i;
+  HRESULT hr;
+  TRACE("(%p)\n", This);
 
-  zero.QuadPart = 0;
+  hr = BlockChainStream_Flush(This->smallBlockRootChain);
 
-  if (!This->entries[This->base.storageDirEntry].read)
-    return;
+  if (SUCCEEDED(hr))
+    hr = BlockChainStream_Flush(This->rootBlockChain);
 
-  cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
+  if (SUCCEEDED(hr))
+    hr = BlockChainStream_Flush(This->smallBlockDepotChain);
 
-  if (cursor == DIRENTRY_NULL)
-    return;
+  for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
+    if (This->blockChainCache[i])
+      hr = BlockChainStream_Flush(This->blockChainCache[i]);
 
-  cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
+  if (SUCCEEDED(hr))
+    hr = ILockBytes_Flush(This->lockBytes);
 
-  while (cursor != DIRENTRY_NULL && cursor != stop)
-  {
-    if (TransactedSnapshotImpl_MadeCopy(This, cursor))
-    {
-      entry = &This->entries[cursor];
+  return hr;
+}
 
-      if (entry->stream_dirty)
-        StorageBaseImpl_StreamSetSize(This->transactedParent,
-          entry->newTransactedParentEntry, zero);
-
-      StorageBaseImpl_DestroyDirEntry(This->transactedParent,
-        entry->newTransactedParentEntry);
+static void StorageImpl_Invalidate(StorageBaseImpl* iface)
+{
+  StorageImpl *This = (StorageImpl*) iface;
 
-      entry->newTransactedParentEntry = entry->transactedParentEntry;
-    }
+  StorageBaseImpl_DeleteAll(&This->base);
 
-    cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
-  }
+  This->base.reverted = TRUE;
 }
 
-/* Make a copy of our edited tree that we can use in the parent. */
-static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
+static void StorageImpl_Destroy(StorageBaseImpl* iface)
 {
-  DirRef cursor;
-  TransactedDirEntry *entry;
-  HRESULT hr = S_OK;
+  StorageImpl *This = (StorageImpl*) iface;
+  int i;
+  TRACE("(%p)\n", This);
 
-  cursor = This->base.storageDirEntry;
-  entry = &This->entries[cursor];
-  entry->parent = DIRENTRY_NULL;
-  entry->newTransactedParentEntry = entry->transactedParentEntry;
+  StorageImpl_Flush(iface);
 
-  if (entry->data.dirRootEntry == DIRENTRY_NULL)
-    return S_OK;
+  StorageImpl_Invalidate(iface);
 
-  This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
+  HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
 
-  cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
-  entry = &This->entries[cursor];
+  BlockChainStream_Destroy(This->smallBlockRootChain);
+  BlockChainStream_Destroy(This->rootBlockChain);
+  BlockChainStream_Destroy(This->smallBlockDepotChain);
 
-  while (cursor != DIRENTRY_NULL)
+  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+    BlockChainStream_Destroy(This->blockChainCache[i]);
+
+  for (i=0; i<sizeof(This->locked_bytes)/sizeof(This->locked_bytes[0]); i++)
   {
-    /* Make a copy of this entry in the transacted parent. */
-    if (!entry->read ||
-        (!entry->dirty && !entry->stream_dirty &&
-         !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
-         !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
-         !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
-      entry->newTransactedParentEntry = entry->transactedParentEntry;
-    else
+    ULARGE_INTEGER offset, cb;
+    cb.QuadPart = 1;
+    if (This->locked_bytes[i] != 0)
     {
-      DirEntry newData;
+      offset.QuadPart = This->locked_bytes[i];
+      ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+    }
+  }
 
-      memcpy(&newData, &entry->data, sizeof(DirEntry));
+  if (This->lockBytes)
+    ILockBytes_Release(This->lockBytes);
+  HeapFree(GetProcessHeap(), 0, This);
+}
 
-      newData.size.QuadPart = 0;
-      newData.startingBlock = BLOCK_END_OF_CHAIN;
 
-      if (newData.leftChild != DIRENTRY_NULL)
-        newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
+static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
+{
+  StorageImpl_Destroy,
+  StorageImpl_Invalidate,
+  StorageImpl_Flush,
+  StorageImpl_GetFilename,
+  StorageImpl_CreateDirEntry,
+  StorageImpl_BaseWriteDirEntry,
+  StorageImpl_BaseReadDirEntry,
+  StorageImpl_DestroyDirEntry,
+  StorageImpl_StreamReadAt,
+  StorageImpl_StreamWriteAt,
+  StorageImpl_StreamSetSize,
+  StorageImpl_StreamLink,
+  StorageImpl_GetTransactionSig,
+  StorageImpl_SetTransactionSig,
+  StorageImpl_LockTransaction,
+  StorageImpl_UnlockTransaction
+};
 
-      if (newData.rightChild != DIRENTRY_NULL)
-        newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
 
-      if (newData.dirRootEntry != DIRENTRY_NULL)
-        newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
+/*
+ * Virtual function table for the IStorageBaseImpl class.
+ */
+static const IStorageVtbl StorageImpl_Vtbl =
+{
+    StorageBaseImpl_QueryInterface,
+    StorageBaseImpl_AddRef,
+    StorageBaseImpl_Release,
+    StorageBaseImpl_CreateStream,
+    StorageBaseImpl_OpenStream,
+    StorageBaseImpl_CreateStorage,
+    StorageBaseImpl_OpenStorage,
+    StorageBaseImpl_CopyTo,
+    StorageBaseImpl_MoveElementTo,
+    StorageBaseImpl_Commit,
+    StorageBaseImpl_Revert,
+    StorageBaseImpl_EnumElements,
+    StorageBaseImpl_DestroyElement,
+    StorageBaseImpl_RenameElement,
+    StorageBaseImpl_SetElementTimes,
+    StorageBaseImpl_SetClass,
+    StorageBaseImpl_SetStateBits,
+    StorageBaseImpl_Stat
+};
 
-      hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
-        &entry->newTransactedParentEntry);
-      if (FAILED(hr))
-      {
-        TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
-        return hr;
-      }
+static HRESULT StorageImpl_Construct(
+  HANDLE       hFile,
+  LPCOLESTR    pwcsName,
+  ILockBytes*  pLkbyt,
+  DWORD        openFlags,
+  BOOL         fileBased,
+  BOOL         create,
+  ULONG        sector_size,
+  StorageImpl** result)
+{
+  StorageImpl* This;
+  HRESULT     hr = S_OK;
 
-      if (entry->stream_dirty)
-      {
-        hr = StorageBaseImpl_CopyStream(
-          This->transactedParent, entry->newTransactedParentEntry,
-          This->scratch, entry->stream_entry);
-      }
-      else if (entry->data.size.QuadPart)
-      {
-        hr = StorageBaseImpl_StreamLink(
-          This->transactedParent, entry->newTransactedParentEntry,
-          entry->transactedParentEntry);
-      }
+  if ( FAILED( validateSTGM(openFlags) ))
+    return STG_E_INVALIDFLAG;
 
-      if (FAILED(hr))
-      {
-        cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
-        TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
-        return hr;
-      }
-    }
+  This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
+  if (!This)
+    return E_OUTOFMEMORY;
 
-    cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
-    entry = &This->entries[cursor];
-  }
+  memset(This, 0, sizeof(StorageImpl));
 
-  return hr;
-}
+  list_init(&This->base.strmHead);
 
-static HRESULT WINAPI TransactedSnapshotImpl_Commit(
-  IStorage*            iface,
-  DWORD                  grfCommitFlags)  /* [in] */
-{
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
-  TransactedDirEntry *root_entry;
-  DirRef i, dir_root_ref;
-  DirEntry data;
-  ULARGE_INTEGER zero;
-  HRESULT hr;
-  ULONG transactionSig;
+  list_init(&This->base.storageHead);
 
-  zero.QuadPart = 0;
+  This->base.IStorage_iface.lpVtbl = &StorageImpl_Vtbl;
+  This->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
+  This->base.IDirectWriterLock_iface.lpVtbl = &DirectWriterLockVtbl;
+  This->base.baseVtbl = &StorageImpl_BaseVtbl;
+  This->base.openFlags = (openFlags & ~STGM_CREATE);
+  This->base.ref = 1;
+  This->base.create = create;
 
-  TRACE("(%p,%x)\n", iface, grfCommitFlags);
+  if (openFlags == (STGM_DIRECT_SWMR|STGM_READWRITE|STGM_SHARE_DENY_WRITE))
+    This->base.lockingrole = SWMR_Writer;
+  else if (openFlags == (STGM_DIRECT_SWMR|STGM_READ|STGM_SHARE_DENY_NONE))
+    This->base.lockingrole = SWMR_Reader;
+  else
+    This->base.lockingrole = SWMR_None;
 
-  /* Cannot commit a read-only transacted storage */
-  if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
-    return STG_E_ACCESSDENIED;
+  This->base.reverted = FALSE;
 
-  hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
-  if (hr == E_NOTIMPL) hr = S_OK;
-  if (SUCCEEDED(hr))
+  /*
+   * Initialize the big block cache.
+   */
+  This->bigBlockSize   = sector_size;
+  This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
+  if (hFile)
+    hr = FileLockBytesImpl_Construct(hFile, openFlags, pwcsName, &This->lockBytes);
+  else
   {
-    hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
-    if (SUCCEEDED(hr))
-    {
-      if (transactionSig != This->lastTransactionSig)
-      {
-        ERR("file was externally modified\n");
-        hr = STG_E_NOTCURRENT;
-      }
-
-      if (SUCCEEDED(hr))
-      {
-        This->lastTransactionSig = transactionSig+1;
-        hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
-      }
-    }
-    else if (hr == E_NOTIMPL)
-      hr = S_OK;
+    This->lockBytes = pLkbyt;
+    ILockBytes_AddRef(pLkbyt);
+  }
 
-    if (FAILED(hr)) goto end;
+  if (SUCCEEDED(hr))
+    hr = StorageImpl_GrabLocks(This, openFlags);
 
-    /* To prevent data loss, we create the new structure in the file before we
-     * delete the old one, so that in case of errors the old data is intact. We
-     * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
-     * needed in the rare situation where we have just enough free disk space to
-     * overwrite the existing data. */
+  if (SUCCEEDED(hr))
+    hr = StorageImpl_Refresh(This, TRUE, create);
 
-    root_entry = &This->entries[This->base.storageDirEntry];
+  if (FAILED(hr))
+  {
+    IStorage_Release(&This->base.IStorage_iface);
+    *result = NULL;
+  }
+  else
+  {
+    StorageImpl_Flush(&This->base);
+    *result = This;
+  }
 
-    if (!root_entry->read)
-      goto end;
+  return hr;
+}
 
-    hr = TransactedSnapshotImpl_CopyTree(This);
-    if (FAILED(hr)) goto end;
 
-    if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
-      dir_root_ref = DIRENTRY_NULL;
-    else
-      dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
+/************************************************************************
+ * StorageInternalImpl implementation
+ ***********************************************************************/
 
-    hr = StorageBaseImpl_Flush(This->transactedParent);
-
-    /* Update the storage to use the new data in one step. */
-    if (SUCCEEDED(hr))
-      hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
-        root_entry->transactedParentEntry, &data);
+static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
+{
+  StorageInternalImpl* This = (StorageInternalImpl*) base;
 
-    if (SUCCEEDED(hr))
-    {
-      data.dirRootEntry = dir_root_ref;
-      data.clsid = root_entry->data.clsid;
-      data.ctime = root_entry->data.ctime;
-      data.mtime = root_entry->data.mtime;
+  if (!This->base.reverted)
+  {
+    TRACE("Storage invalidated (stg=%p)\n", This);
 
-      hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
-        root_entry->transactedParentEntry, &data);
-    }
+    This->base.reverted = TRUE;
 
-    /* Try to flush after updating the root storage, but if the flush fails, keep
-     * going, on the theory that it'll either succeed later or the subsequent
-     * writes will fail. */
-    StorageBaseImpl_Flush(This->transactedParent);
+    This->parentStorage = NULL;
 
-    if (SUCCEEDED(hr))
-    {
-      /* Destroy the old now-orphaned data. */
-      for (i=0; i<This->entries_size; i++)
-      {
-        TransactedDirEntry *entry = &This->entries[i];
-        if (entry->inuse)
-        {
-          if (entry->deleted)
-          {
-            StorageBaseImpl_StreamSetSize(This->transactedParent,
-              entry->transactedParentEntry, zero);
-            StorageBaseImpl_DestroyDirEntry(This->transactedParent,
-              entry->transactedParentEntry);
-            memset(entry, 0, sizeof(TransactedDirEntry));
-            This->firstFreeEntry = min(i, This->firstFreeEntry);
-          }
-          else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
-          {
-            if (entry->transactedParentEntry != DIRENTRY_NULL)
-              StorageBaseImpl_DestroyDirEntry(This->transactedParent,
-                entry->transactedParentEntry);
-            if (entry->stream_dirty)
-            {
-              StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
-              StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
-              entry->stream_dirty = FALSE;
-            }
-            entry->dirty = FALSE;
-            entry->transactedParentEntry = entry->newTransactedParentEntry;
-          }
-        }
-      }
-    }
-    else
-    {
-      TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
-    }
+    StorageBaseImpl_DeleteAll(&This->base);
 
-    if (SUCCEEDED(hr))
-      hr = StorageBaseImpl_Flush(This->transactedParent);
-end:
-    StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
+    list_remove(&This->ParentListEntry);
   }
-
-  return hr;
 }
 
-static HRESULT WINAPI TransactedSnapshotImpl_Revert(
-  IStorage*            iface)
+static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
-  ULARGE_INTEGER zero;
-  ULONG i;
-
-  TRACE("(%p)\n", iface);
-
-  /* Destroy the open objects. */
-  StorageBaseImpl_DeleteAll(&This->base);
-
-  /* Clear out the scratch file. */
-  zero.QuadPart = 0;
-  for (i=0; i<This->entries_size; i++)
-  {
-    if (This->entries[i].stream_dirty)
-    {
-      StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
-        zero);
-
-      StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
-    }
-  }
-
-  memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
+  StorageInternalImpl* This = (StorageInternalImpl*) iface;
 
-  This->firstFreeEntry = 0;
-  This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
+  StorageInternalImpl_Invalidate(&This->base);
 
-  return S_OK;
+  HeapFree(GetProcessHeap(), 0, This);
 }
 
-static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
+static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
 {
-  if (!This->reverted)
-  {
-    TRACE("Storage invalidated (stg=%p)\n", This);
-
-    This->reverted = TRUE;
+  StorageInternalImpl* This = (StorageInternalImpl*) iface;
 
-    StorageBaseImpl_DeleteAll(This);
-  }
+  return StorageBaseImpl_Flush(This->parentStorage);
 }
 
-static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
+static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
+  StorageInternalImpl* This = (StorageInternalImpl*) iface;
 
-  IStorage_Revert(&This->base.IStorage_iface);
-  IStorage_Release(&This->transactedParent->IStorage_iface);
-  IStorage_Release(&This->scratch->IStorage_iface);
-  HeapFree(GetProcessHeap(), 0, This->entries);
-  HeapFree(GetProcessHeap(), 0, This);
+  return StorageBaseImpl_GetFilename(This->parentStorage, result);
 }
 
-static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
+static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
+  const DirEntry *newData, DirRef *index)
 {
-  /* We only need to flush when committing. */
-  return S_OK;
+  StorageInternalImpl* This = (StorageInternalImpl*) base;
+
+  return StorageBaseImpl_CreateDirEntry(This->parentStorage,
+    newData, index);
 }
 
-static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
+static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
+  DirRef index, const DirEntry *data)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
+  StorageInternalImpl* This = (StorageInternalImpl*) base;
 
-  return StorageBaseImpl_GetFilename(This->transactedParent, result);
+  return StorageBaseImpl_WriteDirEntry(This->parentStorage,
+    index, data);
 }
 
-static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
-  const DirEntry *newData, DirRef *index)
+static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
+  DirRef index, DirEntry *data)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-  DirRef new_ref;
-  TransactedDirEntry *new_entry;
-
-  new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
-  if (new_ref == DIRENTRY_NULL)
-    return E_OUTOFMEMORY;
-
-  new_entry = &This->entries[new_ref];
-
-  new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
-  new_entry->read = TRUE;
-  new_entry->dirty = TRUE;
-  memcpy(&new_entry->data, newData, sizeof(DirEntry));
-
-  *index = new_ref;
-
-  TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
+  StorageInternalImpl* This = (StorageInternalImpl*) base;
 
-  return S_OK;
+  return StorageBaseImpl_ReadDirEntry(This->parentStorage,
+    index, data);
 }
 
-static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
-  DirRef index, const DirEntry *data)
+static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
+  DirRef index)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-  HRESULT hr;
-
-  TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
+  StorageInternalImpl* This = (StorageInternalImpl*) base;
 
-  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
-  if (FAILED(hr)) return hr;
+  return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
+    index);
+}
 
-  memcpy(&This->entries[index].data, data, sizeof(DirEntry));
+static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
+  DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
+{
+  StorageInternalImpl* This = (StorageInternalImpl*) base;
 
-  if (index != This->base.storageDirEntry)
-  {
-    This->entries[index].dirty = TRUE;
+  return StorageBaseImpl_StreamReadAt(This->parentStorage,
+    index, offset, size, buffer, bytesRead);
+}
 
-    if (data->size.QuadPart == 0 &&
-        This->entries[index].transactedParentEntry != DIRENTRY_NULL)
-    {
-      /* Since this entry is modified, and we aren't using its stream data, we
-       * no longer care about the original entry. */
-      DirRef delete_ref;
-      delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
+static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
+  DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
+{
+  StorageInternalImpl* This = (StorageInternalImpl*) base;
 
-      if (delete_ref != DIRENTRY_NULL)
-        This->entries[delete_ref].deleted = TRUE;
+  return StorageBaseImpl_StreamWriteAt(This->parentStorage,
+    index, offset, size, buffer, bytesWritten);
+}
 
-      This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
-    }
-  }
+static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
+  DirRef index, ULARGE_INTEGER newsize)
+{
+  StorageInternalImpl* This = (StorageInternalImpl*) base;
 
-  return S_OK;
+  return StorageBaseImpl_StreamSetSize(This->parentStorage,
+    index, newsize);
 }
 
-static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
-  DirRef index, DirEntry *data)
+static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
+  DirRef dst, DirRef src)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-  HRESULT hr;
+  StorageInternalImpl* This = (StorageInternalImpl*) base;
 
-  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
-  if (FAILED(hr)) return hr;
+  return StorageBaseImpl_StreamLink(This->parentStorage,
+    dst, src);
+}
 
-  memcpy(data, &This->entries[index].data, sizeof(DirEntry));
+static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
+  ULONG* result, BOOL refresh)
+{
+  return E_NOTIMPL;
+}
 
-  TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
+static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
+  ULONG value)
+{
+  return E_NOTIMPL;
+}
 
-  return S_OK;
+static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
+{
+  return E_NOTIMPL;
 }
 
-static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
-  DirRef index)
+static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+  return E_NOTIMPL;
+}
 
-  if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
-      This->entries[index].data.size.QuadPart != 0)
-  {
-    /* If we deleted this entry while it has stream data. We must have left the
-     * data because some other entry is using it, and we need to leave the
-     * original entry alone. */
-    memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
-    This->firstFreeEntry = min(index, This->firstFreeEntry);
-  }
-  else
-  {
-    This->entries[index].deleted = TRUE;
-  }
-
-  return S_OK;
-}
-
-static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
-  DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
-{
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-
-  if (This->entries[index].stream_dirty)
-  {
-    return StorageBaseImpl_StreamReadAt(This->scratch,
-        This->entries[index].stream_entry, offset, size, buffer, bytesRead);
-  }
-  else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
-  {
-    /* This stream doesn't live in the parent, and we haven't allocated storage
-     * for it yet */
-    *bytesRead = 0;
-    return S_OK;
-  }
-  else
-  {
-    return StorageBaseImpl_StreamReadAt(This->transactedParent,
-        This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
-  }
-}
-
-static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
-  DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
-{
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-  HRESULT hr;
-
-  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
-  if (FAILED(hr)) return hr;
-
-  hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
-  if (FAILED(hr)) return hr;
-
-  hr = StorageBaseImpl_StreamWriteAt(This->scratch,
-    This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
-
-  if (SUCCEEDED(hr) && size != 0)
-    This->entries[index].data.size.QuadPart = max(
-        This->entries[index].data.size.QuadPart,
-        offset.QuadPart + size);
-
-  return hr;
-}
-
-static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
-  DirRef index, ULARGE_INTEGER newsize)
+/******************************************************************************
+**
+** StorageInternalImpl_Commit
+**
+*/
+static HRESULT WINAPI StorageInternalImpl_Commit(
+  IStorage*            iface,
+  DWORD                  grfCommitFlags)  /* [in] */
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-  HRESULT hr;
-
-  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
-  if (FAILED(hr)) return hr;
-
-  if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
-    return S_OK;
-
-  if (newsize.QuadPart == 0)
-  {
-    /* Destroy any parent references or entries in the scratch file. */
-    if (This->entries[index].stream_dirty)
-    {
-      ULARGE_INTEGER zero;
-      zero.QuadPart = 0;
-      StorageBaseImpl_StreamSetSize(This->scratch,
-        This->entries[index].stream_entry, zero);
-      StorageBaseImpl_DestroyDirEntry(This->scratch,
-        This->entries[index].stream_entry);
-      This->entries[index].stream_dirty = FALSE;
-    }
-    else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
-    {
-      DirRef delete_ref;
-      delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
-
-      if (delete_ref != DIRENTRY_NULL)
-        This->entries[delete_ref].deleted = TRUE;
-
-      This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
-    }
-  }
-  else
-  {
-    hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
-    if (FAILED(hr)) return hr;
-
-    hr = StorageBaseImpl_StreamSetSize(This->scratch,
-      This->entries[index].stream_entry, newsize);
-  }
-
-  if (SUCCEEDED(hr))
-    This->entries[index].data.size = newsize;
-
-  return hr;
+  StorageBaseImpl* This = impl_from_IStorage(iface);
+  TRACE("(%p,%x)\n", iface, grfCommitFlags);
+  return StorageBaseImpl_Flush(This);
 }
 
-static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
-  DirRef dst, DirRef src)
+/******************************************************************************
+**
+** StorageInternalImpl_Revert
+**
+*/
+static HRESULT WINAPI StorageInternalImpl_Revert(
+  IStorage*            iface)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-  HRESULT hr;
-  TransactedDirEntry *dst_entry, *src_entry;
-
-  hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
-  if (FAILED(hr)) return hr;
-
-  hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
-  if (FAILED(hr)) return hr;
-
-  dst_entry = &This->entries[dst];
-  src_entry = &This->entries[src];
-
-  dst_entry->stream_dirty = src_entry->stream_dirty;
-  dst_entry->stream_entry = src_entry->stream_entry;
-  dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
-  dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
-  dst_entry->data.size = src_entry->data.size;
-
+  FIXME("(%p): stub\n", iface);
   return S_OK;
 }
 
-static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
-  ULONG* result, BOOL refresh)
-{
-  return E_NOTIMPL;
-}
-
-static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
-  ULONG value)
-{
-  return E_NOTIMPL;
-}
-
-static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
-{
-  return E_NOTIMPL;
-}
-
-static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
-{
-  return E_NOTIMPL;
-}
-
-static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
+/*
+ * Virtual function table for the StorageInternalImpl class.
+ */
+static const IStorageVtbl StorageInternalImpl_Vtbl =
 {
     StorageBaseImpl_QueryInterface,
     StorageBaseImpl_AddRef,
@@ -5630,8 +5359,8 @@ static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
     StorageBaseImpl_OpenStorage,
     StorageBaseImpl_CopyTo,
     StorageBaseImpl_MoveElementTo,
-    TransactedSnapshotImpl_Commit,
-    TransactedSnapshotImpl_Revert,
+    StorageInternalImpl_Commit,
+    StorageInternalImpl_Revert,
     StorageBaseImpl_EnumElements,
     StorageBaseImpl_DestroyElement,
     StorageBaseImpl_RenameElement,
@@ -5641,934 +5370,1200 @@ static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
     StorageBaseImpl_Stat
 };
 
-static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
+static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
 {
-  TransactedSnapshotImpl_Destroy,
-  TransactedSnapshotImpl_Invalidate,
-  TransactedSnapshotImpl_Flush,
-  TransactedSnapshotImpl_GetFilename,
-  TransactedSnapshotImpl_CreateDirEntry,
-  TransactedSnapshotImpl_WriteDirEntry,
-  TransactedSnapshotImpl_ReadDirEntry,
-  TransactedSnapshotImpl_DestroyDirEntry,
-  TransactedSnapshotImpl_StreamReadAt,
-  TransactedSnapshotImpl_StreamWriteAt,
-  TransactedSnapshotImpl_StreamSetSize,
-  TransactedSnapshotImpl_StreamLink,
-  TransactedSnapshotImpl_GetTransactionSig,
-  TransactedSnapshotImpl_SetTransactionSig,
-  TransactedSnapshotImpl_LockTransaction,
-  TransactedSnapshotImpl_UnlockTransaction
+  StorageInternalImpl_Destroy,
+  StorageInternalImpl_Invalidate,
+  StorageInternalImpl_Flush,
+  StorageInternalImpl_GetFilename,
+  StorageInternalImpl_CreateDirEntry,
+  StorageInternalImpl_WriteDirEntry,
+  StorageInternalImpl_ReadDirEntry,
+  StorageInternalImpl_DestroyDirEntry,
+  StorageInternalImpl_StreamReadAt,
+  StorageInternalImpl_StreamWriteAt,
+  StorageInternalImpl_StreamSetSize,
+  StorageInternalImpl_StreamLink,
+  StorageInternalImpl_GetTransactionSig,
+  StorageInternalImpl_SetTransactionSig,
+  StorageInternalImpl_LockTransaction,
+  StorageInternalImpl_UnlockTransaction
 };
 
-static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
-  TransactedSnapshotImpl** result)
+static StorageInternalImpl* StorageInternalImpl_Construct(
+  StorageBaseImpl* parentStorage,
+  DWORD        openFlags,
+  DirRef       storageDirEntry)
 {
-  HRESULT hr;
+  StorageInternalImpl* newStorage;
 
-  *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
-  if (*result)
+  newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
+
+  if (newStorage!=0)
   {
-    IStorage *scratch;
+    list_init(&newStorage->base.strmHead);
 
-    (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
+    list_init(&newStorage->base.storageHead);
 
-    /* This is OK because the property set storage functions use the IStorage functions. */
-    (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
-    (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
+    /*
+     * Initialize the virtual function table.
+     */
+    newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
+    newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
+    newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
+    newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
 
-    list_init(&(*result)->base.strmHead);
+    newStorage->base.reverted = FALSE;
 
-    list_init(&(*result)->base.storageHead);
+    newStorage->base.ref = 1;
 
-    (*result)->base.ref = 1;
+    newStorage->parentStorage = parentStorage;
 
-    (*result)->base.openFlags = parentStorage->openFlags;
+    /*
+     * Keep a reference to the directory entry of this storage
+     */
+    newStorage->base.storageDirEntry = storageDirEntry;
 
-    /* This cannot fail, except with E_NOTIMPL in which case we don't care */
-    StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
+    newStorage->base.create = FALSE;
 
-    /* Create a new temporary storage to act as the scratch file. */
-    hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
-        0, &scratch);
-    (*result)->scratch = impl_from_IStorage(scratch);
+    return newStorage;
+  }
 
-    if (SUCCEEDED(hr))
-    {
-        ULONG num_entries = 20;
+  return 0;
+}
 
-        (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
-        (*result)->entries_size = num_entries;
-        (*result)->firstFreeEntry = 0;
 
-        if ((*result)->entries)
-        {
-            /* parentStorage already has 1 reference, which we take over here. */
-            (*result)->transactedParent = parentStorage;
+/************************************************************************
+ * TransactedSnapshotImpl implementation
+ ***********************************************************************/
 
-            parentStorage->transactedChild = &(*result)->base;
+static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
+{
+  DirRef result=This->firstFreeEntry;
 
-            (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
-        }
-        else
-        {
-            IStorage_Release(scratch);
+  while (result < This->entries_size && This->entries[result].inuse)
+    result++;
 
-            hr = E_OUTOFMEMORY;
-        }
-    }
+  if (result == This->entries_size)
+  {
+    ULONG new_size = This->entries_size * 2;
+    TransactedDirEntry *new_entries;
 
-    if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
+    new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
+    if (!new_entries) return DIRENTRY_NULL;
 
-    return hr;
+    memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
+    HeapFree(GetProcessHeap(), 0, This->entries);
+
+    This->entries = new_entries;
+    This->entries_size = new_size;
   }
-  else
-    return E_OUTOFMEMORY;
-}
 
-static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
-{
-  if (!This->reverted)
-  {
-    TRACE("Storage invalidated (stg=%p)\n", This);
+  This->entries[result].inuse = TRUE;
 
-    This->reverted = TRUE;
+  This->firstFreeEntry = result+1;
 
-    StorageBaseImpl_DeleteAll(This);
-  }
+  return result;
 }
 
-static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
+static DirRef TransactedSnapshotImpl_CreateStubEntry(
+  TransactedSnapshotImpl *This, DirRef parentEntryRef)
 {
-  TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
+  DirRef stubEntryRef;
+  TransactedDirEntry *entry;
 
-  TransactedSharedImpl_Invalidate(&This->base);
-  IStorage_Release(&This->transactedParent->IStorage_iface);
-  IStorage_Release(&This->scratch->base.IStorage_iface);
-  HeapFree(GetProcessHeap(), 0, This);
-}
+  stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
 
-static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
-{
-  /* We only need to flush when committing. */
-  return S_OK;
-}
+  if (stubEntryRef != DIRENTRY_NULL)
+  {
+    entry = &This->entries[stubEntryRef];
 
-static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
-{
-  TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
+    entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
 
-  return StorageBaseImpl_GetFilename(This->transactedParent, result);
+    entry->read = FALSE;
+  }
+
+  return stubEntryRef;
 }
 
-static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
-  const DirEntry *newData, DirRef *index)
+static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
+  TransactedSnapshotImpl *This, DirRef entry)
 {
-  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+  HRESULT hr=S_OK;
+  DirEntry data;
 
-  return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
-    newData, index);
-}
+  if (!This->entries[entry].read)
+  {
+    hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
+        This->entries[entry].transactedParentEntry,
+        &data);
 
-static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
-  DirRef index, const DirEntry *data)
-{
-  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+    if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
+    {
+      data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
 
-  return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
-    index, data);
-}
+      if (data.leftChild == DIRENTRY_NULL)
+        hr = E_OUTOFMEMORY;
+    }
 
-static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
-  DirRef index, DirEntry *data)
-{
-  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+    if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
+    {
+      data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
 
-  return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
-    index, data);
-}
+      if (data.rightChild == DIRENTRY_NULL)
+        hr = E_OUTOFMEMORY;
+    }
 
-static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
-  DirRef index)
-{
-  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+    if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
+    {
+      data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
 
-  return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
-    index);
-}
+      if (data.dirRootEntry == DIRENTRY_NULL)
+        hr = E_OUTOFMEMORY;
+    }
 
-static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
-  DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
-{
-  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+    if (SUCCEEDED(hr))
+    {
+      memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
+      This->entries[entry].read = TRUE;
+    }
+  }
 
-  return StorageBaseImpl_StreamReadAt(&This->scratch->base,
-    index, offset, size, buffer, bytesRead);
+  return hr;
 }
 
-static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
-  DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
+static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
+  TransactedSnapshotImpl *This, DirRef entry)
 {
-  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+  HRESULT hr = S_OK;
 
-  return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
-    index, offset, size, buffer, bytesWritten);
-}
+  if (!This->entries[entry].stream_dirty)
+  {
+    DirEntry new_entrydata;
 
-static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
-  DirRef index, ULARGE_INTEGER newsize)
-{
-  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+    memset(&new_entrydata, 0, sizeof(DirEntry));
+    new_entrydata.name[0] = 'S';
+    new_entrydata.sizeOfNameString = 1;
+    new_entrydata.stgType = STGTY_STREAM;
+    new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
+    new_entrydata.leftChild = DIRENTRY_NULL;
+    new_entrydata.rightChild = DIRENTRY_NULL;
+    new_entrydata.dirRootEntry = DIRENTRY_NULL;
 
-  return StorageBaseImpl_StreamSetSize(&This->scratch->base,
-    index, newsize);
-}
+    hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
+      &This->entries[entry].stream_entry);
 
-static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
-  DirRef dst, DirRef src)
-{
-  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+    if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
+    {
+      hr = StorageBaseImpl_CopyStream(
+        This->scratch, This->entries[entry].stream_entry,
+        This->transactedParent, This->entries[entry].transactedParentEntry);
 
-  return StorageBaseImpl_StreamLink(&This->scratch->base,
-    dst, src);
-}
+      if (FAILED(hr))
+        StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
+    }
 
-static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
-  ULONG* result, BOOL refresh)
-{
-  return E_NOTIMPL;
-}
+    if (SUCCEEDED(hr))
+      This->entries[entry].stream_dirty = TRUE;
 
-static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
-  ULONG value)
-{
-  return E_NOTIMPL;
-}
+    if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
+    {
+      /* Since this entry is modified, and we aren't using its stream data, we
+       * no longer care about the original entry. */
+      DirRef delete_ref;
+      delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
 
-static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
-{
-  return E_NOTIMPL;
-}
+      if (delete_ref != DIRENTRY_NULL)
+        This->entries[delete_ref].deleted = TRUE;
 
-static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
-{
-  return E_NOTIMPL;
+      This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
+    }
+  }
+
+  return hr;
 }
 
-static HRESULT WINAPI TransactedSharedImpl_Commit(
-  IStorage*            iface,
-  DWORD                  grfCommitFlags)  /* [in] */
+/* Find the first entry in a depth-first traversal. */
+static DirRef TransactedSnapshotImpl_FindFirstChild(
+  TransactedSnapshotImpl* This, DirRef parent)
 {
-  TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
-  DirRef new_storage_ref, prev_storage_ref;
-  DirEntry src_data, dst_data;
-  HRESULT hr;
-  ULONG transactionSig;
-
-  TRACE("(%p,%x)\n", iface, grfCommitFlags);
-
-  /* Cannot commit a read-only transacted storage */
-  if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
-    return STG_E_ACCESSDENIED;
+  DirRef cursor, prev;
+  TransactedDirEntry *entry;
 
-  hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
-  if (hr == E_NOTIMPL) hr = S_OK;
-  if (SUCCEEDED(hr))
-  {
-    hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
-    if (SUCCEEDED(hr))
+  cursor = parent;
+  entry = &This->entries[cursor];
+  while (entry->read)
+  {
+    if (entry->data.leftChild != DIRENTRY_NULL)
     {
-      if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
-        hr = STG_E_NOTCURRENT;
-
-      if (SUCCEEDED(hr))
-        hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
+      prev = cursor;
+      cursor = entry->data.leftChild;
+      entry = &This->entries[cursor];
+      entry->parent = prev;
     }
-    else if (hr == E_NOTIMPL)
-      hr = S_OK;
-
-    if (SUCCEEDED(hr))
-      hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
-
-    /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
-    if (SUCCEEDED(hr))
-      hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
-
-    if (SUCCEEDED(hr))
-      hr = StorageBaseImpl_Flush(This->transactedParent);
-
-    if (SUCCEEDED(hr))
-      hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
-
-    if (SUCCEEDED(hr))
+    else if (entry->data.rightChild != DIRENTRY_NULL)
     {
-      prev_storage_ref = dst_data.dirRootEntry;
-      dst_data.dirRootEntry = new_storage_ref;
-      dst_data.clsid = src_data.clsid;
-      dst_data.ctime = src_data.ctime;
-      dst_data.mtime = src_data.mtime;
-      hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
+      prev = cursor;
+      cursor = entry->data.rightChild;
+      entry = &This->entries[cursor];
+      entry->parent = prev;
     }
-
-    if (SUCCEEDED(hr))
+    else if (entry->data.dirRootEntry != DIRENTRY_NULL)
     {
-      /* Try to flush after updating the root storage, but if the flush fails, keep
-       * going, on the theory that it'll either succeed later or the subsequent
-       * writes will fail. */
-      StorageBaseImpl_Flush(This->transactedParent);
-
-      hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
+      prev = cursor;
+      cursor = entry->data.dirRootEntry;
+      entry = &This->entries[cursor];
+      entry->parent = prev;
     }
+    else
+      break;
+  }
 
-    if (SUCCEEDED(hr))
-      hr = StorageBaseImpl_Flush(This->transactedParent);
+  return cursor;
+}
 
-    StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
+/* Find the next entry in a depth-first traversal. */
+static DirRef TransactedSnapshotImpl_FindNextChild(
+  TransactedSnapshotImpl* This, DirRef current)
+{
+  DirRef parent;
+  TransactedDirEntry *parent_entry;
 
-    if (SUCCEEDED(hr))
-      hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
+  parent = This->entries[current].parent;
+  parent_entry = &This->entries[parent];
 
-    if (SUCCEEDED(hr))
+  if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
+  {
+    if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
     {
-      This->lastTransactionSig = transactionSig+1;
+      This->entries[parent_entry->data.rightChild].parent = parent;
+      return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
+    }
+
+    if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
+    {
+      This->entries[parent_entry->data.dirRootEntry].parent = parent;
+      return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
     }
   }
 
-  return hr;
+  return parent;
 }
 
-static HRESULT WINAPI TransactedSharedImpl_Revert(
-  IStorage*            iface)
+/* Return TRUE if we've made a copy of this entry for committing to the parent. */
+static inline BOOL TransactedSnapshotImpl_MadeCopy(
+  TransactedSnapshotImpl* This, DirRef entry)
 {
-  TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
+  return entry != DIRENTRY_NULL &&
+    This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
+}
 
-  TRACE("(%p)\n", iface);
+/* Destroy the entries created by CopyTree. */
+static void TransactedSnapshotImpl_DestroyTemporaryCopy(
+  TransactedSnapshotImpl* This, DirRef stop)
+{
+  DirRef cursor;
+  TransactedDirEntry *entry;
+  ULARGE_INTEGER zero;
 
-  /* Destroy the open objects. */
-  StorageBaseImpl_DeleteAll(&This->base);
+  zero.QuadPart = 0;
 
-  return IStorage_Revert(&This->scratch->base.IStorage_iface);
-}
+  if (!This->entries[This->base.storageDirEntry].read)
+    return;
 
-static const IStorageVtbl TransactedSharedImpl_Vtbl =
-{
-    StorageBaseImpl_QueryInterface,
-    StorageBaseImpl_AddRef,
-    StorageBaseImpl_Release,
-    StorageBaseImpl_CreateStream,
-    StorageBaseImpl_OpenStream,
-    StorageBaseImpl_CreateStorage,
-    StorageBaseImpl_OpenStorage,
-    StorageBaseImpl_CopyTo,
-    StorageBaseImpl_MoveElementTo,
-    TransactedSharedImpl_Commit,
-    TransactedSharedImpl_Revert,
-    StorageBaseImpl_EnumElements,
-    StorageBaseImpl_DestroyElement,
-    StorageBaseImpl_RenameElement,
-    StorageBaseImpl_SetElementTimes,
-    StorageBaseImpl_SetClass,
-    StorageBaseImpl_SetStateBits,
-    StorageBaseImpl_Stat
-};
+  cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
 
-static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
-{
-  TransactedSharedImpl_Destroy,
-  TransactedSharedImpl_Invalidate,
-  TransactedSharedImpl_Flush,
-  TransactedSharedImpl_GetFilename,
-  TransactedSharedImpl_CreateDirEntry,
-  TransactedSharedImpl_WriteDirEntry,
-  TransactedSharedImpl_ReadDirEntry,
-  TransactedSharedImpl_DestroyDirEntry,
-  TransactedSharedImpl_StreamReadAt,
-  TransactedSharedImpl_StreamWriteAt,
-  TransactedSharedImpl_StreamSetSize,
-  TransactedSharedImpl_StreamLink,
-  TransactedSharedImpl_GetTransactionSig,
-  TransactedSharedImpl_SetTransactionSig,
-  TransactedSharedImpl_LockTransaction,
-  TransactedSharedImpl_UnlockTransaction
-};
+  if (cursor == DIRENTRY_NULL)
+    return;
 
-static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
-  TransactedSharedImpl** result)
-{
-  HRESULT hr;
+  cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
 
-  *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
-  if (*result)
+  while (cursor != DIRENTRY_NULL && cursor != stop)
   {
-    IStorage *scratch;
+    if (TransactedSnapshotImpl_MadeCopy(This, cursor))
+    {
+      entry = &This->entries[cursor];
 
-    (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
+      if (entry->stream_dirty)
+        StorageBaseImpl_StreamSetSize(This->transactedParent,
+          entry->newTransactedParentEntry, zero);
 
-    /* This is OK because the property set storage functions use the IStorage functions. */
-    (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
-    (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
+      StorageBaseImpl_DestroyDirEntry(This->transactedParent,
+        entry->newTransactedParentEntry);
 
-    list_init(&(*result)->base.strmHead);
+      entry->newTransactedParentEntry = entry->transactedParentEntry;
+    }
 
-    list_init(&(*result)->base.storageHead);
+    cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
+  }
+}
 
-    (*result)->base.ref = 1;
+/* Make a copy of our edited tree that we can use in the parent. */
+static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
+{
+  DirRef cursor;
+  TransactedDirEntry *entry;
+  HRESULT hr = S_OK;
 
-    (*result)->base.openFlags = parentStorage->openFlags;
+  cursor = This->base.storageDirEntry;
+  entry = &This->entries[cursor];
+  entry->parent = DIRENTRY_NULL;
+  entry->newTransactedParentEntry = entry->transactedParentEntry;
 
-    hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
+  if (entry->data.dirRootEntry == DIRENTRY_NULL)
+    return S_OK;
 
-    if (SUCCEEDED(hr))
-    {
-      STGOPTIONS stgo;
+  This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
 
-      /* This cannot fail, except with E_NOTIMPL in which case we don't care */
-      StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
+  cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
+  entry = &This->entries[cursor];
 
-      stgo.usVersion = 1;
-      stgo.reserved = 0;
-      stgo.ulSectorSize = 4096;
-      stgo.pwcsTemplateFile = NULL;
+  while (cursor != DIRENTRY_NULL)
+  {
+    /* Make a copy of this entry in the transacted parent. */
+    if (!entry->read ||
+        (!entry->dirty && !entry->stream_dirty &&
+         !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
+         !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
+         !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
+      entry->newTransactedParentEntry = entry->transactedParentEntry;
+    else
+    {
+      DirEntry newData;
 
-      /* Create a new temporary storage to act as the scratch file. */
-      hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
-          STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
-      (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
+      memcpy(&newData, &entry->data, sizeof(DirEntry));
 
-      if (SUCCEEDED(hr))
-      {
-        hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
-          parentStorage, parentStorage->storageDirEntry);
+      newData.size.QuadPart = 0;
+      newData.startingBlock = BLOCK_END_OF_CHAIN;
 
-        if (SUCCEEDED(hr))
-        {
-          hr = IStorage_Commit(scratch, STGC_DEFAULT);
+      if (newData.leftChild != DIRENTRY_NULL)
+        newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
 
-          (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
-          (*result)->transactedParent = parentStorage;
-        }
+      if (newData.rightChild != DIRENTRY_NULL)
+        newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
 
-        if (FAILED(hr))
-          IStorage_Release(scratch);
+      if (newData.dirRootEntry != DIRENTRY_NULL)
+        newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
+
+      hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
+        &entry->newTransactedParentEntry);
+      if (FAILED(hr))
+      {
+        TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
+        return hr;
       }
 
-      StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
-    }
+      if (entry->stream_dirty)
+      {
+        hr = StorageBaseImpl_CopyStream(
+          This->transactedParent, entry->newTransactedParentEntry,
+          This->scratch, entry->stream_entry);
+      }
+      else if (entry->data.size.QuadPart)
+      {
+        hr = StorageBaseImpl_StreamLink(
+          This->transactedParent, entry->newTransactedParentEntry,
+          entry->transactedParentEntry);
+      }
 
-    if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
+      if (FAILED(hr))
+      {
+        cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
+        TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
+        return hr;
+      }
+    }
 
-    return hr;
+    cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
+    entry = &This->entries[cursor];
   }
-  else
-    return E_OUTOFMEMORY;
+
+  return hr;
 }
 
-static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
-  BOOL toplevel, StorageBaseImpl** result)
+static HRESULT WINAPI TransactedSnapshotImpl_Commit(
+  IStorage*            iface,
+  DWORD                  grfCommitFlags)  /* [in] */
 {
-  static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
+  TransactedDirEntry *root_entry;
+  DirRef i, dir_root_ref;
+  DirEntry data;
+  ULARGE_INTEGER zero;
+  HRESULT hr;
+  ULONG transactionSig;
 
-  if (parentStorage->openFlags & fixme_flags)
-  {
-    fixme_flags &= ~parentStorage->openFlags;
-    FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
-  }
+  zero.QuadPart = 0;
 
-  if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
-      STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
-      STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
+  TRACE("(%p,%x)\n", iface, grfCommitFlags);
+
+  /* Cannot commit a read-only transacted storage */
+  if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
+    return STG_E_ACCESSDENIED;
+
+  hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
+  if (hr == E_NOTIMPL) hr = S_OK;
+  if (SUCCEEDED(hr))
   {
-    /* Need to create a temp file for the snapshot */
-    return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
-  }
+    hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
+    if (SUCCEEDED(hr))
+    {
+      if (transactionSig != This->lastTransactionSig)
+      {
+        ERR("file was externally modified\n");
+        hr = STG_E_NOTCURRENT;
+      }
 
-  return TransactedSnapshotImpl_Construct(parentStorage,
-    (TransactedSnapshotImpl**)result);
-}
+      if (SUCCEEDED(hr))
+      {
+        This->lastTransactionSig = transactionSig+1;
+        hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
+      }
+    }
+    else if (hr == E_NOTIMPL)
+      hr = S_OK;
 
-static HRESULT Storage_Construct(
-  HANDLE       hFile,
-  LPCOLESTR    pwcsName,
-  ILockBytes*  pLkbyt,
-  DWORD        openFlags,
-  BOOL         fileBased,
-  BOOL         create,
-  ULONG        sector_size,
-  StorageBaseImpl** result)
-{
-  StorageImpl *newStorage;
-  StorageBaseImpl *newTransactedStorage;
-  HRESULT hr;
+    if (FAILED(hr)) goto end;
 
-  hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
-  if (FAILED(hr)) goto end;
+    /* To prevent data loss, we create the new structure in the file before we
+     * delete the old one, so that in case of errors the old data is intact. We
+     * shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
+     * needed in the rare situation where we have just enough free disk space to
+     * overwrite the existing data. */
 
-  if (openFlags & STGM_TRANSACTED)
-  {
-    hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
-    if (FAILED(hr))
-      IStorage_Release(&newStorage->base.IStorage_iface);
+    root_entry = &This->entries[This->base.storageDirEntry];
+
+    if (!root_entry->read)
+      goto end;
+
+    hr = TransactedSnapshotImpl_CopyTree(This);
+    if (FAILED(hr)) goto end;
+
+    if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
+      dir_root_ref = DIRENTRY_NULL;
     else
-      *result = newTransactedStorage;
-  }
-  else
-    *result = &newStorage->base;
+      dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
+
+    hr = StorageBaseImpl_Flush(This->transactedParent);
+
+    /* Update the storage to use the new data in one step. */
+    if (SUCCEEDED(hr))
+      hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
+        root_entry->transactedParentEntry, &data);
+
+    if (SUCCEEDED(hr))
+    {
+      data.dirRootEntry = dir_root_ref;
+      data.clsid = root_entry->data.clsid;
+      data.ctime = root_entry->data.ctime;
+      data.mtime = root_entry->data.mtime;
+
+      hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
+        root_entry->transactedParentEntry, &data);
+    }
+
+    /* Try to flush after updating the root storage, but if the flush fails, keep
+     * going, on the theory that it'll either succeed later or the subsequent
+     * writes will fail. */
+    StorageBaseImpl_Flush(This->transactedParent);
+
+    if (SUCCEEDED(hr))
+    {
+      /* Destroy the old now-orphaned data. */
+      for (i=0; i<This->entries_size; i++)
+      {
+        TransactedDirEntry *entry = &This->entries[i];
+        if (entry->inuse)
+        {
+          if (entry->deleted)
+          {
+            StorageBaseImpl_StreamSetSize(This->transactedParent,
+              entry->transactedParentEntry, zero);
+            StorageBaseImpl_DestroyDirEntry(This->transactedParent,
+              entry->transactedParentEntry);
+            memset(entry, 0, sizeof(TransactedDirEntry));
+            This->firstFreeEntry = min(i, This->firstFreeEntry);
+          }
+          else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
+          {
+            if (entry->transactedParentEntry != DIRENTRY_NULL)
+              StorageBaseImpl_DestroyDirEntry(This->transactedParent,
+                entry->transactedParentEntry);
+            if (entry->stream_dirty)
+            {
+              StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
+              StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
+              entry->stream_dirty = FALSE;
+            }
+            entry->dirty = FALSE;
+            entry->transactedParentEntry = entry->newTransactedParentEntry;
+          }
+        }
+      }
+    }
+    else
+    {
+      TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
+    }
 
+    if (SUCCEEDED(hr))
+      hr = StorageBaseImpl_Flush(This->transactedParent);
 end:
+    StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
+  }
+
   return hr;
 }
 
-static void StorageInternalImpl_Invalidate( StorageBaseImpl *base )
+static HRESULT WINAPI TransactedSnapshotImpl_Revert(
+  IStorage*            iface)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) base;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*)impl_from_IStorage(iface);
+  ULARGE_INTEGER zero;
+  ULONG i;
 
-  if (!This->base.reverted)
+  TRACE("(%p)\n", iface);
+
+  /* Destroy the open objects. */
+  StorageBaseImpl_DeleteAll(&This->base);
+
+  /* Clear out the scratch file. */
+  zero.QuadPart = 0;
+  for (i=0; i<This->entries_size; i++)
   {
-    TRACE("Storage invalidated (stg=%p)\n", This);
+    if (This->entries[i].stream_dirty)
+    {
+      StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
+        zero);
 
-    This->base.reverted = TRUE;
+      StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
+    }
+  }
 
-    This->parentStorage = NULL;
+  memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
 
-    StorageBaseImpl_DeleteAll(&This->base);
+  This->firstFreeEntry = 0;
+  This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
 
-    list_remove(&This->ParentListEntry);
-  }
+  return S_OK;
 }
 
-static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
+static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) iface;
+  if (!This->reverted)
+  {
+    TRACE("Storage invalidated (stg=%p)\n", This);
 
-  StorageInternalImpl_Invalidate(&This->base);
+    This->reverted = TRUE;
 
-  HeapFree(GetProcessHeap(), 0, This);
+    StorageBaseImpl_DeleteAll(This);
+  }
 }
 
-static HRESULT StorageInternalImpl_Flush(StorageBaseImpl* iface)
+static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) iface;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
 
-  return StorageBaseImpl_Flush(This->parentStorage);
+  IStorage_Revert(&This->base.IStorage_iface);
+  IStorage_Release(&This->transactedParent->IStorage_iface);
+  IStorage_Release(&This->scratch->IStorage_iface);
+  HeapFree(GetProcessHeap(), 0, This->entries);
+  HeapFree(GetProcessHeap(), 0, This);
 }
 
-static HRESULT StorageInternalImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
+static HRESULT TransactedSnapshotImpl_Flush(StorageBaseImpl* iface)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) iface;
-
-  return StorageBaseImpl_GetFilename(This->parentStorage, result);
+  /* We only need to flush when committing. */
+  return S_OK;
 }
 
-static HRESULT StorageInternalImpl_CreateDirEntry(StorageBaseImpl *base,
-  const DirEntry *newData, DirRef *index)
+static HRESULT TransactedSnapshotImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) base;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
 
-  return StorageBaseImpl_CreateDirEntry(This->parentStorage,
-    newData, index);
+  return StorageBaseImpl_GetFilename(This->transactedParent, result);
 }
 
-static HRESULT StorageInternalImpl_WriteDirEntry(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
+  const DirEntry *newData, DirRef *index)
+{
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+  DirRef new_ref;
+  TransactedDirEntry *new_entry;
+
+  new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
+  if (new_ref == DIRENTRY_NULL)
+    return E_OUTOFMEMORY;
+
+  new_entry = &This->entries[new_ref];
+
+  new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
+  new_entry->read = TRUE;
+  new_entry->dirty = TRUE;
+  memcpy(&new_entry->data, newData, sizeof(DirEntry));
+
+  *index = new_ref;
+
+  TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
+
+  return S_OK;
+}
+
+static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
   DirRef index, const DirEntry *data)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) base;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+  HRESULT hr;
 
-  return StorageBaseImpl_WriteDirEntry(This->parentStorage,
-    index, data);
+  TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
+
+  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+  if (FAILED(hr)) return hr;
+
+  memcpy(&This->entries[index].data, data, sizeof(DirEntry));
+
+  if (index != This->base.storageDirEntry)
+  {
+    This->entries[index].dirty = TRUE;
+
+    if (data->size.QuadPart == 0 &&
+        This->entries[index].transactedParentEntry != DIRENTRY_NULL)
+    {
+      /* Since this entry is modified, and we aren't using its stream data, we
+       * no longer care about the original entry. */
+      DirRef delete_ref;
+      delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
+
+      if (delete_ref != DIRENTRY_NULL)
+        This->entries[delete_ref].deleted = TRUE;
+
+      This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
+    }
+  }
+
+  return S_OK;
 }
 
-static HRESULT StorageInternalImpl_ReadDirEntry(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
   DirRef index, DirEntry *data)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) base;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+  HRESULT hr;
 
-  return StorageBaseImpl_ReadDirEntry(This->parentStorage,
-    index, data);
+  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+  if (FAILED(hr)) return hr;
+
+  memcpy(data, &This->entries[index].data, sizeof(DirEntry));
+
+  TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
+
+  return S_OK;
 }
 
-static HRESULT StorageInternalImpl_DestroyDirEntry(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
   DirRef index)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) base;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
 
-  return StorageBaseImpl_DestroyDirEntry(This->parentStorage,
-    index);
+  if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
+      This->entries[index].data.size.QuadPart != 0)
+  {
+    /* If we deleted this entry while it has stream data. We must have left the
+     * data because some other entry is using it, and we need to leave the
+     * original entry alone. */
+    memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
+    This->firstFreeEntry = min(index, This->firstFreeEntry);
+  }
+  else
+  {
+    This->entries[index].deleted = TRUE;
+  }
+
+  return S_OK;
 }
 
-static HRESULT StorageInternalImpl_StreamReadAt(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
   DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) base;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
 
-  return StorageBaseImpl_StreamReadAt(This->parentStorage,
-    index, offset, size, buffer, bytesRead);
+  if (This->entries[index].stream_dirty)
+  {
+    return StorageBaseImpl_StreamReadAt(This->scratch,
+        This->entries[index].stream_entry, offset, size, buffer, bytesRead);
+  }
+  else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
+  {
+    /* This stream doesn't live in the parent, and we haven't allocated storage
+     * for it yet */
+    *bytesRead = 0;
+    return S_OK;
+  }
+  else
+  {
+    return StorageBaseImpl_StreamReadAt(This->transactedParent,
+        This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
+  }
 }
 
-static HRESULT StorageInternalImpl_StreamWriteAt(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
   DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) base;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+  HRESULT hr;
 
-  return StorageBaseImpl_StreamWriteAt(This->parentStorage,
-    index, offset, size, buffer, bytesWritten);
+  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+  if (FAILED(hr)) return hr;
+
+  hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
+  if (FAILED(hr)) return hr;
+
+  hr = StorageBaseImpl_StreamWriteAt(This->scratch,
+    This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
+
+  if (SUCCEEDED(hr) && size != 0)
+    This->entries[index].data.size.QuadPart = max(
+        This->entries[index].data.size.QuadPart,
+        offset.QuadPart + size);
+
+  return hr;
 }
 
-static HRESULT StorageInternalImpl_StreamSetSize(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
   DirRef index, ULARGE_INTEGER newsize)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) base;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+  HRESULT hr;
 
-  return StorageBaseImpl_StreamSetSize(This->parentStorage,
-    index, newsize);
+  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+  if (FAILED(hr)) return hr;
+
+  if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
+    return S_OK;
+
+  if (newsize.QuadPart == 0)
+  {
+    /* Destroy any parent references or entries in the scratch file. */
+    if (This->entries[index].stream_dirty)
+    {
+      ULARGE_INTEGER zero;
+      zero.QuadPart = 0;
+      StorageBaseImpl_StreamSetSize(This->scratch,
+        This->entries[index].stream_entry, zero);
+      StorageBaseImpl_DestroyDirEntry(This->scratch,
+        This->entries[index].stream_entry);
+      This->entries[index].stream_dirty = FALSE;
+    }
+    else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
+    {
+      DirRef delete_ref;
+      delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
+
+      if (delete_ref != DIRENTRY_NULL)
+        This->entries[delete_ref].deleted = TRUE;
+
+      This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
+    }
+  }
+  else
+  {
+    hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
+    if (FAILED(hr)) return hr;
+
+    hr = StorageBaseImpl_StreamSetSize(This->scratch,
+      This->entries[index].stream_entry, newsize);
+  }
+
+  if (SUCCEEDED(hr))
+    This->entries[index].data.size = newsize;
+
+  return hr;
 }
 
-static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
   DirRef dst, DirRef src)
 {
-  StorageInternalImpl* This = (StorageInternalImpl*) base;
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+  HRESULT hr;
+  TransactedDirEntry *dst_entry, *src_entry;
 
-  return StorageBaseImpl_StreamLink(This->parentStorage,
-    dst, src);
+  hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
+  if (FAILED(hr)) return hr;
+
+  hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
+  if (FAILED(hr)) return hr;
+
+  dst_entry = &This->entries[dst];
+  src_entry = &This->entries[src];
+
+  dst_entry->stream_dirty = src_entry->stream_dirty;
+  dst_entry->stream_entry = src_entry->stream_entry;
+  dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
+  dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
+  dst_entry->data.size = src_entry->data.size;
+
+  return S_OK;
 }
 
-static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_GetTransactionSig(StorageBaseImpl *base,
   ULONG* result, BOOL refresh)
 {
   return E_NOTIMPL;
 }
 
-static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
+static HRESULT TransactedSnapshotImpl_SetTransactionSig(StorageBaseImpl *base,
   ULONG value)
 {
   return E_NOTIMPL;
 }
 
-static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
+static HRESULT TransactedSnapshotImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
 {
   return E_NOTIMPL;
 }
 
-static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
+static HRESULT TransactedSnapshotImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
 {
   return E_NOTIMPL;
 }
 
-/******************************************************************************
-**
-** StorageInternalImpl_Commit
-**
-*/
-static HRESULT WINAPI StorageInternalImpl_Commit(
-  IStorage*            iface,
-  DWORD                  grfCommitFlags)  /* [in] */
+static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
 {
-  StorageBaseImpl* This = impl_from_IStorage(iface);
-  TRACE("(%p,%x)\n", iface, grfCommitFlags);
-  return StorageBaseImpl_Flush(This);
-}
+    StorageBaseImpl_QueryInterface,
+    StorageBaseImpl_AddRef,
+    StorageBaseImpl_Release,
+    StorageBaseImpl_CreateStream,
+    StorageBaseImpl_OpenStream,
+    StorageBaseImpl_CreateStorage,
+    StorageBaseImpl_OpenStorage,
+    StorageBaseImpl_CopyTo,
+    StorageBaseImpl_MoveElementTo,
+    TransactedSnapshotImpl_Commit,
+    TransactedSnapshotImpl_Revert,
+    StorageBaseImpl_EnumElements,
+    StorageBaseImpl_DestroyElement,
+    StorageBaseImpl_RenameElement,
+    StorageBaseImpl_SetElementTimes,
+    StorageBaseImpl_SetClass,
+    StorageBaseImpl_SetStateBits,
+    StorageBaseImpl_Stat
+};
 
-/******************************************************************************
-**
-** StorageInternalImpl_Revert
-**
-*/
-static HRESULT WINAPI StorageInternalImpl_Revert(
-  IStorage*            iface)
-{
-  FIXME("(%p): stub\n", iface);
-  return S_OK;
-}
-
-static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
+static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
 {
-  IStorage_Release(&This->parentStorage->IStorage_iface);
-  HeapFree(GetProcessHeap(), 0, This);
-}
+  TransactedSnapshotImpl_Destroy,
+  TransactedSnapshotImpl_Invalidate,
+  TransactedSnapshotImpl_Flush,
+  TransactedSnapshotImpl_GetFilename,
+  TransactedSnapshotImpl_CreateDirEntry,
+  TransactedSnapshotImpl_WriteDirEntry,
+  TransactedSnapshotImpl_ReadDirEntry,
+  TransactedSnapshotImpl_DestroyDirEntry,
+  TransactedSnapshotImpl_StreamReadAt,
+  TransactedSnapshotImpl_StreamWriteAt,
+  TransactedSnapshotImpl_StreamSetSize,
+  TransactedSnapshotImpl_StreamLink,
+  TransactedSnapshotImpl_GetTransactionSig,
+  TransactedSnapshotImpl_SetTransactionSig,
+  TransactedSnapshotImpl_LockTransaction,
+  TransactedSnapshotImpl_UnlockTransaction
+};
 
-static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
-  IEnumSTATSTG*     iface,
-  REFIID            riid,
-  void**            ppvObject)
+static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
+  TransactedSnapshotImpl** result)
 {
-  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
-
-  if (ppvObject==0)
-    return E_INVALIDARG;
-
-  *ppvObject = 0;
+  HRESULT hr;
 
-  if (IsEqualGUID(&IID_IUnknown, riid) ||
-      IsEqualGUID(&IID_IEnumSTATSTG, riid))
+  *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
+  if (*result)
   {
-    *ppvObject = &This->IEnumSTATSTG_iface;
-    IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
-    return S_OK;
-  }
-
-  return E_NOINTERFACE;
-}
-
-static ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
-  IEnumSTATSTG* iface)
-{
-  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
-  return InterlockedIncrement(&This->ref);
-}
+    IStorage *scratch;
 
-static ULONG   WINAPI IEnumSTATSTGImpl_Release(
-  IEnumSTATSTG* iface)
-{
-  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+    (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
 
-  ULONG newRef;
+    /* This is OK because the property set storage functions use the IStorage functions. */
+    (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
+    (*result)->base.baseVtbl = &TransactedSnapshotImpl_BaseVtbl;
 
-  newRef = InterlockedDecrement(&This->ref);
+    list_init(&(*result)->base.strmHead);
 
-  if (newRef==0)
-  {
-    IEnumSTATSTGImpl_Destroy(This);
-  }
+    list_init(&(*result)->base.storageHead);
 
-  return newRef;
-}
+    (*result)->base.ref = 1;
 
-static HRESULT IEnumSTATSTGImpl_GetNextRef(
-  IEnumSTATSTGImpl* This,
-  DirRef *ref)
-{
-  DirRef result = DIRENTRY_NULL;
-  DirRef searchNode;
-  DirEntry entry;
-  HRESULT hr;
-  WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
+    (*result)->base.openFlags = parentStorage->openFlags;
 
-  hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
-    This->parentStorage->storageDirEntry, &entry);
-  searchNode = entry.dirRootEntry;
+    /* This cannot fail, except with E_NOTIMPL in which case we don't care */
+    StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
 
-  while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
-  {
-    hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
+    /* Create a new temporary storage to act as the scratch file. */
+    hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE,
+        0, &scratch);
+    (*result)->scratch = impl_from_IStorage(scratch);
 
     if (SUCCEEDED(hr))
     {
-      LONG diff = entryNameCmp( entry.name, This->name);
+        ULONG num_entries = 20;
 
-      if (diff <= 0)
-      {
-        searchNode = entry.rightChild;
-      }
-      else
-      {
-        result = searchNode;
-        memcpy(result_name, entry.name, sizeof(result_name));
-        searchNode = entry.leftChild;
-      }
-    }
-  }
+        (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
+        (*result)->entries_size = num_entries;
+        (*result)->firstFreeEntry = 0;
 
-  if (SUCCEEDED(hr))
-  {
-    *ref = result;
-    if (result != DIRENTRY_NULL)
-      memcpy(This->name, result_name, sizeof(result_name));
-  }
+        if ((*result)->entries)
+        {
+            /* parentStorage already has 1 reference, which we take over here. */
+            (*result)->transactedParent = parentStorage;
 
-  return hr;
-}
+            parentStorage->transactedChild = &(*result)->base;
 
-static HRESULT WINAPI IEnumSTATSTGImpl_Next(
-  IEnumSTATSTG* iface,
-  ULONG             celt,
-  STATSTG*          rgelt,
-  ULONG*            pceltFetched)
-{
-  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+            (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
+        }
+        else
+        {
+            IStorage_Release(scratch);
 
-  DirEntry    currentEntry;
-  STATSTG*    currentReturnStruct = rgelt;
-  ULONG       objectFetched       = 0;
-  DirRef      currentSearchNode;
-  HRESULT     hr=S_OK;
+            hr = E_OUTOFMEMORY;
+        }
+    }
 
-  if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
-    return E_INVALIDARG;
+    if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
 
-  if (This->parentStorage->reverted)
-    return STG_E_REVERTED;
+    return hr;
+  }
+  else
+    return E_OUTOFMEMORY;
+}
 
-  /*
-   * To avoid the special case, get another pointer to a ULONG value if
-   * the caller didn't supply one.
-   */
-  if (pceltFetched==0)
-    pceltFetched = &objectFetched;
 
-  /*
-   * Start the iteration, we will iterate until we hit the end of the
-   * linked list or until we hit the number of items to iterate through
-   */
-  *pceltFetched = 0;
+/************************************************************************
+ * TransactedSharedImpl implementation
+ ***********************************************************************/
 
-  while ( *pceltFetched < celt )
+static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
+{
+  if (!This->reverted)
   {
-    hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
-
-    if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
-      break;
-
-    /*
-     * Read the entry from the storage.
-     */
-    StorageBaseImpl_ReadDirEntry(This->parentStorage,
-      currentSearchNode,
-      &currentEntry);
+    TRACE("Storage invalidated (stg=%p)\n", This);
 
-    /*
-     * Copy the information to the return buffer.
-     */
-    StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
-      currentReturnStruct,
-      &currentEntry,
-      STATFLAG_DEFAULT);
+    This->reverted = TRUE;
 
-    /*
-     * Step to the next item in the iteration
-     */
-    (*pceltFetched)++;
-    currentReturnStruct++;
+    StorageBaseImpl_DeleteAll(This);
   }
+}
 
-  if (SUCCEEDED(hr) && *pceltFetched != celt)
-    hr = S_FALSE;
+static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
 
-  return hr;
+  TransactedSharedImpl_Invalidate(&This->base);
+  IStorage_Release(&This->transactedParent->IStorage_iface);
+  IStorage_Release(&This->scratch->base.IStorage_iface);
+  HeapFree(GetProcessHeap(), 0, This);
 }
 
+static HRESULT TransactedSharedImpl_Flush(StorageBaseImpl* iface)
+{
+  /* We only need to flush when committing. */
+  return S_OK;
+}
 
-static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
-  IEnumSTATSTG* iface,
-  ULONG             celt)
+static HRESULT TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
 {
-  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+  TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
 
-  ULONG       objectFetched = 0;
-  DirRef      currentSearchNode;
-  HRESULT     hr=S_OK;
+  return StorageBaseImpl_GetFilename(This->transactedParent, result);
+}
 
-  if (This->parentStorage->reverted)
-    return STG_E_REVERTED;
+static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
+  const DirEntry *newData, DirRef *index)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  while ( (objectFetched < celt) )
-  {
-    hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
+  return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
+    newData, index);
+}
 
-    if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
-      break;
+static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
+  DirRef index, const DirEntry *data)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-    objectFetched++;
-  }
+  return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
+    index, data);
+}
 
-  if (SUCCEEDED(hr) && objectFetched != celt)
-    return S_FALSE;
+static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
+  DirRef index, DirEntry *data)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  return hr;
+  return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
+    index, data);
 }
 
-static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
-  IEnumSTATSTG* iface)
+static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
+  DirRef index)
 {
-  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  if (This->parentStorage->reverted)
-    return STG_E_REVERTED;
+  return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
+    index);
+}
 
-  This->name[0] = 0;
+static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
+  DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  return S_OK;
+  return StorageBaseImpl_StreamReadAt(&This->scratch->base,
+    index, offset, size, buffer, bytesRead);
 }
 
-static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
-  IEnumSTATSTG* iface,
-  IEnumSTATSTG**    ppenum)
+static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
+  DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
 {
-  IEnumSTATSTGImpl* const This = impl_from_IEnumSTATSTG(iface);
-  IEnumSTATSTGImpl* newClone;
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  if (This->parentStorage->reverted)
-    return STG_E_REVERTED;
+  return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
+    index, offset, size, buffer, bytesWritten);
+}
 
-  if (ppenum==0)
-    return E_INVALIDARG;
+static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
+  DirRef index, ULARGE_INTEGER newsize)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
-               This->storageDirEntry);
-  if (!newClone)
-  {
-    *ppenum = NULL;
-    return E_OUTOFMEMORY;
-  }
+  return StorageBaseImpl_StreamSetSize(&This->scratch->base,
+    index, newsize);
+}
 
-  /*
-   * The new clone enumeration must point to the same current node as
-   * the old one.
-   */
-  memcpy(newClone->name, This->name, sizeof(newClone->name));
+static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
+  DirRef dst, DirRef src)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  *ppenum = &newClone->IEnumSTATSTG_iface;
+  return StorageBaseImpl_StreamLink(&This->scratch->base,
+    dst, src);
+}
 
-  return S_OK;
+static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
+  ULONG* result, BOOL refresh)
+{
+  return E_NOTIMPL;
 }
 
-/*
- * Virtual function table for the IEnumSTATSTGImpl class.
- */
-static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
+static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
+  ULONG value)
 {
-    IEnumSTATSTGImpl_QueryInterface,
-    IEnumSTATSTGImpl_AddRef,
-    IEnumSTATSTGImpl_Release,
-    IEnumSTATSTGImpl_Next,
-    IEnumSTATSTGImpl_Skip,
-    IEnumSTATSTGImpl_Reset,
-    IEnumSTATSTGImpl_Clone
-};
+  return E_NOTIMPL;
+}
 
-/******************************************************************************
-** IEnumSTATSTGImpl implementation
-*/
+static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
+{
+  return E_NOTIMPL;
+}
 
-static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
-  StorageBaseImpl* parentStorage,
-  DirRef         storageDirEntry)
+static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
 {
-  IEnumSTATSTGImpl* newEnumeration;
+  return E_NOTIMPL;
+}
 
-  newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
+static HRESULT WINAPI TransactedSharedImpl_Commit(
+  IStorage*            iface,
+  DWORD                  grfCommitFlags)  /* [in] */
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
+  DirRef new_storage_ref, prev_storage_ref;
+  DirEntry src_data, dst_data;
+  HRESULT hr;
+  ULONG transactionSig;
 
-  if (newEnumeration)
+  TRACE("(%p,%x)\n", iface, grfCommitFlags);
+
+  /* Cannot commit a read-only transacted storage */
+  if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
+    return STG_E_ACCESSDENIED;
+
+  hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
+  if (hr == E_NOTIMPL) hr = S_OK;
+  if (SUCCEEDED(hr))
   {
-    newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
-    newEnumeration->ref = 1;
-    newEnumeration->name[0] = 0;
+    hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
+    if (SUCCEEDED(hr))
+    {
+      if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
+        hr = STG_E_NOTCURRENT;
 
-    /*
-     * We want to nail-down the reference to the storage in case the
-     * enumeration out-lives the storage in the client application.
-     */
-    newEnumeration->parentStorage = parentStorage;
-    IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
+      if (SUCCEEDED(hr))
+        hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
+    }
+    else if (hr == E_NOTIMPL)
+      hr = S_OK;
 
-    newEnumeration->storageDirEntry = storageDirEntry;
+    if (SUCCEEDED(hr))
+      hr = StorageBaseImpl_ReadDirEntry(&This->scratch->base, This->scratch->base.storageDirEntry, &src_data);
+
+    /* FIXME: If we're current, we should be able to copy only the changes in scratch. */
+    if (SUCCEEDED(hr))
+      hr = StorageBaseImpl_DupStorageTree(This->transactedParent, &new_storage_ref, &This->scratch->base, src_data.dirRootEntry);
+
+    if (SUCCEEDED(hr))
+      hr = StorageBaseImpl_Flush(This->transactedParent);
+
+    if (SUCCEEDED(hr))
+      hr = StorageBaseImpl_ReadDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
+
+    if (SUCCEEDED(hr))
+    {
+      prev_storage_ref = dst_data.dirRootEntry;
+      dst_data.dirRootEntry = new_storage_ref;
+      dst_data.clsid = src_data.clsid;
+      dst_data.ctime = src_data.ctime;
+      dst_data.mtime = src_data.mtime;
+      hr = StorageBaseImpl_WriteDirEntry(This->transactedParent, This->transactedParent->storageDirEntry, &dst_data);
+    }
+
+    if (SUCCEEDED(hr))
+    {
+      /* Try to flush after updating the root storage, but if the flush fails, keep
+       * going, on the theory that it'll either succeed later or the subsequent
+       * writes will fail. */
+      StorageBaseImpl_Flush(This->transactedParent);
+
+      hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
+    }
+
+    if (SUCCEEDED(hr))
+      hr = StorageBaseImpl_Flush(This->transactedParent);
+
+    StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
+
+    if (SUCCEEDED(hr))
+      hr = IStorage_Commit(&This->scratch->base.IStorage_iface, STGC_DEFAULT);
+
+    if (SUCCEEDED(hr))
+    {
+      This->lastTransactionSig = transactionSig+1;
+    }
   }
 
-  return newEnumeration;
+  return hr;
 }
 
-/*
- * Virtual function table for the StorageInternalImpl class.
- */
-static const IStorageVtbl StorageInternalImpl_Vtbl =
+static HRESULT WINAPI TransactedSharedImpl_Revert(
+  IStorage*            iface)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
+
+  TRACE("(%p)\n", iface);
+
+  /* Destroy the open objects. */
+  StorageBaseImpl_DeleteAll(&This->base);
+
+  return IStorage_Revert(&This->scratch->base.IStorage_iface);
+}
+
+static const IStorageVtbl TransactedSharedImpl_Vtbl =
 {
     StorageBaseImpl_QueryInterface,
     StorageBaseImpl_AddRef,
@@ -6579,8 +6574,8 @@ static const IStorageVtbl StorageInternalImpl_Vtbl =
     StorageBaseImpl_OpenStorage,
     StorageBaseImpl_CopyTo,
     StorageBaseImpl_MoveElementTo,
-    StorageInternalImpl_Commit,
-    StorageInternalImpl_Revert,
+    TransactedSharedImpl_Commit,
+    TransactedSharedImpl_Revert,
     StorageBaseImpl_EnumElements,
     StorageBaseImpl_DestroyElement,
     StorageBaseImpl_RenameElement,
@@ -6590,75 +6585,156 @@ static const IStorageVtbl StorageInternalImpl_Vtbl =
     StorageBaseImpl_Stat
 };
 
-static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
+static const StorageBaseImplVtbl TransactedSharedImpl_BaseVtbl =
 {
-  StorageInternalImpl_Destroy,
-  StorageInternalImpl_Invalidate,
-  StorageInternalImpl_Flush,
-  StorageInternalImpl_GetFilename,
-  StorageInternalImpl_CreateDirEntry,
-  StorageInternalImpl_WriteDirEntry,
-  StorageInternalImpl_ReadDirEntry,
-  StorageInternalImpl_DestroyDirEntry,
-  StorageInternalImpl_StreamReadAt,
-  StorageInternalImpl_StreamWriteAt,
-  StorageInternalImpl_StreamSetSize,
-  StorageInternalImpl_StreamLink,
-  StorageInternalImpl_GetTransactionSig,
-  StorageInternalImpl_SetTransactionSig,
-  StorageInternalImpl_LockTransaction,
-  StorageInternalImpl_UnlockTransaction
+  TransactedSharedImpl_Destroy,
+  TransactedSharedImpl_Invalidate,
+  TransactedSharedImpl_Flush,
+  TransactedSharedImpl_GetFilename,
+  TransactedSharedImpl_CreateDirEntry,
+  TransactedSharedImpl_WriteDirEntry,
+  TransactedSharedImpl_ReadDirEntry,
+  TransactedSharedImpl_DestroyDirEntry,
+  TransactedSharedImpl_StreamReadAt,
+  TransactedSharedImpl_StreamWriteAt,
+  TransactedSharedImpl_StreamSetSize,
+  TransactedSharedImpl_StreamLink,
+  TransactedSharedImpl_GetTransactionSig,
+  TransactedSharedImpl_SetTransactionSig,
+  TransactedSharedImpl_LockTransaction,
+  TransactedSharedImpl_UnlockTransaction
 };
 
-/******************************************************************************
-** StorageInternalImpl implementation
-*/
+static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
+  TransactedSharedImpl** result)
+{
+  HRESULT hr;
 
-static StorageInternalImpl* StorageInternalImpl_Construct(
-  StorageBaseImpl* parentStorage,
-  DWORD        openFlags,
-  DirRef       storageDirEntry)
-{
-  StorageInternalImpl* newStorage;
+  *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
+  if (*result)
+  {
+    IStorage *scratch;
 
-  newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
+    (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
 
-  if (newStorage!=0)
-  {
-    list_init(&newStorage->base.strmHead);
+    /* This is OK because the property set storage functions use the IStorage functions. */
+    (*result)->base.IPropertySetStorage_iface.lpVtbl = parentStorage->IPropertySetStorage_iface.lpVtbl;
+    (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
 
-    list_init(&newStorage->base.storageHead);
+    list_init(&(*result)->base.strmHead);
 
-    /*
-     * Initialize the virtual function table.
-     */
-    newStorage->base.IStorage_iface.lpVtbl = &StorageInternalImpl_Vtbl;
-    newStorage->base.IPropertySetStorage_iface.lpVtbl = &IPropertySetStorage_Vtbl;
-    newStorage->base.baseVtbl = &StorageInternalImpl_BaseVtbl;
-    newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
+    list_init(&(*result)->base.storageHead);
 
-    newStorage->base.reverted = FALSE;
+    (*result)->base.ref = 1;
 
-    newStorage->base.ref = 1;
+    (*result)->base.openFlags = parentStorage->openFlags;
 
-    newStorage->parentStorage = parentStorage;
+    hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
 
-    /*
-     * Keep a reference to the directory entry of this storage
-     */
-    newStorage->base.storageDirEntry = storageDirEntry;
+    if (SUCCEEDED(hr))
+    {
+      STGOPTIONS stgo;
 
-    newStorage->base.create = FALSE;
+      /* This cannot fail, except with E_NOTIMPL in which case we don't care */
+      StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
 
-    return newStorage;
+      stgo.usVersion = 1;
+      stgo.reserved = 0;
+      stgo.ulSectorSize = 4096;
+      stgo.pwcsTemplateFile = NULL;
+
+      /* Create a new temporary storage to act as the scratch file. */
+      hr = StgCreateStorageEx(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_DELETEONRELEASE|STGM_TRANSACTED,
+          STGFMT_DOCFILE, 0, &stgo, NULL, &IID_IStorage, (void**)&scratch);
+      (*result)->scratch = (TransactedSnapshotImpl*)impl_from_IStorage(scratch);
+
+      if (SUCCEEDED(hr))
+      {
+        hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
+          parentStorage, parentStorage->storageDirEntry);
+
+        if (SUCCEEDED(hr))
+        {
+          hr = IStorage_Commit(scratch, STGC_DEFAULT);
+
+          (*result)->base.storageDirEntry = (*result)->scratch->base.storageDirEntry;
+          (*result)->transactedParent = parentStorage;
+        }
+
+        if (FAILED(hr))
+          IStorage_Release(scratch);
+      }
+
+      StorageBaseImpl_UnlockTransaction(parentStorage, FALSE);
+    }
+
+    if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
+
+    return hr;
+  }
+  else
+    return E_OUTOFMEMORY;
+}
+
+static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
+  BOOL toplevel, StorageBaseImpl** result)
+{
+  static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
+
+  if (parentStorage->openFlags & fixme_flags)
+  {
+    fixme_flags &= ~parentStorage->openFlags;
+    FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
   }
 
-  return 0;
+  if (toplevel && !(parentStorage->openFlags & STGM_NOSNAPSHOT) &&
+      STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_DENY_WRITE &&
+      STGM_SHARE_MODE(parentStorage->openFlags) != STGM_SHARE_EXCLUSIVE)
+  {
+    /* Need to create a temp file for the snapshot */
+    return TransactedSharedImpl_Construct(parentStorage, (TransactedSharedImpl**)result);
+  }
+
+  return TransactedSnapshotImpl_Construct(parentStorage,
+    (TransactedSnapshotImpl**)result);
 }
 
-/******************************************************************************
-** StorageUtl implementation
-*/
+static HRESULT Storage_Construct(
+  HANDLE       hFile,
+  LPCOLESTR    pwcsName,
+  ILockBytes*  pLkbyt,
+  DWORD        openFlags,
+  BOOL         fileBased,
+  BOOL         create,
+  ULONG        sector_size,
+  StorageBaseImpl** result)
+{
+  StorageImpl *newStorage;
+  StorageBaseImpl *newTransactedStorage;
+  HRESULT hr;
+
+  hr = StorageImpl_Construct(hFile, pwcsName, pLkbyt, openFlags, fileBased, create, sector_size, &newStorage);
+  if (FAILED(hr)) goto end;
+
+  if (openFlags & STGM_TRANSACTED)
+  {
+    hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
+    if (FAILED(hr))
+      IStorage_Release(&newStorage->base.IStorage_iface);
+    else
+      *result = newTransactedStorage;
+  }
+  else
+    *result = &newStorage->base;
+
+end:
+  return hr;
+}
+
+
+/************************************************************************
+ * StorageUtl helper functions
+ ***********************************************************************/
 
 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
 {
@@ -6789,9 +6865,40 @@ void StorageUtl_CopyDirEntryToSTATSTG(
   destination->reserved          = 0;
 }
 
+
+/************************************************************************
+ * BlockChainStream implementation
+ ***********************************************************************/
+
 /******************************************************************************
-** BlockChainStream implementation
-*/
+ *      BlockChainStream_GetHeadOfChain
+ *
+ * Returns the head of this stream chain.
+ * Some special chains don't have directory entries, their heads are kept in
+ * This->headOfStreamPlaceHolder.
+ *
+ */
+static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
+{
+  DirEntry  chainEntry;
+  HRESULT   hr;
+
+  if (This->headOfStreamPlaceHolder != 0)
+    return *(This->headOfStreamPlaceHolder);
+
+  if (This->ownerDirEntry != DIRENTRY_NULL)
+  {
+    hr = StorageImpl_ReadDirEntry(
+                      This->parentStorage,
+                      This->ownerDirEntry,
+                      &chainEntry);
+
+    if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
+      return chainEntry.startingBlock;
+  }
+
+  return BLOCK_END_OF_CHAIN;
+}
 
 /* Read and save the index of all blocks in this stream. */
 HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
@@ -7006,249 +7113,26 @@ void BlockChainStream_Destroy(BlockChainStream* This)
 }
 
 /******************************************************************************
- *      BlockChainStream_GetHeadOfChain
- *
- * Returns the head of this stream chain.
- * Some special chains don't have directory entries, their heads are kept in
- * This->headOfStreamPlaceHolder.
- *
- */
-static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
-{
-  DirEntry  chainEntry;
-  HRESULT   hr;
-
-  if (This->headOfStreamPlaceHolder != 0)
-    return *(This->headOfStreamPlaceHolder);
-
-  if (This->ownerDirEntry != DIRENTRY_NULL)
-  {
-    hr = StorageImpl_ReadDirEntry(
-                      This->parentStorage,
-                      This->ownerDirEntry,
-                      &chainEntry);
-
-    if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
-      return chainEntry.startingBlock;
-  }
-
-  return BLOCK_END_OF_CHAIN;
-}
-
-/******************************************************************************
- *       BlockChainStream_GetCount
- *
- * Returns the number of blocks that comprises this chain.
- * This is not the size of the stream as the last block may not be full!
- */
-static ULONG BlockChainStream_GetCount(BlockChainStream* This)
-{
-  return This->numBlocks;
-}
-
-/******************************************************************************
- *      BlockChainStream_ReadAt
+ *      BlockChainStream_Shrink
  *
- * Reads a specified number of bytes from this chain at the specified offset.
- * bytesRead may be NULL.
- * Failure will be returned if the specified number of bytes has not been read.
+ * Shrinks this chain in the big block depot.
  */
-HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
-  ULARGE_INTEGER offset,
-  ULONG          size,
-  void*          buffer,
-  ULONG*         bytesRead)
+static BOOL BlockChainStream_Shrink(BlockChainStream* This,
+                                    ULARGE_INTEGER    newSize)
 {
-  ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
-  ULONG offsetInBlock     = offset.QuadPart % This->parentStorage->bigBlockSize;
-  ULONG bytesToReadInBuffer;
   ULONG blockIndex;
-  BYTE* bufferWalker;
-  ULARGE_INTEGER stream_size;
-  HRESULT hr;
-  BlockChainBlock *cachedBlock;
-
-  TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
+  ULONG numBlocks;
+  int i;
 
   /*
-   * Find the first block in the stream that contains part of the buffer.
+   * Figure out how many blocks are needed to contain the new size
    */
-  blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
-
-  *bytesRead   = 0;
-
-  stream_size = BlockChainStream_GetSize(This);
-  if (stream_size.QuadPart > offset.QuadPart)
-    size = min(stream_size.QuadPart - offset.QuadPart, size);
-  else
-    return S_OK;
+  numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
 
-  /*
-   * Start reading the buffer.
-   */
-  bufferWalker = buffer;
+  if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
+    numBlocks++;
 
-  while (size > 0)
-  {
-    ULARGE_INTEGER ulOffset;
-    DWORD bytesReadAt;
-
-    /*
-     * Calculate how many bytes we can copy from this big block.
-     */
-    bytesToReadInBuffer =
-      min(This->parentStorage->bigBlockSize - offsetInBlock, size);
-
-    hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
-
-    if (FAILED(hr))
-      return hr;
-
-    if (!cachedBlock)
-    {
-      /* Not in cache, and we're going to read past the end of the block. */
-      ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
-                               offsetInBlock;
-
-      StorageImpl_ReadAt(This->parentStorage,
-           ulOffset,
-           bufferWalker,
-           bytesToReadInBuffer,
-           &bytesReadAt);
-    }
-    else
-    {
-      if (!cachedBlock->read)
-      {
-        ULONG read;
-        if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
-          return STG_E_READFAULT;
-
-        cachedBlock->read = TRUE;
-      }
-
-      memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
-      bytesReadAt = bytesToReadInBuffer;
-    }
-
-    blockNoInSequence++;
-    bufferWalker += bytesReadAt;
-    size         -= bytesReadAt;
-    *bytesRead   += bytesReadAt;
-    offsetInBlock = 0;  /* There is no offset on the next block */
-
-    if (bytesToReadInBuffer != bytesReadAt)
-        break;
-  }
-
-  return S_OK;
-}
-
-/******************************************************************************
- *      BlockChainStream_WriteAt
- *
- * Writes the specified number of bytes to this chain at the specified offset.
- * Will fail if not all specified number of bytes have been written.
- */
-HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
-  ULARGE_INTEGER    offset,
-  ULONG             size,
-  const void*       buffer,
-  ULONG*            bytesWritten)
-{
-  ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
-  ULONG offsetInBlock     = offset.QuadPart % This->parentStorage->bigBlockSize;
-  ULONG bytesToWrite;
-  ULONG blockIndex;
-  const BYTE* bufferWalker;
-  HRESULT hr;
-  BlockChainBlock *cachedBlock;
-
-  *bytesWritten   = 0;
-  bufferWalker = buffer;
-
-  while (size > 0)
-  {
-    ULARGE_INTEGER ulOffset;
-    DWORD bytesWrittenAt;
-
-    /*
-     * Calculate how many bytes we can copy to this big block.
-     */
-    bytesToWrite =
-      min(This->parentStorage->bigBlockSize - offsetInBlock, size);
-
-    hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
-
-    /* BlockChainStream_SetSize should have already been called to ensure we have
-     * enough blocks in the chain to write into */
-    if (FAILED(hr))
-    {
-      ERR("not enough blocks in chain to write data\n");
-      return hr;
-    }
-
-    if (!cachedBlock)
-    {
-      /* Not in cache, and we're going to write past the end of the block. */
-      ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
-                               offsetInBlock;
-
-      StorageImpl_WriteAt(This->parentStorage,
-           ulOffset,
-           bufferWalker,
-           bytesToWrite,
-           &bytesWrittenAt);
-    }
-    else
-    {
-      if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
-      {
-        ULONG read;
-        if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
-          return STG_E_READFAULT;
-      }
-
-      memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
-      bytesWrittenAt = bytesToWrite;
-      cachedBlock->read = TRUE;
-      cachedBlock->dirty = TRUE;
-    }
-
-    blockNoInSequence++;
-    bufferWalker  += bytesWrittenAt;
-    size          -= bytesWrittenAt;
-    *bytesWritten += bytesWrittenAt;
-    offsetInBlock  = 0;      /* There is no offset on the next block */
-
-    if (bytesWrittenAt != bytesToWrite)
-      break;
-  }
-
-  return (size == 0) ? S_OK : STG_E_WRITEFAULT;
-}
-
-/******************************************************************************
- *      BlockChainStream_Shrink
- *
- * Shrinks this chain in the big block depot.
- */
-static BOOL BlockChainStream_Shrink(BlockChainStream* This,
-                                    ULARGE_INTEGER    newSize)
-{
-  ULONG blockIndex;
-  ULONG numBlocks;
-  int i;
-
-  /*
-   * Figure out how many blocks are needed to contain the new size
-   */
-  numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
-
-  if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
-    numBlocks++;
-
-  if (numBlocks)
+  if (numBlocks)
   {
     /*
      * Go to the new end of chain
@@ -7435,36 +7319,6 @@ static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
   return TRUE;
 }
 
-/******************************************************************************
- *      BlockChainStream_SetSize
- *
- * Sets the size of this stream. The big block depot will be updated.
- * The file will grow if we grow the chain.
- *
- * TODO: Free the actual blocks in the file when we shrink the chain.
- *       Currently, the blocks are still in the file. So the file size
- *       doesn't shrink even if we shrink streams.
- */
-BOOL BlockChainStream_SetSize(
-  BlockChainStream* This,
-  ULARGE_INTEGER    newSize)
-{
-  ULARGE_INTEGER size = BlockChainStream_GetSize(This);
-
-  if (newSize.QuadPart == size.QuadPart)
-    return TRUE;
-
-  if (newSize.QuadPart < size.QuadPart)
-  {
-    BlockChainStream_Shrink(This, newSize);
-  }
-  else
-  {
-    BlockChainStream_Enlarge(This, newSize);
-  }
-
-  return TRUE;
-}
 
 /******************************************************************************
  *      BlockChainStream_GetSize
@@ -7505,68 +7359,282 @@ static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
 }
 
 /******************************************************************************
-** SmallBlockChainStream implementation
-*/
-
-SmallBlockChainStream* SmallBlockChainStream_Construct(
-  StorageImpl* parentStorage,
-  ULONG*         headOfStreamPlaceHolder,
-  DirRef         dirEntry)
-{
-  SmallBlockChainStream* newStream;
-
-  newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
-
-  newStream->parentStorage      = parentStorage;
-  newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
-  newStream->ownerDirEntry      = dirEntry;
-
-  return newStream;
-}
-
-void SmallBlockChainStream_Destroy(
-  SmallBlockChainStream* This)
-{
-  HeapFree(GetProcessHeap(), 0, This);
-}
-
-/******************************************************************************
- *      SmallBlockChainStream_GetHeadOfChain
+ *      BlockChainStream_SetSize
  *
- * Returns the head of this chain of small blocks.
+ * Sets the size of this stream. The big block depot will be updated.
+ * The file will grow if we grow the chain.
+ *
+ * TODO: Free the actual blocks in the file when we shrink the chain.
+ *       Currently, the blocks are still in the file. So the file size
+ *       doesn't shrink even if we shrink streams.
  */
-static ULONG SmallBlockChainStream_GetHeadOfChain(
-  SmallBlockChainStream* This)
+BOOL BlockChainStream_SetSize(
+  BlockChainStream* This,
+  ULARGE_INTEGER    newSize)
 {
-  DirEntry  chainEntry;
-  HRESULT   hr;
+  ULARGE_INTEGER size = BlockChainStream_GetSize(This);
 
-  if (This->headOfStreamPlaceHolder != NULL)
-    return *(This->headOfStreamPlaceHolder);
+  if (newSize.QuadPart == size.QuadPart)
+    return TRUE;
 
-  if (This->ownerDirEntry)
+  if (newSize.QuadPart < size.QuadPart)
   {
-    hr = StorageImpl_ReadDirEntry(
-                      This->parentStorage,
-                      This->ownerDirEntry,
-                      &chainEntry);
-
-    if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
-      return chainEntry.startingBlock;
+    BlockChainStream_Shrink(This, newSize);
+  }
+  else
+  {
+    BlockChainStream_Enlarge(This, newSize);
   }
 
-  return BLOCK_END_OF_CHAIN;
+  return TRUE;
 }
 
 /******************************************************************************
- *      SmallBlockChainStream_GetNextBlockInChain
- *
- * Returns the index of the next small block in this chain.
+ *      BlockChainStream_ReadAt
  *
- * Return Values:
- *    - BLOCK_END_OF_CHAIN: end of this chain
- *    - BLOCK_UNUSED: small block 'blockIndex' is free
- */
+ * Reads a specified number of bytes from this chain at the specified offset.
+ * bytesRead may be NULL.
+ * Failure will be returned if the specified number of bytes has not been read.
+ */
+HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
+  ULARGE_INTEGER offset,
+  ULONG          size,
+  void*          buffer,
+  ULONG*         bytesRead)
+{
+  ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
+  ULONG offsetInBlock     = offset.QuadPart % This->parentStorage->bigBlockSize;
+  ULONG bytesToReadInBuffer;
+  ULONG blockIndex;
+  BYTE* bufferWalker;
+  ULARGE_INTEGER stream_size;
+  HRESULT hr;
+  BlockChainBlock *cachedBlock;
+
+  TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
+
+  /*
+   * Find the first block in the stream that contains part of the buffer.
+   */
+  blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
+
+  *bytesRead   = 0;
+
+  stream_size = BlockChainStream_GetSize(This);
+  if (stream_size.QuadPart > offset.QuadPart)
+    size = min(stream_size.QuadPart - offset.QuadPart, size);
+  else
+    return S_OK;
+
+  /*
+   * Start reading the buffer.
+   */
+  bufferWalker = buffer;
+
+  while (size > 0)
+  {
+    ULARGE_INTEGER ulOffset;
+    DWORD bytesReadAt;
+
+    /*
+     * Calculate how many bytes we can copy from this big block.
+     */
+    bytesToReadInBuffer =
+      min(This->parentStorage->bigBlockSize - offsetInBlock, size);
+
+    hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer);
+
+    if (FAILED(hr))
+      return hr;
+
+    if (!cachedBlock)
+    {
+      /* Not in cache, and we're going to read past the end of the block. */
+      ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
+                               offsetInBlock;
+
+      StorageImpl_ReadAt(This->parentStorage,
+           ulOffset,
+           bufferWalker,
+           bytesToReadInBuffer,
+           &bytesReadAt);
+    }
+    else
+    {
+      if (!cachedBlock->read)
+      {
+        ULONG read;
+        if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
+          return STG_E_READFAULT;
+
+        cachedBlock->read = TRUE;
+      }
+
+      memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer);
+      bytesReadAt = bytesToReadInBuffer;
+    }
+
+    blockNoInSequence++;
+    bufferWalker += bytesReadAt;
+    size         -= bytesReadAt;
+    *bytesRead   += bytesReadAt;
+    offsetInBlock = 0;  /* There is no offset on the next block */
+
+    if (bytesToReadInBuffer != bytesReadAt)
+        break;
+  }
+
+  return S_OK;
+}
+
+/******************************************************************************
+ *      BlockChainStream_WriteAt
+ *
+ * Writes the specified number of bytes to this chain at the specified offset.
+ * Will fail if not all specified number of bytes have been written.
+ */
+HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
+  ULARGE_INTEGER    offset,
+  ULONG             size,
+  const void*       buffer,
+  ULONG*            bytesWritten)
+{
+  ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
+  ULONG offsetInBlock     = offset.QuadPart % This->parentStorage->bigBlockSize;
+  ULONG bytesToWrite;
+  ULONG blockIndex;
+  const BYTE* bufferWalker;
+  HRESULT hr;
+  BlockChainBlock *cachedBlock;
+
+  *bytesWritten   = 0;
+  bufferWalker = buffer;
+
+  while (size > 0)
+  {
+    ULARGE_INTEGER ulOffset;
+    DWORD bytesWrittenAt;
+
+    /*
+     * Calculate how many bytes we can copy to this big block.
+     */
+    bytesToWrite =
+      min(This->parentStorage->bigBlockSize - offsetInBlock, size);
+
+    hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite);
+
+    /* BlockChainStream_SetSize should have already been called to ensure we have
+     * enough blocks in the chain to write into */
+    if (FAILED(hr))
+    {
+      ERR("not enough blocks in chain to write data\n");
+      return hr;
+    }
+
+    if (!cachedBlock)
+    {
+      /* Not in cache, and we're going to write past the end of the block. */
+      ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
+                               offsetInBlock;
+
+      StorageImpl_WriteAt(This->parentStorage,
+           ulOffset,
+           bufferWalker,
+           bytesToWrite,
+           &bytesWrittenAt);
+    }
+    else
+    {
+      if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize)
+      {
+        ULONG read;
+        if (FAILED(StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data, &read)) && !read)
+          return STG_E_READFAULT;
+      }
+
+      memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite);
+      bytesWrittenAt = bytesToWrite;
+      cachedBlock->read = TRUE;
+      cachedBlock->dirty = TRUE;
+    }
+
+    blockNoInSequence++;
+    bufferWalker  += bytesWrittenAt;
+    size          -= bytesWrittenAt;
+    *bytesWritten += bytesWrittenAt;
+    offsetInBlock  = 0;      /* There is no offset on the next block */
+
+    if (bytesWrittenAt != bytesToWrite)
+      break;
+  }
+
+  return (size == 0) ? S_OK : STG_E_WRITEFAULT;
+}
+
+
+/************************************************************************
+ * SmallBlockChainStream implementation
+ ***********************************************************************/
+
+SmallBlockChainStream* SmallBlockChainStream_Construct(
+  StorageImpl* parentStorage,
+  ULONG*         headOfStreamPlaceHolder,
+  DirRef         dirEntry)
+{
+  SmallBlockChainStream* newStream;
+
+  newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
+
+  newStream->parentStorage      = parentStorage;
+  newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
+  newStream->ownerDirEntry      = dirEntry;
+
+  return newStream;
+}
+
+void SmallBlockChainStream_Destroy(
+  SmallBlockChainStream* This)
+{
+  HeapFree(GetProcessHeap(), 0, This);
+}
+
+/******************************************************************************
+ *      SmallBlockChainStream_GetHeadOfChain
+ *
+ * Returns the head of this chain of small blocks.
+ */
+static ULONG SmallBlockChainStream_GetHeadOfChain(
+  SmallBlockChainStream* This)
+{
+  DirEntry  chainEntry;
+  HRESULT   hr;
+
+  if (This->headOfStreamPlaceHolder != NULL)
+    return *(This->headOfStreamPlaceHolder);
+
+  if (This->ownerDirEntry)
+  {
+    hr = StorageImpl_ReadDirEntry(
+                      This->parentStorage,
+                      This->ownerDirEntry,
+                      &chainEntry);
+
+    if (SUCCEEDED(hr) && chainEntry.startingBlock < BLOCK_FIRST_SPECIAL)
+      return chainEntry.startingBlock;
+  }
+
+  return BLOCK_END_OF_CHAIN;
+}
+
+/******************************************************************************
+ *      SmallBlockChainStream_GetNextBlockInChain
+ *
+ * Returns the index of the next small block in this chain.
+ *
+ * Return Values:
+ *    - BLOCK_END_OF_CHAIN: end of this chain
+ *    - BLOCK_UNUSED: small block 'blockIndex' is free
+ */
 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
   SmallBlockChainStream* This,
   ULONG                  blockIndex,
@@ -8203,6 +8271,11 @@ static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
   return chainEntry.size;
 }
 
+
+/************************************************************************
+ * Miscellaneous storage functions
+ ***********************************************************************/
+
 static HRESULT create_storagefile(
   LPCOLESTR pwcsName,
   DWORD       grfMode,
@@ -8385,18 +8458,18 @@ HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD st
     if (stgfmt != STGFMT_FILE && grfAttrs != 0)
     {
         ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
-        return STG_E_INVALIDPARAMETER;  
+        return STG_E_INVALIDPARAMETER;
     }
 
     if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
     {
         ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
-        return STG_E_INVALIDPARAMETER;  
+        return STG_E_INVALIDPARAMETER;
     }
 
     if (stgfmt == STGFMT_FILE)
     {
-        ERR("Cannot use STGFMT_FILE - this is NTFS only\n");  
+        ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
         return STG_E_INVALIDPARAMETER;
     }
 
@@ -8437,15 +8510,15 @@ HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgf
     if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
     {
         ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
-        return STG_E_INVALIDPARAMETER;  
+        return STG_E_INVALIDPARAMETER;
     }
 
     switch (stgfmt)
     {
     case STGFMT_FILE:
-        ERR("Cannot use STGFMT_FILE - this is NTFS only\n");  
+        ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
         return STG_E_INVALIDPARAMETER;
-        
+
     case STGFMT_STORAGE:
         break;
 
@@ -8453,7 +8526,7 @@ HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgf
         if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
         {
             ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
-            return STG_E_INVALIDPARAMETER;  
+            return STG_E_INVALIDPARAMETER;
         }
         FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
         break;
@@ -8694,393 +8767,647 @@ HRESULT WINAPI StgCreateDocfileOnILockBytes(
     return hr;
   }
 
-  *ppstgOpen = &newStorage->IStorage_iface;
+  *ppstgOpen = &newStorage->IStorage_iface;
+
+  return hr;
+}
+
+/******************************************************************************
+ *    StgOpenStorageOnILockBytes    [OLE32.@]
+ */
+HRESULT WINAPI StgOpenStorageOnILockBytes(
+      ILockBytes *plkbyt,
+      IStorage *pstgPriority,
+      DWORD grfMode,
+      SNB snbExclude,
+      DWORD reserved,
+      IStorage **ppstgOpen)
+{
+  StorageBaseImpl* newStorage = 0;
+  HRESULT        hr = S_OK;
+
+  if ((plkbyt == 0) || (ppstgOpen == 0))
+    return STG_E_INVALIDPOINTER;
+
+  if ( FAILED( validateSTGM(grfMode) ))
+    return STG_E_INVALIDFLAG;
+
+  *ppstgOpen = 0;
+
+  /*
+   * Allocate and initialize the new IStorage object.
+   */
+  hr = Storage_Construct(
+         0,
+         0,
+         plkbyt,
+         grfMode,
+         FALSE,
+         FALSE,
+         512,
+         &newStorage);
+
+  if (FAILED(hr))
+  {
+    return hr;
+  }
+
+  *ppstgOpen = &newStorage->IStorage_iface;
+
+  return hr;
+}
+
+/******************************************************************************
+ *              StgSetTimes [ole32.@]
+ *              StgSetTimes [OLE32.@]
+ *
+ *
+ */
+HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
+                           FILETIME const *patime, FILETIME const *pmtime)
+{
+  IStorage *stg = NULL;
+  HRESULT r;
+
+  TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
+
+  r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
+                     0, 0, &stg);
+  if( SUCCEEDED(r) )
+  {
+    r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
+    IStorage_Release(stg);
+  }
+
+  return r;
+}
+
+/******************************************************************************
+ *              StgIsStorageILockBytes        [OLE32.@]
+ *
+ * Determines if the ILockBytes contains a storage object.
+ */
+HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
+{
+  BYTE sig[sizeof(STORAGE_magic)];
+  ULARGE_INTEGER offset;
+  ULONG read = 0;
+
+  offset.u.HighPart = 0;
+  offset.u.LowPart  = 0;
+
+  ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
+
+  if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
+    return S_OK;
+
+  return S_FALSE;
+}
+
+/******************************************************************************
+ *              WriteClassStg        [OLE32.@]
+ *
+ * This method will store the specified CLSID in the specified storage object
+ */
+HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
+{
+  if(!pStg)
+    return E_INVALIDARG;
+
+  if(!rclsid)
+    return STG_E_INVALIDPOINTER;
+
+  return IStorage_SetClass(pStg, rclsid);
+}
+
+/***********************************************************************
+ *    ReadClassStg (OLE32.@)
+ *
+ * This method reads the CLSID previously written to a storage object with
+ * the WriteClassStg.
+ *
+ * PARAMS
+ *  pstg    [I] IStorage pointer
+ *  pclsid  [O] Pointer to where the CLSID is written
+ *
+ * RETURNS
+ *  Success: S_OK.
+ *  Failure: HRESULT code.
+ */
+HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
+
+    STATSTG pstatstg;
+    HRESULT hRes;
+
+    TRACE("(%p, %p)\n", pstg, pclsid);
+
+    if(!pstg || !pclsid)
+        return E_INVALIDARG;
+
+   /*
+    * read a STATSTG structure (contains the clsid) from the storage
+    */
+    hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
+
+    if(SUCCEEDED(hRes))
+        *pclsid=pstatstg.clsid;
+
+    return hRes;
+}
+
+/***********************************************************************
+ *    OleLoadFromStream (OLE32.@)
+ *
+ * This function loads an object from stream
+ */
+HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
+{
+    CLSID	clsid;
+    HRESULT	res;
+    LPPERSISTSTREAM	xstm;
+
+    TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
+
+    res=ReadClassStm(pStm,&clsid);
+    if (FAILED(res))
+	return res;
+    res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
+    if (FAILED(res))
+	return res;
+    res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
+    if (FAILED(res)) {
+	IUnknown_Release((IUnknown*)*ppvObj);
+	return res;
+    }
+    res=IPersistStream_Load(xstm,pStm);
+    IPersistStream_Release(xstm);
+    /* FIXME: all refcounts ok at this point? I think they should be:
+     * 		pStm	: unchanged
+     *		ppvObj	: 1
+     *		xstm	: 0 (released)
+     */
+    return res;
+}
+
+/***********************************************************************
+ *    OleSaveToStream (OLE32.@)
+ *
+ * This function saves an object with the IPersistStream interface on it
+ * to the specified stream.
+ */
+HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
+{
+
+    CLSID clsid;
+    HRESULT res;
+
+    TRACE("(%p,%p)\n",pPStm,pStm);
+
+    res=IPersistStream_GetClassID(pPStm,&clsid);
+
+    if (SUCCEEDED(res)){
+
+        res=WriteClassStm(pStm,&clsid);
+
+        if (SUCCEEDED(res))
+
+            res=IPersistStream_Save(pPStm,pStm,TRUE);
+    }
+
+    TRACE("Finished Save\n");
+    return res;
+}
+
+/*************************************************************************
+ * STORAGE_CreateOleStream [Internal]
+ *
+ * Creates the "\001OLE" stream in the IStorage if necessary.
+ *
+ * PARAMS
+ *     storage     [I] Dest storage to create the stream in
+ *     flags       [I] flags to be set for newly created stream
+ *
+ * RETURNS
+ *     HRESULT return value
+ *
+ * NOTES
+ *
+ *     This stream is still unknown, MS Word seems to have extra data
+ *     but since the data is stored in the OLESTREAM there should be
+ *     no need to recreate the stream.  If the stream is manually
+ *     deleted it will create it with this default data.
+ *
+ */
+HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
+{
+    static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
+    static const DWORD version_magic = 0x02000001;
+    IStream *stream;
+    HRESULT hr;
+
+    hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
+    if (hr == S_OK)
+    {
+        struct empty_1ole_stream {
+            DWORD version_magic;
+            DWORD flags;
+            DWORD update_options;
+            DWORD reserved;
+            DWORD mon_stream_size;
+        };
+        struct empty_1ole_stream stream_data;
+
+        stream_data.version_magic = version_magic;
+        stream_data.flags = flags;
+        stream_data.update_options = 0;
+        stream_data.reserved = 0;
+        stream_data.mon_stream_size = 0;
+
+        hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
+        IStream_Release(stream);
+    }
+
+    return hr;
+}
+
+/* write a string to a stream, preceded by its length */
+static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
+{
+    HRESULT r;
+    LPSTR str;
+    DWORD len = 0;
+
+    if( string )
+        len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
+    r = IStream_Write( stm, &len, sizeof(len), NULL);
+    if( FAILED( r ) )
+        return r;
+    if(len == 0)
+        return r;
+    str = CoTaskMemAlloc( len );
+    WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
+    r = IStream_Write( stm, str, len, NULL);
+    CoTaskMemFree( str );
+    return r;
+}
+
+/* read a string preceded by its length from a stream */
+static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
+{
+    HRESULT r;
+    DWORD len, count = 0;
+    LPSTR str;
+    LPWSTR wstr;
+
+    r = IStream_Read( stm, &len, sizeof(len), &count );
+    if( FAILED( r ) )
+        return r;
+    if( count != sizeof(len) )
+        return E_OUTOFMEMORY;
+
+    TRACE("%d bytes\n",len);
+
+    str = CoTaskMemAlloc( len );
+    if( !str )
+        return E_OUTOFMEMORY;
+    count = 0;
+    r = IStream_Read( stm, str, len, &count );
+    if( FAILED( r ) )
+        return r;
+    if( count != len )
+    {
+        CoTaskMemFree( str );
+        return E_OUTOFMEMORY;
+    }
+
+    TRACE("Read string %s\n",debugstr_an(str,len));
+
+    len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
+    wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
+    if( wstr )
+    {
+         MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
+         wstr[len] = 0;
+    }
+    CoTaskMemFree( str );
+
+    *string = wstr;
 
-  return hr;
+    return r;
 }
 
-/******************************************************************************
- *    StgOpenStorageOnILockBytes    [OLE32.@]
- */
-HRESULT WINAPI StgOpenStorageOnILockBytes(
-      ILockBytes *plkbyt,
-      IStorage *pstgPriority,
-      DWORD grfMode,
-      SNB snbExclude,
-      DWORD reserved,
-      IStorage **ppstgOpen)
+
+static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
+    LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
 {
-  StorageBaseImpl* newStorage = 0;
-  HRESULT        hr = S_OK;
+    IStream *pstm;
+    HRESULT r = S_OK;
+    static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
 
-  if ((plkbyt == 0) || (ppstgOpen == 0))
-    return STG_E_INVALIDPOINTER;
+    static const BYTE unknown1[12] =
+       { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
+         0xFF, 0xFF, 0xFF, 0xFF};
+    static const BYTE unknown2[16] =
+       { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 
-  if ( FAILED( validateSTGM(grfMode) ))
-    return STG_E_INVALIDFLAG;
+    TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
+           debugstr_w(lpszUserType), debugstr_w(szClipName),
+           debugstr_w(szProgIDName));
 
-  *ppstgOpen = 0;
+    /*  Create a CompObj stream */
+    r = IStorage_CreateStream(pstg, szwStreamName,
+        STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
+    if( FAILED (r) )
+        return r;
 
-  /*
-   * Allocate and initialize the new IStorage object.
-   */
-  hr = Storage_Construct(
-         0,
-         0,
-         plkbyt,
-         grfMode,
-         FALSE,
-         FALSE,
-         512,
-         &newStorage);
+    /* Write CompObj Structure to stream */
+    r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
 
-  if (FAILED(hr))
-  {
-    return hr;
-  }
+    if( SUCCEEDED( r ) )
+        r = WriteClassStm( pstm, clsid );
 
-  *ppstgOpen = &newStorage->IStorage_iface;
+    if( SUCCEEDED( r ) )
+        r = STREAM_WriteString( pstm, lpszUserType );
+    if( SUCCEEDED( r ) )
+        r = STREAM_WriteString( pstm, szClipName );
+    if( SUCCEEDED( r ) )
+        r = STREAM_WriteString( pstm, szProgIDName );
+    if( SUCCEEDED( r ) )
+        r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
 
-  return hr;
+    IStream_Release( pstm );
+
+    return r;
 }
 
-/******************************************************************************
- *              StgSetTimes [ole32.@]
- *              StgSetTimes [OLE32.@]
- *
- *
+/***********************************************************************
+ *               WriteFmtUserTypeStg (OLE32.@)
  */
-HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
-                           FILETIME const *patime, FILETIME const *pmtime)
+HRESULT WINAPI WriteFmtUserTypeStg(
+	  LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
 {
-  IStorage *stg = NULL;
-  HRESULT r;
- 
-  TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
-
-  r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
-                     0, 0, &stg);
-  if( SUCCEEDED(r) )
-  {
-    r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
-    IStorage_Release(stg);
-  }
+    STATSTG stat;
+    HRESULT r;
+    WCHAR szwClipName[0x40];
+    CLSID clsid;
+    LPWSTR wstrProgID = NULL;
+    DWORD n;
 
-  return r;
-}
+    TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
 
-/******************************************************************************
- *              StgIsStorageILockBytes        [OLE32.@]
- *
- * Determines if the ILockBytes contains a storage object.
- */
-HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
-{
-  BYTE sig[sizeof(STORAGE_magic)];
-  ULARGE_INTEGER offset;
-  ULONG read = 0;
+    /* get the clipboard format name */
+    if( cf )
+    {
+        n = GetClipboardFormatNameW( cf, szwClipName,
+                sizeof(szwClipName)/sizeof(szwClipName[0]) );
+        szwClipName[n]=0;
+    }
 
-  offset.u.HighPart = 0;
-  offset.u.LowPart  = 0;
+    TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
 
-  ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), &read);
+    r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
+    if(SUCCEEDED(r))
+        clsid = stat.clsid;
+    else
+        clsid = CLSID_NULL;
 
-  if (read == sizeof(sig) && memcmp(sig, STORAGE_magic, sizeof(sig)) == 0)
-    return S_OK;
+    ProgIDFromCLSID(&clsid, &wstrProgID);
 
-  return S_FALSE;
-}
+    TRACE("progid is %s\n",debugstr_w(wstrProgID));
 
-/******************************************************************************
- *              WriteClassStg        [OLE32.@]
- *
- * This method will store the specified CLSID in the specified storage object
- */
-HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
-{
-  if(!pStg)
-    return E_INVALIDARG;
+    r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
+            cf ? szwClipName : NULL, wstrProgID );
 
-  if(!rclsid)
-    return STG_E_INVALIDPOINTER;
+    CoTaskMemFree(wstrProgID);
 
-  return IStorage_SetClass(pStg, rclsid);
+    return r;
 }
 
-/***********************************************************************
- *    ReadClassStg (OLE32.@)
- *
- * This method reads the CLSID previously written to a storage object with
- * the WriteClassStg.
- *
- * PARAMS
- *  pstg    [I] IStorage pointer
- *  pclsid  [O] Pointer to where the CLSID is written
- *
- * RETURNS
- *  Success: S_OK.
- *  Failure: HRESULT code.
+
+/******************************************************************************
+ *              ReadFmtUserTypeStg        [OLE32.@]
  */
-HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
+HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
+{
+    HRESULT r;
+    IStream *stm = 0;
+    static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
+    unsigned char unknown1[12];
+    unsigned char unknown2[16];
+    DWORD count;
+    LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
+    CLSID clsid;
 
-    STATSTG pstatstg;
-    HRESULT hRes;
+    TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
 
-    TRACE("(%p, %p)\n", pstg, pclsid);
+    r = IStorage_OpenStream( pstg, szCompObj, NULL,
+                    STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
+    if( FAILED ( r ) )
+    {
+        WARN("Failed to open stream r = %08x\n", r);
+        return r;
+    }
 
-    if(!pstg || !pclsid)
-        return E_INVALIDARG;
+    /* read the various parts of the structure */
+    r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
+    if( FAILED( r ) || ( count != sizeof(unknown1) ) )
+        goto end;
+    r = ReadClassStm( stm, &clsid );
+    if( FAILED( r ) )
+        goto end;
 
-   /*
-    * read a STATSTG structure (contains the clsid) from the storage
-    */
-    hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
+    r = STREAM_ReadString( stm, &szCLSIDName );
+    if( FAILED( r ) )
+        goto end;
 
-    if(SUCCEEDED(hRes))
-        *pclsid=pstatstg.clsid;
+    r = STREAM_ReadString( stm, &szOleTypeName );
+    if( FAILED( r ) )
+        goto end;
 
-    return hRes;
-}
+    r = STREAM_ReadString( stm, &szProgIDName );
+    if( FAILED( r ) )
+        goto end;
 
-/***********************************************************************
- *    OleLoadFromStream (OLE32.@)
- *
- * This function loads an object from stream
- */
-HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
-{
-    CLSID	clsid;
-    HRESULT	res;
-    LPPERSISTSTREAM	xstm;
+    r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
+    if( FAILED( r ) || ( count != sizeof(unknown2) ) )
+        goto end;
 
-    TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
+    /* ok, success... now we just need to store what we found */
+    if( pcf )
+        *pcf = RegisterClipboardFormatW( szOleTypeName );
 
-    res=ReadClassStm(pStm,&clsid);
-    if (FAILED(res))
-	return res;
-    res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
-    if (FAILED(res))
-	return res;
-    res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
-    if (FAILED(res)) {
-	IUnknown_Release((IUnknown*)*ppvObj);
-	return res;
-    }
-    res=IPersistStream_Load(xstm,pStm);
-    IPersistStream_Release(xstm);
-    /* FIXME: all refcounts ok at this point? I think they should be:
-     * 		pStm	: unchanged
-     *		ppvObj	: 1
-     *		xstm	: 0 (released)
-     */
-    return res;
+    if( lplpszUserType )
+    {
+        *lplpszUserType = szCLSIDName;
+        szCLSIDName = NULL;
+    }
+
+end:
+    CoTaskMemFree( szCLSIDName );
+    CoTaskMemFree( szOleTypeName );
+    CoTaskMemFree( szProgIDName );
+    IStream_Release( stm );
+
+    return r;
 }
 
-/***********************************************************************
- *    OleSaveToStream (OLE32.@)
+/******************************************************************************
+ * StgIsStorageFile [OLE32.@]
+ * Verify if the file contains a storage object
  *
- * This function saves an object with the IPersistStream interface on it
- * to the specified stream.
+ * PARAMS
+ *  fn      [ I] Filename
+ *
+ * RETURNS
+ *  S_OK    if file has magic bytes as a storage object
+ *  S_FALSE if file is not storage
  */
-HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
+HRESULT WINAPI
+StgIsStorageFile(LPCOLESTR fn)
 {
+	HANDLE		hf;
+	BYTE		magic[8];
+	DWORD		bytes_read;
 
-    CLSID clsid;
-    HRESULT res;
-
-    TRACE("(%p,%p)\n",pPStm,pStm);
+	TRACE("%s\n", debugstr_w(fn));
+	hf = CreateFileW(fn, GENERIC_READ,
+	                 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+	                 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
 
-    res=IPersistStream_GetClassID(pPStm,&clsid);
+	if (hf == INVALID_HANDLE_VALUE)
+		return STG_E_FILENOTFOUND;
 
-    if (SUCCEEDED(res)){
+	if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
+	{
+		WARN(" unable to read file\n");
+		CloseHandle(hf);
+		return S_FALSE;
+	}
 
-        res=WriteClassStm(pStm,&clsid);
+	CloseHandle(hf);
 
-        if (SUCCEEDED(res))
+	if (bytes_read != 8) {
+		TRACE(" too short\n");
+		return S_FALSE;
+	}
 
-            res=IPersistStream_Save(pPStm,pStm,TRUE);
-    }
+	if (!memcmp(magic,STORAGE_magic,8)) {
+		TRACE(" -> YES\n");
+		return S_OK;
+	}
 
-    TRACE("Finished Save\n");
-    return res;
+	TRACE(" -> Invalid header.\n");
+	return S_FALSE;
 }
 
-/****************************************************************************
- * This method validate a STGM parameter that can contain the values below
- *
- * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
- * The stgm values contained in 0xffff0000 are bitmasks.
+/***********************************************************************
+ *		WriteClassStm (OLE32.@)
  *
- * STGM_DIRECT               0x00000000
- * STGM_TRANSACTED           0x00010000
- * STGM_SIMPLE               0x08000000
+ * Writes a CLSID to a stream.
  *
- * STGM_READ                 0x00000000
- * STGM_WRITE                0x00000001
- * STGM_READWRITE            0x00000002
+ * PARAMS
+ *  pStm   [I] Stream to write to.
+ *  rclsid [I] CLSID to write.
  *
- * STGM_SHARE_DENY_NONE      0x00000040
- * STGM_SHARE_DENY_READ      0x00000030
- * STGM_SHARE_DENY_WRITE     0x00000020
- * STGM_SHARE_EXCLUSIVE      0x00000010
+ * RETURNS
+ *  Success: S_OK.
+ *  Failure: HRESULT code.
+ */
+HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
+{
+    TRACE("(%p,%p)\n",pStm,rclsid);
+
+    if (!pStm || !rclsid)
+        return E_INVALIDARG;
+
+    return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
+}
+
+/***********************************************************************
+ *		ReadClassStm (OLE32.@)
  *
- * STGM_PRIORITY             0x00040000
- * STGM_DELETEONRELEASE      0x04000000
+ * Reads a CLSID from a stream.
  *
- * STGM_CREATE               0x00001000
- * STGM_CONVERT              0x00020000
- * STGM_FAILIFTHERE          0x00000000
+ * PARAMS
+ *  pStm   [I] Stream to read from.
+ *  rclsid [O] CLSID to read.
  *
- * STGM_NOSCRATCH            0x00100000
- * STGM_NOSNAPSHOT           0x00200000
+ * RETURNS
+ *  Success: S_OK.
+ *  Failure: HRESULT code.
  */
-static HRESULT validateSTGM(DWORD stgm)
+HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
 {
-  DWORD access = STGM_ACCESS_MODE(stgm);
-  DWORD share  = STGM_SHARE_MODE(stgm);
-  DWORD create = STGM_CREATE_MODE(stgm);
-
-  if (stgm&~STGM_KNOWN_FLAGS)
-  {
-    ERR("unknown flags %08x\n", stgm);
-    return E_FAIL;
-  }
+    ULONG nbByte;
+    HRESULT res;
 
-  switch (access)
-  {
-  case STGM_READ:
-  case STGM_WRITE:
-  case STGM_READWRITE:
-    break;
-  default:
-    return E_FAIL;
-  }
+    TRACE("(%p,%p)\n",pStm,pclsid);
 
-  switch (share)
-  {
-  case STGM_SHARE_DENY_NONE:
-  case STGM_SHARE_DENY_READ:
-  case STGM_SHARE_DENY_WRITE:
-  case STGM_SHARE_EXCLUSIVE:
-    break;
-  case 0:
-    if (!(stgm & STGM_TRANSACTED))
-      return E_FAIL;
-    break;
-  default:
-    return E_FAIL;
-  }
+    if (!pStm || !pclsid)
+        return E_INVALIDARG;
 
-  switch (create)
-  {
-  case STGM_CREATE:
-  case STGM_FAILIFTHERE:
-    break;
-  default:
-    return E_FAIL;
-  }
+    /* clear the output args */
+    *pclsid = CLSID_NULL;
 
-  /*
-   * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
-   */
-  if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
-      return E_FAIL;
+    res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
 
-  /*
-   * STGM_CREATE | STGM_CONVERT
-   * if both are false, STGM_FAILIFTHERE is set to TRUE
-   */
-  if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
-    return E_FAIL;
+    if (FAILED(res))
+        return res;
 
-  /*
-   * STGM_NOSCRATCH requires STGM_TRANSACTED
-   */
-  if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
-    return E_FAIL;
+    if (nbByte != sizeof(CLSID))
+        return STG_E_READFAULT;
+    else
+        return S_OK;
+}
 
-  /*
-   * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
-   * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
-   */
-  if ( (stgm & STGM_NOSNAPSHOT) &&
-        (!(stgm & STGM_TRANSACTED) ||
-         share == STGM_SHARE_EXCLUSIVE ||
-         share == STGM_SHARE_DENY_WRITE) )
-    return E_FAIL;
 
-  return S_OK;
-}
+/************************************************************************
+ * OleConvert Functions
+ ***********************************************************************/
 
-/****************************************************************************
- *      GetShareModeFromSTGM
- *
- * This method will return a share mode flag from a STGM value.
- * The STGM value is assumed valid.
- */
-static DWORD GetShareModeFromSTGM(DWORD stgm)
-{
-  switch (STGM_SHARE_MODE(stgm))
-  {
-  case 0:
-    assert(stgm & STGM_TRANSACTED);
-    /* fall-through */
-  case STGM_SHARE_DENY_NONE:
-    return FILE_SHARE_READ | FILE_SHARE_WRITE;
-  case STGM_SHARE_DENY_READ:
-    return FILE_SHARE_WRITE;
-  case STGM_SHARE_DENY_WRITE:
-  case STGM_SHARE_EXCLUSIVE:
-    return FILE_SHARE_READ;
-  }
-  ERR("Invalid share mode!\n");
-  assert(0);
-  return 0;
-}
+#define OLESTREAM_ID 0x501
+#define OLESTREAM_MAX_STR_LEN 255
 
-/****************************************************************************
- *      GetAccessModeFromSTGM
- *
- * This method will return an access mode flag from a STGM value.
- * The STGM value is assumed valid.
- */
-static DWORD GetAccessModeFromSTGM(DWORD stgm)
+/* OLESTREAM memory structure to use for Get and Put Routines */
+typedef struct
 {
-  switch (STGM_ACCESS_MODE(stgm))
-  {
-  case STGM_READ:
-    return GENERIC_READ;
-  case STGM_WRITE:
-  case STGM_READWRITE:
-    return GENERIC_READ | GENERIC_WRITE;
-  }
-  ERR("Invalid access mode!\n");
-  assert(0);
-  return 0;
-}
+    DWORD dwOleID;
+    DWORD dwTypeID;
+    DWORD dwOleTypeNameLength;
+    CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
+    CHAR  *pstrOleObjFileName;
+    DWORD dwOleObjFileNameLength;
+    DWORD dwMetaFileWidth;
+    DWORD dwMetaFileHeight;
+    CHAR  strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
+    DWORD dwDataLength;
+    BYTE *pData;
+} OLECONVERT_OLESTREAM_DATA;
 
-/****************************************************************************
- *      GetCreationModeFromSTGM
- *
- * This method will return a creation mode flag from a STGM value.
- * The STGM value is assumed valid.
- */
-static DWORD GetCreationModeFromSTGM(DWORD stgm)
+/* CompObj Stream structure */
+typedef struct
 {
-  switch(STGM_CREATE_MODE(stgm))
-  {
-  case STGM_CREATE:
-    return CREATE_ALWAYS;
-  case STGM_CONVERT:
-    FIXME("STGM_CONVERT not implemented!\n");
-    return CREATE_NEW;
-  case STGM_FAILIFTHERE:
-    return CREATE_NEW;
-  }
-  ERR("Invalid create mode!\n");
-  assert(0);
-  return 0;
-}
+    BYTE byUnknown1[12];
+    CLSID clsid;
+    DWORD dwCLSIDNameLength;
+    CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
+    DWORD dwOleTypeNameLength;
+    CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
+    DWORD dwProgIDNameLength;
+    CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
+    BYTE byUnknown2[16];
+} OLECONVERT_ISTORAGE_COMPOBJ;
+
+/* Ole Presentation Stream structure */
+typedef struct
+{
+    BYTE byUnknown1[28];
+    DWORD dwExtentX;
+    DWORD dwExtentY;
+    DWORD dwSize;
+    BYTE *pData;
+} OLECONVERT_ISTORAGE_OLEPRES;
 
 
 /*************************************************************************
@@ -9254,494 +9581,219 @@ static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM
 }
 
 /*************************************************************************
- * OLECONVERT_SaveOLE10 [Internal]
- *
- * Saves the OLE10 STREAM From memory
- *
- * PARAMS
- *     pData        [I] Data Structure for the OLESTREAM Data
- *     pOleStream   [I] The OLESTREAM to save
- *
- * RETURNS
- *     Success:  S_OK
- *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
- *
- * NOTES
- *     This function is used by OleConvertIStorageToOLESTREAM only.
- *
- */
-static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
-{
-    DWORD dwSize;
-    HRESULT hRes = S_OK;
-
-
-   /* Set the OleID */
-    dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
-    if(dwSize != sizeof(pData->dwOleID))
-    {
-        hRes = CONVERT10_E_OLESTREAM_PUT;
-    }
-
-    if(hRes == S_OK)
-    {
-        /* Set the TypeID */
-        dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
-        if(dwSize != sizeof(pData->dwTypeID))
-        {
-            hRes = CONVERT10_E_OLESTREAM_PUT;
-        }
-    }
-
-    if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
-    {
-        /* Set the Length of the OleTypeName */
-        dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
-        if(dwSize != sizeof(pData->dwOleTypeNameLength))
-        {
-            hRes = CONVERT10_E_OLESTREAM_PUT;
-        }
-
-        if(hRes == S_OK)
-        {
-            if(pData->dwOleTypeNameLength > 0)
-            {
-                /* Set the OleTypeName */
-                dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
-                if(dwSize != pData->dwOleTypeNameLength)
-                {
-                    hRes = CONVERT10_E_OLESTREAM_PUT;
-                }
-            }
-        }
-
-        if(hRes == S_OK)
-        {
-            /* Set the width of the Metafile */
-            dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
-            if(dwSize != sizeof(pData->dwMetaFileWidth))
-            {
-                hRes = CONVERT10_E_OLESTREAM_PUT;
-            }
-        }
-
-        if(hRes == S_OK)
-        {
-            /* Set the height of the Metafile */
-            dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
-            if(dwSize != sizeof(pData->dwMetaFileHeight))
-            {
-                hRes = CONVERT10_E_OLESTREAM_PUT;
-            }
-        }
-
-        if(hRes == S_OK)
-        {
-            /* Set the length of the Data */
-            dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
-            if(dwSize != sizeof(pData->dwDataLength))
-            {
-                hRes = CONVERT10_E_OLESTREAM_PUT;
-            }
-        }
-
-        if(hRes == S_OK)
-        {
-            if(pData->dwDataLength > 0)
-            {
-                /* Set the Data (eg. IStorage, Metafile, Bitmap) */
-                dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->pData, pData->dwDataLength);
-                if(dwSize != pData->dwDataLength)
-                {
-                    hRes = CONVERT10_E_OLESTREAM_PUT;
-                }
-            }
-        }
-    }
-    return hRes;
-}
-
-/*************************************************************************
- * OLECONVERT_GetOLE20FromOLE10[Internal]
- *
- * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
- * opens it, and copies the content to the dest IStorage for
- * OleConvertOLESTREAMToIStorage
- *
- *
- * PARAMS
- *     pDestStorage  [I] The IStorage to copy the data to
- *     pBuffer       [I] Buffer that contains the IStorage from the OLESTREAM
- *     nBufferLength [I] The size of the buffer
- *
- * RETURNS
- *     Nothing
- *
- * NOTES
- *
- *
- */
-static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
-{
-    HRESULT hRes;
-    HANDLE hFile;
-    IStorage *pTempStorage;
-    DWORD dwNumOfBytesWritten;
-    WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
-    static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
-
-    /* Create a temp File */
-    GetTempPathW(MAX_PATH, wstrTempDir);
-    GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
-    hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
-
-    if(hFile != INVALID_HANDLE_VALUE)
-    {
-        /* Write IStorage Data to File */
-        WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
-        CloseHandle(hFile);
-
-        /* Open and copy temp storage to the Dest Storage */
-        hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
-        if(hRes == S_OK)
-        {
-            hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
-            IStorage_Release(pTempStorage);
-        }
-        DeleteFileW(wstrTempFile);
-    }
-}
-
-
-/*************************************************************************
- * OLECONVERT_WriteOLE20ToBuffer [Internal]
- *
- * Saves the OLE10 STREAM From memory
- *
- * PARAMS
- *     pStorage  [I] The Src IStorage to copy
- *     pData     [I] The Dest Memory to write to.
- *
- * RETURNS
- *     The size in bytes allocated for pData
- *
- * NOTES
- *     Memory allocated for pData must be freed by the caller
- *
- *     Used by OleConvertIStorageToOLESTREAM only.
- *
- */
-static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
-{
-    HANDLE hFile;
-    HRESULT hRes;
-    DWORD nDataLength = 0;
-    IStorage *pTempStorage;
-    WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
-    static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
-
-    *pData = NULL;
-
-    /* Create temp Storage */
-    GetTempPathW(MAX_PATH, wstrTempDir);
-    GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
-    hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
-
-    if(hRes == S_OK)
-    {
-        /* Copy Src Storage to the Temp Storage */
-        IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
-        IStorage_Release(pTempStorage);
-
-        /* Open Temp Storage as a file and copy to memory */
-        hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
-        if(hFile != INVALID_HANDLE_VALUE)
-        {
-            nDataLength = GetFileSize(hFile, NULL);
-            *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
-            ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
-            CloseHandle(hFile);
-        }
-        DeleteFileW(wstrTempFile);
-    }
-    return nDataLength;
-}
-
-/*************************************************************************
- * STORAGE_CreateOleStream [Internal]
+ * OLECONVERT_SaveOLE10 [Internal]
  *
- * Creates the "\001OLE" stream in the IStorage if necessary.
+ * Saves the OLE10 STREAM From memory
  *
  * PARAMS
- *     storage     [I] Dest storage to create the stream in
- *     flags       [I] flags to be set for newly created stream
+ *     pData        [I] Data Structure for the OLESTREAM Data
+ *     pOleStream   [I] The OLESTREAM to save
  *
  * RETURNS
- *     HRESULT return value
+ *     Success:  S_OK
+ *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
  *
  * NOTES
- *
- *     This stream is still unknown, MS Word seems to have extra data
- *     but since the data is stored in the OLESTREAM there should be
- *     no need to recreate the stream.  If the stream is manually
- *     deleted it will create it with this default data.
+ *     This function is used by OleConvertIStorageToOLESTREAM only.
  *
  */
-HRESULT STORAGE_CreateOleStream(IStorage *storage, DWORD flags)
-{
-    static const WCHAR stream_1oleW[] = {1,'O','l','e',0};
-    static const DWORD version_magic = 0x02000001;
-    IStream *stream;
-    HRESULT hr;
-
-    hr = IStorage_CreateStream(storage, stream_1oleW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
-    if (hr == S_OK)
-    {
-        struct empty_1ole_stream {
-            DWORD version_magic;
-            DWORD flags;
-            DWORD update_options;
-            DWORD reserved;
-            DWORD mon_stream_size;
-        };
-        struct empty_1ole_stream stream_data;
-
-        stream_data.version_magic = version_magic;
-        stream_data.flags = flags;
-        stream_data.update_options = 0;
-        stream_data.reserved = 0;
-        stream_data.mon_stream_size = 0;
-
-        hr = IStream_Write(stream, &stream_data, sizeof(stream_data), NULL);
-        IStream_Release(stream);
-    }
-
-    return hr;
-}
-
-/* write a string to a stream, preceded by its length */
-static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
-{
-    HRESULT r;
-    LPSTR str;
-    DWORD len = 0;
-
-    if( string )
-        len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
-    r = IStream_Write( stm, &len, sizeof(len), NULL);
-    if( FAILED( r ) )
-        return r;
-    if(len == 0)
-        return r;
-    str = CoTaskMemAlloc( len );
-    WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
-    r = IStream_Write( stm, str, len, NULL);
-    CoTaskMemFree( str );
-    return r;
-}
-
-/* read a string preceded by its length from a stream */
-static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
+static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
 {
-    HRESULT r;
-    DWORD len, count = 0;
-    LPSTR str;
-    LPWSTR wstr;
+    DWORD dwSize;
+    HRESULT hRes = S_OK;
 
-    r = IStream_Read( stm, &len, sizeof(len), &count );
-    if( FAILED( r ) )
-        return r;
-    if( count != sizeof(len) )
-        return E_OUTOFMEMORY;
 
-    TRACE("%d bytes\n",len);
-    
-    str = CoTaskMemAlloc( len );
-    if( !str )
-        return E_OUTOFMEMORY;
-    count = 0;
-    r = IStream_Read( stm, str, len, &count );
-    if( FAILED( r ) )
-        return r;
-    if( count != len )
+   /* Set the OleID */
+    dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
+    if(dwSize != sizeof(pData->dwOleID))
     {
-        CoTaskMemFree( str );
-        return E_OUTOFMEMORY;
+        hRes = CONVERT10_E_OLESTREAM_PUT;
     }
 
-    TRACE("Read string %s\n",debugstr_an(str,len));
-
-    len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
-    wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
-    if( wstr )
+    if(hRes == S_OK)
     {
-         MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
-         wstr[len] = 0;
+        /* Set the TypeID */
+        dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
+        if(dwSize != sizeof(pData->dwTypeID))
+        {
+            hRes = CONVERT10_E_OLESTREAM_PUT;
+        }
     }
-    CoTaskMemFree( str );
-
-    *string = wstr;
-
-    return r;
-}
-
-
-static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
-    LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
-{
-    IStream *pstm;
-    HRESULT r = S_OK;
-    static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
-
-    static const BYTE unknown1[12] =
-       { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
-         0xFF, 0xFF, 0xFF, 0xFF};
-    static const BYTE unknown2[16] =
-       { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
-         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
-    TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
-           debugstr_w(lpszUserType), debugstr_w(szClipName),
-           debugstr_w(szProgIDName));
-
-    /*  Create a CompObj stream */
-    r = IStorage_CreateStream(pstg, szwStreamName,
-        STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
-    if( FAILED (r) )
-        return r;
-
-    /* Write CompObj Structure to stream */
-    r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
-
-    if( SUCCEEDED( r ) )
-        r = WriteClassStm( pstm, clsid );
-
-    if( SUCCEEDED( r ) )
-        r = STREAM_WriteString( pstm, lpszUserType );
-    if( SUCCEEDED( r ) )
-        r = STREAM_WriteString( pstm, szClipName );
-    if( SUCCEEDED( r ) )
-        r = STREAM_WriteString( pstm, szProgIDName );
-    if( SUCCEEDED( r ) )
-        r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
-
-    IStream_Release( pstm );
-
-    return r;
-}
-
-/***********************************************************************
- *               WriteFmtUserTypeStg (OLE32.@)
- */
-HRESULT WINAPI WriteFmtUserTypeStg(
-	  LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
-{
-    STATSTG stat;
-    HRESULT r;
-    WCHAR szwClipName[0x40];
-    CLSID clsid;
-    LPWSTR wstrProgID = NULL;
-    DWORD n;
-
-    TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
 
-    /* get the clipboard format name */
-    if( cf )
+    if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
     {
-        n = GetClipboardFormatNameW( cf, szwClipName,
-                sizeof(szwClipName)/sizeof(szwClipName[0]) );
-        szwClipName[n]=0;
-    }
-
-    TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
-
-    r = IStorage_Stat(pstg, &stat, STATFLAG_NONAME);
-    if(SUCCEEDED(r))
-        clsid = stat.clsid;
-    else
-        clsid = CLSID_NULL;
+        /* Set the Length of the OleTypeName */
+        dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
+        if(dwSize != sizeof(pData->dwOleTypeNameLength))
+        {
+            hRes = CONVERT10_E_OLESTREAM_PUT;
+        }
 
-    ProgIDFromCLSID(&clsid, &wstrProgID);
+        if(hRes == S_OK)
+        {
+            if(pData->dwOleTypeNameLength > 0)
+            {
+                /* Set the OleTypeName */
+                dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
+                if(dwSize != pData->dwOleTypeNameLength)
+                {
+                    hRes = CONVERT10_E_OLESTREAM_PUT;
+                }
+            }
+        }
 
-    TRACE("progid is %s\n",debugstr_w(wstrProgID));
+        if(hRes == S_OK)
+        {
+            /* Set the width of the Metafile */
+            dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
+            if(dwSize != sizeof(pData->dwMetaFileWidth))
+            {
+                hRes = CONVERT10_E_OLESTREAM_PUT;
+            }
+        }
 
-    r = STORAGE_WriteCompObj( pstg, &clsid, lpszUserType,
-            cf ? szwClipName : NULL, wstrProgID );
+        if(hRes == S_OK)
+        {
+            /* Set the height of the Metafile */
+            dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
+            if(dwSize != sizeof(pData->dwMetaFileHeight))
+            {
+                hRes = CONVERT10_E_OLESTREAM_PUT;
+            }
+        }
 
-    CoTaskMemFree(wstrProgID);
+        if(hRes == S_OK)
+        {
+            /* Set the length of the Data */
+            dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
+            if(dwSize != sizeof(pData->dwDataLength))
+            {
+                hRes = CONVERT10_E_OLESTREAM_PUT;
+            }
+        }
 
-    return r;
+        if(hRes == S_OK)
+        {
+            if(pData->dwDataLength > 0)
+            {
+                /* Set the Data (eg. IStorage, Metafile, Bitmap) */
+                dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->pData, pData->dwDataLength);
+                if(dwSize != pData->dwDataLength)
+                {
+                    hRes = CONVERT10_E_OLESTREAM_PUT;
+                }
+            }
+        }
+    }
+    return hRes;
 }
 
-
-/******************************************************************************
- *              ReadFmtUserTypeStg        [OLE32.@]
+/*************************************************************************
+ * OLECONVERT_GetOLE20FromOLE10[Internal]
+ *
+ * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
+ * opens it, and copies the content to the dest IStorage for
+ * OleConvertOLESTREAMToIStorage
+ *
+ *
+ * PARAMS
+ *     pDestStorage  [I] The IStorage to copy the data to
+ *     pBuffer       [I] Buffer that contains the IStorage from the OLESTREAM
+ *     nBufferLength [I] The size of the buffer
+ *
+ * RETURNS
+ *     Nothing
+ *
+ * NOTES
+ *
+ *
  */
-HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
+static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
 {
-    HRESULT r;
-    IStream *stm = 0;
-    static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
-    unsigned char unknown1[12];
-    unsigned char unknown2[16];
-    DWORD count;
-    LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
-    CLSID clsid;
+    HRESULT hRes;
+    HANDLE hFile;
+    IStorage *pTempStorage;
+    DWORD dwNumOfBytesWritten;
+    WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
+    static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
 
-    TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
+    /* Create a temp File */
+    GetTempPathW(MAX_PATH, wstrTempDir);
+    GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
+    hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
 
-    r = IStorage_OpenStream( pstg, szCompObj, NULL, 
-                    STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
-    if( FAILED ( r ) )
+    if(hFile != INVALID_HANDLE_VALUE)
     {
-        WARN("Failed to open stream r = %08x\n", r);
-        return r;
-    }
-
-    /* read the various parts of the structure */
-    r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
-    if( FAILED( r ) || ( count != sizeof(unknown1) ) )
-        goto end;
-    r = ReadClassStm( stm, &clsid );
-    if( FAILED( r ) )
-        goto end;
+        /* Write IStorage Data to File */
+        WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
+        CloseHandle(hFile);
 
-    r = STREAM_ReadString( stm, &szCLSIDName );
-    if( FAILED( r ) )
-        goto end;
+        /* Open and copy temp storage to the Dest Storage */
+        hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
+        if(hRes == S_OK)
+        {
+            hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
+            IStorage_Release(pTempStorage);
+        }
+        DeleteFileW(wstrTempFile);
+    }
+}
 
-    r = STREAM_ReadString( stm, &szOleTypeName );
-    if( FAILED( r ) )
-        goto end;
 
-    r = STREAM_ReadString( stm, &szProgIDName );
-    if( FAILED( r ) )
-        goto end;
+/*************************************************************************
+ * OLECONVERT_WriteOLE20ToBuffer [Internal]
+ *
+ * Saves the OLE10 STREAM From memory
+ *
+ * PARAMS
+ *     pStorage  [I] The Src IStorage to copy
+ *     pData     [I] The Dest Memory to write to.
+ *
+ * RETURNS
+ *     The size in bytes allocated for pData
+ *
+ * NOTES
+ *     Memory allocated for pData must be freed by the caller
+ *
+ *     Used by OleConvertIStorageToOLESTREAM only.
+ *
+ */
+static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
+{
+    HANDLE hFile;
+    HRESULT hRes;
+    DWORD nDataLength = 0;
+    IStorage *pTempStorage;
+    WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
+    static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
 
-    r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
-    if( FAILED( r ) || ( count != sizeof(unknown2) ) )
-        goto end;
+    *pData = NULL;
 
-    /* ok, success... now we just need to store what we found */
-    if( pcf )
-        *pcf = RegisterClipboardFormatW( szOleTypeName );
+    /* Create temp Storage */
+    GetTempPathW(MAX_PATH, wstrTempDir);
+    GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
+    hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
 
-    if( lplpszUserType )
+    if(hRes == S_OK)
     {
-        *lplpszUserType = szCLSIDName;
-        szCLSIDName = NULL;
-    }
-
-end:
-    CoTaskMemFree( szCLSIDName );
-    CoTaskMemFree( szOleTypeName );
-    CoTaskMemFree( szProgIDName );
-    IStream_Release( stm );
+        /* Copy Src Storage to the Temp Storage */
+        IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
+        IStorage_Release(pTempStorage);
 
-    return r;
+        /* Open Temp Storage as a file and copy to memory */
+        hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+        if(hFile != INVALID_HANDLE_VALUE)
+        {
+            nDataLength = GetFileSize(hFile, NULL);
+            *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
+            ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
+            CloseHandle(hFile);
+        }
+        DeleteFileW(wstrTempFile);
+    }
+    return nDataLength;
 }
 
-
 /*************************************************************************
  * OLECONVERT_CreateCompObjStream [Internal]
  *
@@ -10435,112 +10487,3 @@ HRESULT WINAPI SetConvertStg(IStorage *storage, BOOL convert)
     IStream_Release(stream);
     return hr;
 }
-
-/******************************************************************************
- * StgIsStorageFile [OLE32.@]
- * Verify if the file contains a storage object
- *
- * PARAMS
- *  fn      [ I] Filename
- *
- * RETURNS
- *  S_OK    if file has magic bytes as a storage object
- *  S_FALSE if file is not storage
- */
-HRESULT WINAPI
-StgIsStorageFile(LPCOLESTR fn)
-{
-	HANDLE		hf;
-	BYTE		magic[8];
-	DWORD		bytes_read;
-
-	TRACE("%s\n", debugstr_w(fn));
-	hf = CreateFileW(fn, GENERIC_READ,
-	                 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
-	                 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
-
-	if (hf == INVALID_HANDLE_VALUE)
-		return STG_E_FILENOTFOUND;
-
-	if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
-	{
-		WARN(" unable to read file\n");
-		CloseHandle(hf);
-		return S_FALSE;
-	}
-
-	CloseHandle(hf);
-
-	if (bytes_read != 8) {
-		TRACE(" too short\n");
-		return S_FALSE;
-	}
-
-	if (!memcmp(magic,STORAGE_magic,8)) {
-		TRACE(" -> YES\n");
-		return S_OK;
-	}
-
-	TRACE(" -> Invalid header.\n");
-	return S_FALSE;
-}
-
-/***********************************************************************
- *		WriteClassStm (OLE32.@)
- *
- * Writes a CLSID to a stream.
- *
- * PARAMS
- *  pStm   [I] Stream to write to.
- *  rclsid [I] CLSID to write.
- *
- * RETURNS
- *  Success: S_OK.
- *  Failure: HRESULT code.
- */
-HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
-{
-    TRACE("(%p,%p)\n",pStm,rclsid);
-
-    if (!pStm || !rclsid)
-        return E_INVALIDARG;
-
-    return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
-}
-
-/***********************************************************************
- *		ReadClassStm (OLE32.@)
- *
- * Reads a CLSID from a stream.
- *
- * PARAMS
- *  pStm   [I] Stream to read from.
- *  rclsid [O] CLSID to read.
- *
- * RETURNS
- *  Success: S_OK.
- *  Failure: HRESULT code.
- */
-HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
-{
-    ULONG nbByte;
-    HRESULT res;
-
-    TRACE("(%p,%p)\n",pStm,pclsid);
-
-    if (!pStm || !pclsid)
-        return E_INVALIDARG;
-
-    /* clear the output args */
-    *pclsid = CLSID_NULL;
-
-    res = IStream_Read(pStm, pclsid, sizeof(CLSID), &nbByte);
-
-    if (FAILED(res))
-        return res;
-
-    if (nbByte != sizeof(CLSID))
-        return STG_E_READFAULT;
-    else
-        return S_OK;
-}
diff --git a/dlls/ole32/storage32.h b/dlls/ole32/storage32.h
index f894582..6c69655 100644
--- a/dlls/ole32/storage32.h
+++ b/dlls/ole32/storage32.h
@@ -416,39 +416,6 @@ struct StorageImpl
   ULONG locked_bytes[8];
 };
 
-HRESULT StorageImpl_ReadRawDirEntry(
-            StorageImpl *This,
-            ULONG index,
-            BYTE *buffer) DECLSPEC_HIDDEN;
-
-void UpdateRawDirEntry(
-    BYTE *buffer,
-    const DirEntry *newData) DECLSPEC_HIDDEN;
-
-HRESULT StorageImpl_WriteRawDirEntry(
-            StorageImpl *This,
-            ULONG index,
-            const BYTE *buffer) DECLSPEC_HIDDEN;
-
-HRESULT StorageImpl_ReadDirEntry(
-            StorageImpl*    This,
-            DirRef          index,
-            DirEntry*       buffer) DECLSPEC_HIDDEN;
-
-HRESULT StorageImpl_WriteDirEntry(
-            StorageImpl*        This,
-            DirRef              index,
-            const DirEntry*     buffer) DECLSPEC_HIDDEN;
-
-BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
-                      StorageImpl* This,
-                      SmallBlockChainStream** ppsbChain) DECLSPEC_HIDDEN;
-
-SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
-                      StorageImpl* This,
-                      BlockChainStream** ppbbChain,
-                      ULARGE_INTEGER newSize) DECLSPEC_HIDDEN;
-
 /****************************************************************************
  * StgStreamImpl definitions.
  *
@@ -628,6 +595,15 @@ struct BlockChainStream
 /*
  * Methods for the BlockChainStream class.
  */
+
+/* Returns the number of blocks that comprises this chain.
+ * This is not the size of the stream as the last block may not be full!
+ */
+static inline ULONG BlockChainStream_GetCount(BlockChainStream* This)
+{
+  return This->numBlocks;
+}
+
 BlockChainStream* BlockChainStream_Construct(
 		StorageImpl* parentStorage,
 		ULONG*         headOfStreamPlaceHolder,
-- 
2.1.4




More information about the wine-patches mailing list