[OLE32]
authorAmine Khaldi <amine.khaldi@reactos.org>
Fri, 3 Oct 2014 11:44:27 +0000 (11:44 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Fri, 3 Oct 2014 11:44:27 +0000 (11:44 +0000)
* Sync with Wine 1.7.27.
CORE-8540

svn path=/trunk/; revision=64491

reactos/dll/win32/ole32/compobj.c
reactos/dll/win32/ole32/filelockbytes.c
reactos/dll/win32/ole32/filemoniker.c
reactos/dll/win32/ole32/ole2.c
reactos/dll/win32/ole32/stg_stream.c
reactos/dll/win32/ole32/storage32.c
reactos/dll/win32/ole32/storage32.h
reactos/media/doc/README.WINE

index 070e55e..276143a 100644 (file)
@@ -314,7 +314,7 @@ static const WCHAR classes_rootW[] =
 static HKEY classes_root_hkey;
 
 /* create the special HKEY_CLASSES_ROOT key */
-static HKEY create_classes_root_hkey(void)
+static HKEY create_classes_root_hkey(DWORD access)
 {
     HKEY hkey, ret = 0;
     OBJECT_ATTRIBUTES attr;
@@ -327,23 +327,39 @@ static HKEY create_classes_root_hkey(void)
     attr.SecurityDescriptor = NULL;
     attr.SecurityQualityOfService = NULL;
     RtlInitUnicodeString( &name, classes_rootW );
-    if (create_key( &hkey, MAXIMUM_ALLOWED, &attr )) return 0;
+    if (create_key( &hkey, access, &attr )) return 0;
     TRACE( "%s -> %p\n", debugstr_w(attr.ObjectName->Buffer), hkey );
 
-    if (!(ret = InterlockedCompareExchangePointer( (void **)&classes_root_hkey, hkey, 0 )))
-        ret = hkey;
+    if (!(access & KEY_WOW64_64KEY))
+    {
+        if (!(ret = InterlockedCompareExchangePointer( (void **)&classes_root_hkey, hkey, 0 )))
+            ret = hkey;
+        else
+            NtClose( hkey );  /* somebody beat us to it */
+    }
     else
-        NtClose( hkey );  /* somebody beat us to it */
+        ret = hkey;
     return ret;
 }
 
 /* map the hkey from special root to normal key if necessary */
-static inline HKEY get_classes_root_hkey( HKEY hkey )
+static inline HKEY get_classes_root_hkey( HKEY hkey, REGSAM access )
 {
     HKEY ret = hkey;
+    const BOOL is_win64 = sizeof(void*) > sizeof(int);
+    const BOOL force_wow32 = is_win64 && (access & KEY_WOW64_32KEY);
 
-    if (hkey == HKEY_CLASSES_ROOT && !(ret = classes_root_hkey))
-        ret = create_classes_root_hkey();
+    if (hkey == HKEY_CLASSES_ROOT &&
+        ((access & KEY_WOW64_64KEY) || !(ret = classes_root_hkey)))
+        ret = create_classes_root_hkey(MAXIMUM_ALLOWED | (access & KEY_WOW64_64KEY));
+    if (force_wow32 && ret && ret == classes_root_hkey)
+    {
+        static const WCHAR wow6432nodeW[] = {'W','o','w','6','4','3','2','N','o','d','e',0};
+        access &= ~KEY_WOW64_32KEY;
+        if (create_classes_key(classes_root_hkey, wow6432nodeW, access, &hkey))
+            return 0;
+        ret = hkey;
+    }
 
     return ret;
 }
@@ -353,7 +369,7 @@ LSTATUS create_classes_key( HKEY hkey, const WCHAR *name, REGSAM access, HKEY *r
     OBJECT_ATTRIBUTES attr;
     UNICODE_STRING nameW;
 
-    if (!(hkey = get_classes_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
+    if (!(hkey = get_classes_root_hkey( hkey, access ))) return ERROR_INVALID_HANDLE;
 
     attr.Length = sizeof(attr);
     attr.RootDirectory = hkey;
@@ -371,7 +387,7 @@ LSTATUS open_classes_key( HKEY hkey, const WCHAR *name, REGSAM access, HKEY *ret
     OBJECT_ATTRIBUTES attr;
     UNICODE_STRING nameW;
 
-    if (!(hkey = get_classes_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
+    if (!(hkey = get_classes_root_hkey( hkey, access ))) return ERROR_INVALID_HANDLE;
 
     attr.Length = sizeof(attr);
     attr.RootDirectory = hkey;
@@ -1174,7 +1190,7 @@ DWORD apartment_release(struct apartment *apt)
     return ret;
 }
 
-/* The given OXID must be local to this process:
+/* The given OXID must be local to this process: 
  *
  * The ref parameter is here mostly to ensure people remember that
  * they get one, you should normally take a ref for thread safety.
@@ -1639,14 +1655,27 @@ static BOOL WINAPI register_class( INIT_ONCE *once, void *param, void **context
  * already been created */
 HRESULT apartment_createwindowifneeded(struct apartment *apt)
 {
+#ifndef __REACTOS__
+    static INIT_ONCE class_init_once = INIT_ONCE_STATIC_INIT;
+#endif
+
     if (apt->multi_threaded)
         return S_OK;
 
     if (!apt->win)
     {
+#ifndef __REACTOS__
+        HWND hwnd;
+
+        InitOnceExecuteOnce( &class_init_once, register_class, NULL, NULL );
+
+        hwnd = CreateWindowW(wszAptWinClass, NULL, 0, 0, 0, 0, 0,
+                             HWND_MESSAGE, 0, hProxyDll, NULL);
+#else
         HWND hwnd = CreateWindowW(wszAptWinClass, NULL, 0,
                                   0, 0, 0, 0,
                                   HWND_MESSAGE, 0, hProxyDll, NULL);
+#endif
         if (!hwnd)
         {
             ERR("CreateWindow failed with error %d\n", GetLastError());
@@ -2054,7 +2083,11 @@ HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved )
  */
 HRESULT WINAPI CoCreateGuid(GUID *pguid)
 {
-    DWORD status = UuidCreate(pguid);
+    DWORD status;
+
+    if(!pguid) return E_INVALIDARG;
+
+    status = UuidCreate(pguid);
     if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY) return S_OK;
     return HRESULT_FROM_WIN32( status );
 }
@@ -2474,6 +2507,28 @@ HRESULT WINAPI CLSIDFromProgIDEx(LPCOLESTR progid, LPCLSID clsid)
     return CLSIDFromProgID(progid, clsid);
 }
 
+static HRESULT get_ps_clsid_from_registry(const WCHAR* path, REGSAM access, CLSID *pclsid)
+{
+    HKEY hkey;
+    WCHAR value[CHARS_IN_GUID];
+    DWORD len;
+
+    access |= KEY_READ;
+
+    if (open_classes_key(HKEY_CLASSES_ROOT, path, access, &hkey))
+        return REGDB_E_IIDNOTREG;
+
+    len = sizeof(value);
+    if (ERROR_SUCCESS != RegQueryValueExW(hkey, NULL, NULL, NULL, (BYTE *)value, &len))
+        return REGDB_E_IIDNOTREG;
+    RegCloseKey(hkey);
+
+    if (CLSIDFromString(value, pclsid) != NOERROR)
+        return REGDB_E_IIDNOTREG;
+
+    return S_OK;
+}
+
 /*****************************************************************************
  *             CoGetPSClsid [OLE32.@]
  *
@@ -2483,7 +2538,7 @@ HRESULT WINAPI CLSIDFromProgIDEx(LPCOLESTR progid, LPCLSID clsid)
  * PARAMS
  *  riid   [I] Interface whose proxy/stub CLSID is to be returned.
  *  pclsid [O] Where to store returned proxy/stub CLSID.
- *
+ * 
  * RETURNS
  *   S_OK
  *   E_OUTOFMEMORY
@@ -2515,12 +2570,12 @@ HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
     static const WCHAR wszInterface[] = {'I','n','t','e','r','f','a','c','e','\\',0};
     static const WCHAR wszPSC[] = {'\\','P','r','o','x','y','S','t','u','b','C','l','s','i','d','3','2',0};
     WCHAR path[ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1 + ARRAYSIZE(wszPSC)];
-    WCHAR value[CHARS_IN_GUID];
-    LONG len;
-    HKEY hkey;
     APARTMENT *apt = COM_CurrentApt();
     struct registered_psclsid *registered_psclsid;
     ACTCTX_SECTION_KEYED_DATA data;
+    HRESULT hr;
+    REGSAM opposite = (sizeof(void*) > sizeof(int)) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY;
+    BOOL is_wow64;
 
     TRACE("() riid=%s, pclsid=%p\n", debugstr_guid(riid), pclsid);
 
@@ -2559,31 +2614,17 @@ HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
     StringFromGUID2(riid, path + ARRAYSIZE(wszInterface) - 1, CHARS_IN_GUID);
     strcpyW(path + ARRAYSIZE(wszInterface) - 1 + CHARS_IN_GUID - 1, wszPSC);
 
-    /* Open the key.. */
-    if (open_classes_key(HKEY_CLASSES_ROOT, path, KEY_READ, &hkey))
-    {
-        WARN("No PSFactoryBuffer object is registered for IID %s\n", debugstr_guid(riid));
-        return REGDB_E_IIDNOTREG;
-    }
-
-    /* ... Once we have the key, query the registry to get the
-       value of CLSID as a string, and convert it into a
-       proper CLSID structure to be passed back to the app */
-    len = sizeof(value);
-    if (ERROR_SUCCESS != RegQueryValueW(hkey, NULL, value, &len))
-    {
-        RegCloseKey(hkey);
-        return REGDB_E_IIDNOTREG;
-    }
-    RegCloseKey(hkey);
+    hr = get_ps_clsid_from_registry(path, 0, pclsid);
+    if (FAILED(hr) && (opposite == KEY_WOW64_32KEY ||
+                       (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64)))
+        hr = get_ps_clsid_from_registry(path, opposite, pclsid);
 
-    /* We have the CLSID we want back from the registry as a string, so
-       let's convert it into a CLSID structure */
-    if (CLSIDFromString(value, pclsid) != NOERROR)
-        return REGDB_E_IIDNOTREG;
+    if (hr == S_OK)
+        TRACE ("() Returning CLSID=%s\n", debugstr_guid(pclsid));
+    else
+        WARN("No PSFactoryBuffer object is registered for IID %s\n", debugstr_guid(riid));
 
-    TRACE ("() Returning CLSID=%s\n", debugstr_guid(pclsid));
-    return S_OK;
+    return hr;
 }
 
 /*****************************************************************************
@@ -2595,7 +2636,7 @@ HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
  * PARAMS
  *  riid   [I] Interface whose proxy/stub CLSID is to be registered.
  *  rclsid [I] CLSID of the proxy/stub.
- *
+ * 
  * RETURNS
  *   Success: S_OK
  *   Failure: E_OUTOFMEMORY
@@ -3159,6 +3200,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstance(
     HRESULT hres;
     LPCLASSFACTORY lpclf = 0;
     APARTMENT *apt;
+    CLSID clsid;
 
     TRACE("(rclsid=%s, pUnkOuter=%p, dwClsContext=%08x, riid=%s, ppv=%p)\n", debugstr_guid(rclsid),
           pUnkOuter, dwClsContext, debugstr_guid(iid), ppv);
@@ -3166,6 +3208,10 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstance(
     if (ppv==0)
         return E_POINTER;
 
+    hres = CoGetTreatAsClass(rclsid, &clsid);
+    if(FAILED(hres))
+        clsid = *rclsid;
+
     *ppv = 0;
 
     if (!(apt = COM_CurrentApt()))
@@ -3181,7 +3227,7 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstance(
     /*
      * The Standard Global Interface Table (GIT) object is a process-wide singleton.
      */
-    if (IsEqualIID(rclsid, &CLSID_StdGlobalInterfaceTable))
+    if (IsEqualIID(&clsid, &CLSID_StdGlobalInterfaceTable))
     {
         IGlobalInterfaceTable *git = get_std_git();
         hres = IGlobalInterfaceTable_QueryInterface(git, iid, ppv);
@@ -3191,13 +3237,13 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstance(
         return S_OK;
     }
 
-    if (IsEqualCLSID(rclsid, &CLSID_ManualResetEvent))
+    if (IsEqualCLSID(&clsid, &CLSID_ManualResetEvent))
         return ManualResetEvent_Construct(pUnkOuter, iid, ppv);
 
     /*
      * Get a class factory to construct the object we want.
      */
-    hres = CoGetClassObject(rclsid,
+    hres = CoGetClassObject(&clsid,
                             dwClsContext,
                             NULL,
                             &IID_IClassFactory,
@@ -3214,11 +3260,11 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstance(
     if (FAILED(hres))
     {
         if (hres == CLASS_E_NOAGGREGATION && pUnkOuter)
-            FIXME("Class %s does not support aggregation\n", debugstr_guid(rclsid));
+            FIXME("Class %s does not support aggregation\n", debugstr_guid(&clsid));
         else
             FIXME("no instance created for interface %s of class %s, hres is 0x%08x\n",
                   debugstr_guid(iid),
-                  debugstr_guid(rclsid),hres);
+                  debugstr_guid(&clsid),hres);
     }
 
     return hres;
@@ -3321,7 +3367,7 @@ HRESULT WINAPI CoGetInstanceFromFile(
 
   init_multi_qi(count, results);
 
-  /* optionaly get CLSID from a file */
+  /* optionally get CLSID from a file */
   if (!rclsid)
   {
     hr = GetClassFile(filename, &clsid);
@@ -3383,7 +3429,7 @@ HRESULT WINAPI CoGetInstanceFromIStorage(
 
   init_multi_qi(count, results);
 
-  /* optionaly get CLSID from a file */
+  /* optionally get CLSID from a file */
   if (!rclsid)
   {
     memset(&stat.clsid, 0, sizeof(stat.clsid));
@@ -3577,14 +3623,14 @@ HRESULT WINAPI CoLockObjectExternal(
     if (!apt) return CO_E_NOTINITIALIZED;
 
     stubmgr = get_stub_manager_from_object(apt, pUnk);
-
+    
     if (stubmgr)
     {
         if (fLock)
             stub_manager_ext_addref(stubmgr, 1, FALSE);
         else
             stub_manager_ext_release(stubmgr, 1, FALSE, fLastUnlockReleases);
-
+        
         stub_manager_int_release(stubmgr);
 
         return S_OK;
@@ -3730,7 +3776,8 @@ HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew)
     res = COM_OpenKeyForCLSID(clsidOld, NULL, KEY_READ | KEY_WRITE, &hkey);
     if (FAILED(res))
         goto done;
-    if (!memcmp( clsidOld, clsidNew, sizeof(*clsidOld) ))
+
+    if (IsEqualGUID( clsidOld, clsidNew ))
     {
        if (!RegQueryValueW(hkey, wszAutoTreatAs, auto_treat_as, &auto_treat_as_size) &&
            CLSIDFromString(auto_treat_as, &id) == S_OK)
@@ -3743,15 +3790,28 @@ HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew)
        }
        else
        {
-           RegDeleteKeyW(hkey, wszTreatAs);
+           if(RegDeleteKeyW(hkey, wszTreatAs))
+               res = REGDB_E_WRITEREGDB;
            goto done;
        }
     }
-    else if (!StringFromGUID2(clsidNew, szClsidNew, ARRAYSIZE(szClsidNew)) &&
-             !RegSetValueW(hkey, wszTreatAs, REG_SZ, szClsidNew, sizeof(szClsidNew)))
+    else
     {
-        res = REGDB_E_WRITEREGDB;
-       goto done;
+        if(IsEqualGUID(clsidNew, &CLSID_NULL)){
+           RegDeleteKeyW(hkey, wszTreatAs);
+        }else{
+            if(!StringFromGUID2(clsidNew, szClsidNew, ARRAYSIZE(szClsidNew))){
+                WARN("StringFromGUID2 failed\n");
+                res = E_FAIL;
+                goto done;
+            }
+
+            if(RegSetValueW(hkey, wszTreatAs, REG_SZ, szClsidNew, sizeof(szClsidNew)) != ERROR_SUCCESS){
+                WARN("RegSetValue failed\n");
+                res = REGDB_E_WRITEREGDB;
+                goto done;
+            }
+        }
     }
 
 done:
@@ -4037,7 +4097,7 @@ HRESULT WINAPI CoAllowSetForegroundWindow(IUnknown *pUnk, void *pvReserved)
     FIXME("(%p, %p): stub\n", pUnk, pvReserved);
     return S_OK;
 }
-
 /***********************************************************************
  *           CoQueryProxyBlanket [OLE32.@]
  *
@@ -4987,13 +5047,19 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID reserved)
     switch(fdwReason) {
     case DLL_PROCESS_ATTACH:
         hProxyDll = hinstDLL;
+#ifdef __REACTOS__
         COMPOBJ_InitProcess();
+#endif
        break;
 
     case DLL_PROCESS_DETACH:
         if (reserved) break;
         release_std_git();
+#ifdef __REACTOS__
         COMPOBJ_UninitProcess();
+#else
+        UnregisterClassW( wszAptWinClass, hProxyDll );
+#endif
         RPC_UnregisterAllChannelHooks();
         COMPOBJ_DllList_Free();
         DeleteCriticalSection(&csRegisteredClassList);
index e151e55..d45cc3f 100644 (file)
@@ -29,7 +29,6 @@ typedef struct FileLockBytesImpl
 {
     ILockBytes ILockBytes_iface;
     LONG ref;
-    ULARGE_INTEGER filesize;
     HANDLE hfile;
     DWORD flProtect;
     LPWSTR pwcsName;
@@ -88,8 +87,6 @@ HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsN
   This->ILockBytes_iface.lpVtbl = &FileLockBytesImpl_Vtbl;
   This->ref = 1;
   This->hfile = hFile;
-  This->filesize.u.LowPart = GetFileSize(This->hfile,
-                                        &This->filesize.u.HighPart);
   This->flProtect = GetProtectMode(openFlags);
 
   if(pwcsName) {
@@ -109,8 +106,6 @@ HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsN
   else
     This->pwcsName = NULL;
 
-  TRACE("file len %u\n", This->filesize.u.LowPart);
-
   *pLockBytes = &This->ILockBytes_iface;
 
   return S_OK;
@@ -228,7 +223,6 @@ static HRESULT WINAPI FileLockBytesImpl_WriteAt(
       ULONG*         pcbWritten)  /* [out] */
 {
     FileLockBytesImpl* This = impl_from_ILockBytes(iface);
-    ULONG size_needed = ulOffset.u.LowPart + cb;
     ULONG bytes_left = cb;
     const BYTE *writePtr = pv;
     BOOL ret;
@@ -246,27 +240,19 @@ static HRESULT WINAPI FileLockBytesImpl_WriteAt(
     if (pcbWritten)
         *pcbWritten = 0;
 
-    if (size_needed > This->filesize.u.LowPart)
-    {
-        ULARGE_INTEGER newSize;
-        newSize.u.HighPart = 0;
-        newSize.u.LowPart = size_needed;
-        ILockBytes_SetSize(iface, newSize);
-    }
-
     offset.QuadPart = ulOffset.QuadPart;
 
     ret = SetFilePointerEx(This->hfile, offset, NULL, FILE_BEGIN);
 
     if (!ret)
-        return STG_E_READFAULT;
+        return STG_E_WRITEFAULT;
 
     while (bytes_left)
     {
         ret = WriteFile(This->hfile, writePtr, bytes_left, &cbWritten, NULL);
 
         if (!ret)
-            return STG_E_READFAULT;
+            return STG_E_WRITEFAULT;
 
         if (pcbWritten)
             *pcbWritten += cbWritten;
@@ -296,10 +282,7 @@ static HRESULT WINAPI FileLockBytesImpl_SetSize(ILockBytes* iface, ULARGE_INTEGE
     HRESULT hr = S_OK;
     LARGE_INTEGER newpos;
 
-    if (This->filesize.u.LowPart == newSize.u.LowPart)
-        return hr;
-
-    TRACE("from %u to %u\n", This->filesize.u.LowPart, newSize.u.LowPart);
+    TRACE("new size %u\n", newSize.u.LowPart);
 
     newpos.QuadPart = newSize.QuadPart;
     if (SetFilePointerEx(This->hfile, newpos, NULL, FILE_BEGIN))
@@ -307,22 +290,82 @@ static HRESULT WINAPI FileLockBytesImpl_SetSize(ILockBytes* iface, ULARGE_INTEGE
         SetEndOfFile(This->hfile);
     }
 
-    This->filesize = newSize;
     return hr;
 }
 
+static HRESULT get_lock_error(void)
+{
+    switch (GetLastError())
+    {
+    case ERROR_LOCK_VIOLATION: return STG_E_LOCKVIOLATION; break;
+    case ERROR_ACCESS_DENIED:  return STG_E_ACCESSDENIED; break;
+    case ERROR_NOT_SUPPORTED:  return STG_E_INVALIDFUNCTION; break;
+    default:
+        FIXME("no mapping for error %d\n", GetLastError());
+        return STG_E_INVALIDFUNCTION;
+    }
+}
+
 static HRESULT WINAPI FileLockBytesImpl_LockRegion(ILockBytes* iface,
     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
 {
-    FIXME("stub\n");
-    return E_NOTIMPL;
+    FileLockBytesImpl* This = impl_from_ILockBytes(iface);
+    OVERLAPPED ol;
+    DWORD lock_flags = LOCKFILE_FAIL_IMMEDIATELY;
+
+    TRACE("ofs %u count %u flags %x\n", libOffset.u.LowPart, cb.u.LowPart, dwLockType);
+
+    if (dwLockType & LOCK_WRITE)
+        return STG_E_INVALIDFUNCTION;
+
+    if (dwLockType & (LOCK_EXCLUSIVE|LOCK_ONLYONCE))
+        lock_flags |= LOCKFILE_EXCLUSIVE_LOCK;
+
+    ol.hEvent = 0;
+    ol.u.s.Offset = libOffset.u.LowPart;
+    ol.u.s.OffsetHigh = libOffset.u.HighPart;
+
+    if (LockFileEx(This->hfile, lock_flags, 0, cb.u.LowPart, cb.u.HighPart, &ol))
+        return S_OK;
+    return get_lock_error();
+}
+
+HRESULT FileLockBytesImpl_LockRegionSync(ILockBytes* iface,
+    ULARGE_INTEGER libOffset, ULARGE_INTEGER cb)
+{
+    FileLockBytesImpl* This = impl_from_ILockBytes(iface);
+    OVERLAPPED ol;
+
+    if (iface->lpVtbl != &FileLockBytesImpl_Vtbl)
+        return E_NOTIMPL;
+
+    ol.hEvent = 0;
+    ol.u.s.Offset = libOffset.u.LowPart;
+    ol.u.s.OffsetHigh = libOffset.u.HighPart;
+
+    if (LockFileEx(This->hfile, LOCKFILE_EXCLUSIVE_LOCK, 0, cb.u.LowPart, cb.u.HighPart, &ol))
+        return S_OK;
+    return get_lock_error();
 }
 
 static HRESULT WINAPI FileLockBytesImpl_UnlockRegion(ILockBytes* iface,
     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
 {
-    FIXME("stub\n");
-    return E_NOTIMPL;
+    FileLockBytesImpl* This = impl_from_ILockBytes(iface);
+    OVERLAPPED ol;
+
+    TRACE("ofs %u count %u flags %x\n", libOffset.u.LowPart, cb.u.LowPart, dwLockType);
+
+    if (dwLockType & LOCK_WRITE)
+        return STG_E_INVALIDFUNCTION;
+
+    ol.hEvent = 0;
+    ol.u.s.Offset = libOffset.u.LowPart;
+    ol.u.s.OffsetHigh = libOffset.u.HighPart;
+
+    if (UnlockFileEx(This->hfile, 0, cb.u.LowPart, cb.u.HighPart, &ol))
+        return S_OK;
+    return get_lock_error();
 }
 
 static HRESULT WINAPI FileLockBytesImpl_Stat(ILockBytes* iface,
@@ -341,7 +384,8 @@ static HRESULT WINAPI FileLockBytesImpl_Stat(ILockBytes* iface,
         pstatstg->pwcsName = NULL;
 
     pstatstg->type = STGTY_LOCKBYTES;
-    pstatstg->cbSize = This->filesize;
+
+    pstatstg->cbSize.u.LowPart = GetFileSize(This->hfile, &pstatstg->cbSize.u.HighPart);
     /* FIXME: If the implementation is exported, we'll need to set other fields. */
 
     return S_OK;
index 95e0e13..e3b5ab9 100644 (file)
@@ -588,22 +588,11 @@ FileMonikerImpl_BindToStorage(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLef
             /* get the file name */
             IMoniker_GetDisplayName(iface,pbc,pmkToLeft,&filePath);
 
-            /* verify if the file contains a storage object */
-            res=StgIsStorageFile(filePath);
+            res=StgOpenStorage(filePath,NULL,STGM_READWRITE|STGM_SHARE_DENY_WRITE,NULL,0,&pstg);
 
-            if(res==S_OK){
+            if (SUCCEEDED(res))
+                *ppvObject=pstg;
 
-                res=StgOpenStorage(filePath,NULL,STGM_READWRITE|STGM_SHARE_DENY_WRITE,NULL,0,&pstg);
-
-                if (SUCCEEDED(res)){
-
-                    *ppvObject=pstg;
-
-                    IStorage_AddRef(pstg);
-
-                    return res;
-                }
-            }
             CoTaskMemFree(filePath);
         }
         else
index 363dfc8..9d7a19e 100644 (file)
@@ -2179,7 +2179,9 @@ static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
     case WM_MBUTTONDOWN:
     case WM_RBUTTONDOWN:
     {
-      OLEDD_TrackStateChange((TrackerWindowInfo*)GetWindowLongPtrA(hwnd, 0));
+      TrackerWindowInfo *trackerInfo = (TrackerWindowInfo*)GetWindowLongPtrA(hwnd, 0);
+      if (trackerInfo->trackingDone) break;
+      OLEDD_TrackStateChange(trackerInfo);
       break;
     }
     case WM_DESTROY:
@@ -2843,6 +2845,8 @@ static inline HRESULT PROPVARIANT_ValidateType(VARTYPE vt)
     case VT_LPWSTR:
     case VT_FILETIME:
     case VT_BLOB:
+    case VT_DISPATCH:
+    case VT_UNKNOWN:
     case VT_STREAM:
     case VT_STORAGE:
     case VT_STREAMED_OBJECT:
@@ -2919,6 +2923,8 @@ HRESULT WINAPI PropVariantClear(PROPVARIANT * pvar) /* [in/out] */
     case VT_UINT:
     case VT_FILETIME:
         break;
+    case VT_DISPATCH:
+    case VT_UNKNOWN:
     case VT_STREAM:
     case VT_STREAMED_OBJECT:
     case VT_STORAGE:
@@ -3032,6 +3038,8 @@ HRESULT WINAPI PropVariantCopy(PROPVARIANT *pvarDest,      /* [out] */
     case VT_DATE:
     case VT_FILETIME:
         break;
+    case VT_DISPATCH:
+    case VT_UNKNOWN:
     case VT_STREAM:
     case VT_STREAMED_OBJECT:
     case VT_STORAGE:
index e5e6963..10ddafe 100644 (file)
@@ -151,7 +151,7 @@ static HRESULT WINAPI StgStreamImpl_Read(
     /*
      * Advance the pointer for the number of positions read.
      */
-    This->currentPosition.u.LowPart += *pcbRead;
+    This->currentPosition.QuadPart += *pcbRead;
   }
 
   TRACE("<-- %08x\n", res);
@@ -232,12 +232,12 @@ static HRESULT WINAPI StgStreamImpl_Write(
   /*
    * Advance the position pointer for the number of positions written.
    */
-  This->currentPosition.u.LowPart += *pcbWritten;
+  This->currentPosition.QuadPart += *pcbWritten;
 
   if (SUCCEEDED(res))
     res = StorageBaseImpl_Flush(This->parentStorage);
 
-  TRACE("<-- S_OK, written %u\n", *pcbWritten);
+  TRACE("<-- %08x, written %u\n", res, *pcbWritten);
   return res;
 }
 
index fd59674..7c4bb21 100644 (file)
@@ -83,6 +83,7 @@ static const IStorageVtbl Storage32InternalImpl_Vtbl;
 /* Method definitions for the Storage32InternalImpl 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);
@@ -91,8 +92,9 @@ static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const
 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);
+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);
@@ -172,10 +174,31 @@ typedef struct TransactedSnapshotImpl
    * Changes are committed to the transacted parent.
    */
   StorageBaseImpl *transactedParent;
+
+  /* The transaction signature from when we last committed */
+  ULONG lastTransactionSig;
 } TransactedSnapshotImpl;
 
+typedef struct TransactedSharedImpl
+{
+  struct StorageBaseImpl base;
+
+  /*
+   * Snapshot and uncommitted changes go here.
+   */
+  TransactedSnapshotImpl *scratch;
+
+  /*
+   * Changes are committed to the transacted parent.
+   */
+  StorageBaseImpl *transactedParent;
+
+  /* The transaction signature from when we last committed */
+  ULONG lastTransactionSig;
+} TransactedSharedImpl;
+
 /* Generic function to create a transacted wrapper for a direct storage object. */
-static HRESULT Storage_ConstructTransacted(StorageBaseImpl* parent, StorageBaseImpl** result);
+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 */
@@ -311,9 +334,9 @@ static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
 ** Block Functions
 */
 
-static ULONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
+static ULONGLONG StorageImpl_GetBigBlockOffset(StorageImpl* This, ULONG index)
 {
-    return (index+1) * This->bigBlockSize;
+    return (ULONGLONG)(index+1) * This->bigBlockSize;
 }
 
 /************************************************************************
@@ -647,7 +670,7 @@ static HRESULT WINAPI StorageBaseImpl_OpenStorage(
     {
       if (grfMode & STGM_TRANSACTED)
       {
-        res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
+        res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
 
         if (FAILED(res))
         {
@@ -1280,8 +1303,7 @@ static HRESULT StorageImpl_CreateDirEntry(
     /*
      * initialize the size used by the directory stream
      */
-    newSize.u.HighPart = 0;
-    newSize.u.LowPart  = storage->bigBlockSize * blockCount;
+    newSize.QuadPart  = (ULONGLONG)storage->bigBlockSize * blockCount;
 
     /*
      * add a block to the directory stream
@@ -2560,7 +2582,6 @@ static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
   if (FAILED(hr)) return hr;
 
   /* Grow the stream if necessary */
-  newSize.QuadPart = 0;
   newSize.QuadPart = offset.QuadPart + size;
 
   if (newSize.QuadPart > data.size.QuadPart)
@@ -2621,6 +2642,108 @@ static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
   return hr;
 }
 
+static HRESULT StorageImpl_GetTransactionSig(StorageBaseImpl *base,
+  ULONG* result, BOOL refresh)
+{
+  StorageImpl *This = (StorageImpl*)base;
+  HRESULT hr=S_OK;
+  DWORD oldTransactionSig = This->transactionSig;
+
+  if (refresh)
+  {
+    ULARGE_INTEGER offset;
+    ULONG bytes_read;
+    BYTE data[4];
+
+    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;
+    }
+  }
+
+  *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)
+{
+  StorageImpl *This = (StorageImpl*)base;
+  HRESULT hr;
+  ULARGE_INTEGER offset, cb;
+
+  if (write)
+  {
+    /* 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;
+  }
+
+  hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE);
+
+  if (hr == STG_E_INVALIDFUNCTION)
+    hr = S_OK;
+
+  return hr;
+}
+
+static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
+{
+  StorageImpl *This = (StorageImpl*)base;
+  HRESULT hr;
+  ULARGE_INTEGER offset, cb;
+
+  if (write)
+  {
+    offset.QuadPart = RANGELOCK_TRANSACTION_FIRST;
+    cb.QuadPart = RANGELOCK_TRANSACTION_LAST - RANGELOCK_TRANSACTION_FIRST + 1;
+  }
+  else
+  {
+    offset.QuadPart = RANGELOCK_COMMIT;
+    cb.QuadPart = 1;
+  }
+
+  hr = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+
+  if (hr == STG_E_INVALIDFUNCTION)
+    hr = S_OK;
+
+  return hr;
+}
+
 static HRESULT StorageImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
 {
   StorageImpl *This = (StorageImpl*) iface;
@@ -2721,69 +2844,163 @@ static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
   StorageImpl_StreamReadAt,
   StorageImpl_StreamWriteAt,
   StorageImpl_StreamSetSize,
-  StorageImpl_StreamLink
+  StorageImpl_StreamLink,
+  StorageImpl_GetTransactionSig,
+  StorageImpl_SetTransactionSig,
+  StorageImpl_LockTransaction,
+  StorageImpl_UnlockTransaction
 };
 
-static HRESULT StorageImpl_Construct(
-  HANDLE       hFile,
-  LPCOLESTR    pwcsName,
-  ILockBytes*  pLkbyt,
-  DWORD        openFlags,
-  BOOL         fileBased,
-  BOOL         create,
-  ULONG        sector_size,
-  StorageImpl** result)
+static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
+    ULARGE_INTEGER cb, DWORD dwLockType)
 {
-  StorageImpl* This;
-  HRESULT     hr = S_OK;
-  DirEntry currentEntry;
-  DirRef      currentEntryRef;
+    HRESULT hr;
+    int delay = 0;
 
-  if ( FAILED( validateSTGM(openFlags) ))
-    return STG_E_INVALIDFLAG;
+    /* if it's a FileLockBytesImpl use LockFileEx in blocking mode */
+    if (SUCCEEDED(FileLockBytesImpl_LockRegionSync(This->lockBytes, offset, cb)))
+        return S_OK;
 
-  This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
-  if (!This)
-    return E_OUTOFMEMORY;
+    /* otherwise we have to fake it based on an async lock */
+    do
+    {
+        hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
 
-  memset(This, 0, sizeof(StorageImpl));
+        if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
+        {
+            Sleep(delay);
+            if (delay < 150) delay++;
+        }
+    } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
 
-  list_init(&This->base.strmHead);
+    return hr;
+}
 
-  list_init(&This->base.storageHead);
+static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start,
+    ULONG end, HRESULT fail_hr)
+{
+    HRESULT hr;
+    ULARGE_INTEGER offset, cb;
 
-  This->base.IStorage_iface.lpVtbl = &Storage32Impl_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;
+    offset.QuadPart = start;
+    cb.QuadPart = 1 + end - start;
 
-  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;
+    hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+    if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
 
-  This->base.reverted = FALSE;
+    if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION)
+        return fail_hr;
+    else
+        return S_OK;
+}
 
-  /*
-   * 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
-  {
-    This->lockBytes = pLkbyt;
-    ILockBytes_AddRef(pLkbyt);
-  }
+static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end)
+{
+    HRESULT hr=S_OK;
+    int i, j;
+    ULARGE_INTEGER offset, cb;
 
-  if (FAILED(hr))
-    goto end;
+    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))
+    {
+        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;
+}
+
+static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags)
+{
+    HRESULT hr;
+    ULARGE_INTEGER offset;
+    ULARGE_INTEGER cb;
+    DWORD share_mode = STGM_SHARE_MODE(openFlags);
+
+    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;
+    }
+
+    /* 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 the ILockBytes doesn't support locking that's ok. */
+    if (FAILED(hr)) return S_OK;
+
+    hr = S_OK;
+
+    /* First check for any conflicting locks. */
+    if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
+        hr = StorageImpl_CheckLockRange(This, RANGELOCK_COMMIT, RANGELOCK_COMMIT, STG_E_LOCKVIOLATION);
+
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
+        hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_READ_FIRST, RANGELOCK_DENY_READ_LAST, STG_E_SHAREVIOLATION);
+
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
+        hr = StorageImpl_CheckLockRange(This, RANGELOCK_DENY_WRITE_FIRST, RANGELOCK_DENY_WRITE_LAST, STG_E_SHAREVIOLATION);
+
+    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)
+    {
+        hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY1_FIRST, RANGELOCK_PRIORITY1_LAST);
+        if (SUCCEEDED(hr))
+            hr = StorageImpl_LockOne(This, RANGELOCK_PRIORITY2_FIRST, RANGELOCK_PRIORITY2_LAST);
+    }
+
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
+        hr = StorageImpl_LockOne(This, RANGELOCK_READ_FIRST, RANGELOCK_READ_LAST);
+
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
+        hr = StorageImpl_LockOne(This, RANGELOCK_WRITE_FIRST, RANGELOCK_WRITE_LAST);
+
+    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 (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 (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
+        hr = StorageImpl_LockOne(This, RANGELOCK_NOSNAPSHOT_FIRST, RANGELOCK_NOSNAPSHOT_LAST);
+
+    offset.QuadPart = RANGELOCK_CHECKLOCKS;
+    cb.QuadPart = 1;
+    ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+
+    return hr;
+}
+
+static HRESULT StorageImpl_Refresh(StorageImpl *This, BOOL new_object, BOOL create)
+{
+  HRESULT hr=S_OK;
+  DirEntry currentEntry;
+  DirRef      currentEntryRef;
+  BlockChainStream *blockChainStream;
 
   if (create)
   {
@@ -2809,7 +3026,7 @@ static HRESULT StorageImpl_Construct(
     This->rootStartBlock        = 1;
     This->smallBlockLimit       = LIMIT_TO_USE_SMALL_BLOCK;
     This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
-    if (sector_size == 4096)
+    if (This->bigBlockSize == 4096)
       This->bigBlockSizeBits      = MAX_BIG_BLOCK_SIZE_BITS;
     else
       This->bigBlockSizeBits      = MIN_BIG_BLOCK_SIZE_BITS;
@@ -2843,7 +3060,7 @@ static HRESULT StorageImpl_Construct(
 
     if (FAILED(hr))
     {
-      goto end;
+      return hr;
     }
   }
 
@@ -2870,8 +3087,7 @@ static HRESULT StorageImpl_Construct(
     This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
     if (!This->extBigBlockDepotLocations)
     {
-      hr = E_OUTOFMEMORY;
-      goto end;
+      return E_OUTOFMEMORY;
     }
 
     This->extBigBlockDepotLocationsSize = cache_size;
@@ -2881,8 +3097,7 @@ static HRESULT StorageImpl_Construct(
       if (current_block == BLOCK_END_OF_CHAIN)
       {
         WARN("File has too few extended big block depot blocks.\n");
-        hr = STG_E_DOCFILECORRUPT;
-        goto end;
+        return STG_E_DOCFILECORRUPT;
       }
       This->extBigBlockDepotLocations[i] = current_block;
       current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
@@ -2897,20 +3112,24 @@ static HRESULT StorageImpl_Construct(
   /*
    * Create the block chain abstractions.
    */
-  if(!(This->rootBlockChain =
+  if(!(blockChainStream =
        BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
   {
-    hr = STG_E_READFAULT;
-    goto end;
+    return STG_E_READFAULT;
   }
+  if (!new_object)
+    BlockChainStream_Destroy(This->rootBlockChain);
+  This->rootBlockChain = blockChainStream;
 
-  if(!(This->smallBlockDepotChain =
+  if(!(blockChainStream =
        BlockChainStream_Construct(This, &This->smallBlockDepotStart,
                                  DIRENTRY_NULL)))
   {
-    hr = STG_E_READFAULT;
-    goto end;
+    return STG_E_READFAULT;
   }
+  if (!new_object)
+    BlockChainStream_Destroy(This->smallBlockDepotChain);
+  This->smallBlockDepotChain = blockChainStream;
 
   /*
    * Write the root storage entry (memory only)
@@ -2963,62 +3182,149 @@ static HRESULT StorageImpl_Construct(
 
   if (FAILED(hr))
   {
-    hr = STG_E_READFAULT;
-    goto end;
+    return STG_E_READFAULT;
   }
 
   /*
    * Create the block chain abstraction for the small block root chain.
    */
-  if(!(This->smallBlockRootChain =
+  if(!(blockChainStream =
        BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
   {
-    hr = STG_E_READFAULT;
+    return STG_E_READFAULT;
   }
+  if (!new_object)
+    BlockChainStream_Destroy(This->smallBlockRootChain);
+  This->smallBlockRootChain = blockChainStream;
 
-end:
-  if (FAILED(hr))
-  {
-    IStorage_Release(&This->base.IStorage_iface);
-    *result = NULL;
-  }
-  else
+  if (!new_object)
   {
-    StorageImpl_Flush(&This->base);
-    *result = This;
+    int i;
+    for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+    {
+      BlockChainStream_Destroy(This->blockChainCache[i]);
+      This->blockChainCache[i] = NULL;
+    }
   }
 
   return hr;
 }
 
-static void StorageImpl_Invalidate(StorageBaseImpl* iface)
+static HRESULT StorageImpl_Construct(
+  HANDLE       hFile,
+  LPCOLESTR    pwcsName,
+  ILockBytes*  pLkbyt,
+  DWORD        openFlags,
+  BOOL         fileBased,
+  BOOL         create,
+  ULONG        sector_size,
+  StorageImpl** result)
 {
-  StorageImpl *This = (StorageImpl*) iface;
+  StorageImpl* This;
+  HRESULT     hr = S_OK;
 
-  StorageBaseImpl_DeleteAll(&This->base);
+  if ( FAILED( validateSTGM(openFlags) ))
+    return STG_E_INVALIDFLAG;
 
-  This->base.reverted = TRUE;
-}
+  This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
+  if (!This)
+    return E_OUTOFMEMORY;
 
-static void StorageImpl_Destroy(StorageBaseImpl* iface)
-{
-  StorageImpl *This = (StorageImpl*) iface;
-  int i;
-  TRACE("(%p)\n", This);
+  memset(This, 0, sizeof(StorageImpl));
 
-  StorageImpl_Flush(iface);
+  list_init(&This->base.strmHead);
 
-  StorageImpl_Invalidate(iface);
+  list_init(&This->base.storageHead);
 
-  HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
+  This->base.IStorage_iface.lpVtbl = &Storage32Impl_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;
 
-  BlockChainStream_Destroy(This->smallBlockRootChain);
-  BlockChainStream_Destroy(This->rootBlockChain);
-  BlockChainStream_Destroy(This->smallBlockDepotChain);
+  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;
 
-  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+  This->base.reverted = FALSE;
+
+  /*
+   * 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
+  {
+    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);
+
+  StorageImpl_Flush(iface);
+
+  StorageImpl_Invalidate(iface);
+
+  HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
+
+  BlockChainStream_Destroy(This->smallBlockRootChain);
+  BlockChainStream_Destroy(This->rootBlockChain);
+  BlockChainStream_Destroy(This->smallBlockDepotChain);
+
+  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++)
+  {
+    ULARGE_INTEGER offset, cb;
+    cb.QuadPart = 1;
+    if (This->locked_bytes[i] != 0)
+    {
+      offset.QuadPart = This->locked_bytes[i];
+      ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+    }
+  }
+
   if (This->lockBytes)
     ILockBytes_Release(This->lockBytes);
   HeapFree(GetProcessHeap(), 0, This);
@@ -3092,7 +3398,7 @@ static ULONG StorageImpl_GetNextFreeBigBlock(
         /*
          * Add a block depot.
          */
-        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
+        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
         This->bigBlockDepotCount++;
         This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
 
@@ -3135,7 +3441,7 @@ static ULONG StorageImpl_GetNextFreeBigBlock(
         /*
          * Add a block depot and mark it in the extended block.
          */
-        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
+        Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
         This->bigBlockDepotCount++;
         Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
 
@@ -3200,14 +3506,24 @@ static ULONG StorageImpl_GetNextFreeBigBlock(
  * This will create a depot block, essentially it is a block initialized
  * to BLOCK_UNUSEDs.
  */
-static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
+static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
 {
   BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
+  ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
+  ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
+  ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
 
   /*
    * Initialize blocks as free
    */
   memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
+
+  /* Reserve the range lock sector */
+  if (depotIndex == rangeLockDepot)
+  {
+    ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
+  }
+
   StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
 }
 
@@ -3504,6 +3820,13 @@ static void StorageImpl_SetNextBlockInChain(
   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)
   {
     depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
@@ -3590,6 +3913,11 @@ static HRESULT StorageImpl_LoadFileHeader(
       OFFSET_ROOTSTARTBLOCK,
       &This->rootStartBlock);
 
+    StorageUtl_ReadDWord(
+      headerBigBlock,
+      OFFSET_TRANSACTIONSIG,
+      &This->transactionSig);
+
     StorageUtl_ReadDWord(
       headerBigBlock,
       OFFSET_SMALLBLOCKLIMIT,
@@ -3748,6 +4076,11 @@ static void StorageImpl_SaveFileHeader(
     OFFSET_ROOTSTARTBLOCK,
     This->rootStartBlock);
 
+  StorageUtl_WriteDWord(
+    headerBigBlock,
+    OFFSET_TRANSACTIONSIG,
+    This->transactionSig);
+
   StorageUtl_WriteDWord(
     headerBigBlock,
     OFFSET_SMALLBLOCKLIMIT,
@@ -3801,8 +4134,7 @@ HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer
   HRESULT hr;
   ULONG bytesRead;
 
-  offset.u.HighPart = 0;
-  offset.u.LowPart  = index * RAW_DIRENTRY_SIZE;
+  offset.QuadPart  = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
 
   hr = BlockChainStream_ReadAt(
                     This->rootBlockChain,
@@ -3829,8 +4161,7 @@ HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE
   ULARGE_INTEGER offset;
   ULONG bytesRead;
 
-  offset.u.HighPart = 0;
-  offset.u.LowPart  = index * RAW_DIRENTRY_SIZE;
+  offset.QuadPart  = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
 
   return BlockChainStream_WriteAt(
                     This->rootBlockChain,
@@ -3912,6 +4243,11 @@ void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
     buffer,
       OFFSET_PS_SIZE,
       newData->size.u.LowPart);
+
+  StorageUtl_WriteDWord(
+    buffer,
+      OFFSET_PS_SIZE_HIGH,
+      newData->size.u.HighPart);
 }
 
 /******************************************************************************
@@ -3995,7 +4331,10 @@ HRESULT StorageImpl_ReadDirEntry(
       OFFSET_PS_SIZE,
       &buffer->size.u.LowPart);
 
-    buffer->size.u.HighPart = 0;
+    StorageUtl_ReadDWord(
+      currentEntry,
+      OFFSET_PS_SIZE_HIGH,
+      &buffer->size.u.HighPart);
   }
 
   return readRes;
@@ -4026,8 +4365,7 @@ static HRESULT StorageImpl_ReadBigBlock(
   DWORD  read=0;
   HRESULT hr;
 
-  ulOffset.u.HighPart = 0;
-  ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
 
   hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
 
@@ -4052,9 +4390,8 @@ static BOOL StorageImpl_ReadDWordFromBigBlock(
   DWORD  read;
   DWORD  tmp;
 
-  ulOffset.u.HighPart = 0;
-  ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
-  ulOffset.u.LowPart += offset;
+  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+  ulOffset.QuadPart += offset;
 
   StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
   *value = lendian32toh(tmp);
@@ -4069,8 +4406,7 @@ static BOOL StorageImpl_WriteBigBlock(
   ULARGE_INTEGER ulOffset;
   DWORD  wrote;
 
-  ulOffset.u.HighPart = 0;
-  ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
 
   StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
   return (wrote == This->bigBlockSize);
@@ -4085,9 +4421,8 @@ static BOOL StorageImpl_WriteDWordToBigBlock(
   ULARGE_INTEGER ulOffset;
   DWORD  wrote;
 
-  ulOffset.u.HighPart = 0;
-  ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
-  ulOffset.u.LowPart += offset;
+  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
+  ulOffset.QuadPart += offset;
 
   value = htole32(value);
   StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
@@ -4342,6 +4677,106 @@ static HRESULT StorageBaseImpl_CopyStream(
   return hr;
 }
 
+static HRESULT StorageBaseImpl_DupStorageTree(
+  StorageBaseImpl *dst, DirRef *dst_entry,
+  StorageBaseImpl *src, DirRef src_entry)
+{
+  HRESULT hr;
+  DirEntry data;
+  BOOL has_stream=FALSE;
+
+  if (src_entry == DIRENTRY_NULL)
+  {
+    *dst_entry = DIRENTRY_NULL;
+    return S_OK;
+  }
+
+  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
+  if (SUCCEEDED(hr))
+  {
+    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);
+  }
+
+  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);
+
+  return hr;
+}
+
+static HRESULT StorageBaseImpl_CopyStorageTree(
+  StorageBaseImpl *dst, DirRef dst_entry,
+  StorageBaseImpl *src, DirRef src_entry)
+{
+  HRESULT hr;
+  DirEntry src_data, dst_data;
+  DirRef new_root_entry;
+
+  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
+
+  if (SUCCEEDED(hr))
+  {
+    hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
+  }
+
+  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;
+  }
+
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_WriteDirEntry(dst, dst_entry, &dst_data);
+
+  return hr;
+}
+
+static HRESULT StorageBaseImpl_DeleteStorageTree(StorageBaseImpl *This, DirRef entry, BOOL include_siblings)
+{
+  HRESULT hr;
+  DirEntry data;
+  ULARGE_INTEGER zero;
+
+  if (entry == DIRENTRY_NULL)
+    return S_OK;
+
+  zero.QuadPart = 0;
+
+  hr = StorageBaseImpl_ReadDirEntry(This, entry, &data);
+
+  if (SUCCEEDED(hr) && include_siblings)
+    hr = StorageBaseImpl_DeleteStorageTree(This, data.leftChild, TRUE);
+
+  if (SUCCEEDED(hr) && include_siblings)
+    hr = StorageBaseImpl_DeleteStorageTree(This, data.rightChild, TRUE);
+
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_DeleteStorageTree(This, data.dirRootEntry, TRUE);
+
+  if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
+    hr = StorageBaseImpl_StreamSetSize(This, entry, zero);
+
+  if (SUCCEEDED(hr))
+    hr = StorageBaseImpl_DestroyDirEntry(This, entry);
+
+  return hr;
+}
+
 static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
 {
   DirRef result=This->firstFreeEntry;
@@ -4696,6 +5131,7 @@ static HRESULT WINAPI TransactedSnapshotImpl_Commit(
   DirEntry data;
   ULARGE_INTEGER zero;
   HRESULT hr;
+  ULONG transactionSig;
 
   zero.QuadPart = 0;
 
@@ -4705,89 +5141,116 @@ static HRESULT WINAPI TransactedSnapshotImpl_Commit(
   if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
     return STG_E_ACCESSDENIED;
 
-  /* 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. */
+  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))
+    {
+      if (transactionSig != This->lastTransactionSig)
+      {
+        ERR("file was externally modified\n");
+        hr = STG_E_NOTCURRENT;
+      }
 
-  root_entry = &This->entries[This->base.storageDirEntry];
+      if (SUCCEEDED(hr))
+      {
+        This->lastTransactionSig = transactionSig+1;
+        hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, This->lastTransactionSig);
+      }
+    }
+    else if (hr == E_NOTIMPL)
+      hr = S_OK;
 
-  if (!root_entry->read)
-    return S_OK;
+    if (FAILED(hr)) goto end;
 
-  hr = TransactedSnapshotImpl_CopyTree(This);
-  if (FAILED(hr)) return hr;
+    /* 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 (root_entry->data.dirRootEntry == DIRENTRY_NULL)
-    dir_root_ref = DIRENTRY_NULL;
-  else
-    dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
+    root_entry = &This->entries[This->base.storageDirEntry];
 
-  hr = StorageBaseImpl_Flush(This->transactedParent);
+    if (!root_entry->read)
+      goto end;
 
-  /* Update the storage to use the new data in one step. */
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
-      root_entry->transactedParentEntry, &data);
+    hr = TransactedSnapshotImpl_CopyTree(This);
+    if (FAILED(hr)) goto end;
 
-  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 (root_entry->data.dirRootEntry == DIRENTRY_NULL)
+      dir_root_ref = DIRENTRY_NULL;
+    else
+      dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
 
-    hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
-      root_entry->transactedParentEntry, &data);
-  }
+    hr = StorageBaseImpl_Flush(This->transactedParent);
 
-  /* 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);
+    /* 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))
-  {
-    /* Destroy the old now-orphaned data. */
-    for (i=0; i<This->entries_size; i++)
+    if (SUCCEEDED(hr))
     {
-      TransactedDirEntry *entry = &This->entries[i];
-      if (entry->inuse)
+      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++)
       {
-        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)
+        TransactedDirEntry *entry = &This->entries[i];
+        if (entry->inuse)
         {
-          if (entry->transactedParentEntry != DIRENTRY_NULL)
+          if (entry->deleted)
+          {
+            StorageBaseImpl_StreamSetSize(This->transactedParent,
+              entry->transactedParentEntry, zero);
             StorageBaseImpl_DestroyDirEntry(This->transactedParent,
               entry->transactedParentEntry);
-          if (entry->stream_dirty)
+            memset(entry, 0, sizeof(TransactedDirEntry));
+            This->firstFreeEntry = min(i, This->firstFreeEntry);
+          }
+          else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
           {
-            StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
-            StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
-            entry->stream_dirty = FALSE;
+            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;
           }
-          entry->dirty = FALSE;
-          entry->transactedParentEntry = entry->newTransactedParentEntry;
         }
       }
     }
-  }
-  else
-  {
-    TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
-  }
+    else
+    {
+      TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
+    }
 
-  if (SUCCEEDED(hr))
-    hr = StorageBaseImpl_Flush(This->transactedParent);
+    if (SUCCEEDED(hr))
+      hr = StorageBaseImpl_Flush(This->transactedParent);
+end:
+    StorageBaseImpl_UnlockTransaction(This->transactedParent, TRUE);
+  }
 
   return hr;
 }
@@ -4932,157 +5395,510 @@ static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
 
   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);
+  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 TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
+  DirRef index)
+{
+  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+
+  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)
+{
+  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;
+}
+
+static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
+  DirRef dst, DirRef src)
+{
+  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;
+
+  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 =
+{
+    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
+};
+
+static const StorageBaseImplVtbl TransactedSnapshotImpl_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
+};
+
+static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
+  TransactedSnapshotImpl** result)
+{
+  HRESULT hr;
+
+  *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
+  if (*result)
+  {
+    IStorage *scratch;
+
+    (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
+
+    /* 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;
+
+    list_init(&(*result)->base.strmHead);
+
+    list_init(&(*result)->base.storageHead);
+
+    (*result)->base.ref = 1;
+
+    (*result)->base.openFlags = parentStorage->openFlags;
+
+    /* This cannot fail, except with E_NOTIMPL in which case we don't care */
+    StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, 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);
+
+    if (SUCCEEDED(hr))
+    {
+        ULONG num_entries = 20;
+
+        (*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;
+
+            parentStorage->transactedChild = &(*result)->base;
+
+            (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
+        }
+        else
+        {
+            IStorage_Release(scratch);
+
+            hr = E_OUTOFMEMORY;
+        }
+    }
+
+    if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, *result);
+
+    return hr;
+  }
+  else
+    return E_OUTOFMEMORY;
+}
+
+static void TransactedSharedImpl_Invalidate(StorageBaseImpl* This)
+{
+  if (!This->reverted)
+  {
+    TRACE("Storage invalidated (stg=%p)\n", This);
+
+    This->reverted = TRUE;
+
+    StorageBaseImpl_DeleteAll(This);
+  }
+}
+
+static void TransactedSharedImpl_Destroy( StorageBaseImpl *iface)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
+
+  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 TransactedSharedImpl_GetFilename(StorageBaseImpl* iface, LPWSTR *result)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) iface;
+
+  return StorageBaseImpl_GetFilename(This->transactedParent, result);
+}
+
+static HRESULT TransactedSharedImpl_CreateDirEntry(StorageBaseImpl *base,
+  const DirEntry *newData, DirRef *index)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+
+  return StorageBaseImpl_CreateDirEntry(&This->scratch->base,
+    newData, index);
+}
+
+static HRESULT TransactedSharedImpl_WriteDirEntry(StorageBaseImpl *base,
+  DirRef index, const DirEntry *data)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
+
+  return StorageBaseImpl_WriteDirEntry(&This->scratch->base,
+    index, data);
+}
+
+static HRESULT TransactedSharedImpl_ReadDirEntry(StorageBaseImpl *base,
+  DirRef index, DirEntry *data)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  return S_OK;
+  return StorageBaseImpl_ReadDirEntry(&This->scratch->base,
+    index, data);
 }
 
-static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
+static HRESULT TransactedSharedImpl_DestroyDirEntry(StorageBaseImpl *base,
   DirRef index)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-
-  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;
-  }
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  return S_OK;
+  return StorageBaseImpl_DestroyDirEntry(&This->scratch->base,
+    index);
 }
 
-static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
+static HRESULT TransactedSharedImpl_StreamReadAt(StorageBaseImpl *base,
   DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+  TransactedSharedImpl* This = (TransactedSharedImpl*) 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);
-  }
+  return StorageBaseImpl_StreamReadAt(&This->scratch->base,
+    index, offset, size, buffer, bytesRead);
 }
 
-static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
+static HRESULT TransactedSharedImpl_StreamWriteAt(StorageBaseImpl *base,
   DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-  HRESULT hr;
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
-  if (FAILED(hr)) return hr;
+  return StorageBaseImpl_StreamWriteAt(&This->scratch->base,
+    index, offset, size, buffer, bytesWritten);
+}
 
-  hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
-  if (FAILED(hr)) return hr;
+static HRESULT TransactedSharedImpl_StreamSetSize(StorageBaseImpl *base,
+  DirRef index, ULARGE_INTEGER newsize)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  hr = StorageBaseImpl_StreamWriteAt(This->scratch,
-    This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
+  return StorageBaseImpl_StreamSetSize(&This->scratch->base,
+    index, newsize);
+}
 
-  if (SUCCEEDED(hr) && size != 0)
-    This->entries[index].data.size.QuadPart = max(
-        This->entries[index].data.size.QuadPart,
-        offset.QuadPart + size);
+static HRESULT TransactedSharedImpl_StreamLink(StorageBaseImpl *base,
+  DirRef dst, DirRef src)
+{
+  TransactedSharedImpl* This = (TransactedSharedImpl*) base;
 
-  return hr;
+  return StorageBaseImpl_StreamLink(&This->scratch->base,
+    dst, src);
 }
 
-static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
-  DirRef index, ULARGE_INTEGER newsize)
+static HRESULT TransactedSharedImpl_GetTransactionSig(StorageBaseImpl *base,
+  ULONG* result, BOOL refresh)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+  return E_NOTIMPL;
+}
+
+static HRESULT TransactedSharedImpl_SetTransactionSig(StorageBaseImpl *base,
+  ULONG value)
+{
+  return E_NOTIMPL;
+}
+
+static HRESULT TransactedSharedImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
+{
+  return E_NOTIMPL;
+}
+
+static HRESULT TransactedSharedImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
+{
+  return E_NOTIMPL;
+}
+
+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;
 
-  hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
-  if (FAILED(hr)) return hr;
+  TRACE("(%p,%x)\n", iface, grfCommitFlags);
 
-  if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
-    return S_OK;
+  /* Cannot commit a read-only transacted storage */
+  if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
+    return STG_E_ACCESSDENIED;
 
-  if (newsize.QuadPart == 0)
+  hr = StorageBaseImpl_LockTransaction(This->transactedParent, TRUE);
+  if (hr == E_NOTIMPL) hr = S_OK;
+  if (SUCCEEDED(hr))
   {
-    /* Destroy any parent references or entries in the scratch file. */
-    if (This->entries[index].stream_dirty)
+    hr = StorageBaseImpl_GetTransactionSig(This->transactedParent, &transactionSig, TRUE);
+    if (SUCCEEDED(hr))
     {
-      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;
+      if ((grfCommitFlags & STGC_ONLYIFCURRENT) && transactionSig != This->lastTransactionSig)
+        hr = STG_E_NOTCURRENT;
+
+      if (SUCCEEDED(hr))
+        hr = StorageBaseImpl_SetTransactionSig(This->transactedParent, transactionSig+1);
     }
-    else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
+    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))
     {
-      DirRef delete_ref;
-      delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
+      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 (delete_ref != DIRENTRY_NULL)
-        This->entries[delete_ref].deleted = TRUE;
+    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);
 
-      This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
+      hr = StorageBaseImpl_DeleteStorageTree(This->transactedParent, prev_storage_ref, TRUE);
     }
-  }
-  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))
+      hr = StorageBaseImpl_Flush(This->transactedParent);
 
-  if (SUCCEEDED(hr))
-    This->entries[index].data.size = newsize;
+    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 hr;
 }
 
-static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
-  DirRef dst, DirRef src)
+static HRESULT WINAPI TransactedSharedImpl_Revert(
+  IStorage*            iface)
 {
-  TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-  HRESULT hr;
-  TransactedDirEntry *dst_entry, *src_entry;
-
-  hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
-  if (FAILED(hr)) return hr;
+  TransactedSharedImpl* This = (TransactedSharedImpl*)impl_from_IStorage(iface);
 
-  hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
-  if (FAILED(hr)) return hr;
-
-  dst_entry = &This->entries[dst];
-  src_entry = &This->entries[src];
+  TRACE("(%p)\n", iface);
 
-  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;
+  /* Destroy the open objects. */
+  StorageBaseImpl_DeleteAll(&This->base);
 
-  return S_OK;
+  return IStorage_Revert(&This->scratch->base.IStorage_iface);
 }
 
-static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
+static const IStorageVtbl TransactedSharedImpl_Vtbl =
 {
     StorageBaseImpl_QueryInterface,
     StorageBaseImpl_AddRef,
@@ -5093,8 +5909,8 @@ static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
     StorageBaseImpl_OpenStorage,
     StorageBaseImpl_CopyTo,
     StorageBaseImpl_MoveElementTo,
-    TransactedSnapshotImpl_Commit,
-    TransactedSnapshotImpl_Revert,
+    TransactedSharedImpl_Commit,
+    TransactedSharedImpl_Revert,
     StorageBaseImpl_EnumElements,
     StorageBaseImpl_DestroyElement,
     StorageBaseImpl_RenameElement,
@@ -5104,37 +5920,41 @@ static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
     StorageBaseImpl_Stat
 };
 
-static const StorageBaseImplVtbl TransactedSnapshotImpl_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
+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
 };
 
-static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
-  TransactedSnapshotImpl** result)
+static HRESULT TransactedSharedImpl_Construct(StorageBaseImpl *parentStorage,
+  TransactedSharedImpl** result)
 {
   HRESULT hr;
 
-  *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
+  *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSharedImpl));
   if (*result)
   {
     IStorage *scratch;
 
-    (*result)->base.IStorage_iface.lpVtbl = &TransactedSnapshotImpl_Vtbl;
+    (*result)->base.IStorage_iface.lpVtbl = &TransactedSharedImpl_Vtbl;
 
     /* 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;
+    (*result)->base.baseVtbl = &TransactedSharedImpl_BaseVtbl;
 
     list_init(&(*result)->base.strmHead);
 
@@ -5144,34 +5964,43 @@ static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
 
     (*result)->base.openFlags = parentStorage->openFlags;
 
-    /* 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);
+    hr = StorageBaseImpl_LockTransaction(parentStorage, FALSE);
 
     if (SUCCEEDED(hr))
     {
-        ULONG num_entries = 20;
+      STGOPTIONS stgo;
 
-        (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
-        (*result)->entries_size = num_entries;
-        (*result)->firstFreeEntry = 0;
+      /* This cannot fail, except with E_NOTIMPL in which case we don't care */
+      StorageBaseImpl_GetTransactionSig(parentStorage, &(*result)->lastTransactionSig, FALSE);
 
-        if ((*result)->entries)
-        {
-            /* parentStorage already has 1 reference, which we take over here. */
-            (*result)->transactedParent = parentStorage;
+      stgo.usVersion = 1;
+      stgo.reserved = 0;
+      stgo.ulSectorSize = 4096;
+      stgo.pwcsTemplateFile = NULL;
 
-            parentStorage->transactedChild = &(*result)->base;
+      /* 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);
 
-            (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
-        }
-        else
+      if (SUCCEEDED(hr))
+      {
+        hr = StorageBaseImpl_CopyStorageTree(&(*result)->scratch->base, (*result)->scratch->base.storageDirEntry,
+          parentStorage, parentStorage->storageDirEntry);
+
+        if (SUCCEEDED(hr))
         {
-            IStorage_Release(scratch);
+          hr = IStorage_Commit(scratch, STGC_DEFAULT);
 
-            hr = E_OUTOFMEMORY;
+          (*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);
@@ -5183,15 +6012,24 @@ static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
 }
 
 static HRESULT Storage_ConstructTransacted(StorageBaseImpl *parentStorage,
-  StorageBaseImpl** result)
+  BOOL toplevel, StorageBaseImpl** result)
 {
-  static int fixme=0;
+  static int fixme_flags=STGM_NOSCRATCH|STGM_NOSNAPSHOT;
 
-  if (parentStorage->openFlags & (STGM_NOSCRATCH|STGM_NOSNAPSHOT) && !fixme++)
+  if (parentStorage->openFlags & fixme_flags)
   {
+    fixme_flags &= ~parentStorage->openFlags;
     FIXME("Unimplemented flags %x\n", parentStorage->openFlags);
   }
 
+  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);
 }
@@ -5215,7 +6053,7 @@ static HRESULT Storage_Construct(
 
   if (openFlags & STGM_TRANSACTED)
   {
-    hr = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
+    hr = Storage_ConstructTransacted(&newStorage->base, TRUE, &newTransactedStorage);
     if (FAILED(hr))
       IStorage_Release(&newStorage->base.IStorage_iface);
     else
@@ -5341,6 +6179,28 @@ static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
     dst, src);
 }
 
+static HRESULT StorageInternalImpl_GetTransactionSig(StorageBaseImpl *base,
+  ULONG* result, BOOL refresh)
+{
+  return E_NOTIMPL;
+}
+
+static HRESULT StorageInternalImpl_SetTransactionSig(StorageBaseImpl *base,
+  ULONG value)
+{
+  return E_NOTIMPL;
+}
+
+static HRESULT StorageInternalImpl_LockTransaction(StorageBaseImpl *base, BOOL write)
+{
+  return E_NOTIMPL;
+}
+
+static HRESULT StorageInternalImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write)
+{
+  return E_NOTIMPL;
+}
+
 /******************************************************************************
 **
 ** Storage32InternalImpl_Commit
@@ -5691,7 +6551,11 @@ static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl =
   StorageInternalImpl_StreamReadAt,
   StorageInternalImpl_StreamWriteAt,
   StorageInternalImpl_StreamSetSize,
-  StorageInternalImpl_StreamLink
+  StorageInternalImpl_StreamLink,
+  StorageInternalImpl_GetTransactionSig,
+  StorageInternalImpl_SetTransactionSig,
+  StorageInternalImpl_LockTransaction,
+  StorageInternalImpl_UnlockTransaction
 };
 
 /******************************************************************************
@@ -6145,8 +7009,8 @@ HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
   void*          buffer,
   ULONG*         bytesRead)
 {
-  ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
-  ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
+  ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
+  ULONG offsetInBlock     = offset.QuadPart % This->parentStorage->bigBlockSize;
   ULONG bytesToReadInBuffer;
   ULONG blockIndex;
   BYTE* bufferWalker;
@@ -6193,8 +7057,7 @@ HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
     if (!cachedBlock)
     {
       /* Not in cache, and we're going to read past the end of the block. */
-      ulOffset.u.HighPart = 0;
-      ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
+      ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
                                offsetInBlock;
 
       StorageImpl_ReadAt(This->parentStorage,
@@ -6243,8 +7106,8 @@ HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
   const void*       buffer,
   ULONG*            bytesWritten)
 {
-  ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
-  ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
+  ULONG blockNoInSequence = offset.QuadPart / This->parentStorage->bigBlockSize;
+  ULONG offsetInBlock     = offset.QuadPart % This->parentStorage->bigBlockSize;
   ULONG bytesToWrite;
   ULONG blockIndex;
   const BYTE* bufferWalker;
@@ -6278,8 +7141,7 @@ HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
     if (!cachedBlock)
     {
       /* Not in cache, and we're going to write past the end of the block. */
-      ulOffset.u.HighPart = 0;
-      ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
+      ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) +
                                offsetInBlock;
 
       StorageImpl_WriteAt(This->parentStorage,
@@ -6331,9 +7193,9 @@ static BOOL BlockChainStream_Shrink(BlockChainStream* This,
   /*
    * Figure out how many blocks are needed to contain the new size
    */
-  numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
+  numBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
 
-  if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
+  if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
     numBlocks++;
 
   if (numBlocks)
@@ -6462,9 +7324,9 @@ static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
   /*
    * Figure out how many blocks are needed to contain this stream
    */
-  newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
+  newNumBlocks = newSize.QuadPart / This->parentStorage->bigBlockSize;
 
-  if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
+  if ((newSize.QuadPart % This->parentStorage->bigBlockSize) != 0)
     newNumBlocks++;
 
   /*
@@ -6539,10 +7401,10 @@ BOOL BlockChainStream_SetSize(
 {
   ULARGE_INTEGER size = BlockChainStream_GetSize(This);
 
-  if (newSize.u.LowPart == size.u.LowPart)
+  if (newSize.QuadPart == size.QuadPart)
     return TRUE;
 
-  if (newSize.u.LowPart < size.u.LowPart)
+  if (newSize.QuadPart < size.QuadPart)
   {
     BlockChainStream_Shrink(This, newSize);
   }
@@ -6584,10 +7446,8 @@ static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
      * size of them
      */
     ULARGE_INTEGER result;
-    result.u.HighPart = 0;
-
-    result.u.LowPart  =
-      BlockChainStream_GetCount(This) *
+    result.QuadPart =
+      (ULONGLONG)BlockChainStream_GetCount(This) *
       This->parentStorage->bigBlockSize;
 
     return result;
@@ -6672,8 +7532,7 @@ static HRESULT SmallBlockChainStream_GetNextBlockInChain(
 
   *nextBlockInChain = BLOCK_END_OF_CHAIN;
 
-  offsetOfBlockInDepot.u.HighPart = 0;
-  offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
+  offsetOfBlockInDepot.QuadPart  = (ULONGLONG)blockIndex * sizeof(ULONG);
 
   /*
    * Read those bytes in the buffer from the small block file.
@@ -6714,8 +7573,7 @@ static void SmallBlockChainStream_SetNextBlockInChain(
   DWORD  buffer;
   ULONG  bytesWritten;
 
-  offsetOfBlockInDepot.u.HighPart = 0;
-  offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
+  offsetOfBlockInDepot.QuadPart  = (ULONGLONG)blockIndex * sizeof(ULONG);
 
   StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
 
@@ -6770,7 +7628,7 @@ static ULONG SmallBlockChainStream_GetNextFreeBlock(
    */
   while (nextBlockIndex != BLOCK_UNUSED)
   {
-    offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
+    offsetOfBlockInDepot.QuadPart = (ULONGLONG)blockIndex * sizeof(ULONG);
 
     res = BlockChainStream_ReadAt(
                 This->parentStorage->smallBlockDepotChain,
@@ -6798,14 +7656,14 @@ static ULONG SmallBlockChainStream_GetNextFreeBlock(
       ULARGE_INTEGER newSize, offset;
       ULONG bytesWritten;
 
-      newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
+      newSize.QuadPart = (ULONGLONG)(count + 1) * This->parentStorage->bigBlockSize;
       BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
 
       /*
        * Initialize all the small blocks to free
        */
       memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
-      offset.QuadPart = count * This->parentStorage->bigBlockSize;
+      offset.QuadPart = (ULONGLONG)count * This->parentStorage->bigBlockSize;
       BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
         offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
 
@@ -6823,7 +7681,7 @@ static ULONG SmallBlockChainStream_GetNextFreeBlock(
    */
   blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
 
-  size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
+  size_required.QuadPart = (ULONGLONG)blocksRequired * This->parentStorage->bigBlockSize;
 
   old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
 
@@ -6917,11 +7775,10 @@ HRESULT SmallBlockChainStream_ReadAt(
     /*
      * Calculate the offset of the small block in the small block file.
      */
-    offsetInBigBlockFile.u.HighPart  = 0;
-    offsetInBigBlockFile.u.LowPart   =
-      blockIndex * This->parentStorage->smallBlockSize;
+    offsetInBigBlockFile.QuadPart   =
+      (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
 
-    offsetInBigBlockFile.u.LowPart  += offsetInBlock;
+    offsetInBigBlockFile.QuadPart  += offsetInBlock;
 
     /*
      * Read those bytes in the buffer from the small block file.
@@ -7013,11 +7870,10 @@ HRESULT SmallBlockChainStream_WriteAt(
     /*
      * Calculate the offset of the small block in the small block file.
      */
-    offsetInBigBlockFile.u.HighPart  = 0;
-    offsetInBigBlockFile.u.LowPart   =
-      blockIndex * This->parentStorage->smallBlockSize;
+    offsetInBigBlockFile.QuadPart   =
+      (ULONGLONG)blockIndex * This->parentStorage->smallBlockSize;
 
-    offsetInBigBlockFile.u.LowPart  += offsetInBlock;
+    offsetInBigBlockFile.QuadPart  += offsetInBlock;
 
     /*
      * Write those bytes in the buffer to the small block file.
@@ -7034,9 +7890,9 @@ HRESULT SmallBlockChainStream_WriteAt(
     /*
      * Step to the next big block.
      */
-    if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
-                                                       &blockIndex)))
-      return FALSE;
+    res = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
+    if (FAILED(res))
+      return res;
     bufferWalker  += bytesWrittenToBigBlockFile;
     size          -= bytesWrittenToBigBlockFile;
     *bytesWritten += bytesWrittenToBigBlockFile;
@@ -7378,7 +8234,7 @@ static HRESULT create_storagefile(
   /*
    * Interpret the STGM value grfMode
    */
-  shareMode    = FILE_SHARE_READ | FILE_SHARE_WRITE;
+  shareMode    = GetShareModeFromSTGM(grfMode);
   accessMode   = GetAccessModeFromSTGM(grfMode);
 
   if (grfMode & STGM_DELETEONRELEASE)
@@ -7386,13 +8242,6 @@ static HRESULT create_storagefile(
   else
     fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
 
-  if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
-  {
-    static int fixme;
-    if (!fixme++)
-      FIXME("Storage share mode not implemented.\n");
-  }
-
   *ppstgOpen = 0;
 
   hFile = CreateFileW(pwcsName,
@@ -7633,13 +8482,6 @@ HRESULT WINAPI StgOpenStorage(
       return STG_E_INVALIDFLAG;
     grfMode &= ~0xf0; /* remove the existing sharing mode */
     grfMode |= STGM_SHARE_DENY_NONE;
-
-    /* STGM_PRIORITY stops other IStorage objects on the same file from
-     * committing until the STGM_PRIORITY IStorage is closed. it also
-     * stops non-transacted mode StgOpenStorage calls with write access from
-     * succeeding. obviously, both of these cannot be achieved through just
-     * file share flags */
-    FIXME("STGM_PRIORITY mode not implemented correctly\n");
   }
 
   /*
@@ -8074,6 +8916,10 @@ static HRESULT validateSTGM(DWORD stgm)
   case STGM_SHARE_DENY_WRITE:
   case STGM_SHARE_EXCLUSIVE:
     break;
+  case 0:
+    if (!(stgm & STGM_TRANSACTED))
+      return E_FAIL;
+    break;
   default:
     return E_FAIL;
   }
@@ -8129,14 +8975,16 @@ 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:
-    return FILE_SHARE_READ;
   case STGM_SHARE_EXCLUSIVE:
-    return 0;
+    return FILE_SHARE_READ;
   }
   ERR("Invalid share mode!\n");
   assert(0);
index 939a2e0..c6b34f0 100644 (file)
@@ -42,6 +42,7 @@ static const ULONG OFFSET_SMALLBLOCKSIZEBITS = 0x00000020;
 static const ULONG OFFSET_DIRSECTORCOUNT     = 0x00000028;
 static const ULONG OFFSET_BBDEPOTCOUNT      = 0x0000002C;
 static const ULONG OFFSET_ROOTSTARTBLOCK     = 0x00000030;
+static const ULONG OFFSET_TRANSACTIONSIG     = 0x00000034;
 static const ULONG OFFSET_SMALLBLOCKLIMIT    = 0x00000038;
 static const ULONG OFFSET_SBDEPOTSTART      = 0x0000003C;
 static const ULONG OFFSET_SBDEPOTCOUNT       = 0x00000040;
@@ -61,6 +62,7 @@ static const ULONG OFFSET_PS_MTIMELOW        = 0x0000006C;
 static const ULONG OFFSET_PS_MTIMEHIGH       = 0x00000070;
 static const ULONG OFFSET_PS_STARTBLOCK             = 0x00000074;
 static const ULONG OFFSET_PS_SIZE           = 0x00000078;
+static const ULONG OFFSET_PS_SIZE_HIGH      = 0x0000007C;
 static const WORD  DEF_BIG_BLOCK_SIZE_BITS   = 0x0009;
 static const WORD  MIN_BIG_BLOCK_SIZE_BITS   = 0x0009;
 static const WORD  MAX_BIG_BLOCK_SIZE_BITS   = 0x000c;
@@ -147,6 +149,8 @@ struct DirEntry
 
 HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsName, ILockBytes **pLockBytes) DECLSPEC_HIDDEN;
 
+HRESULT FileLockBytesImpl_LockRegionSync(ILockBytes* iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb) DECLSPEC_HIDDEN;
+
 /*************************************************************************
  * Ole Convert support
  */
@@ -237,6 +241,10 @@ struct StorageBaseImplVtbl {
   HRESULT (*StreamWriteAt)(StorageBaseImpl*,DirRef,ULARGE_INTEGER,ULONG,const void*,ULONG*);
   HRESULT (*StreamSetSize)(StorageBaseImpl*,DirRef,ULARGE_INTEGER);
   HRESULT (*StreamLink)(StorageBaseImpl*,DirRef,DirRef);
+  HRESULT (*GetTransactionSig)(StorageBaseImpl*,ULONG*,BOOL);
+  HRESULT (*SetTransactionSig)(StorageBaseImpl*,ULONG);
+  HRESULT (*LockTransaction)(StorageBaseImpl*,BOOL);
+  HRESULT (*UnlockTransaction)(StorageBaseImpl*,BOOL);
 };
 
 static inline void StorageBaseImpl_Destroy(StorageBaseImpl *This)
@@ -314,6 +322,28 @@ static inline HRESULT StorageBaseImpl_StreamLink(StorageBaseImpl *This,
   return This->baseVtbl->StreamLink(This, dst, src);
 }
 
+static inline HRESULT StorageBaseImpl_GetTransactionSig(StorageBaseImpl *This,
+  ULONG* result, BOOL refresh)
+{
+  return This->baseVtbl->GetTransactionSig(This, result, refresh);
+}
+
+static inline HRESULT StorageBaseImpl_SetTransactionSig(StorageBaseImpl *This,
+  ULONG value)
+{
+  return This->baseVtbl->SetTransactionSig(This, value);
+}
+
+static inline HRESULT StorageBaseImpl_LockTransaction(StorageBaseImpl *This, BOOL write)
+{
+  return This->baseVtbl->LockTransaction(This, write);
+}
+
+static inline HRESULT StorageBaseImpl_UnlockTransaction(StorageBaseImpl *This, BOOL write)
+{
+  return This->baseVtbl->UnlockTransaction(This, write);
+}
+
 /****************************************************************************
  * StorageBaseImpl stream list handlers
  */
@@ -350,6 +380,7 @@ struct StorageImpl
   ULONG extBigBlockDepotLocationsSize;
   ULONG extBigBlockDepotCount;
   ULONG bigBlockDepotStart[COUNT_BBDEPOTINHEADER];
+  ULONG transactionSig;
 
   ULONG extBlockDepotCached[MAX_BIG_BLOCK_SIZE / 4];
   ULONG indexExtBlockDepotCached;
@@ -373,6 +404,8 @@ struct StorageImpl
   UINT blockChainToEvict;
 
   ILockBytes* lockBytes;
+
+  ULONG locked_bytes[8];
 };
 
 HRESULT StorageImpl_ReadRawDirEntry(
@@ -459,6 +492,54 @@ StgStreamImpl* StgStreamImpl_Construct(
     DirRef           dirEntry) DECLSPEC_HIDDEN;
 
 
+/* Range lock constants.
+ *
+ * The storage format reserves the region from 0x7fffff00-0x7fffffff for
+ * locking and synchronization. Unfortunately, the spec doesn't say which bytes
+ * within that range are used, and for what. These are guesses based on testing.
+ * In particular, ends of ranges may be wrong.
+
+ 0x0 through 0x57: Unknown. Causes read-only exclusive opens to fail.
+ 0x58 through 0x6b: Priority mode.
+ 0x6c through 0x7f: No snapshot mode.
+ 0x80: Commit lock.
+ 0x81 through 0x91: Priority mode, again. Not sure why it uses two regions.
+ 0x92: Lock-checking lock. Held while opening so ranges can be tested without
+  causing spurious failures if others try to grab or test those ranges at the
+  same time.
+ 0x93 through 0xa6: Read mode.
+ 0xa7 through 0xba: Write mode.
+ 0xbb through 0xce: Deny read.
+ 0xcf through 0xe2: Deny write.
+ 0xe2 through 0xff: Unknown. Causes read-only exclusive opens to fail.
+*/
+
+#define RANGELOCK_UNK1_FIRST            0x7fffff00
+#define RANGELOCK_UNK1_LAST             0x7fffff57
+#define RANGELOCK_PRIORITY1_FIRST       0x7fffff58
+#define RANGELOCK_PRIORITY1_LAST        0x7fffff6b
+#define RANGELOCK_NOSNAPSHOT_FIRST      0x7fffff6c
+#define RANGELOCK_NOSNAPSHOT_LAST       0x7fffff7f
+#define RANGELOCK_COMMIT                0x7fffff80
+#define RANGELOCK_PRIORITY2_FIRST       0x7fffff81
+#define RANGELOCK_PRIORITY2_LAST        0x7fffff91
+#define RANGELOCK_CHECKLOCKS            0x7fffff92
+#define RANGELOCK_READ_FIRST            0x7fffff93
+#define RANGELOCK_READ_LAST             0x7fffffa6
+#define RANGELOCK_WRITE_FIRST           0x7fffffa7
+#define RANGELOCK_WRITE_LAST            0x7fffffba
+#define RANGELOCK_DENY_READ_FIRST       0x7fffffbb
+#define RANGELOCK_DENY_READ_LAST        0x7fffffce
+#define RANGELOCK_DENY_WRITE_FIRST      0x7fffffcf
+#define RANGELOCK_DENY_WRITE_LAST       0x7fffffe2
+#define RANGELOCK_UNK2_FIRST            0x7fffffe3
+#define RANGELOCK_UNK2_LAST             0x7fffffff
+#define RANGELOCK_TRANSACTION_FIRST     RANGELOCK_COMMIT
+#define RANGELOCK_TRANSACTION_LAST      RANGELOCK_CHECKLOCKS
+#define RANGELOCK_FIRST                 RANGELOCK_UNK1_FIRST
+#define RANGELOCK_LAST                  RANGELOCK_UNK2_LAST
+
+
 /******************************************************************************
  * Endian conversion macros
  */
index 70f77dc..e011fab 100644 (file)
@@ -150,7 +150,7 @@ reactos/dll/win32/ntprint             # Synced to Wine-1.7.17
 reactos/dll/win32/objsel              # Synced to Wine-1.7.17
 reactos/dll/win32/odbc32              # Synced to Wine-1.7.17. Depends on port of Linux ODBC.
 reactos/dll/win32/odbccp32            # Synced to Wine-1.7.17
-reactos/dll/win32/ole32               # Synced to Wine-1.7.17
+reactos/dll/win32/ole32               # Synced to Wine-1.7.27
 reactos/dll/win32/oleacc              # Synced to Wine-1.7.17
 reactos/dll/win32/oleaut32            # Synced to Wine-1.7.17
 reactos/dll/win32/olecli32            # Synced to Wine-1.7.17