windows-nt/Source/XPSP1/NT/net/config/netcfg/engine/wrlock.cpp
2020-09-26 16:20:57 +08:00

317 lines
8.1 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1999.
//
// File: W R L O C K . C P P
//
// Contents: Defines the interface to the netcfg write lock used to
// protect the network configuration information from being
// modified by more than one writer at a time.
//
// Notes:
//
// Author: shaunco 15 Jan 1999
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#include "nccom.h"
#include "ncreg.h"
#include "util.h"
#include "wrlock.h"
#define MUTEX_NAME L"Global\\NetCfgWriteLock"
#define LOCK_HOLDER_SUBKEY L"NetCfgLockHolder"
CWriteLock::~CWriteLock ()
{
// If we have the mutex created, release it if we own it
// and close its handle.
//
if (m_hMutex)
{
ReleaseIfOwned ();
CloseHandle (m_hMutex);
}
}
HRESULT
CWriteLock::HrEnsureMutexCreated ()
{
if (m_hMutex)
{
return S_OK;
}
// Ensure the mutex has been created. It is important to create it
// with a security descriptor that allows access to the world because
// we may be running under the localsystem account and someone else
// may be running under a user account. If we didn't give the world
// explicit access, the user account clients would get access denied
// because the mutex would have inherited the security level of our
// process.
//
HRESULT hr;
Assert (!m_hMutex);
Assert (!m_fOwned);
hr = HrCreateMutexWithWorldAccess (
MUTEX_NAME,
FALSE, // not initially owned,
NULL,
&m_hMutex);
TraceHr (ttidError, FAL, hr, FALSE, "CWriteLock::HrEnsureMutexCreated");
return hr;
}
BOOL
CWriteLock::WaitToAcquire (
IN DWORD dwMilliseconds,
IN PCWSTR pszNewOwnerDesc,
OUT PWSTR* ppszCurrentOwnerDesc OPTIONAL)
{
HRESULT hr;
BOOL fAcquired = FALSE;
hr = HrEnsureMutexCreated ();
if (S_OK == hr)
{
// Now wait for the mutext to become available. (Pump messages while
// waiting so we don't hang the clients UI.)
//
while (1)
{
DWORD dwWait;
dwWait = MsgWaitForMultipleObjects (
1, &m_hMutex, FALSE,
dwMilliseconds, QS_ALLINPUT);
if ((WAIT_OBJECT_0 + 1) == dwWait)
{
// We have messages to pump.
//
MSG msg;
while (PeekMessage (&msg, NULL, NULL, NULL, PM_REMOVE))
{
DispatchMessage (&msg);
}
}
else
{
if (WAIT_OBJECT_0 == dwWait)
{
fAcquired = TRUE;
}
else if (WAIT_ABANDONED_0 == dwWait)
{
fAcquired = TRUE;
TraceTag (ttidError, "NetCfg write lock was abandoned!");
}
else if (WAIT_TIMEOUT == dwWait)
{
hr = HRESULT_FROM_WIN32 (ERROR_TIMEOUT);
}
else
{
hr = HrFromLastWin32Error ();
TraceHr (ttidError, FAL, hr, FALSE,
"MsgWaitForMultipleObjects");
}
// If we acquired the mutex, set the new owner.
//
if (fAcquired)
{
m_fOwned = TRUE;
SetOrQueryLockHolder (TRUE,
pszNewOwnerDesc, ppszCurrentOwnerDesc);
}
else if (ppszCurrentOwnerDesc)
{
// Query the lock holder description.
//
SetOrQueryLockHolder (FALSE,
NULL, ppszCurrentOwnerDesc);
}
break;
}
}
}
return fAcquired;
}
BOOL
CWriteLock::FIsLockedByAnyone (
OUT PWSTR* ppszCurrentOwnerDesc OPTIONAL)
{
// It's locked if we own it.
//
BOOL fLocked = m_fOwned;
// If we don't own it, check to see if some other process does.
//
if (!fLocked)
{
HRESULT hr;
hr = HrEnsureMutexCreated ();
if (S_OK == hr)
{
DWORD dw;
// Wait for the mutex, but with a zero timeout. This is
// equivalent to a quick check. (But we still need to release
// it if we acquire ownership. If we timeout, it means that
// someone else owns it.
//
dw = WaitForSingleObject (m_hMutex, 0);
if (WAIT_OBJECT_0 == dw)
{
ReleaseMutex (m_hMutex);
}
else if (WAIT_TIMEOUT == dw)
{
// Someone else owns it.
//
fLocked = TRUE;
}
}
}
if (fLocked)
{
// Query the lock holder description.
//
SetOrQueryLockHolder (FALSE, NULL, ppszCurrentOwnerDesc);
}
return fLocked;
}
VOID
CWriteLock::ReleaseIfOwned ()
{
if (m_fOwned)
{
Assert (m_hMutex);
// Clear the lock holder now that no one is about to own it.
//
SetOrQueryLockHolder (TRUE, NULL, NULL);
ReleaseMutex (m_hMutex);
m_fOwned = FALSE;
}
}
VOID
CWriteLock::SetOrQueryLockHolder (
IN BOOL fSet,
IN PCWSTR pszNewOwnerDesc OPTIONAL,
OUT PWSTR* ppszCurrentOwnerDesc OPTIONAL)
{
HRESULT hr;
HKEY hkeyNetwork;
HKEY hkeyLockHolder;
REGSAM samDesired;
BOOL fClear;
// We're clearing the value if we're asked to set it to NULL.
//
fClear = fSet && !pszNewOwnerDesc;
// Initialize the output parameter if specified.
//
if (ppszCurrentOwnerDesc)
{
*ppszCurrentOwnerDesc = NULL;
}
// If we're setting the lock holder, we need write access. Otherwise,
// we only need read access.
//
samDesired = (fSet) ? KEY_READ_WRITE_DELETE : KEY_READ;
hr = HrOpenNetworkKey (samDesired, &hkeyNetwork);
if (S_OK == hr)
{
// The lock holder is represented by the default value of a
// volatile subkey under the Network subtree.
//
if (fClear)
{
RegDeleteKey (hkeyNetwork, LOCK_HOLDER_SUBKEY);
}
else if (fSet)
{
DWORD dwDisposition;
Assert (pszNewOwnerDesc);
hr = HrRegCreateKeyWithWorldAccess (
hkeyNetwork,
LOCK_HOLDER_SUBKEY,
REG_OPTION_VOLATILE,
KEY_WRITE,
&hkeyLockHolder,
&dwDisposition);
// Set the lock holder and close the key.
//
if (S_OK == hr)
{
(VOID) HrRegSetSz (hkeyLockHolder, NULL, pszNewOwnerDesc);
RegCloseKey (hkeyLockHolder);
}
}
else
{
// Query for the lock holder by opening the key (if it exists)
// and reading the default value. We return the string
// allocated with CoTaskMemAlloc because we use this
// directly from the COM implementation.
//
Assert (ppszCurrentOwnerDesc);
hr = HrRegOpenKeyEx (
hkeyNetwork,
LOCK_HOLDER_SUBKEY,
KEY_READ,
&hkeyLockHolder);
if (S_OK == hr)
{
PWSTR pszLockHolder;
hr = HrRegQuerySzWithAlloc (
hkeyLockHolder,
NULL,
&pszLockHolder);
if (S_OK == hr)
{
hr = HrCoTaskMemAllocAndDupSz (
pszLockHolder, ppszCurrentOwnerDesc);
MemFree (pszLockHolder);
}
RegCloseKey (hkeyLockHolder);
}
}
RegCloseKey (hkeyNetwork);
}
}