windows-nt/Source/XPSP1/NT/base/pnp/umpnpmgr/rutil.c
2020-09-26 16:20:57 +08:00

2381 lines
53 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1995-2001 Microsoft Corporation
Module Name:
rutil.c
Abstract:
This module contains general utility routines used by umpnpmgr.
PNP_ENTER_SYNCHRONOUS_CALL
PNP_LEAVE_SYNCHRONOUS_CALL
IsClientUsingLocalConsole
IsClientInteractive
VerifyClientAccess
SplitClassInstanceString
CreateDeviceIDRegKey
IsValidGuid
IsRootDeviceID
MultiSzAppendW
MultiSzFindNextStringW
MultiSzSearchStringW
MultiSzSizeW
MultiSzDeleteStringW
IsValidDeviceID
IsDevicePhantom
GetDeviceStatus
SetDeviceStatus
ClearDeviceStatus
GetProfileCount
CopyRegistryTree
PathToString
IsDeviceMoved
MakeKeyVolatile
MakeKeyNonVolatile
OpenLogConfKey
GetActiveService
IsDeviceIdPresent
GetDeviceConfigFlags
MapNtStatusToCmError
VerifyKernelInitiatedEjectPermissions
GuidFromString
StringFromGuid
Author:
Paula Tomlinson (paulat) 7-12-1995
Environment:
User mode only.
Revision History:
12-July-1995 paulat
Creation and initial implementation.
--*/
//
// includes
//
#include "precomp.h"
#include "umpnpi.h"
#include "umpnpdat.h"
#include <winsta.h>
#include <syslib.h>
//
// global data
//
extern HKEY ghEnumKey; // Key to HKLM\CCC\System\Enum - DO NOT MODIFY
extern HKEY ghServicesKey; // Key to HKLM\CCC\System\Services - DO NOT MODIFY
extern CRITICAL_SECTION PnpSynchronousCall;
//
// Declare data used in GUID->string conversion (from ole32\common\ccompapi.cxx).
//
static const BYTE GuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-',
8, 9, '-', 10, 11, 12, 13, 14, 15 };
static const TCHAR szDigits[] = TEXT("0123456789ABCDEF");
VOID
PNP_ENTER_SYNCHRONOUS_CALL(
VOID
)
{
EnterCriticalSection(&PnpSynchronousCall);
} // PNP_ENTER_SYNCHRONOUS_CALL
VOID
PNP_LEAVE_SYNCHRONOUS_CALL(
VOID
)
{
LeaveCriticalSection(&PnpSynchronousCall);
} // PNP_LEAVE_SYNCHRONOUS_CALL
BOOL
IsClientUsingLocalConsole(
IN handle_t hBinding
)
/*++
Routine Description:
This routine impersonates the client associated with hBinding and checks
if the client is using the current active console session.
Arguments:
hBinding RPC Binding handle
Return value:
The return value is TRUE if the client is using the current active console
session, FALSE if not or if an error occurs.
--*/
{
RPC_STATUS rpcStatus;
BOOL bResult = FALSE;
rpcStatus = RpcImpersonateClient(hBinding);
if (rpcStatus != RPC_S_OK) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
rpcStatus));
return FALSE;
}
if (GetClientLogonId() == GetActiveConsoleSessionId()) {
bResult = TRUE;
}
rpcStatus = RpcRevertToSelf();
if (rpcStatus != RPC_S_OK) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
rpcStatus));
}
return bResult;
} // IsClientUsingLocalConsole
BOOL
IsClientLocal(
IN handle_t hBinding
)
/*++
Routine Description:
This routine determines if the client associated with hBinding is on the
local machine.
Arguments:
hBinding RPC Binding handle
Return value:
The return value is TRUE if the client is local to this machine, FALSE if
not or if an error occurs.
--*/
{
RPC_STATUS RpcStatus;
UINT ClientLocalFlag;
//
// If the specified RPC binding handle is NULL, this is an internal call so
// we assume that the privilege has already been checked.
//
if (hBinding == NULL) {
return TRUE;
}
//
// Retrieve the ClientLocalFlags from the RPC binding handle.
//
RpcStatus =
I_RpcBindingIsClientLocal(
hBinding,
&ClientLocalFlag);
if (RpcStatus != RPC_S_OK) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: I_RpcBindingIsClientLocal failed, RpcStatus=%d\n",
RpcStatus));
return FALSE;
}
//
// If the ClientLocalFlag is not zero, RPC client is local to server.
//
if (ClientLocalFlag != 0) {
return TRUE;
}
//
// Client is not local to this server.
//
return FALSE;
} // IsClientLocal
BOOL
IsClientInteractive(
IN handle_t hBinding
)
/*++
Routine Description:
This routine impersonates the client associated with hBinding and checks
if the client is a member of the INTERACTIVE well-known group.
Arguments:
hBinding RPC Binding handle
Return value:
The return value is TRUE if the client is interactive, FALSE if not
or if an error occurs.
--*/
{
RPC_STATUS rpcStatus;
BOOL bIsMember;
HANDLE hToken;
PSID sidInteractiveGroup;
BOOL bResult = FALSE;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if (hBinding == NULL) {
//
// This is an internal call so we assume that the privilege has already
// been checked.
//
return TRUE;
}
if (!AllocateAndInitializeSid( &NtAuthority,
1, // one authority - INTERACTIVE
SECURITY_INTERACTIVE_RID, // interactive logged on users only
0, 0, 0, 0, 0, 0, 0, // unused authority locations
&sidInteractiveGroup)) {
return FALSE;
}
rpcStatus = RpcImpersonateClient(hBinding);
if (rpcStatus != RPC_S_OK) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
rpcStatus));
FreeSid(sidInteractiveGroup);
return FALSE;
}
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) {
if (CheckTokenMembership(hToken,
sidInteractiveGroup,
&bIsMember)) {
if (bIsMember) {
bResult = TRUE;
}
} else {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: CheckTokenMembership failed, error = %d\n",
GetLastError()));
}
CloseHandle(hToken);
} else {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: OpenThreadToken failed, error = %d\n",
GetLastError()));
}
FreeSid(sidInteractiveGroup);
rpcStatus = RpcRevertToSelf();
if (rpcStatus != RPC_S_OK) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
rpcStatus));
}
return bResult;
} // IsClientInteractive
BOOL
IsClientAdministrator(
IN handle_t hBinding
)
/*++
Routine Description:
This routine impersonates the client associated with hBinding and checks
if the client is a member of the Local Administrators group.
Arguments:
hBinding RPC Binding handle
Return value:
The return value is TRUE if the client is a Local Administrator, FALSE if
not or if an error occurs.
--*/
{
RPC_STATUS rpcStatus;
BOOL bIsMember;
HANDLE hToken;
PSID sidAdministratorsGroup;
BOOL bResult = FALSE;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if (hBinding == NULL) {
//
// This is an internal call so we assume that the privilege has already
// been checked.
//
return TRUE;
}
if (!AllocateAndInitializeSid( &NtAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&sidAdministratorsGroup)) {
return FALSE;
}
rpcStatus = RpcImpersonateClient(hBinding);
if (rpcStatus != RPC_S_OK) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
rpcStatus));
FreeSid(sidAdministratorsGroup);
return FALSE;
}
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) {
if (CheckTokenMembership(hToken,
sidAdministratorsGroup,
&bIsMember)) {
if (bIsMember) {
bResult = TRUE;
}
} else {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: CheckTokenMembership failed, error = %d\n",
GetLastError()));
}
CloseHandle(hToken);
} else {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: OpenThreadToken failed, error = %d\n",
GetLastError()));
}
rpcStatus = RpcRevertToSelf();
if (rpcStatus != RPC_S_OK) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
rpcStatus));
}
FreeSid(sidAdministratorsGroup);
return bResult;
} // IsClientAdministrator
BOOL
VerifyClientAccess(
IN handle_t hBinding,
IN PLUID pPrivilegeLuid
)
/*++
Routine Description:
This routine impersonates the client associated with hBinding and checks
if the client possesses the specified privilege.
Arguments:
hBinding RPC Binding handle
pPrivilegeLuid LUID representing privilege to be checked.
Return value:
The return value is TRUE if the client possesses the privilege, FALSE if not
or if an error occurs.
--*/
{
RPC_STATUS rpcStatus;
BOOL bResult;
HANDLE hToken;
PRIVILEGE_SET privilegeSet;
if (hBinding == NULL) {
//
// This is an internal call so we assume that the privilege has already
// been checked.
//
return TRUE;
}
if (pPrivilegeLuid->LowPart == 0 && pPrivilegeLuid->HighPart == 0) {
//
// Uninitialized LUID, most likely LookupPrivilegeValue failed during
// initialization, we'll pretend like they don't have access in this
// case.
//
return FALSE;
}
rpcStatus = RpcImpersonateClient(hBinding);
if (rpcStatus != RPC_S_OK) {
//
// Since we can't impersonate the client we better not do the security
// checks as ourself (they would always succeed).
//
return FALSE;
}
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) {
privilegeSet.PrivilegeCount = 1;
privilegeSet.Control = 0;
privilegeSet.Privilege[0].Luid = *pPrivilegeLuid;
privilegeSet.Privilege[0].Attributes = 0;
if (!PrivilegeCheck(hToken, &privilegeSet, &bResult)) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: PrivilegeCheck failed, error = %d\n",
GetLastError()));
bResult = FALSE;
}
CloseHandle(hToken);
} else {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: OpenThreadToken failed, error = %d\n",
GetLastError()));
bResult = FALSE;
}
rpcStatus = RpcRevertToSelf();
if (rpcStatus != RPC_S_OK) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
rpcStatus));
}
return bResult;
} // VerifyClientAccess
BOOL
SplitClassInstanceString(
IN LPCWSTR pszClassInstance,
OUT LPWSTR pszClass,
OUT LPWSTR pszInstance
)
/*++
Routine Description:
This routine parses a class instance string into it's two component parts.
Arguments:
Return value:
The return value is TRUE if the function suceeds and FALSE if it fails.
--*/
{
UINT ulLength, i;
ulLength = lstrlen(pszClassInstance);
//
// parse the string for the backslash character
//
for (i=0; i < ulLength && pszClassInstance[i] != '\0' &&
pszClassInstance[i] != '\\'; i++);
if (pszClassInstance[i] != '\\') {
return FALSE;
}
i++; // increment past the backslash character
if (i < ulLength && pszClassInstance[i] != '\0') {
if (pszClass != NULL) {
lstrcpyn(pszClass, pszClassInstance, i);
}
if (pszInstance != NULL) {
lstrcpy(pszInstance, &pszClassInstance[i]);
}
}
else {
return FALSE;
}
return TRUE;
} // SplitClassInstanceString
BOOL
CreateDeviceIDRegKey(
HKEY hParentKey,
LPCWSTR pDeviceID
)
/*++
Routine Description:
This routine creates the specified device id subkeys in the registry.
Arguments:
hParentKey Key under which the device id key will be created
pDeviceID Device instance ID string to open
Return value:
The return value is TRUE if the function suceeds and FALSE if it fails.
--*/
{
WCHAR szBase[MAX_DEVICE_ID_LEN];
WCHAR szDevice[MAX_DEVICE_ID_LEN];
WCHAR szInstance[MAX_DEVICE_ID_LEN];
HKEY hBaseKey, hDeviceKey, hInstanceKey;
if (!SplitDeviceInstanceString(
pDeviceID, szBase, szDevice, szInstance)) {
return FALSE;
}
//
// just try creating each component of the device id
//
if (RegCreateKeyEx(
hParentKey, szBase, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL, &hBaseKey, NULL) != ERROR_SUCCESS) {
return FALSE;
}
if (RegCreateKeyEx(
hBaseKey, szDevice, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL, &hDeviceKey, NULL) != ERROR_SUCCESS) {
RegCloseKey(hBaseKey);
return FALSE;
}
if (RegCreateKeyEx(
hDeviceKey, szInstance, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL, &hInstanceKey, NULL) != ERROR_SUCCESS) {
RegCloseKey(hBaseKey);
RegCloseKey(hDeviceKey);
return FALSE;
}
RegCloseKey(hBaseKey);
RegCloseKey(hDeviceKey);
RegCloseKey(hInstanceKey);
return TRUE;
} // CreateDeviceIDRegKey
BOOL
IsValidGuid(
LPWSTR pszGuid
)
/*++
Routine Description:
This routine determines whether a string is of the proper Guid form.
Arguments:
pszGuid Pointer to a string that will be checked for the standard Guid
format.
Return value:
The return value is TRUE if the string is a valid Guid and FALSE if it
is not.
--*/
{
//----------------------------------------------------------------
// NOTE: This may change later, but for now I am just verifying
// that the string has exactly MAX_GUID_STRING_LEN characters
//----------------------------------------------------------------
if (lstrlen(pszGuid) != MAX_GUID_STRING_LEN-1) {
return FALSE;
}
return TRUE;
} // IsValidGuid
BOOL
IsRootDeviceID(
LPCWSTR pDeviceID
)
/*++
Routine Description:
This routine determines whether the specified device id is the root
device id.
Arguments:
pDeviceID Pointer to a device id string
Return value:
The return value is TRUE if the string is the root device id and
FALSE if it is not.
--*/
{
return (lstrcmpi(pDeviceID, pszRegRootEnumerator) == 0);
} // IsRootDeviceID
BOOL
MultiSzAppendW(
LPWSTR pszMultiSz,
PULONG pulSize,
LPCWSTR pszString
)
/*++
Routine Description:
Appends a string to a multi_sz string.
Arguments:
pszMultiSz Pointer to a multi_sz string
pulSize On input, Size of the multi_sz string buffer in bytes,
On return, amount copied to the buffer (in bytes)
pszString String to append to pszMultiSz
Return value:
The return value is TRUE if the function succeeded and FALSE if an
error occured.
--*/
{
BOOL bStatus = TRUE;
LPWSTR pTail;
ULONG ulSize;
try {
//
// if it's an empty string, just copy it
//
if (*pszMultiSz == '\0') {
ulSize = (lstrlen(pszString) + 2) * sizeof(WCHAR);
if (ulSize > *pulSize) {
bStatus = FALSE;
goto Clean0;
}
lstrcpy(pszMultiSz, pszString);
pszMultiSz[lstrlen(pszMultiSz) + 1] = '\0'; // add second NULL term char
*pulSize = ulSize;
goto Clean0;
}
//
// first find the end of the multi_sz string
//
pTail = pszMultiSz;
while ((ULONG)(pTail - pszMultiSz) * sizeof(WCHAR) < *pulSize) {
while (*pTail != '\0') {
pTail++;
}
pTail++; // skip past the null terminator
if (*pTail == '\0') {
break; // found the double null terminator
}
}
if ((pTail - pszMultiSz + lstrlen(pszString) + 2) * sizeof(WCHAR)
> *pulSize) {
bStatus = FALSE; // the copy would overflow the buffer
goto Clean0;
}
lstrcpy(pTail, pszString); // copies over the second null terminator
pTail += lstrlen(pszString) + 1;
*pTail = '\0'; // add second null terminator
//
// return buffer size in bytes
//
*pulSize = (ULONG)((pTail - pszMultiSz + 1)) * sizeof(WCHAR);
Clean0:
NOTHING;
} except(EXCEPTION_EXECUTE_HANDLER) {
bStatus = FALSE;
}
return bStatus;
} // MultiSzAppendW
LPWSTR
MultiSzFindNextStringW(
LPWSTR pMultiSz
)
/*++
Routine Description:
Finds next string in a multi_sz string.
device id.
Arguments:
pMultiSz Pointer to a multi_sz string
Return value:
The return value is a pointer to the next string or NULL.
--*/
{
LPWSTR lpNextString = pMultiSz;
//
// find the next NULL terminator
//
while (*lpNextString != '\0') {
lpNextString++;
}
lpNextString++; // skip over the NULL terminator
if (*lpNextString == '\0') {
//
// two NULL terminators in a row means we're at the end
//
lpNextString = NULL;
}
return lpNextString;
} // MultiSzFindNextStringW
BOOL
MultiSzSearchStringW(
IN LPCWSTR pString,
IN LPCWSTR pSubString
)
{
LPCWSTR pCurrent = pString;
//
// compare each string in the multi_sz pString with pSubString
//
while (*pCurrent != '\0') {
if (lstrcmpi(pCurrent, pSubString) == 0) {
return TRUE;
}
//
// go to the next string
//
while (*pCurrent != '\0') {
pCurrent++;
}
pCurrent++; // skip past the null terminator
if (*pCurrent == '\0') {
break; // found the double null terminator
}
}
return FALSE; // pSubString match not found within pString
} // MultiSzSearchStringW
ULONG
MultiSzSizeW(
IN LPCWSTR pString
)
{
LPCWSTR p = NULL;
if (pString == NULL) {
return 0;
}
for (p = pString; *p; p += lstrlen(p)+1) {
// this should fall out with p pointing to the
// second null in double-null terminator
}
//
// returns size in WCHAR
//
return (ULONG)(p - pString + 1);
} // MultiSzSizeW
BOOL
MultiSzDeleteStringW(
IN OUT LPWSTR pString,
IN LPCWSTR pSubString
)
{
LPWSTR p = NULL, pNext = NULL, pBuffer = NULL;
ULONG ulSize = 0;
if (pString == NULL || pSubString == NULL) {
return FALSE;
}
for (p = pString; *p; p += lstrlen(p)+1) {
if (lstrcmpi(p, pSubString) == 0) {
//
// found a match, this is the string to remove.
//
pNext = p + lstrlen(p) + 1;
//
// If this is the last string then just truncate it
//
if (*pNext == '\0') {
*p = '\0';
*(++p) = '\0'; // double null-terminator
return TRUE;
}
//
// retrieve the size of the multi_sz string (in bytes)
// starting with the substring after the matching substring
//
ulSize = MultiSzSizeW(pNext) * sizeof(WCHAR);
if (ulSize == 0) {
return FALSE;
}
pBuffer = HeapAlloc(ghPnPHeap, 0, ulSize);
if (pBuffer == NULL) {
return FALSE;
}
//
// Make a copy of the multi_sz string starting at the
// substring immediately after the matching substring
//
memcpy(pBuffer, pNext, ulSize);
//
// Copy that buffer back to the original buffer, but this
// time copy over the top of the matching substring. This
// effectively removes the matching substring and shifts
// any remaining substrings up in multi_sz string.
//
memcpy(p, pBuffer, ulSize);
HeapFree(ghPnPHeap, 0, pBuffer);
return TRUE;
}
}
//
// if we got here, there was no match but I consider this a success
// since the multi_sz does not contain the substring when we're done
// (which is the desired goal)
//
return TRUE;
} // MultiSzDeleteStringW
BOOL
IsValidDeviceID(
IN LPCWSTR pszDeviceID,
IN HKEY hKey,
IN ULONG ulFlags
)
/*++
Routine Description:
This routine checks if the given device id is valid (present, not moved,
not phantom).
Arguments:
pszDeviceID Device instance string to validate
hKey Can specify open registry key to pszDeviceID, also
ulFlag Controls how much verification to do
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
LONG RegStatus = ERROR_SUCCESS;
WCHAR RegStr[MAX_CM_PATH];
HKEY hDevKey;
ULONG ulValue = 0, ulSize = sizeof(ULONG);
//
// Does the device id exist in the registry?
//
if (hKey == NULL) {
wsprintf(RegStr, TEXT("%s\\%s"),
pszRegPathEnum,
pszDeviceID);
RegStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegStr, 0, KEY_READ, &hDevKey);
if (RegStatus != ERROR_SUCCESS) {
return FALSE;
}
}
else {
hDevKey = hKey;
}
//-----------------------------------------------------------
// Is the device id present?
//-----------------------------------------------------------
if (ulFlags & PNP_PRESENT) {
if (!IsDeviceIdPresent(pszDeviceID)) {
if (hKey == NULL && hDevKey != NULL) {
RegCloseKey(hDevKey);
}
return FALSE;
}
}
//-----------------------------------------------------------
// Is it a phantom device id?
//-----------------------------------------------------------
if (ulFlags & PNP_NOT_PHANTOM) {
RegStatus = RegQueryValueEx(
hDevKey, pszRegValuePhantom, NULL, NULL,
(LPBYTE)&ulValue, &ulSize);
if (RegStatus == ERROR_SUCCESS) {
if (ulValue) {
if (hKey == NULL && hDevKey != NULL) {
RegCloseKey(hDevKey);
}
return FALSE;
}
}
}
//-----------------------------------------------------------
// Has the device id been moved?
//-----------------------------------------------------------
if (ulFlags & PNP_NOT_MOVED) {
if (IsDeviceMoved(pszDeviceID, hDevKey)) {
return FALSE;
}
}
//-----------------------------------------------------------
// Has the device id been removed?
//-----------------------------------------------------------
if (ulFlags & PNP_NOT_REMOVED) {
ULONG ulProblem = 0, ulStatus = 0;
if (GetDeviceStatus(pszDeviceID, &ulStatus, &ulProblem) == CR_SUCCESS) {
if (ulStatus & DN_WILL_BE_REMOVED) {
if (hKey == NULL && hDevKey != NULL) {
RegCloseKey(hDevKey);
}
return FALSE;
}
}
}
if (hKey == NULL && hDevKey != NULL) {
RegCloseKey(hDevKey);
}
return TRUE;
} // IsValidDeviceID
BOOL
IsDevicePhantom(
IN LPWSTR pszDeviceID
)
/*++
Routine Description:
In this case, the check is actually really "is this not present?". The
only comparison is done against FoundAtEnum. UPDATE: for NT 5.0, the
FoundAtEnum registry value has been obsoleted, it's been replaced by a
simple check for the presense of the devnode in memory.
Arguments:
pszDeviceID Device instance string to validate
Return value:
Returns TRUE if the device is a phantom and FALSE if it isn't.
--*/
{
return !IsDeviceIdPresent(pszDeviceID);
} // IsDevicePhantom
CONFIGRET
GetDeviceStatus(
IN LPCWSTR pszDeviceID,
OUT PULONG pulStatus,
OUT PULONG pulProblem
)
/*++
Routine Description:
This routine retrieves the status and problem values for the given
device instance.
Arguments:
pszDeviceID Specifies the device instance to retrieve info for
pulStatus Returns the device's status
pulProblem Returns the device's problem
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
PLUGPLAY_CONTROL_STATUS_DATA ControlData;
NTSTATUS ntStatus;
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_STATUS_DATA));
RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
ControlData.Operation = PNP_GET_STATUS;
ControlData.DeviceStatus = 0;
ControlData.DeviceProblem = 0;
ntStatus = NtPlugPlayControl(PlugPlayControlDeviceStatus,
&ControlData,
sizeof(ControlData));
if (NT_SUCCESS(ntStatus)) {
*pulStatus = ControlData.DeviceStatus;
*pulProblem = ControlData.DeviceProblem;
} else {
Status = MapNtStatusToCmError(ntStatus);
}
return Status;
} // GetDeviceStatus
CONFIGRET
SetDeviceStatus(
IN LPCWSTR pszDeviceID,
IN ULONG ulStatus,
IN ULONG ulProblem
)
/*++
Routine Description:
This routine sets the status and problem values for the given
device instance.
Arguments:
pszDeviceID Specifies the device instance to retrieve info for
pulStatus Specifies the device's status
pulProblem Specifies the device's problem
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
PLUGPLAY_CONTROL_STATUS_DATA ControlData;
NTSTATUS ntStatus;
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_STATUS_DATA));
RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
ControlData.Operation = PNP_SET_STATUS;
ControlData.DeviceStatus = ulStatus;
ControlData.DeviceProblem = ulProblem;
ntStatus = NtPlugPlayControl(PlugPlayControlDeviceStatus,
&ControlData,
sizeof(ControlData));
if (!NT_SUCCESS(ntStatus)) {
Status = MapNtStatusToCmError(ntStatus);
}
return Status;
} // SetDeviceStatus
CONFIGRET
ClearDeviceStatus(
IN LPCWSTR pszDeviceID,
IN ULONG ulStatus,
IN ULONG ulProblem
)
/*++
Routine Description:
This routine clears the followingstatus and problem values for the given
device instance.
Arguments:
pszDeviceID Specifies the device instance to retrieve info for
pulStatus Specifies the device's status
pulProblem Specifies the device's problem
Return value:
The return value is CR_SUCCESS if the function suceeds and one of the
CR_* values if it fails.
--*/
{
CONFIGRET Status = CR_SUCCESS;
PLUGPLAY_CONTROL_STATUS_DATA ControlData;
NTSTATUS ntStatus;
memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_STATUS_DATA));
RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
ControlData.Operation = PNP_CLEAR_STATUS;
ControlData.DeviceStatus = ulStatus;
ControlData.DeviceProblem = ulProblem;
ntStatus = NtPlugPlayControl(PlugPlayControlDeviceStatus,
&ControlData,
sizeof(ControlData));
if (!NT_SUCCESS(ntStatus)) {
Status = MapNtStatusToCmError(ntStatus);
}
return Status;
} // ClearDeviceStatus
CONFIGRET
GetProfileCount(
OUT PULONG pulProfiles
)
{
WCHAR RegStr[MAX_CM_PATH];
HKEY hKey = NULL;
//
// open the Known Docking States key
//
wsprintf(RegStr, TEXT("%s\\%s"),
pszRegPathIDConfigDB,
pszRegKeyKnownDockingStates);
if (RegOpenKeyEx(
HKEY_LOCAL_MACHINE, RegStr, 0, KEY_READ,
&hKey) != ERROR_SUCCESS) {
*pulProfiles = 0;
return CR_REGISTRY_ERROR;
}
//
// find out the total number of profiles
//
if (RegQueryInfoKey(
hKey, NULL, NULL, NULL, pulProfiles, NULL, NULL, NULL,
NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
*pulProfiles = 0;
RegCloseKey(hKey);
return CR_REGISTRY_ERROR;
}
RegCloseKey(hKey);
return CR_SUCCESS;
} // GetProfileCount
CONFIGRET
CopyRegistryTree(
IN HKEY hSrcKey,
IN HKEY hDestKey,
IN ULONG ulOption
)
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
HKEY hSrcSubKey, hDestSubKey;
WCHAR RegStr[MAX_PATH];
ULONG ulMaxValueName, ulMaxValueData;
ULONG ulDataSize, ulLength, ulType, i;
LPWSTR pszValueName=NULL;
LPBYTE pValueData=NULL;
PSECURITY_DESCRIPTOR pSecDesc;
//----------------------------------------------------------------
// copy all values for this key
//----------------------------------------------------------------
//
// find out the maximum size of any of the value names
// and value data under the source device instance key
//
RegStatus = RegQueryInfoKey(
hSrcKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&ulMaxValueName, &ulMaxValueData, NULL, NULL);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
ulMaxValueName++; // size doesn't already include null terminator
//
// allocate a buffer big enough to hold the largest value name and
// the largest value data (note that the max value name is in chars
// (not including the null terminator) and the max value data is
// in bytes
//
pszValueName = HeapAlloc(ghPnPHeap, 0, ulMaxValueName * sizeof(WCHAR));
if (pszValueName == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
pValueData = HeapAlloc(ghPnPHeap, 0, ulMaxValueData);
if (pValueData == NULL) {
Status = CR_OUT_OF_MEMORY;
goto Clean0;
}
//
// enumerate and copy each value
//
for (i=0; RegStatus == ERROR_SUCCESS; i++) {
ulLength = ulMaxValueName;
ulDataSize = ulMaxValueData;
RegStatus = RegEnumValue(
hSrcKey, i, pszValueName, &ulLength, NULL,
&ulType, pValueData, &ulDataSize);
if (RegStatus == ERROR_SUCCESS) {
RegSetValueEx(
hDestKey, pszValueName, 0, ulType, pValueData,
ulDataSize);
}
}
HeapFree(ghPnPHeap, 0, pszValueName);
pszValueName = NULL;
HeapFree(ghPnPHeap, 0, pValueData);
pValueData = NULL;
//---------------------------------------------------------------
// recursively call CopyRegistryNode to copy all subkeys
//---------------------------------------------------------------
RegStatus = ERROR_SUCCESS;
for (i=0; RegStatus == ERROR_SUCCESS; i++) {
ulLength = MAX_PATH;
RegStatus = RegEnumKey(hSrcKey, i, RegStr, ulLength);
if (RegStatus == ERROR_SUCCESS) {
if (RegOpenKey(hSrcKey, RegStr, &hSrcSubKey) == ERROR_SUCCESS) {
if (RegCreateKeyEx(
hDestKey, RegStr, 0, NULL, ulOption, KEY_ALL_ACCESS,
NULL, &hDestSubKey, NULL) == ERROR_SUCCESS) {
RegGetKeySecurity(hSrcSubKey, DACL_SECURITY_INFORMATION,
NULL, &ulDataSize);
pSecDesc = HeapAlloc(ghPnPHeap, 0, ulDataSize);
if (pSecDesc == NULL) {
Status = CR_OUT_OF_MEMORY;
RegCloseKey(hSrcSubKey);
RegCloseKey(hDestSubKey);
goto Clean0;
}
RegGetKeySecurity(hSrcSubKey, DACL_SECURITY_INFORMATION,
pSecDesc, &ulDataSize);
CopyRegistryTree(hSrcSubKey, hDestSubKey, ulOption);
RegSetKeySecurity(hDestSubKey, DACL_SECURITY_INFORMATION, pSecDesc);
HeapFree(ghPnPHeap, 0, pSecDesc);
RegCloseKey(hDestSubKey);
}
RegCloseKey(hSrcSubKey);
}
}
}
Clean0:
if (pszValueName != NULL) {
HeapFree(ghPnPHeap, 0, pszValueName);
}
if (pValueData != NULL) {
pValueData = NULL;
}
return Status;
} // CopyRegistryTree
BOOL
PathToString(
IN LPWSTR pszString,
IN LPCWSTR pszPath,
IN ULONG ulLen
)
{
LPWSTR p;
lstrcpyn(pszString, pszPath,ulLen);
for (p = pszString; *p; p++) {
if (*p == TEXT('\\')) {
*p = TEXT('&');
}
}
return TRUE;
} // PathToString
BOOL
IsDeviceMoved(
IN LPCWSTR pszDeviceID,
IN HKEY hKey
)
{
HKEY hTempKey;
WCHAR RegStr[MAX_CM_PATH];
PathToString(RegStr, pszDeviceID,MAX_CM_PATH);
if (RegOpenKeyEx(
hKey, RegStr, 0, KEY_READ, &hTempKey) == ERROR_SUCCESS) {
RegCloseKey(hTempKey);
return TRUE;
}
return FALSE;
} // IsDeviceMoved
CONFIGRET
MakeKeyVolatile(
IN LPCWSTR pszParentKey,
IN LPCWSTR pszChildKey
)
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
WCHAR RegStr[MAX_CM_PATH], szTempKey[MAX_CM_PATH];
HKEY hParentKey = NULL, hChildKey = NULL, hKey = NULL,
hTempKey = NULL;
//---------------------------------------------------------------------
// Convert the registry key specified by pszChildKey (a subkey of
// pszParentKey) to a volatile key by copying it to a temporary key
// and recreating a volatile key, then copying the original
// registry info back. This also converts and subkeys of pszChildKey.
//---------------------------------------------------------------------
//
// Open a key to the parent
//
RegStatus = RegOpenKeyEx(
HKEY_LOCAL_MACHINE, pszParentKey, 0, KEY_ALL_ACCESS, &hParentKey);
if (RegStatus != ERROR_SUCCESS) {
goto Clean0; // nothing to convert
}
//
// open a key to the child subkey
//
RegStatus = RegOpenKeyEx(
hParentKey, pszChildKey, 0, KEY_ALL_ACCESS, &hChildKey);
if (RegStatus != ERROR_SUCCESS) {
goto Clean0; // nothing to convert
}
//
// 1. Open a unique temporary volatile key under the special Deleted Key.
// Use the parent key path to form the unique tempory key. There shouldn't
// already be such a key, but if there is then just overwrite it.
//
RegStatus = RegOpenKeyEx(
HKEY_LOCAL_MACHINE, pszRegPathCurrentControlSet, 0,
KEY_ALL_ACCESS, &hKey);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
wsprintf(RegStr, TEXT("%s\\%s"),
pszParentKey,
pszChildKey);
PathToString(szTempKey, RegStr,MAX_CM_PATH);
wsprintf(RegStr, TEXT("%s\\%s"),
pszRegKeyDeleted,
szTempKey);
RegStatus = RegCreateKeyEx(
hKey, RegStr, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS,
NULL, &hTempKey, NULL);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// 2. Save the current child key (any any subkeys) to a temporary
// location
//
Status = CopyRegistryTree(hChildKey, hTempKey, REG_OPTION_VOLATILE);
if (Status != CR_SUCCESS) {
goto CleanupTempKeys;
}
RegCloseKey(hChildKey);
hChildKey = NULL;
//
// 3. Delete the current child key (and any subkeys)
//
if (!RegDeleteNode(hParentKey, pszChildKey)) {
Status = CR_REGISTRY_ERROR;
goto CleanupTempKeys;
}
//
// 4. Recreate the current child key as a volatile key
//
RegStatus = RegCreateKeyEx(
hParentKey, pszChildKey, 0, NULL, REG_OPTION_VOLATILE,
KEY_ALL_ACCESS, NULL, &hChildKey, NULL);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto CleanupTempKeys;
}
//
// 5. Copy the original child key (and any subkeys) back
// to the new volatile child key
//
Status = CopyRegistryTree(hTempKey, hChildKey, REG_OPTION_VOLATILE);
if (Status != CR_SUCCESS) {
goto CleanupTempKeys;
}
//
// 6. Remove the temporary volatile instance key (and any subkeys)
//
CleanupTempKeys:
if (hTempKey != NULL) {
RegCloseKey(hTempKey);
hTempKey = NULL;
}
wsprintf(RegStr, TEXT("%s\\%s"),
pszRegPathCurrentControlSet,
pszRegKeyDeleted);
RegStatus = RegOpenKeyEx(
HKEY_LOCAL_MACHINE, RegStr, 0, KEY_ALL_ACCESS, &hTempKey);
if (RegStatus != ERROR_SUCCESS) {
goto Clean0;
}
RegDeleteNode(hTempKey, szTempKey);
Clean0:
if (hParentKey != NULL) {
RegCloseKey(hParentKey);
}
if (hChildKey != NULL) {
RegCloseKey(hChildKey);
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
if (hTempKey != NULL) {
RegCloseKey(hTempKey);
}
return Status;
} // MakeKeyVolatile
CONFIGRET
MakeKeyNonVolatile(
IN LPCWSTR pszParentKey,
IN LPCWSTR pszChildKey
)
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
WCHAR RegStr[MAX_CM_PATH], szTempKey[MAX_CM_PATH];
HKEY hParentKey = NULL, hChildKey = NULL, hKey = NULL,
hTempKey = NULL;
//---------------------------------------------------------------------
// Convert the registry key specified by pszChildKey (a subkey of
// pszParentKey) to a non volatile key by copying it to a temporary key
// and recreating a nonvolatile key, then copying the original
// registry info back. This also converts any subkeys of pszChildKey.
//---------------------------------------------------------------------
//
// Open a key to the parent
//
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszParentKey, 0, KEY_ALL_ACCESS,
&hParentKey) != ERROR_SUCCESS) {
goto Clean0; // nothing to convert
}
//
// open a key to the child subkey
//
if (RegOpenKeyEx(hParentKey, pszChildKey, 0, KEY_ALL_ACCESS,
&hChildKey) != ERROR_SUCCESS) {
goto Clean0; // nothing to convert
}
//
// 1. Open a unique temporary volatile key under the special Deleted Key.
// Use the parent key path to form the unique tempory key. There shouldn't
// already be such a key, but if there is then just overwrite it.
//
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszRegPathCurrentControlSet, 0,
KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
wsprintf(RegStr, TEXT("%s\\%s"),
pszParentKey,
pszChildKey);
PathToString(szTempKey, RegStr,MAX_CM_PATH);
wsprintf(RegStr, TEXT("%s\\%s"),
pszRegKeyDeleted,
szTempKey);
if (RegCreateKeyEx(hKey, RegStr, 0, NULL, REG_OPTION_VOLATILE,
KEY_ALL_ACCESS, NULL, &hTempKey, NULL) != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto Clean0;
}
//
// 2. Save the current child key (and any subkeys) to a temporary
// location
//
Status = CopyRegistryTree(hChildKey, hTempKey, REG_OPTION_VOLATILE);
if (Status != CR_SUCCESS) {
goto CleanupTempKeys;
}
RegCloseKey(hChildKey);
hChildKey = NULL;
//
// 3. Delete the current child key (and any subkeys)
//
if (!RegDeleteNode(hParentKey, pszChildKey)) {
Status = CR_REGISTRY_ERROR;
goto CleanupTempKeys;
}
//
// 4. Recreate the current child key as a non-volatile key
//
if (RegCreateKeyEx(hParentKey, pszChildKey, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&hChildKey, NULL) != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
goto CleanupTempKeys;
}
//
// 5. Copy the original child key (and any subkeys) back
// to the new volatile child key
//
Status = CopyRegistryTree(hTempKey, hChildKey, REG_OPTION_NON_VOLATILE);
if (Status != CR_SUCCESS) {
goto CleanupTempKeys;
}
//
// 6. Remove the temporary volatile instance key (and any subkeys)
//
CleanupTempKeys:
if (hTempKey != NULL) {
RegCloseKey(hTempKey);
hTempKey = NULL;
}
wsprintf(RegStr, TEXT("%s\\%s"),
pszRegPathCurrentControlSet,
pszRegKeyDeleted);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegStr, 0, KEY_ALL_ACCESS,
&hTempKey) != ERROR_SUCCESS) {
goto Clean0;
}
RegDeleteNode(hTempKey, szTempKey);
Clean0:
if (hParentKey != NULL) {
RegCloseKey(hParentKey);
}
if (hChildKey != NULL) {
RegCloseKey(hChildKey);
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
if (hTempKey != NULL) {
RegCloseKey(hTempKey);
}
return Status;
} // MakeKeyNonVolatile
CONFIGRET
OpenLogConfKey(
IN LPCWSTR pszDeviceID,
IN ULONG LogConfType,
OUT PHKEY phKey
)
{
CONFIGRET Status = CR_SUCCESS;
LONG RegStatus = ERROR_SUCCESS;
HKEY hKey = NULL;
ULONG ulSize = 0;
try {
//
// Open a key to the device ID
//
RegStatus = RegOpenKeyEx(ghEnumKey, pszDeviceID, 0,
KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY,
&hKey);
if (RegStatus != ERROR_SUCCESS) {
Status = CR_INVALID_DEVINST;
goto Clean0;
}
//
// Alloc/Filtered configs are the exception, it's stored in the volative Control
// subkey, all the other log confs are stored under the nonvolatile
// LogConf subkey.
//
if ((LogConfType == ALLOC_LOG_CONF) || (LogConfType == FILTERED_LOG_CONF)) {
//
// Try the control key first, if no alloc config value there,
// then try the log conf key.
//
RegStatus = RegCreateKeyEx(hKey, pszRegKeyDeviceControl, 0, NULL,
REG_OPTION_VOLATILE, KEY_ALL_ACCESS,
NULL, phKey, NULL);
if (RegStatus == ERROR_SUCCESS) {
if (RegQueryValueEx(*phKey, pszRegValueAllocConfig, NULL, NULL,
NULL, &ulSize) == ERROR_SUCCESS) {
goto Clean0;
}
RegCloseKey(*phKey);
}
RegStatus = RegCreateKeyEx(hKey, pszRegKeyLogConf, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
NULL, phKey, NULL);
} else {
RegStatus = RegCreateKeyEx(hKey, pszRegKeyLogConf, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
NULL, phKey, NULL);
}
if (RegStatus != ERROR_SUCCESS) {
Status = CR_REGISTRY_ERROR;
}
Clean0:
NOTHING;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = CR_FAILURE;
}
if (hKey != NULL) {
RegCloseKey(hKey);
}
return Status;
} // OpenLogConfKey
BOOL
GetActiveService(
IN PCWSTR pszDevice,
OUT PWSTR pszService
)
{
WCHAR RegStr[MAX_PATH];
HKEY hKey = NULL;
ULONG ulSize = MAX_SERVICE_NAME_LEN * sizeof(WCHAR);
if (pszService == NULL || pszDevice == NULL) {
return FALSE;
}
*pszService = TEXT('\0');
//
// open the volatile control key under the device instance
//
wsprintf(RegStr, TEXT("%s\\%s\\%s"),
pszRegPathEnum,
pszDevice,
pszRegKeyDeviceControl);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegStr, 0, KEY_READ,
&hKey) != ERROR_SUCCESS) {
return FALSE;
}
//
// query the active service value
//
if (RegQueryValueEx(hKey, pszRegValueActiveService, NULL, NULL,
(LPBYTE)pszService, &ulSize) != ERROR_SUCCESS) {
RegCloseKey(hKey);
*pszService = TEXT('\0');
return FALSE;
}
RegCloseKey(hKey);
return TRUE;
} // GetActiveService
BOOL
IsDeviceIdPresent(
IN LPCWSTR pszDeviceID
)
/*++
Routine Description:
This routine determines whether the specified device instance is
considered physically present or not. This used to be based on a check
of the old "FoundAtEnum" registry setting. Now we just look for the presense
of an in-memory devnode associated with this device instance to decide whether
it's present or not.
Arguments:
pszDeviceID - device instance string to test for presense on
Return value:
The return value is TRUE if the function suceeds and FALSE if it fails.
--*/
{
ULONG ulStatus, ulProblem;
//
// If the call failed, then assume the device isn't present
//
return GetDeviceStatus(pszDeviceID, &ulStatus, &ulProblem) == CR_SUCCESS;
} // IsDeviceIdPresent
ULONG
GetDeviceConfigFlags(
IN LPCWSTR pszDeviceID,
IN HKEY hKey
)
{
HKEY hDevKey = NULL;
ULONG ulValue = 0, ulSize = sizeof(ULONG);
//
// If hKey is null, then open a key to the device instance.
//
if (hKey == NULL) {
if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ,
&hDevKey) != ERROR_SUCCESS) {
goto Clean0;
}
} else {
hDevKey = hKey;
}
//
// Retrieve the configflag value
//
if (RegQueryValueEx(hDevKey, pszRegValueConfigFlags, NULL, NULL,
(LPBYTE)&ulValue, &ulSize) != ERROR_SUCCESS) {
ulValue = 0;
}
Clean0:
if ((hKey == NULL) && (hDevKey != NULL)) {
RegCloseKey(hDevKey);
}
return ulValue;
} // GetDeviceConfigFlags
ULONG
MapNtStatusToCmError(
ULONG NtStatus
)
{
switch (NtStatus) {
case STATUS_BUFFER_TOO_SMALL:
return CR_BUFFER_SMALL;
case STATUS_NO_SUCH_DEVICE:
return CR_NO_SUCH_DEVINST;
case STATUS_INVALID_PARAMETER:
return CR_INVALID_DATA;
case STATUS_NOT_IMPLEMENTED:
return CR_CALL_NOT_IMPLEMENTED;
case STATUS_ACCESS_DENIED:
return CR_ACCESS_DENIED;
case STATUS_OBJECT_NAME_NOT_FOUND:
return CR_NO_SUCH_VALUE;
default:
return CR_FAILURE;
}
} // MapNtStatusToCmError
BOOL
VerifyKernelInitiatedEjectPermissions(
IN HANDLE UserToken OPTIONAL,
IN BOOL DockDevice
)
/*++
Routine Description:
Checks that the user has eject permissions for the specified type of
hardware.
Arguments:
UserToken - Token of the logged in console user, NULL if no console user
is logged in.
DockDevice - TRUE if a dock is being ejected, FALSE if an ordinary device
was specified.
Return Value:
TRUE if the eject should procceed, FALSE otherwise.
--*/
{
BOOL bSuccess, bResult;
PRIVILEGE_SET privilegeSet;
WCHAR regStr[MAX_CM_PATH];
HKEY hKey = NULL;
ULONG ulSize, ulResult;
if (!DockDevice) {
//
// We do not specify per device ejection security. This is not
// typically a problem as most devices are in no way secure from
// removal.
//
return TRUE;
}
//
// Not logged in, no user.
//
if (UserToken == NULL) {
//
// Open the policy key.
//
wsprintf(regStr, TEXT("%s\\%s"), pszRegPathPolicies, pszRegKeySystem);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
regStr,
0,
KEY_READ,
&hKey) != ERROR_SUCCESS) {
return FALSE;
}
//
// Snarf out the value.
//
ulSize = sizeof(ULONG);
if (RegQueryValueEx(hKey,
pszRegValueUndockWithoutLogon,
NULL,
NULL,
(LPBYTE)&ulResult,
&ulSize) != ERROR_SUCCESS) {
//
// No key means allow any undock.
//
bResult = TRUE;
} else {
//
// One means allow any undock, zero means require login.
//
bResult = (ulResult != 0);
}
RegCloseKey(hKey);
return bResult;
}
if ((gLuidUndockPrivilege.LowPart == 0) &&
(gLuidUndockPrivilege.HighPart == 0)) {
//
// Uninitialized LUID, most likely LookupPrivilegeValue failed
// during initialization, we'll pretend like they don't have
// access in this case.
//
return FALSE;
}
privilegeSet.PrivilegeCount = 1;
privilegeSet.Control = 0;
privilegeSet.Privilege[0].Luid = gLuidUndockPrivilege;
privilegeSet.Privilege[0].Attributes = 0;
if (!PrivilegeCheck(UserToken, &privilegeSet, &bResult)) {
KdPrintEx((DPFLTR_PNPMGR_ID,
DBGF_ERRORS,
"UMPNPMGR: PrivilegeCheck failed, error = %d\n",
GetLastError()));
}
return bResult;
} // VerifyKernelInitiatedEjectPermissions
DWORD
GuidFromString(
IN PCTSTR GuidString,
OUT LPGUID Guid
)
/*++
Routine Description:
This routine converts the character representation of a GUID into its binary
form (a GUID struct). The GUID is in the following form:
{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
where 'x' is a hexadecimal digit.
Arguments:
GuidString - Supplies a pointer to the null-terminated GUID string. The
Guid - Supplies a pointer to the variable that receives the GUID structure.
Return Value:
If the function succeeds, the return value is NO_ERROR.
If the function fails, the return value is RPC_S_INVALID_STRING_UUID.
--*/
{
TCHAR UuidBuffer[GUID_STRING_LEN - 1];
//
// Since we're using a RPC UUID routine, we need to strip off the surrounding
// curly braces first.
//
if(*GuidString++ != TEXT('{')) {
return RPC_S_INVALID_STRING_UUID;
}
lstrcpyn(UuidBuffer, GuidString, SIZECHARS(UuidBuffer));
if((lstrlen(UuidBuffer) != GUID_STRING_LEN - 2) ||
(UuidBuffer[GUID_STRING_LEN - 3] != TEXT('}'))) {
return RPC_S_INVALID_STRING_UUID;
}
UuidBuffer[GUID_STRING_LEN - 3] = TEXT('\0');
return ((UuidFromString(UuidBuffer, Guid) == RPC_S_OK) ? NO_ERROR : RPC_S_INVALID_STRING_UUID);
} // GuidFromString
DWORD
StringFromGuid(
IN CONST GUID *Guid,
OUT PTSTR GuidString,
IN DWORD GuidStringSize
)
/*++
Routine Description:
This routine converts a GUID into a null-terminated string which represents
it. This string is of the form:
{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
where x represents a hexadecimal digit.
This routine comes from ole32\common\ccompapi.cxx. It is included here to avoid linking
to ole32.dll. (The RPC version allocates memory, so it was avoided as well.)
Arguments:
Guid - Supplies a pointer to the GUID whose string representation is
to be retrieved.
GuidString - Supplies a pointer to character buffer that receives the
string. This buffer must be _at least_ 39 (GUID_STRING_LEN) characters
long.
Return Value:
If success, the return value is NO_ERROR.
if failure, the return value is
--*/
{
CONST BYTE *GuidBytes;
INT i;
if(GuidStringSize < GUID_STRING_LEN) {
return ERROR_INSUFFICIENT_BUFFER;
}
GuidBytes = (CONST BYTE *)Guid;
*GuidString++ = TEXT('{');
for(i = 0; i < sizeof(GuidMap); i++) {
if(GuidMap[i] == '-') {
*GuidString++ = TEXT('-');
} else {
*GuidString++ = szDigits[ (GuidBytes[GuidMap[i]] & 0xF0) >> 4 ];
*GuidString++ = szDigits[ (GuidBytes[GuidMap[i]] & 0x0F) ];
}
}
*GuidString++ = TEXT('}');
*GuidString = TEXT('\0');
return NO_ERROR;
} // StringFromGuid