[4/4] ole32: Initial storage file locking implementation.

Vincent Povirk madewokherd at gmail.com
Mon Apr 28 16:33:59 CDT 2014


-------------- next part --------------
From 540d26b9b532ecde43179df47c4217a703de0c21 Mon Sep 17 00:00:00 2001
From: Vincent Povirk <vincent at codeweavers.com>
Date: Fri, 18 Apr 2014 16:37:56 -0500
Subject: [PATCH 4/8] ole32: Initial storage file locking implementation.

---
 dlls/ole32/filelockbytes.c   |  40 +++++++++--
 dlls/ole32/storage32.c       | 167 +++++++++++++++++++++++++++++++++++++++++++
 dlls/ole32/storage32.h       |   2 +
 dlls/ole32/tests/storage32.c |   4 +-
 4 files changed, 207 insertions(+), 6 deletions(-)

diff --git a/dlls/ole32/filelockbytes.c b/dlls/ole32/filelockbytes.c
index ce74566..1f4ac55 100644
--- a/dlls/ole32/filelockbytes.c
+++ b/dlls/ole32/filelockbytes.c
@@ -334,15 +334,47 @@ static HRESULT WINAPI FileLockBytesImpl_SetSize(ILockBytes* iface, ULARGE_INTEGE
 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;
+    else
+        return STG_E_ACCESSDENIED;
 }
 
 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;
+    else
+        return STG_E_ACCESSDENIED;
 }
 
 static HRESULT WINAPI FileLockBytesImpl_Stat(ILockBytes* iface,
diff --git a/dlls/ole32/storage32.c b/dlls/ole32/storage32.c
index 7303392..ddd2642 100644
--- a/dlls/ole32/storage32.c
+++ b/dlls/ole32/storage32.c
@@ -2743,6 +2743,157 @@ static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
   StorageImpl_StreamLink
 };
 
+/* The storage format reserves the region from 0x7fffff00-0x7fffffff for
+ * locking and synchronization. Unfortuantely, the spec doesn't say which bytes
+ * within that range are used, and for what. Here's what I've been able to
+ * gather based on testing (ends of ranges may be wrong):
+
+ 0x0 through 0x57: Unknown. Causes read-only exclusive opens to fail.
+ 0x58 through 0x7f: Priority 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.
+*/
+
+static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset,
+    ULARGE_INTEGER cb, DWORD dwLockType)
+{
+    HRESULT hr;
+
+    /* potential optimization: if we have an HFILE use LockFileEx in blocking mode directly */
+
+    do
+    {
+        int delay=0;
+
+        hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
+
+        if (hr == STG_E_ACCESSDENIED)
+        {
+            Sleep(delay);
+            if (delay < 150) delay++;
+        }
+    } while (hr == STG_E_ACCESSDENIED);
+
+    return hr;
+}
+
+static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, unsigned char start,
+    unsigned char end, HRESULT fail_hr)
+{
+    HRESULT hr;
+    ULARGE_INTEGER offset, cb;
+
+    offset.QuadPart = 0x7fffff00 + start;
+    cb.QuadPart = 1 + end - start;
+
+    hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+    if (SUCCEEDED(hr)) ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+
+    if (hr == STG_E_ACCESSDENIED)
+        return fail_hr;
+    else
+        return S_OK;
+}
+
+static HRESULT StorageImpl_LockOne(StorageImpl *This, unsigned char start, unsigned char end)
+{
+    HRESULT hr=S_OK;
+    int i, j;
+    ULARGE_INTEGER offset, cb;
+
+    cb.QuadPart = 1;
+
+    for (i=start; i<=end; i++)
+    {
+        offset.QuadPart = 0x7fffff00 + i;
+        hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+        if (hr != STG_E_ACCESSDENIED)
+            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);
+
+    /* Wrap all other locking inside a single lock so we can check ranges safely */
+    offset.QuadPart = 0x7fffff92;
+    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, 0x80, 0x80, STG_E_LOCKVIOLATION);
+
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
+        hr = StorageImpl_CheckLockRange(This, 0xbb, 0xce, STG_E_SHAREVIOLATION);
+
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
+        hr = StorageImpl_CheckLockRange(This, 0xcf, 0xe2, STG_E_SHAREVIOLATION);
+
+    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
+        hr = StorageImpl_CheckLockRange(This, 0x93, 0xa6, STG_E_LOCKVIOLATION);
+
+    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
+        hr = StorageImpl_CheckLockRange(This, 0xa7, 0xba, STG_E_LOCKVIOLATION);
+
+    /* Then grab our locks. */
+    if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
+    {
+        hr = StorageImpl_LockOne(This, 0x58, 0x7f);
+        if (SUCCEEDED(hr))
+            hr = StorageImpl_LockOne(This, 0x81, 0x91);
+    }
+
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
+        hr = StorageImpl_LockOne(This, 0x93, 0xa6);
+
+    if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
+        hr = StorageImpl_LockOne(This, 0xa7, 0xba);
+
+    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
+        hr = StorageImpl_LockOne(This, 0xbb, 0xce);
+
+    if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
+        hr = StorageImpl_LockOne(This, 0xcf, 0xe2);
+
+    offset.QuadPart = 0x7fffff92;
+    cb.QuadPart = 1;
+    ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+
+    return hr;
+}
+
 static HRESULT StorageImpl_Construct(
   HANDLE       hFile,
   LPCOLESTR    pwcsName,
@@ -2804,6 +2955,11 @@ static HRESULT StorageImpl_Construct(
   if (FAILED(hr))
     goto end;
 
+  hr = StorageImpl_GrabLocks(This, openFlags);
+
+  if (FAILED(hr))
+    goto end;
+
   if (create)
   {
     ULARGE_INTEGER size;
@@ -3038,6 +3194,17 @@ static void StorageImpl_Destroy(StorageBaseImpl* iface)
   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 = 0x7fffff00 + This->locked_bytes[i];
+      ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE);
+    }
+  }
+
   if (This->lockBytes)
     ILockBytes_Release(This->lockBytes);
   HeapFree(GetProcessHeap(), 0, This);
diff --git a/dlls/ole32/storage32.h b/dlls/ole32/storage32.h
index c75b02a..9a61fd8 100644
--- a/dlls/ole32/storage32.h
+++ b/dlls/ole32/storage32.h
@@ -382,6 +382,8 @@ struct StorageImpl
   UINT blockChainToEvict;
 
   ILockBytes* lockBytes;
+
+  unsigned char locked_bytes[8];
 };
 
 HRESULT StorageImpl_ReadRawDirEntry(
diff --git a/dlls/ole32/tests/storage32.c b/dlls/ole32/tests/storage32.c
index d50bfaa..6a3d3d5 100644
--- a/dlls/ole32/tests/storage32.c
+++ b/dlls/ole32/tests/storage32.c
@@ -3172,10 +3172,10 @@ static const int pr_fail_ranges[] = { 0x80,0x81, 0xbb,0xcf, -1 };
 static const int roex_fail_ranges[] = { 0x0,-1 };
 
 static const struct lock_test lock_tests[] = {
-    { STGM_PRIORITY, FALSE, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, priority_locked_bytes, pr_fail_ranges, TRUE },
+    { STGM_PRIORITY, FALSE, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, priority_locked_bytes, pr_fail_ranges, FALSE },
     { STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, TRUE, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, rwex_locked_bytes, 0, TRUE },
     { STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE|STGM_TRANSACTED, TRUE, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, rwex_locked_bytes, 0, TRUE },
-    { STGM_CREATE|STGM_READWRITE|STGM_TRANSACTED, TRUE, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, rw_locked_bytes, 0, TRUE },
+    { STGM_CREATE|STGM_READWRITE|STGM_TRANSACTED, TRUE, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, rw_locked_bytes, 0, FALSE },
     { STGM_CREATE|STGM_READWRITE|STGM_SHARE_DENY_WRITE|STGM_TRANSACTED, TRUE, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, rwdw_locked_bytes, 0, TRUE },
     { STGM_CREATE|STGM_WRITE|STGM_SHARE_DENY_WRITE|STGM_TRANSACTED, TRUE, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, wodw_locked_bytes, 0, TRUE },
     { STGM_SHARE_EXCLUSIVE|STGM_READWRITE, FALSE, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, rwex_locked_bytes, rwex_fail_ranges, TRUE },
-- 
1.8.3.2



More information about the wine-patches mailing list