//+--------------------------------------------------------------------------- // // 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 #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); } }