diff --git a/dlls/ole32/filelockbytes.c b/dlls/ole32/filelockbytes.c index 594da1bab74..c5cb45f77fc 100644 --- a/dlls/ole32/filelockbytes.c +++ b/dlls/ole32/filelockbytes.c @@ -386,6 +386,8 @@ static HRESULT WINAPI FileLockBytesImpl_Stat(ILockBytes* iface, pstatstg->cbSize.u.LowPart = GetFileSize(This->hfile, &pstatstg->cbSize.u.HighPart); /* FIXME: If the implementation is exported, we'll need to set other fields. */ + pstatstg->grfLocksSupported = LOCK_EXCLUSIVE|LOCK_ONLYONCE|WINE_LOCK_READ; + return S_OK; } diff --git a/dlls/ole32/storage32.c b/dlls/ole32/storage32.c index aa53758c74d..4b6f630870f 100644 --- a/dlls/ole32/storage32.c +++ b/dlls/ole32/storage32.c @@ -4822,9 +4822,31 @@ static HRESULT StorageImpl_SetTransactionSig(StorageBaseImpl *base, return S_OK; } +static HRESULT StorageImpl_LockRegion(StorageImpl *This, ULARGE_INTEGER offset, + ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported) +{ + if ((dwLockType & This->locks_supported) == 0) + { + if (supported) *supported = FALSE; + return S_OK; + } + + if (supported) *supported = TRUE; + return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType); +} + +static HRESULT StorageImpl_UnlockRegion(StorageImpl *This, ULARGE_INTEGER offset, + ULARGE_INTEGER cb, DWORD dwLockType) +{ + if ((dwLockType & This->locks_supported) == 0) + return S_OK; + + return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType); +} + /* Internal function */ static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offset, - ULARGE_INTEGER cb, DWORD dwLockType) + ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported) { HRESULT hr; int delay = 0; @@ -4837,7 +4859,7 @@ static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offs do { - hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType); + hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported); if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION) { @@ -4857,17 +4879,12 @@ static HRESULT StorageImpl_LockRegionSync(StorageImpl *This, ULARGE_INTEGER offs * * This can collide with another attempt to open the file in * exclusive mode, but it's unlikely, and someone would fail anyway. */ - hr = ILockBytes_LockRegion(This->lockBytes, sanity_offset, sanity_cb, 0); + hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL); if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION) break; - if (hr == STG_E_INVALIDFUNCTION) - { - /* ignore this, lockbytes might support dwLockType but not 0 */ - hr = STG_E_ACCESSDENIED; - } if (SUCCEEDED(hr)) { - ILockBytes_UnlockRegion(This->lockBytes, sanity_offset, sanity_cb, 0); + StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ); hr = STG_E_ACCESSDENIED; } @@ -4900,10 +4917,7 @@ static HRESULT StorageImpl_LockTransaction(StorageBaseImpl *base, BOOL write) cb.QuadPart = 1; } - hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE); - - if (hr == STG_E_INVALIDFUNCTION) - hr = S_OK; + hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL); return hr; } @@ -4925,10 +4939,7 @@ static HRESULT StorageImpl_UnlockTransaction(StorageBaseImpl *base, BOOL write) cb.QuadPart = 1; } - hr = ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE); - - if (hr == STG_E_INVALIDFUNCTION) - hr = S_OK; + hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); return hr; } @@ -4955,10 +4966,10 @@ static HRESULT StorageImpl_CheckLockRange(StorageImpl *This, ULONG start, offset.QuadPart = 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); + hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL); + if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); - if (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION) + if (FAILED(hr)) return fail_hr; else return S_OK; @@ -4975,7 +4986,7 @@ static HRESULT StorageImpl_LockOne(StorageImpl *This, ULONG start, ULONG end) for (i=start; i<=end; i++) { offset.QuadPart = i; - hr = ILockBytes_LockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE); + hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL); if (hr != STG_E_ACCESSDENIED && hr != STG_E_LOCKVIOLATION) break; } @@ -5001,6 +5012,7 @@ static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags) ULARGE_INTEGER offset; ULARGE_INTEGER cb; DWORD share_mode = STGM_SHARE_MODE(openFlags); + BOOL supported; if (openFlags & STGM_NOSNAPSHOT) { @@ -5012,10 +5024,10 @@ static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags) /* 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); + hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported); /* If the ILockBytes doesn't support locking that's ok. */ - if (hr == STG_E_INVALIDFUNCTION || hr == STG_E_UNIMPLEMENTEDFUNCTION) return S_OK; + if (!supported) return S_OK; else if (FAILED(hr)) return hr; hr = S_OK; @@ -5069,7 +5081,7 @@ static HRESULT StorageImpl_GrabLocks(StorageImpl *This, DWORD openFlags) offset.QuadPart = RANGELOCK_CHECKLOCKS; cb.QuadPart = 1; - ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE); + StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); return hr; } @@ -5134,7 +5146,7 @@ static void StorageImpl_Destroy(StorageBaseImpl* iface) if (This->locked_bytes[i] != 0) { offset.QuadPart = This->locked_bytes[i]; - ILockBytes_UnlockRegion(This->lockBytes, offset, cb, LOCK_ONLYONCE); + StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE); } } @@ -5201,7 +5213,8 @@ static HRESULT StorageImpl_Construct( StorageImpl** result) { StorageImpl* This; - HRESULT hr = S_OK; + HRESULT hr = S_OK; + STATSTG stat; if ( FAILED( validateSTGM(openFlags) )) return STG_E_INVALIDFLAG; @@ -5247,7 +5260,17 @@ static HRESULT StorageImpl_Construct( } if (SUCCEEDED(hr)) + hr = ILockBytes_Stat(This->lockBytes, &stat, STATFLAG_NONAME); + + if (SUCCEEDED(hr)) + { + This->locks_supported = stat.grfLocksSupported; + if (!hFile) + /* Don't try to use wine-internal locking flag with custom ILockBytes */ + This->locks_supported &= ~WINE_LOCK_READ; + hr = StorageImpl_GrabLocks(This, openFlags); + } if (SUCCEEDED(hr)) hr = StorageImpl_Refresh(This, TRUE, create); diff --git a/dlls/ole32/storage32.h b/dlls/ole32/storage32.h index 59d52ef6477..4fcfd9c3629 100644 --- a/dlls/ole32/storage32.h +++ b/dlls/ole32/storage32.h @@ -411,6 +411,8 @@ struct StorageImpl BlockChainStream* blockChainCache[BLOCKCHAIN_CACHE_SIZE]; UINT blockChainToEvict; + ULONG locks_supported; + ILockBytes* lockBytes; ULONG locked_bytes[8]; @@ -517,6 +519,9 @@ StgStreamImpl* StgStreamImpl_Construct( #define RANGELOCK_FIRST RANGELOCK_UNK1_FIRST #define RANGELOCK_LAST RANGELOCK_UNK2_LAST +/* internal value for LockRegion/UnlockRegion */ +#define WINE_LOCK_READ 0x80000000 + /****************************************************************************** * Endian conversion macros diff --git a/dlls/ole32/tests/storage32.c b/dlls/ole32/tests/storage32.c index 63a803a719c..fe5ddbc1ac6 100644 --- a/dlls/ole32/tests/storage32.c +++ b/dlls/ole32/tests/storage32.c @@ -63,6 +63,9 @@ typedef struct TestLockBytes { BYTE* contents; ULONG size; ULONG buffer_size; + HRESULT lock_hr; + ULONG locks_supported; + ULONG lock_called; } TestLockBytes; static inline TestLockBytes *impl_from_ILockBytes(ILockBytes *iface) @@ -181,13 +184,16 @@ static HRESULT WINAPI TestLockBytes_SetSize(ILockBytes *iface, static HRESULT WINAPI TestLockBytes_LockRegion(ILockBytes *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { - return S_OK; + TestLockBytes *This = impl_from_ILockBytes(iface); + This->lock_called++; + return This->lock_hr; } static HRESULT WINAPI TestLockBytes_UnlockRegion(ILockBytes *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { - return S_OK; + TestLockBytes *This = impl_from_ILockBytes(iface); + return This->lock_hr; } static HRESULT WINAPI TestLockBytes_Stat(ILockBytes *iface, @@ -209,6 +215,7 @@ static HRESULT WINAPI TestLockBytes_Stat(ILockBytes *iface, pstatstg->type = STGTY_LOCKBYTES; pstatstg->cbSize.QuadPart = This->size; + pstatstg->grfLocksSupported = This->locks_supported; return S_OK; } @@ -3855,6 +3862,29 @@ static void test_custom_lockbytes(void) IStorage_Release(stg); + ok(!lockbytes->lock_called, "unexpected call to LockRegion\n"); + + lockbytes->locks_supported = LOCK_WRITE|LOCK_EXCLUSIVE|LOCK_ONLYONCE; + + hr = StgCreateDocfileOnILockBytes(&lockbytes->ILockBytes_iface, STGM_CREATE|STGM_READWRITE|STGM_TRANSACTED, 0, &stg); + ok(hr==S_OK, "StgCreateDocfileOnILockBytes failed %x\n", hr); + + hr = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, 0, &stm); + ok(hr==S_OK, "IStorage_CreateStream failed %x\n", hr); + + IStream_Release(stm); + + hr = IStorage_Commit(stg, 0); + + IStorage_Release(stg); + + ok(lockbytes->lock_called, "expected LockRegion to be called\n"); + + lockbytes->lock_hr = STG_E_INVALIDFUNCTION; + + hr = StgCreateDocfileOnILockBytes(&lockbytes->ILockBytes_iface, STGM_CREATE|STGM_READWRITE|STGM_TRANSACTED, 0, &stg); + ok(hr==STG_E_INVALIDFUNCTION, "StgCreateDocfileOnILockBytes failed %x\n", hr); + DeleteTestLockBytes(lockbytes); }