317 lines
8.1 KiB
C++
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);
|
|
}
|
|
}
|
|
|