2381 lines
53 KiB
C
2381 lines
53 KiB
C
|
/*++
|
|||
|
|
|||
|
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
|
|||
|
|