From: Vincent Povirk Subject: [4/4] ole32: Initial storage file locking implementation. Message-Id: Date: Wed, 23 Apr 2014 15:10:37 -0500 From 123ee8eac09230d7b7ade3160f7962c8b2f18459 Mon Sep 17 00:00:00 2001 From: Vincent Povirk 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; jlocked_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; iblockChainCache[i]); + for (i=0; ilocked_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 2c56b9c..08b496d 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