windows-nt/Source/XPSP1/NT/shell/osshell/dskquota/control/sidname.cpp

1344 lines
44 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
///////////////////////////////////////////////////////////////////////////////
/* File: sidname.cpp
Description: Implements the SID-to-NAME resolver. It is anticipated that
resolving a user's SID to it's corresponding name can be a lengthy
process. We don't want the quota controller's client to experience
slow user enumerations just because it takes a long time to resolve
a name. Therefore, this object was created to perform the
SID-to-name resolutions on a background thread and notify the
client whenever a name has been resolved. That way, the client
can display a list of users then fill in the names as names
are resolved.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/12/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
#include "pch.h" // PCH
#pragma hdrstop
#include "control.h"
#include "guidsp.h" // Private GUIDs.
#include "registry.h"
#include "sidname.h"
#include "sidcache.h"
//
// Verify that build is UNICODE.
//
#if !defined(UNICODE)
# error This module must be compiled UNICODE.
#endif
//
// SID/Name resolver messages (SNRM_XXXXXXXX).
//
#define SNRM_CLEAR_INPUT_QUEUE (WM_USER + 1)
#define SNRM_EXIT_THREAD (WM_USER + 2)
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::SidNameResolver
Description: SidNameResolver constructor.
Arguments:
rQuotaController - Reference to quota controller that this resolver is
working for.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/12/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
SidNameResolver::SidNameResolver(
DiskQuotaControl& rQuotaController)
: m_cRef(0),
m_rQuotaController(rQuotaController),
m_hsemQueueNotEmpty(NULL),
m_hMutex(NULL),
m_dwResolverThreadId(0),
m_hResolverThread(NULL),
m_heventResolverThreadReady(NULL)
{
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::SidNameResolver")));
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::~SidNameResolver
Description: SidNameResolver destructor.
Arguments: None.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/12/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
SidNameResolver::~SidNameResolver(void)
{
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::~SidNameResolver")));
if (NULL != m_hsemQueueNotEmpty)
{
CloseHandle(m_hsemQueueNotEmpty);
}
if (NULL != m_hMutex)
{
CloseHandle(m_hMutex);
}
if (NULL != m_hResolverThread)
{
CloseHandle(m_hResolverThread);
}
if (NULL != m_heventResolverThreadReady)
{
CloseHandle(m_heventResolverThreadReady);
}
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::QueryInterface
Description: Returns an interface pointer to the object's IUnknown or
ISidNameResolver interface.
Only IID_IUnknown, IID_ISidNameResolver are recognized.
The object referenced by the returned interface pointer is uninitialized.
The recipient of the pointer must call Initialize() before the object
is usable.
Arguments:
riid - Reference to requested interface ID.
ppvOut - Address of interface pointer variable to accept interface ptr.
Returns:
NO_ERROR - Success.
E_NOINTERFACE - Requested interface not supported.
E_INVALIDARG - ppvOut argument was NULL.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/07/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
SidNameResolver::QueryInterface(
REFIID riid,
LPVOID *ppvOut
)
{
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::QueryInterface")));
DBGPRINTIID(DM_RESOLVER, DL_MID, riid);
HRESULT hResult = E_NOINTERFACE;
if (NULL == ppvOut)
return E_INVALIDARG;
*ppvOut = NULL;
if (IID_IUnknown == riid || IID_ISidNameResolver == riid)
{
*ppvOut = this;
((LPUNKNOWN)*ppvOut)->AddRef();
hResult = NOERROR;
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::AddRef
Description: Increments object reference count.
Arguments: None.
Returns: New reference count value.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(ULONG)
SidNameResolver::AddRef(
VOID
)
{
DBGTRACE((DM_RESOLVER, DL_LOW, TEXT("SidNameResolver::AddRef")));
DBGPRINT((DM_RESOLVER, DL_LOW, TEXT("\t0x%08X %d -> %d"),
this, m_cRef, m_cRef + 1));
ULONG ulReturn = m_cRef + 1;
InterlockedIncrement(&m_cRef);
return ulReturn;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::Release
Description: Decrements object reference count. If count drops to 0,
object is deleted.
Arguments: None.
Returns: New reference count value.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(ULONG)
SidNameResolver::Release(
VOID
)
{
DBGTRACE((DM_RESOLVER, DL_LOW, TEXT("SidNameResolver::Release")));
DBGPRINT((DM_RESOLVER, DL_LOW, TEXT("\t0x%08X %d -> %d"),
this, m_cRef, m_cRef - 1));
ULONG ulReturn = m_cRef - 1;
if (InterlockedDecrement(&m_cRef) == 0)
{
delete this;
ulReturn = 0;
}
return ulReturn;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::Initialize
Description: Initializes a SidNameResolver object.
This function performs lot's of initialization steps so I chose to
use the "jump to label on failure" approach. It avoids a lot of
deeply nested "ifs".
Arguments: None.
Returns:
NO_ERROR - Success.
E_FAIL - Initialization failed.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/11/96 Initial creation. BrianAu
08/14/96 Moved SID/Name cache initialization to BrianAu
FindCachedUserName() method. Only initialize cache
when it is truly needed.
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
SidNameResolver::Initialize(
VOID
)
{
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::Initialize")));
HRESULT hResult = NO_ERROR;
DWORD dwThreadId = 0;
//
// Configure the user queue so it grows in chunks of 100.
//
m_UserQueue.SetSize(100);
m_UserQueue.SetGrow(100);
//
// IMPORTANT: There is code in the QuotaControl object that
// counts on the thread being created LAST in this function.
// See DiskQuotaControl::CreateEnumUsers.
// Thread creation must be the last thing performed in this
// function. The caller assumes that if this function returns
// E_FAIL, no thread was created.
//
m_hMutex = CreateMutex(NULL, FALSE, NULL);
if (NULL == m_hMutex)
goto InitFailed;
m_hsemQueueNotEmpty = CreateSemaphore(NULL, // No security.
0, // Initially empty queue.
0x7FFFFFFF, // Max count (a lot).
NULL); // No name.
if (NULL == m_hsemQueueNotEmpty)
goto InitFailed;
m_heventResolverThreadReady = CreateEvent(NULL, // No security.
TRUE, // Manual reset.
FALSE, // Initially non-signaled.
NULL); // No name.
if (NULL == m_heventResolverThreadReady)
goto InitFailed;
hResult = CreateResolverThread(&m_hResolverThread, &m_dwResolverThreadId);
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("Resolver thread. hThread = 0x%08X, idThread = %d"),
m_hResolverThread, m_dwResolverThreadId));
if (FAILED(hResult))
goto InitFailed;
InitFailed:
//
// Failure only returns E_FAIL.
//
if (FAILED(hResult))
hResult = E_FAIL;
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::Shutdown
Description: Commands the resolver to terminate its activities.
When the resolver's client is through with the resolver's services,
it should call Shutdown() followed by IUnknown::Release(). The function
sends a WM_QUIT message to the resolver thread.
Arguments: None.
Returns:
NO_ERROR - Success
E_FAIL - Failed to send WM_QUIT message.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/29/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
SidNameResolver::Shutdown(
BOOL bWait
)
{
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::Shutdown")));
DBGPRINT((DM_RESOLVER, DL_HIGH, TEXT("\tThread ID = %d"), m_dwResolverThreadId));
BOOL bResult = FALSE;
if (0 != m_dwResolverThreadId)
{
bResult = PostThreadMessage(m_dwResolverThreadId, WM_QUIT, 0, 0);
if (bResult && bWait && NULL != m_hResolverThread)
{
//
// Wait for thread to terminate normally.
//
DBGPRINT((DM_RESOLVER, DL_HIGH, TEXT("Resolver waiting for thread to exit...")));
WaitForSingleObject(m_hResolverThread, INFINITE);
}
}
return bResult ? NO_ERROR : E_FAIL;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::GetUserSid
Description: Private method that allocates a SID buffer and retrieves
the user's SID into that buffer. The caller must free the returned
buffer when done with it.
Arguments:
pUser - Pointer to user's IDiskQuotaUser interface.
ppSid - Address of a PSID variable to receive the address of the
allocated SID buffer.
Returns:
NO_ERROR - Success.
Exceptions: OutOfMemory.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
07/08/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
SidNameResolver::GetUserSid(
PDISKQUOTA_USER pUser,
PSID *ppSid
)
{
HRESULT hResult = NO_ERROR;
DWORD cbSid = 0;
DBGASSERT((NULL != pUser));
hResult = pUser->GetSidLength(&cbSid);
if (SUCCEEDED(hResult))
{
PSID pUserSid = NULL;
pUserSid = (PSID) new BYTE[cbSid];
hResult = pUser->GetSid((PBYTE)pUserSid, cbSid);
if (SUCCEEDED(hResult))
{
DBGASSERT((IsValidSid(pUserSid)));
DBGASSERT((NULL != ppSid));
*ppSid = pUserSid; // Return address of buffer to caller.
// Caller must free it when done.
}
else
{
DBGERROR((TEXT("RESOLVER - GetSid failed.")));
delete[] pUserSid; // Failed to get SID. Free buffer.
}
}
else
{
DBGERROR((TEXT("RESOLVER - GetUserSid failed.")));
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::AddUserToResolverQueue
Description: Adds a user pointer to the resolver's input queue.
Arguments:
pUser - Address of IDiskQuotaUser ptr.
Returns:
NO_ERROR - Success.
Exceptions: OutOfMemory.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
08/09/96 Initial creation. BrianAu
09/03/96 Added exception handling. BrianAu
12/10/96 Removed interface marshaling. Using free-threading BrianAu
model.
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
SidNameResolver::AddUserToResolverQueue(
PDISKQUOTA_USER pUser
)
{
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::AddUserToResolverQueue")));
DBGASSERT((NULL != pUser));
HRESULT hResult = NO_ERROR;
//
// Add user object pointer to resolver input queue.
// This can throw OutOfMemory.
//
pUser->AddRef();
try
{
m_UserQueue.Add(pUser);
}
catch(CAllocException& e)
{
pUser->Release();
hResult = E_OUTOFMEMORY;
}
//
// Increment queue's semaphore count.
// Means there's something in queue to process.
//
ReleaseSemaphore(m_hsemQueueNotEmpty, 1, NULL);
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::RemoveUserFromResolverQueue
Description: Removes a user pointer from the resolver's input queue.
Arguments:
ppUser - Address of pointer variable to receive IDiskQuotaUser ptr.
Returns:
NO_ERROR - Success.
E_UNEXPECTED - Resolver queue was empty.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
08/09/96 Initial creation. BrianAu
12/10/96 Removed interface marshaling. Using free-threading BrianAu
model.
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
SidNameResolver::RemoveUserFromResolverQueue(
PDISKQUOTA_USER *ppUser
)
{
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::RemoveUserFromResolverQueue")));
DBGASSERT((NULL != ppUser));
HRESULT hResult = E_UNEXPECTED;
*ppUser = NULL;
if (!m_UserQueue.IsEmpty() &&
m_UserQueue.Remove(*ppUser))
{
hResult = NO_ERROR;
}
else
{
DBGERROR((TEXT("RESOLVER - Input queue unexpectedly empty.")));
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::PromoteUserToQueueHead
Description: Promotes a user object to the head of the resolver queue
if the user object is in the queue. This can be used to
give specific user objects higher priority if desired.
In particular, the initial requirement behind this feature
is so that user objects highlighted in the details list view
get higher name-resolution priority so that the user
(app user) feels the UI is responsive to their inputs.
Arguments:
pUser - Address of IDiskQuotaUser interface for user object.
Returns:
NO_ERROR - Success.
S_FALSE - User record not in queue.
E_OUTOFMEMORY - Insufficient memory adding item.
E_UNEXPECTED - Exception caught. User record not promoted.
E_INVALIDARG - pUser argument was NULL.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/18/97 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
SidNameResolver::PromoteUserToQueueHead(
PDISKQUOTA_USER pUser
)
{
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::PromoteUserToQueueHead")));
HRESULT hResult = S_FALSE;
if (NULL == pUser)
return E_INVALIDARG;
m_UserQueue.Lock();
try
{
//
// Find the user in the resolver's queue.
//
INT iUser = m_UserQueue.Find(pUser);
if (-1 != iUser)
{
//
// Note we don't mess with the ref count of the
// user object. We're merely deleting a user and re
// inserting it into the queue. The queue's original
// AddRef() is retained.
//
m_UserQueue.Delete(iUser);
m_UserQueue.Add(pUser);
}
}
catch(CAllocException& e)
{
hResult = E_OUTOFMEMORY;
}
m_UserQueue.ReleaseLock();
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::ResolveSidToName
Description: Finds the name corresponding to a user's SID.
Once the name is found, it is sent to the user object for storage.
Arguments:
pUser - Pointer to user objects's IDiskQuotaUser interface.
Returns:
NO_ERROR - Success.
E_FAIL - Couldn't resolve SID to a name.
ERROR_NONE_MAPPED (hr) - No SID-to-Name mapping available.
No need to try again.
Exceptions: OutOfMemory.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/11/96 Initial creation. BrianAu
08/09/96 Set hResult to E_FAIL when LookupAccountSid fails. BrianAu
09/05/96 Added user domain name string. BrianAu
05/18/97 Changed to report deleted SIDs. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
SidNameResolver::ResolveSidToName(
PDISKQUOTA_USER pUser
)
{
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::ResolveSidToName")));
HRESULT hResult = NO_ERROR;
array_autoptr<BYTE> ptrUserSid;
DBGASSERT((NULL != pUser));
hResult = GetUserSid(pUser, (PSID *)(ptrUserSid.getaddr()));
if (SUCCEEDED(hResult))
{
SID_NAME_USE SidNameUse = SidTypeUnknown;
CString strContainer;
CString strLogonName;
CString strDisplayName;
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Calling LookupAccountBySid.")));
hResult = m_rQuotaController.m_NTDS.LookupAccountBySid(
NULL,
(PSID)(ptrUserSid.get()),
&strContainer,
&strLogonName,
&strDisplayName,
&SidNameUse);
if (SUCCEEDED(hResult))
{
switch(SidNameUse)
{
case SidTypeDeletedAccount:
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_DELETED);
break;
case SidTypeInvalid:
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_INVALID);
break;
case SidTypeUnknown:
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_UNKNOWN);
break;
default:
{
//
// Valid account.
//
SidNameCache *pSidCache;
HRESULT hr = SidNameCache_Get(&pSidCache);
if (SUCCEEDED(hr))
{
//
// Add SID/Name pair to cache.
// Indicate failure only with a debug msg. If cache
// addition fails, we'll still work OK, just slower.
// This can throw OutOfMemory.
//
hr = pSidCache->Add((PSID)(ptrUserSid.get()),
strContainer,
strLogonName,
strDisplayName);
if (FAILED(hr))
{
DBGERROR((TEXT("SIDNAME - Addition of %s\\%s failed."), strLogonName.Cstr()));
}
}
//
// Set the user object's account name strings.
//
hResult = static_cast<DiskQuotaUser *>(pUser)->SetName(
strContainer,
strLogonName,
strDisplayName);
if (SUCCEEDED(hResult))
{
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_RESOLVED);
}
else
{
DBGERROR((TEXT("SIDNAME - SetName failed in ResolveSidToName. hResult = 0x%08X"),
hResult));
}
break;
}
}
}
else
{
//
// Failed asynch name resolution.
//
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_UNAVAILABLE);
if (ERROR_NONE_MAPPED == GetLastError())
hResult = HRESULT_FROM_WIN32(ERROR_NONE_MAPPED);
else
hResult = E_FAIL;
}
}
else
{
DBGERROR((TEXT("SIDNAME - GetUserSid failed in ResolveSidToName, hResult = 0x%08X"),
hResult));
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::FindCachedUserName
Description: Accepts a user object's IDiskQuotaUser interface pointer and
looks for it's SID/Name pair in the SID/Name cache. If found, the
name is set in the user object and the function returns NO_ERROR.
Arguments:
pUser - Pointer to user objects's IDiskQuotaUser interface.
Returns:
NO_ERROR - Success. User's SID found in cache.
ERROR_FILE_NOT_FOUND (hr) - User's SID not in cache.
Exceptions: OutOfMemory
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/27/96 Initial creation. BrianAu
08/14/96 Moved initialization of SID/Name cache from BrianAu
SidNameResolver::Initialize().
09/05/96 Added user domain name string. BrianAu
09/21/96 New cache design. BrianAu
03/18/98 Replaced "domain", "name" and "full name" with BrianAu
"container", "logon name" and "display name" to
better match the actual contents. This was in
reponse to making the quota UI DS-aware. The
"logon name" is now a unique key as it contains
both account name and domain-like information.
i.e. "REDMOND\brianau" or "brianau@microsoft.com".
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
SidNameResolver::FindCachedUserName(
PDISKQUOTA_USER pUser
)
{
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::FindCachedUserName")));
HRESULT hResult = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); // Assume not found.
PSID pUserSid = NULL;
hResult = GetUserSid(pUser, &pUserSid);
if (SUCCEEDED(hResult))
{
LPTSTR pszContainer = NULL;
LPTSTR pszLogonName = NULL;
LPTSTR pszDisplayName = NULL;
try
{
//
// Can throw OutOfMemory.
//
SidNameCache *pSidCache;
hResult = SidNameCache_Get(&pSidCache);
if (SUCCEEDED(hResult))
{
//
// Check cache for SID/Name pair.
// This can throw OutOfMemory.
//
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Query cache for user 0x%08X."), pUser));
hResult = pSidCache->Lookup(pUserSid,
&pszContainer,
&pszLogonName,
&pszDisplayName);
if (SUCCEEDED(hResult))
{
//
// Name was cached. Set it in the user object and return NO_ERROR.
//
hResult = static_cast<DiskQuotaUser *>(pUser)->SetName(
pszContainer,
pszLogonName,
pszDisplayName);
if (SUCCEEDED(hResult))
{
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_RESOLVED);
}
}
}
else
{
//
// Set the return value so the caller knows to resolve
// the user name.
//
hResult = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
DBGERROR((TEXT("RESOLVER - SID/Name cache not available.")));
}
}
catch(CAllocException& e)
{
hResult = E_OUTOFMEMORY;
}
delete[] pszContainer;
delete[] pszLogonName;
delete[] pszDisplayName;
delete[] pUserSid;
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::FindUserName
Description: Accepts a user object's IDiskQuotaUser interface pointer and
looks for it's SID/Name pair in the SID/Name cache. If the information
is not cached, the function calls ResolveSidToName to synchronously
determine the SID's account name. The function blocks until the name
is retrieved.
Arguments:
pUser - Pointer to user objects's IDiskQuotaUser interface.
Returns:
NO_ERROR - Success.
E_FAIL - Couldn't resolve SID to name.
ERROR_NONE_MAPPED (hr) - No SID-to-Name mapping found.
Exceptions: OutOfMemory.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/27/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
SidNameResolver::FindUserName(
PDISKQUOTA_USER pUser
)
{
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::FindUserName")));
HRESULT hResult = NO_ERROR;
DBGASSERT((NULL != pUser));
hResult = FindCachedUserName(pUser); // Can throw OutOfMemory.
if (ERROR_FILE_NOT_FOUND == HRESULT_CODE(hResult))
{
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - User 0x%08X not cached. Resolving..."),
pUser));
hResult = ResolveSidToName(pUser); // Can throw OutOfMemory.
}
else
{
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - User 0x%08X found in cache."), pUser));
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::FindUserNameAsync
Description: Accepts a user object's IDiskQuotaUser interface pointer and
looks for it's SID/Name pair in the SID/Name cache. If the information
is not cached, the user object is submitted to the resolver for
background processing and asynchronous client notification when the
operation is complete.
Arguments:
pUser - Pointer to user objects's IDiskQuotaUser interface.
Returns:
NO_ERROR - Success.
E_FAIL - Resolver thread not active. Can't resolve Async.
S_FALSE - User name not in cache. Submitted for background
processing. Client will be notified when name is
found and set in user object.
Exceptions: OutOfMemory.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/11/96 Initial creation. BrianAu
06/25/96 Added SID/Name caching. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
SidNameResolver::FindUserNameAsync(
PDISKQUOTA_USER pUser
)
{
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::FindUserNameAsync")));
HRESULT hResult = NO_ERROR;
DBGASSERT((NULL != pUser));
hResult = FindCachedUserName(pUser);
if (ERROR_FILE_NOT_FOUND == HRESULT_CODE(hResult))
{
if (0 != m_dwResolverThreadId)
{
DBGPRINT((DM_RESOLVER, DL_MID,
TEXT("RESOLVER - User 0x%08X not cached. Submitting to Resolver."),
pUser));
//
// Name was not cached. Add the user object to the resolver's input queue
// so that the name can be located by the resolver's background thread.
//
hResult = AddUserToResolverQueue(pUser);
}
else
{
DBGERROR((TEXT("RESOLVER - Thread not active. Can't resolve user 0x%08X async."),
pUser));
hResult = E_FAIL;
}
}
else
{
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - User 0x%08X found in cache."),
pUser));
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::ClearInputQueue.
Description: Called by a SidNameResolver thread after the thread receives
a WM_QUIT message. This function removes all user object pointers
from the input queue before the thread exists.
Arguments: None.
Returns:
NO_ERROR - Always returns NO_ERROR.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/18/96 Initial creation. BrianAu
12/10/96 Moved Semaphore reduction from method BrianAu
HandleShutdownMessages then deleted that method.
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
SidNameResolver::ClearInputQueue(
void
)
{
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::ClearInputQueue")));
PDISKQUOTA_USER pUser = NULL;
//
// Decrement the queue-not-empty semaphore to 0 so that the thread
// doesn't try to remove any more queue entries.
// Set the resolver's thread ID to 0 so that FindNameAsync will not
// submit any more users to the resolver.
//
m_dwResolverThreadId = 0;
while(WAIT_OBJECT_0 == WaitForSingleObject(m_hsemQueueNotEmpty, 0))
NULL;
//
// Remove all remaining items from input queue
// Remove will return E_FAIL if list is empty.
//
m_UserQueue.Lock();
while(m_UserQueue.Count() > 0)
{
HRESULT hResult = RemoveUserFromResolverQueue(&pUser);
if (SUCCEEDED(hResult) && NULL != pUser)
{
pUser->Release(); // Release user object.
}
}
m_UserQueue.ReleaseLock();
return NO_ERROR;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::ThreadOnQueueNotEmpty
Description: Called by a SidNameResolver thread when the resolver's input
queue is not empty. This function removes the next entry
from the queue, resolves the user's SID to its name, sets the name
in the user object and notifies the client of the name change.
Arguments: None.
Returns:
NO_ERROR - Always returns NO_ERROR.
Exceptions: OutOfMemory
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/18/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
SidNameResolver::ThreadOnQueueNotEmpty(
void
)
{
DBGTRACE((DM_RESOLVER, DL_LOW, TEXT("SidNameResolver::ThreadOnQueueNotEmpty")));
HRESULT hResult = NO_ERROR;
PDISKQUOTA_USER pUser = NULL;
LPSTREAM pstm = NULL;
//
// Remove item from queue
// RemoveFirst() will return E_FAIL if list is empty.
//
try
{
hResult = RemoveUserFromResolverQueue(&pUser);
if (SUCCEEDED(hResult) && NULL != pUser)
{
ResolveSidToName(pUser);
//
// If successful or not, notify client event sink.
// Even if we couldn't resolve the name, the object's account
// status has changed. The client will want to respond to this.
//
// Don't bother with return value. We don't care if it fails.
// The client will but we don't.
//
m_rQuotaController.NotifyUserNameChanged(pUser);
pUser->Release(); // Release pointer obtained from resolver queue.
}
else
{
DBGERROR((TEXT("RESOLVER - Error removing stream ptr from input queue.")));
}
}
catch(CAllocException& e)
{
if (NULL != pUser)
pUser->Release();
hResult = E_OUTOFMEMORY;
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::ThreadProc
Description: This thread procedure sits in a loop handling events and
thread messages.
Conditions that cause the thread to exit.
1. OleInitalize() fails.
2. Thread receives a WM_QUIT message.
3. Wait function failure or timeout.
Arguments:
pvParam - "this" pointer for the SidNameResolver object.
Required since ThreadProc must be static to be a THREADPROC.
Returns:
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/11/96 Initial creation. BrianAu
03/22/00 Changed dwParam for ia64 compat. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
DWORD
SidNameResolver::ThreadProc(
LPVOID pvParam
)
{
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::ThreadProc")));
DBGPRINT((DM_RESOLVER, DL_HIGH, TEXT("\tThreadID = %d"), GetCurrentThreadId()));
SidNameResolver *pThis = (SidNameResolver *)pvParam;
BOOL bExitThread = FALSE;
//
// Make sure DLL stays loaded while this thread is active.
//
InterlockedIncrement(&g_cRefThisDll);
//
// Must call CoInitializeEx() for each thread.
//
if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
{
BOOL bReadyToReceiveMsgs = FALSE;
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER: Thread %d entering msg loop."), GetCurrentThreadId()));
while(!bExitThread)
{
MSG msg;
try
{
//
// Allow blocked thread to respond to sent messages.
//
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && !bExitThread)
{
if ( WM_QUIT != msg.message )
{
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER: Thread %d dispatching msg %d"),
GetCurrentThreadId(), msg.message));
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//
// Rcvd WM_QUIT. Clear the resolver's input queue and
// exit the msg loop.
//
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER: Thread %d received WM_QUIT"),
GetCurrentThreadId()));
pThis->ClearInputQueue();
bExitThread = TRUE;
}
}
if (!bExitThread)
{
DWORD dwWaitResult = 0;
if (!bReadyToReceiveMsgs)
{
//
// Tell the thread creation function that it can
// now return. The thread is ready to accept messages.
//
SetEvent(pThis->m_heventResolverThreadReady);
bReadyToReceiveMsgs = TRUE;
}
//
// Sleep if the queue is empty.
// Wake up on queue-not-empty or any thread messages.
//
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Thread %d waiting for messages..."),
GetCurrentThreadId()));
dwWaitResult = MsgWaitForMultipleObjects(
1,
&(pThis->m_hsemQueueNotEmpty),
FALSE,
INFINITE,
QS_ALLINPUT);
switch(dwWaitResult)
{
case WAIT_OBJECT_0:
//
// Have data in input queue. Process one user.
//
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Something added to input queue.")));
pThis->ThreadOnQueueNotEmpty();
break;
case WAIT_OBJECT_0 + 1:
//
// Received input message(s).
// Loop back around and handle them.
//
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Thread %d rcvd message(s)."),
GetCurrentThreadId()));
break;
case WAIT_FAILED:
case WAIT_TIMEOUT:
default:
//
// Something bad happened.
//
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Thread %d wait failed."),
GetCurrentThreadId()));
DBGASSERT((FALSE));
bExitThread = TRUE;
break;
}
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("RESOLVER - Out of memory")));
bExitThread = TRUE;
}
}
CoUninitialize();
}
else
{
DBGERROR((TEXT("RESOLVER - OleInitialize failed for thread %d."),
GetCurrentThreadId()));
}
DBGPRINT((DM_RESOLVER, DL_HIGH, TEXT("RESOLVER - Exit thread %d"), GetCurrentThreadId()));
pThis->m_dwResolverThreadId = 0;
InterlockedDecrement(&g_cRefThisDll);
return 0;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: SidNameResolver::CreateResolverThread
Description: Creates a thread that will process user objects and resolve
their SIDs to account names.
Arguments:
phThread [optional] - Address of handle variable to receive handle of
new thread. Can be NULL.
pdwThreadId [optional] - Address of DWORD to receive thread ID.
Can be NULL.
Returns:
NO_ERROR - Started thread.
E_FAIL - Couldn't start thread.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/27/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
SidNameResolver::CreateResolverThread(
PHANDLE phThread,
LPDWORD pdwThreadId
)
{
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::CreateResolverThread")));
HRESULT hResult = NO_ERROR;
DWORD dwThreadId = 0;
HANDLE hThread = NULL;
//
// Launch new thread.
//
hThread = CreateThread(NULL, // No security attributes.
0, // Default stack size.
&ThreadProc,
this, // Static thread proc needs this.
0, // Not suspended.
&dwThreadId);
if (NULL != hThread)
{
if (NULL != phThread)
*phThread = hThread; // Caller want's handle value.
else
CloseHandle(hThread); // Caller doesn't want it.
if (NULL != pdwThreadId)
*pdwThreadId = dwThreadId; // Caller want's thread ID.
//
// Wait here until the thread is ready to receive thread messages.
// This event is set in ThreadProc.
//
WaitForSingleObject(m_heventResolverThreadReady, INFINITE);
}
else
{
hResult = E_FAIL;
}
return hResult;
}