Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2380 lines
53 KiB

  1. /*++
  2. Copyright (c) 1995-2001 Microsoft Corporation
  3. Module Name:
  4. rutil.c
  5. Abstract:
  6. This module contains general utility routines used by umpnpmgr.
  7. PNP_ENTER_SYNCHRONOUS_CALL
  8. PNP_LEAVE_SYNCHRONOUS_CALL
  9. IsClientUsingLocalConsole
  10. IsClientInteractive
  11. VerifyClientAccess
  12. SplitClassInstanceString
  13. CreateDeviceIDRegKey
  14. IsValidGuid
  15. IsRootDeviceID
  16. MultiSzAppendW
  17. MultiSzFindNextStringW
  18. MultiSzSearchStringW
  19. MultiSzSizeW
  20. MultiSzDeleteStringW
  21. IsValidDeviceID
  22. IsDevicePhantom
  23. GetDeviceStatus
  24. SetDeviceStatus
  25. ClearDeviceStatus
  26. GetProfileCount
  27. CopyRegistryTree
  28. PathToString
  29. IsDeviceMoved
  30. MakeKeyVolatile
  31. MakeKeyNonVolatile
  32. OpenLogConfKey
  33. GetActiveService
  34. IsDeviceIdPresent
  35. GetDeviceConfigFlags
  36. MapNtStatusToCmError
  37. VerifyKernelInitiatedEjectPermissions
  38. GuidFromString
  39. StringFromGuid
  40. Author:
  41. Paula Tomlinson (paulat) 7-12-1995
  42. Environment:
  43. User mode only.
  44. Revision History:
  45. 12-July-1995 paulat
  46. Creation and initial implementation.
  47. --*/
  48. //
  49. // includes
  50. //
  51. #include "precomp.h"
  52. #include "umpnpi.h"
  53. #include "umpnpdat.h"
  54. #include <winsta.h>
  55. #include <syslib.h>
  56. //
  57. // global data
  58. //
  59. extern HKEY ghEnumKey; // Key to HKLM\CCC\System\Enum - DO NOT MODIFY
  60. extern HKEY ghServicesKey; // Key to HKLM\CCC\System\Services - DO NOT MODIFY
  61. extern CRITICAL_SECTION PnpSynchronousCall;
  62. //
  63. // Declare data used in GUID->string conversion (from ole32\common\ccompapi.cxx).
  64. //
  65. static const BYTE GuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-',
  66. 8, 9, '-', 10, 11, 12, 13, 14, 15 };
  67. static const TCHAR szDigits[] = TEXT("0123456789ABCDEF");
  68. VOID
  69. PNP_ENTER_SYNCHRONOUS_CALL(
  70. VOID
  71. )
  72. {
  73. EnterCriticalSection(&PnpSynchronousCall);
  74. } // PNP_ENTER_SYNCHRONOUS_CALL
  75. VOID
  76. PNP_LEAVE_SYNCHRONOUS_CALL(
  77. VOID
  78. )
  79. {
  80. LeaveCriticalSection(&PnpSynchronousCall);
  81. } // PNP_LEAVE_SYNCHRONOUS_CALL
  82. BOOL
  83. IsClientUsingLocalConsole(
  84. IN handle_t hBinding
  85. )
  86. /*++
  87. Routine Description:
  88. This routine impersonates the client associated with hBinding and checks
  89. if the client is using the current active console session.
  90. Arguments:
  91. hBinding RPC Binding handle
  92. Return value:
  93. The return value is TRUE if the client is using the current active console
  94. session, FALSE if not or if an error occurs.
  95. --*/
  96. {
  97. RPC_STATUS rpcStatus;
  98. BOOL bResult = FALSE;
  99. rpcStatus = RpcImpersonateClient(hBinding);
  100. if (rpcStatus != RPC_S_OK) {
  101. KdPrintEx((DPFLTR_PNPMGR_ID,
  102. DBGF_ERRORS,
  103. "UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
  104. rpcStatus));
  105. return FALSE;
  106. }
  107. if (GetClientLogonId() == GetActiveConsoleSessionId()) {
  108. bResult = TRUE;
  109. }
  110. rpcStatus = RpcRevertToSelf();
  111. if (rpcStatus != RPC_S_OK) {
  112. KdPrintEx((DPFLTR_PNPMGR_ID,
  113. DBGF_ERRORS,
  114. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  115. rpcStatus));
  116. }
  117. return bResult;
  118. } // IsClientUsingLocalConsole
  119. BOOL
  120. IsClientLocal(
  121. IN handle_t hBinding
  122. )
  123. /*++
  124. Routine Description:
  125. This routine determines if the client associated with hBinding is on the
  126. local machine.
  127. Arguments:
  128. hBinding RPC Binding handle
  129. Return value:
  130. The return value is TRUE if the client is local to this machine, FALSE if
  131. not or if an error occurs.
  132. --*/
  133. {
  134. RPC_STATUS RpcStatus;
  135. UINT ClientLocalFlag;
  136. //
  137. // If the specified RPC binding handle is NULL, this is an internal call so
  138. // we assume that the privilege has already been checked.
  139. //
  140. if (hBinding == NULL) {
  141. return TRUE;
  142. }
  143. //
  144. // Retrieve the ClientLocalFlags from the RPC binding handle.
  145. //
  146. RpcStatus =
  147. I_RpcBindingIsClientLocal(
  148. hBinding,
  149. &ClientLocalFlag);
  150. if (RpcStatus != RPC_S_OK) {
  151. KdPrintEx((DPFLTR_PNPMGR_ID,
  152. DBGF_ERRORS,
  153. "UMPNPMGR: I_RpcBindingIsClientLocal failed, RpcStatus=%d\n",
  154. RpcStatus));
  155. return FALSE;
  156. }
  157. //
  158. // If the ClientLocalFlag is not zero, RPC client is local to server.
  159. //
  160. if (ClientLocalFlag != 0) {
  161. return TRUE;
  162. }
  163. //
  164. // Client is not local to this server.
  165. //
  166. return FALSE;
  167. } // IsClientLocal
  168. BOOL
  169. IsClientInteractive(
  170. IN handle_t hBinding
  171. )
  172. /*++
  173. Routine Description:
  174. This routine impersonates the client associated with hBinding and checks
  175. if the client is a member of the INTERACTIVE well-known group.
  176. Arguments:
  177. hBinding RPC Binding handle
  178. Return value:
  179. The return value is TRUE if the client is interactive, FALSE if not
  180. or if an error occurs.
  181. --*/
  182. {
  183. RPC_STATUS rpcStatus;
  184. BOOL bIsMember;
  185. HANDLE hToken;
  186. PSID sidInteractiveGroup;
  187. BOOL bResult = FALSE;
  188. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  189. if (hBinding == NULL) {
  190. //
  191. // This is an internal call so we assume that the privilege has already
  192. // been checked.
  193. //
  194. return TRUE;
  195. }
  196. if (!AllocateAndInitializeSid( &NtAuthority,
  197. 1, // one authority - INTERACTIVE
  198. SECURITY_INTERACTIVE_RID, // interactive logged on users only
  199. 0, 0, 0, 0, 0, 0, 0, // unused authority locations
  200. &sidInteractiveGroup)) {
  201. return FALSE;
  202. }
  203. rpcStatus = RpcImpersonateClient(hBinding);
  204. if (rpcStatus != RPC_S_OK) {
  205. KdPrintEx((DPFLTR_PNPMGR_ID,
  206. DBGF_ERRORS,
  207. "UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
  208. rpcStatus));
  209. FreeSid(sidInteractiveGroup);
  210. return FALSE;
  211. }
  212. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) {
  213. if (CheckTokenMembership(hToken,
  214. sidInteractiveGroup,
  215. &bIsMember)) {
  216. if (bIsMember) {
  217. bResult = TRUE;
  218. }
  219. } else {
  220. KdPrintEx((DPFLTR_PNPMGR_ID,
  221. DBGF_ERRORS,
  222. "UMPNPMGR: CheckTokenMembership failed, error = %d\n",
  223. GetLastError()));
  224. }
  225. CloseHandle(hToken);
  226. } else {
  227. KdPrintEx((DPFLTR_PNPMGR_ID,
  228. DBGF_ERRORS,
  229. "UMPNPMGR: OpenThreadToken failed, error = %d\n",
  230. GetLastError()));
  231. }
  232. FreeSid(sidInteractiveGroup);
  233. rpcStatus = RpcRevertToSelf();
  234. if (rpcStatus != RPC_S_OK) {
  235. KdPrintEx((DPFLTR_PNPMGR_ID,
  236. DBGF_ERRORS,
  237. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  238. rpcStatus));
  239. }
  240. return bResult;
  241. } // IsClientInteractive
  242. BOOL
  243. IsClientAdministrator(
  244. IN handle_t hBinding
  245. )
  246. /*++
  247. Routine Description:
  248. This routine impersonates the client associated with hBinding and checks
  249. if the client is a member of the Local Administrators group.
  250. Arguments:
  251. hBinding RPC Binding handle
  252. Return value:
  253. The return value is TRUE if the client is a Local Administrator, FALSE if
  254. not or if an error occurs.
  255. --*/
  256. {
  257. RPC_STATUS rpcStatus;
  258. BOOL bIsMember;
  259. HANDLE hToken;
  260. PSID sidAdministratorsGroup;
  261. BOOL bResult = FALSE;
  262. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  263. if (hBinding == NULL) {
  264. //
  265. // This is an internal call so we assume that the privilege has already
  266. // been checked.
  267. //
  268. return TRUE;
  269. }
  270. if (!AllocateAndInitializeSid( &NtAuthority, 2,
  271. SECURITY_BUILTIN_DOMAIN_RID,
  272. DOMAIN_ALIAS_RID_ADMINS,
  273. 0, 0, 0, 0, 0, 0,
  274. &sidAdministratorsGroup)) {
  275. return FALSE;
  276. }
  277. rpcStatus = RpcImpersonateClient(hBinding);
  278. if (rpcStatus != RPC_S_OK) {
  279. KdPrintEx((DPFLTR_PNPMGR_ID,
  280. DBGF_ERRORS,
  281. "UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
  282. rpcStatus));
  283. FreeSid(sidAdministratorsGroup);
  284. return FALSE;
  285. }
  286. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) {
  287. if (CheckTokenMembership(hToken,
  288. sidAdministratorsGroup,
  289. &bIsMember)) {
  290. if (bIsMember) {
  291. bResult = TRUE;
  292. }
  293. } else {
  294. KdPrintEx((DPFLTR_PNPMGR_ID,
  295. DBGF_ERRORS,
  296. "UMPNPMGR: CheckTokenMembership failed, error = %d\n",
  297. GetLastError()));
  298. }
  299. CloseHandle(hToken);
  300. } else {
  301. KdPrintEx((DPFLTR_PNPMGR_ID,
  302. DBGF_ERRORS,
  303. "UMPNPMGR: OpenThreadToken failed, error = %d\n",
  304. GetLastError()));
  305. }
  306. rpcStatus = RpcRevertToSelf();
  307. if (rpcStatus != RPC_S_OK) {
  308. KdPrintEx((DPFLTR_PNPMGR_ID,
  309. DBGF_ERRORS,
  310. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  311. rpcStatus));
  312. }
  313. FreeSid(sidAdministratorsGroup);
  314. return bResult;
  315. } // IsClientAdministrator
  316. BOOL
  317. VerifyClientAccess(
  318. IN handle_t hBinding,
  319. IN PLUID pPrivilegeLuid
  320. )
  321. /*++
  322. Routine Description:
  323. This routine impersonates the client associated with hBinding and checks
  324. if the client possesses the specified privilege.
  325. Arguments:
  326. hBinding RPC Binding handle
  327. pPrivilegeLuid LUID representing privilege to be checked.
  328. Return value:
  329. The return value is TRUE if the client possesses the privilege, FALSE if not
  330. or if an error occurs.
  331. --*/
  332. {
  333. RPC_STATUS rpcStatus;
  334. BOOL bResult;
  335. HANDLE hToken;
  336. PRIVILEGE_SET privilegeSet;
  337. if (hBinding == NULL) {
  338. //
  339. // This is an internal call so we assume that the privilege has already
  340. // been checked.
  341. //
  342. return TRUE;
  343. }
  344. if (pPrivilegeLuid->LowPart == 0 && pPrivilegeLuid->HighPart == 0) {
  345. //
  346. // Uninitialized LUID, most likely LookupPrivilegeValue failed during
  347. // initialization, we'll pretend like they don't have access in this
  348. // case.
  349. //
  350. return FALSE;
  351. }
  352. rpcStatus = RpcImpersonateClient(hBinding);
  353. if (rpcStatus != RPC_S_OK) {
  354. //
  355. // Since we can't impersonate the client we better not do the security
  356. // checks as ourself (they would always succeed).
  357. //
  358. return FALSE;
  359. }
  360. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) {
  361. privilegeSet.PrivilegeCount = 1;
  362. privilegeSet.Control = 0;
  363. privilegeSet.Privilege[0].Luid = *pPrivilegeLuid;
  364. privilegeSet.Privilege[0].Attributes = 0;
  365. if (!PrivilegeCheck(hToken, &privilegeSet, &bResult)) {
  366. KdPrintEx((DPFLTR_PNPMGR_ID,
  367. DBGF_ERRORS,
  368. "UMPNPMGR: PrivilegeCheck failed, error = %d\n",
  369. GetLastError()));
  370. bResult = FALSE;
  371. }
  372. CloseHandle(hToken);
  373. } else {
  374. KdPrintEx((DPFLTR_PNPMGR_ID,
  375. DBGF_ERRORS,
  376. "UMPNPMGR: OpenThreadToken failed, error = %d\n",
  377. GetLastError()));
  378. bResult = FALSE;
  379. }
  380. rpcStatus = RpcRevertToSelf();
  381. if (rpcStatus != RPC_S_OK) {
  382. KdPrintEx((DPFLTR_PNPMGR_ID,
  383. DBGF_ERRORS,
  384. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  385. rpcStatus));
  386. }
  387. return bResult;
  388. } // VerifyClientAccess
  389. BOOL
  390. SplitClassInstanceString(
  391. IN LPCWSTR pszClassInstance,
  392. OUT LPWSTR pszClass,
  393. OUT LPWSTR pszInstance
  394. )
  395. /*++
  396. Routine Description:
  397. This routine parses a class instance string into it's two component parts.
  398. Arguments:
  399. Return value:
  400. The return value is TRUE if the function suceeds and FALSE if it fails.
  401. --*/
  402. {
  403. UINT ulLength, i;
  404. ulLength = lstrlen(pszClassInstance);
  405. //
  406. // parse the string for the backslash character
  407. //
  408. for (i=0; i < ulLength && pszClassInstance[i] != '\0' &&
  409. pszClassInstance[i] != '\\'; i++);
  410. if (pszClassInstance[i] != '\\') {
  411. return FALSE;
  412. }
  413. i++; // increment past the backslash character
  414. if (i < ulLength && pszClassInstance[i] != '\0') {
  415. if (pszClass != NULL) {
  416. lstrcpyn(pszClass, pszClassInstance, i);
  417. }
  418. if (pszInstance != NULL) {
  419. lstrcpy(pszInstance, &pszClassInstance[i]);
  420. }
  421. }
  422. else {
  423. return FALSE;
  424. }
  425. return TRUE;
  426. } // SplitClassInstanceString
  427. BOOL
  428. CreateDeviceIDRegKey(
  429. HKEY hParentKey,
  430. LPCWSTR pDeviceID
  431. )
  432. /*++
  433. Routine Description:
  434. This routine creates the specified device id subkeys in the registry.
  435. Arguments:
  436. hParentKey Key under which the device id key will be created
  437. pDeviceID Device instance ID string to open
  438. Return value:
  439. The return value is TRUE if the function suceeds and FALSE if it fails.
  440. --*/
  441. {
  442. WCHAR szBase[MAX_DEVICE_ID_LEN];
  443. WCHAR szDevice[MAX_DEVICE_ID_LEN];
  444. WCHAR szInstance[MAX_DEVICE_ID_LEN];
  445. HKEY hBaseKey, hDeviceKey, hInstanceKey;
  446. if (!SplitDeviceInstanceString(
  447. pDeviceID, szBase, szDevice, szInstance)) {
  448. return FALSE;
  449. }
  450. //
  451. // just try creating each component of the device id
  452. //
  453. if (RegCreateKeyEx(
  454. hParentKey, szBase, 0, NULL, REG_OPTION_NON_VOLATILE,
  455. KEY_ALL_ACCESS, NULL, &hBaseKey, NULL) != ERROR_SUCCESS) {
  456. return FALSE;
  457. }
  458. if (RegCreateKeyEx(
  459. hBaseKey, szDevice, 0, NULL, REG_OPTION_NON_VOLATILE,
  460. KEY_ALL_ACCESS, NULL, &hDeviceKey, NULL) != ERROR_SUCCESS) {
  461. RegCloseKey(hBaseKey);
  462. return FALSE;
  463. }
  464. if (RegCreateKeyEx(
  465. hDeviceKey, szInstance, 0, NULL, REG_OPTION_NON_VOLATILE,
  466. KEY_ALL_ACCESS, NULL, &hInstanceKey, NULL) != ERROR_SUCCESS) {
  467. RegCloseKey(hBaseKey);
  468. RegCloseKey(hDeviceKey);
  469. return FALSE;
  470. }
  471. RegCloseKey(hBaseKey);
  472. RegCloseKey(hDeviceKey);
  473. RegCloseKey(hInstanceKey);
  474. return TRUE;
  475. } // CreateDeviceIDRegKey
  476. BOOL
  477. IsValidGuid(
  478. LPWSTR pszGuid
  479. )
  480. /*++
  481. Routine Description:
  482. This routine determines whether a string is of the proper Guid form.
  483. Arguments:
  484. pszGuid Pointer to a string that will be checked for the standard Guid
  485. format.
  486. Return value:
  487. The return value is TRUE if the string is a valid Guid and FALSE if it
  488. is not.
  489. --*/
  490. {
  491. //----------------------------------------------------------------
  492. // NOTE: This may change later, but for now I am just verifying
  493. // that the string has exactly MAX_GUID_STRING_LEN characters
  494. //----------------------------------------------------------------
  495. if (lstrlen(pszGuid) != MAX_GUID_STRING_LEN-1) {
  496. return FALSE;
  497. }
  498. return TRUE;
  499. } // IsValidGuid
  500. BOOL
  501. IsRootDeviceID(
  502. LPCWSTR pDeviceID
  503. )
  504. /*++
  505. Routine Description:
  506. This routine determines whether the specified device id is the root
  507. device id.
  508. Arguments:
  509. pDeviceID Pointer to a device id string
  510. Return value:
  511. The return value is TRUE if the string is the root device id and
  512. FALSE if it is not.
  513. --*/
  514. {
  515. return (lstrcmpi(pDeviceID, pszRegRootEnumerator) == 0);
  516. } // IsRootDeviceID
  517. BOOL
  518. MultiSzAppendW(
  519. LPWSTR pszMultiSz,
  520. PULONG pulSize,
  521. LPCWSTR pszString
  522. )
  523. /*++
  524. Routine Description:
  525. Appends a string to a multi_sz string.
  526. Arguments:
  527. pszMultiSz Pointer to a multi_sz string
  528. pulSize On input, Size of the multi_sz string buffer in bytes,
  529. On return, amount copied to the buffer (in bytes)
  530. pszString String to append to pszMultiSz
  531. Return value:
  532. The return value is TRUE if the function succeeded and FALSE if an
  533. error occured.
  534. --*/
  535. {
  536. BOOL bStatus = TRUE;
  537. LPWSTR pTail;
  538. ULONG ulSize;
  539. try {
  540. //
  541. // if it's an empty string, just copy it
  542. //
  543. if (*pszMultiSz == '\0') {
  544. ulSize = (lstrlen(pszString) + 2) * sizeof(WCHAR);
  545. if (ulSize > *pulSize) {
  546. bStatus = FALSE;
  547. goto Clean0;
  548. }
  549. lstrcpy(pszMultiSz, pszString);
  550. pszMultiSz[lstrlen(pszMultiSz) + 1] = '\0'; // add second NULL term char
  551. *pulSize = ulSize;
  552. goto Clean0;
  553. }
  554. //
  555. // first find the end of the multi_sz string
  556. //
  557. pTail = pszMultiSz;
  558. while ((ULONG)(pTail - pszMultiSz) * sizeof(WCHAR) < *pulSize) {
  559. while (*pTail != '\0') {
  560. pTail++;
  561. }
  562. pTail++; // skip past the null terminator
  563. if (*pTail == '\0') {
  564. break; // found the double null terminator
  565. }
  566. }
  567. if ((pTail - pszMultiSz + lstrlen(pszString) + 2) * sizeof(WCHAR)
  568. > *pulSize) {
  569. bStatus = FALSE; // the copy would overflow the buffer
  570. goto Clean0;
  571. }
  572. lstrcpy(pTail, pszString); // copies over the second null terminator
  573. pTail += lstrlen(pszString) + 1;
  574. *pTail = '\0'; // add second null terminator
  575. //
  576. // return buffer size in bytes
  577. //
  578. *pulSize = (ULONG)((pTail - pszMultiSz + 1)) * sizeof(WCHAR);
  579. Clean0:
  580. NOTHING;
  581. } except(EXCEPTION_EXECUTE_HANDLER) {
  582. bStatus = FALSE;
  583. }
  584. return bStatus;
  585. } // MultiSzAppendW
  586. LPWSTR
  587. MultiSzFindNextStringW(
  588. LPWSTR pMultiSz
  589. )
  590. /*++
  591. Routine Description:
  592. Finds next string in a multi_sz string.
  593. device id.
  594. Arguments:
  595. pMultiSz Pointer to a multi_sz string
  596. Return value:
  597. The return value is a pointer to the next string or NULL.
  598. --*/
  599. {
  600. LPWSTR lpNextString = pMultiSz;
  601. //
  602. // find the next NULL terminator
  603. //
  604. while (*lpNextString != '\0') {
  605. lpNextString++;
  606. }
  607. lpNextString++; // skip over the NULL terminator
  608. if (*lpNextString == '\0') {
  609. //
  610. // two NULL terminators in a row means we're at the end
  611. //
  612. lpNextString = NULL;
  613. }
  614. return lpNextString;
  615. } // MultiSzFindNextStringW
  616. BOOL
  617. MultiSzSearchStringW(
  618. IN LPCWSTR pString,
  619. IN LPCWSTR pSubString
  620. )
  621. {
  622. LPCWSTR pCurrent = pString;
  623. //
  624. // compare each string in the multi_sz pString with pSubString
  625. //
  626. while (*pCurrent != '\0') {
  627. if (lstrcmpi(pCurrent, pSubString) == 0) {
  628. return TRUE;
  629. }
  630. //
  631. // go to the next string
  632. //
  633. while (*pCurrent != '\0') {
  634. pCurrent++;
  635. }
  636. pCurrent++; // skip past the null terminator
  637. if (*pCurrent == '\0') {
  638. break; // found the double null terminator
  639. }
  640. }
  641. return FALSE; // pSubString match not found within pString
  642. } // MultiSzSearchStringW
  643. ULONG
  644. MultiSzSizeW(
  645. IN LPCWSTR pString
  646. )
  647. {
  648. LPCWSTR p = NULL;
  649. if (pString == NULL) {
  650. return 0;
  651. }
  652. for (p = pString; *p; p += lstrlen(p)+1) {
  653. // this should fall out with p pointing to the
  654. // second null in double-null terminator
  655. }
  656. //
  657. // returns size in WCHAR
  658. //
  659. return (ULONG)(p - pString + 1);
  660. } // MultiSzSizeW
  661. BOOL
  662. MultiSzDeleteStringW(
  663. IN OUT LPWSTR pString,
  664. IN LPCWSTR pSubString
  665. )
  666. {
  667. LPWSTR p = NULL, pNext = NULL, pBuffer = NULL;
  668. ULONG ulSize = 0;
  669. if (pString == NULL || pSubString == NULL) {
  670. return FALSE;
  671. }
  672. for (p = pString; *p; p += lstrlen(p)+1) {
  673. if (lstrcmpi(p, pSubString) == 0) {
  674. //
  675. // found a match, this is the string to remove.
  676. //
  677. pNext = p + lstrlen(p) + 1;
  678. //
  679. // If this is the last string then just truncate it
  680. //
  681. if (*pNext == '\0') {
  682. *p = '\0';
  683. *(++p) = '\0'; // double null-terminator
  684. return TRUE;
  685. }
  686. //
  687. // retrieve the size of the multi_sz string (in bytes)
  688. // starting with the substring after the matching substring
  689. //
  690. ulSize = MultiSzSizeW(pNext) * sizeof(WCHAR);
  691. if (ulSize == 0) {
  692. return FALSE;
  693. }
  694. pBuffer = HeapAlloc(ghPnPHeap, 0, ulSize);
  695. if (pBuffer == NULL) {
  696. return FALSE;
  697. }
  698. //
  699. // Make a copy of the multi_sz string starting at the
  700. // substring immediately after the matching substring
  701. //
  702. memcpy(pBuffer, pNext, ulSize);
  703. //
  704. // Copy that buffer back to the original buffer, but this
  705. // time copy over the top of the matching substring. This
  706. // effectively removes the matching substring and shifts
  707. // any remaining substrings up in multi_sz string.
  708. //
  709. memcpy(p, pBuffer, ulSize);
  710. HeapFree(ghPnPHeap, 0, pBuffer);
  711. return TRUE;
  712. }
  713. }
  714. //
  715. // if we got here, there was no match but I consider this a success
  716. // since the multi_sz does not contain the substring when we're done
  717. // (which is the desired goal)
  718. //
  719. return TRUE;
  720. } // MultiSzDeleteStringW
  721. BOOL
  722. IsValidDeviceID(
  723. IN LPCWSTR pszDeviceID,
  724. IN HKEY hKey,
  725. IN ULONG ulFlags
  726. )
  727. /*++
  728. Routine Description:
  729. This routine checks if the given device id is valid (present, not moved,
  730. not phantom).
  731. Arguments:
  732. pszDeviceID Device instance string to validate
  733. hKey Can specify open registry key to pszDeviceID, also
  734. ulFlag Controls how much verification to do
  735. Return value:
  736. The return value is CR_SUCCESS if the function suceeds and one of the
  737. CR_* values if it fails.
  738. --*/
  739. {
  740. LONG RegStatus = ERROR_SUCCESS;
  741. WCHAR RegStr[MAX_CM_PATH];
  742. HKEY hDevKey;
  743. ULONG ulValue = 0, ulSize = sizeof(ULONG);
  744. //
  745. // Does the device id exist in the registry?
  746. //
  747. if (hKey == NULL) {
  748. wsprintf(RegStr, TEXT("%s\\%s"),
  749. pszRegPathEnum,
  750. pszDeviceID);
  751. RegStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegStr, 0, KEY_READ, &hDevKey);
  752. if (RegStatus != ERROR_SUCCESS) {
  753. return FALSE;
  754. }
  755. }
  756. else {
  757. hDevKey = hKey;
  758. }
  759. //-----------------------------------------------------------
  760. // Is the device id present?
  761. //-----------------------------------------------------------
  762. if (ulFlags & PNP_PRESENT) {
  763. if (!IsDeviceIdPresent(pszDeviceID)) {
  764. if (hKey == NULL && hDevKey != NULL) {
  765. RegCloseKey(hDevKey);
  766. }
  767. return FALSE;
  768. }
  769. }
  770. //-----------------------------------------------------------
  771. // Is it a phantom device id?
  772. //-----------------------------------------------------------
  773. if (ulFlags & PNP_NOT_PHANTOM) {
  774. RegStatus = RegQueryValueEx(
  775. hDevKey, pszRegValuePhantom, NULL, NULL,
  776. (LPBYTE)&ulValue, &ulSize);
  777. if (RegStatus == ERROR_SUCCESS) {
  778. if (ulValue) {
  779. if (hKey == NULL && hDevKey != NULL) {
  780. RegCloseKey(hDevKey);
  781. }
  782. return FALSE;
  783. }
  784. }
  785. }
  786. //-----------------------------------------------------------
  787. // Has the device id been moved?
  788. //-----------------------------------------------------------
  789. if (ulFlags & PNP_NOT_MOVED) {
  790. if (IsDeviceMoved(pszDeviceID, hDevKey)) {
  791. return FALSE;
  792. }
  793. }
  794. //-----------------------------------------------------------
  795. // Has the device id been removed?
  796. //-----------------------------------------------------------
  797. if (ulFlags & PNP_NOT_REMOVED) {
  798. ULONG ulProblem = 0, ulStatus = 0;
  799. if (GetDeviceStatus(pszDeviceID, &ulStatus, &ulProblem) == CR_SUCCESS) {
  800. if (ulStatus & DN_WILL_BE_REMOVED) {
  801. if (hKey == NULL && hDevKey != NULL) {
  802. RegCloseKey(hDevKey);
  803. }
  804. return FALSE;
  805. }
  806. }
  807. }
  808. if (hKey == NULL && hDevKey != NULL) {
  809. RegCloseKey(hDevKey);
  810. }
  811. return TRUE;
  812. } // IsValidDeviceID
  813. BOOL
  814. IsDevicePhantom(
  815. IN LPWSTR pszDeviceID
  816. )
  817. /*++
  818. Routine Description:
  819. In this case, the check is actually really "is this not present?". The
  820. only comparison is done against FoundAtEnum. UPDATE: for NT 5.0, the
  821. FoundAtEnum registry value has been obsoleted, it's been replaced by a
  822. simple check for the presense of the devnode in memory.
  823. Arguments:
  824. pszDeviceID Device instance string to validate
  825. Return value:
  826. Returns TRUE if the device is a phantom and FALSE if it isn't.
  827. --*/
  828. {
  829. return !IsDeviceIdPresent(pszDeviceID);
  830. } // IsDevicePhantom
  831. CONFIGRET
  832. GetDeviceStatus(
  833. IN LPCWSTR pszDeviceID,
  834. OUT PULONG pulStatus,
  835. OUT PULONG pulProblem
  836. )
  837. /*++
  838. Routine Description:
  839. This routine retrieves the status and problem values for the given
  840. device instance.
  841. Arguments:
  842. pszDeviceID Specifies the device instance to retrieve info for
  843. pulStatus Returns the device's status
  844. pulProblem Returns the device's problem
  845. Return value:
  846. The return value is CR_SUCCESS if the function suceeds and one of the
  847. CR_* values if it fails.
  848. --*/
  849. {
  850. CONFIGRET Status = CR_SUCCESS;
  851. PLUGPLAY_CONTROL_STATUS_DATA ControlData;
  852. NTSTATUS ntStatus;
  853. memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_STATUS_DATA));
  854. RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
  855. ControlData.Operation = PNP_GET_STATUS;
  856. ControlData.DeviceStatus = 0;
  857. ControlData.DeviceProblem = 0;
  858. ntStatus = NtPlugPlayControl(PlugPlayControlDeviceStatus,
  859. &ControlData,
  860. sizeof(ControlData));
  861. if (NT_SUCCESS(ntStatus)) {
  862. *pulStatus = ControlData.DeviceStatus;
  863. *pulProblem = ControlData.DeviceProblem;
  864. } else {
  865. Status = MapNtStatusToCmError(ntStatus);
  866. }
  867. return Status;
  868. } // GetDeviceStatus
  869. CONFIGRET
  870. SetDeviceStatus(
  871. IN LPCWSTR pszDeviceID,
  872. IN ULONG ulStatus,
  873. IN ULONG ulProblem
  874. )
  875. /*++
  876. Routine Description:
  877. This routine sets the status and problem values for the given
  878. device instance.
  879. Arguments:
  880. pszDeviceID Specifies the device instance to retrieve info for
  881. pulStatus Specifies the device's status
  882. pulProblem Specifies the device's problem
  883. Return value:
  884. The return value is CR_SUCCESS if the function suceeds and one of the
  885. CR_* values if it fails.
  886. --*/
  887. {
  888. CONFIGRET Status = CR_SUCCESS;
  889. PLUGPLAY_CONTROL_STATUS_DATA ControlData;
  890. NTSTATUS ntStatus;
  891. memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_STATUS_DATA));
  892. RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
  893. ControlData.Operation = PNP_SET_STATUS;
  894. ControlData.DeviceStatus = ulStatus;
  895. ControlData.DeviceProblem = ulProblem;
  896. ntStatus = NtPlugPlayControl(PlugPlayControlDeviceStatus,
  897. &ControlData,
  898. sizeof(ControlData));
  899. if (!NT_SUCCESS(ntStatus)) {
  900. Status = MapNtStatusToCmError(ntStatus);
  901. }
  902. return Status;
  903. } // SetDeviceStatus
  904. CONFIGRET
  905. ClearDeviceStatus(
  906. IN LPCWSTR pszDeviceID,
  907. IN ULONG ulStatus,
  908. IN ULONG ulProblem
  909. )
  910. /*++
  911. Routine Description:
  912. This routine clears the followingstatus and problem values for the given
  913. device instance.
  914. Arguments:
  915. pszDeviceID Specifies the device instance to retrieve info for
  916. pulStatus Specifies the device's status
  917. pulProblem Specifies the device's problem
  918. Return value:
  919. The return value is CR_SUCCESS if the function suceeds and one of the
  920. CR_* values if it fails.
  921. --*/
  922. {
  923. CONFIGRET Status = CR_SUCCESS;
  924. PLUGPLAY_CONTROL_STATUS_DATA ControlData;
  925. NTSTATUS ntStatus;
  926. memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_STATUS_DATA));
  927. RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
  928. ControlData.Operation = PNP_CLEAR_STATUS;
  929. ControlData.DeviceStatus = ulStatus;
  930. ControlData.DeviceProblem = ulProblem;
  931. ntStatus = NtPlugPlayControl(PlugPlayControlDeviceStatus,
  932. &ControlData,
  933. sizeof(ControlData));
  934. if (!NT_SUCCESS(ntStatus)) {
  935. Status = MapNtStatusToCmError(ntStatus);
  936. }
  937. return Status;
  938. } // ClearDeviceStatus
  939. CONFIGRET
  940. GetProfileCount(
  941. OUT PULONG pulProfiles
  942. )
  943. {
  944. WCHAR RegStr[MAX_CM_PATH];
  945. HKEY hKey = NULL;
  946. //
  947. // open the Known Docking States key
  948. //
  949. wsprintf(RegStr, TEXT("%s\\%s"),
  950. pszRegPathIDConfigDB,
  951. pszRegKeyKnownDockingStates);
  952. if (RegOpenKeyEx(
  953. HKEY_LOCAL_MACHINE, RegStr, 0, KEY_READ,
  954. &hKey) != ERROR_SUCCESS) {
  955. *pulProfiles = 0;
  956. return CR_REGISTRY_ERROR;
  957. }
  958. //
  959. // find out the total number of profiles
  960. //
  961. if (RegQueryInfoKey(
  962. hKey, NULL, NULL, NULL, pulProfiles, NULL, NULL, NULL,
  963. NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
  964. *pulProfiles = 0;
  965. RegCloseKey(hKey);
  966. return CR_REGISTRY_ERROR;
  967. }
  968. RegCloseKey(hKey);
  969. return CR_SUCCESS;
  970. } // GetProfileCount
  971. CONFIGRET
  972. CopyRegistryTree(
  973. IN HKEY hSrcKey,
  974. IN HKEY hDestKey,
  975. IN ULONG ulOption
  976. )
  977. {
  978. CONFIGRET Status = CR_SUCCESS;
  979. LONG RegStatus = ERROR_SUCCESS;
  980. HKEY hSrcSubKey, hDestSubKey;
  981. WCHAR RegStr[MAX_PATH];
  982. ULONG ulMaxValueName, ulMaxValueData;
  983. ULONG ulDataSize, ulLength, ulType, i;
  984. LPWSTR pszValueName=NULL;
  985. LPBYTE pValueData=NULL;
  986. PSECURITY_DESCRIPTOR pSecDesc;
  987. //----------------------------------------------------------------
  988. // copy all values for this key
  989. //----------------------------------------------------------------
  990. //
  991. // find out the maximum size of any of the value names
  992. // and value data under the source device instance key
  993. //
  994. RegStatus = RegQueryInfoKey(
  995. hSrcKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  996. &ulMaxValueName, &ulMaxValueData, NULL, NULL);
  997. if (RegStatus != ERROR_SUCCESS) {
  998. Status = CR_REGISTRY_ERROR;
  999. goto Clean0;
  1000. }
  1001. ulMaxValueName++; // size doesn't already include null terminator
  1002. //
  1003. // allocate a buffer big enough to hold the largest value name and
  1004. // the largest value data (note that the max value name is in chars
  1005. // (not including the null terminator) and the max value data is
  1006. // in bytes
  1007. //
  1008. pszValueName = HeapAlloc(ghPnPHeap, 0, ulMaxValueName * sizeof(WCHAR));
  1009. if (pszValueName == NULL) {
  1010. Status = CR_OUT_OF_MEMORY;
  1011. goto Clean0;
  1012. }
  1013. pValueData = HeapAlloc(ghPnPHeap, 0, ulMaxValueData);
  1014. if (pValueData == NULL) {
  1015. Status = CR_OUT_OF_MEMORY;
  1016. goto Clean0;
  1017. }
  1018. //
  1019. // enumerate and copy each value
  1020. //
  1021. for (i=0; RegStatus == ERROR_SUCCESS; i++) {
  1022. ulLength = ulMaxValueName;
  1023. ulDataSize = ulMaxValueData;
  1024. RegStatus = RegEnumValue(
  1025. hSrcKey, i, pszValueName, &ulLength, NULL,
  1026. &ulType, pValueData, &ulDataSize);
  1027. if (RegStatus == ERROR_SUCCESS) {
  1028. RegSetValueEx(
  1029. hDestKey, pszValueName, 0, ulType, pValueData,
  1030. ulDataSize);
  1031. }
  1032. }
  1033. HeapFree(ghPnPHeap, 0, pszValueName);
  1034. pszValueName = NULL;
  1035. HeapFree(ghPnPHeap, 0, pValueData);
  1036. pValueData = NULL;
  1037. //---------------------------------------------------------------
  1038. // recursively call CopyRegistryNode to copy all subkeys
  1039. //---------------------------------------------------------------
  1040. RegStatus = ERROR_SUCCESS;
  1041. for (i=0; RegStatus == ERROR_SUCCESS; i++) {
  1042. ulLength = MAX_PATH;
  1043. RegStatus = RegEnumKey(hSrcKey, i, RegStr, ulLength);
  1044. if (RegStatus == ERROR_SUCCESS) {
  1045. if (RegOpenKey(hSrcKey, RegStr, &hSrcSubKey) == ERROR_SUCCESS) {
  1046. if (RegCreateKeyEx(
  1047. hDestKey, RegStr, 0, NULL, ulOption, KEY_ALL_ACCESS,
  1048. NULL, &hDestSubKey, NULL) == ERROR_SUCCESS) {
  1049. RegGetKeySecurity(hSrcSubKey, DACL_SECURITY_INFORMATION,
  1050. NULL, &ulDataSize);
  1051. pSecDesc = HeapAlloc(ghPnPHeap, 0, ulDataSize);
  1052. if (pSecDesc == NULL) {
  1053. Status = CR_OUT_OF_MEMORY;
  1054. RegCloseKey(hSrcSubKey);
  1055. RegCloseKey(hDestSubKey);
  1056. goto Clean0;
  1057. }
  1058. RegGetKeySecurity(hSrcSubKey, DACL_SECURITY_INFORMATION,
  1059. pSecDesc, &ulDataSize);
  1060. CopyRegistryTree(hSrcSubKey, hDestSubKey, ulOption);
  1061. RegSetKeySecurity(hDestSubKey, DACL_SECURITY_INFORMATION, pSecDesc);
  1062. HeapFree(ghPnPHeap, 0, pSecDesc);
  1063. RegCloseKey(hDestSubKey);
  1064. }
  1065. RegCloseKey(hSrcSubKey);
  1066. }
  1067. }
  1068. }
  1069. Clean0:
  1070. if (pszValueName != NULL) {
  1071. HeapFree(ghPnPHeap, 0, pszValueName);
  1072. }
  1073. if (pValueData != NULL) {
  1074. pValueData = NULL;
  1075. }
  1076. return Status;
  1077. } // CopyRegistryTree
  1078. BOOL
  1079. PathToString(
  1080. IN LPWSTR pszString,
  1081. IN LPCWSTR pszPath,
  1082. IN ULONG ulLen
  1083. )
  1084. {
  1085. LPWSTR p;
  1086. lstrcpyn(pszString, pszPath,ulLen);
  1087. for (p = pszString; *p; p++) {
  1088. if (*p == TEXT('\\')) {
  1089. *p = TEXT('&');
  1090. }
  1091. }
  1092. return TRUE;
  1093. } // PathToString
  1094. BOOL
  1095. IsDeviceMoved(
  1096. IN LPCWSTR pszDeviceID,
  1097. IN HKEY hKey
  1098. )
  1099. {
  1100. HKEY hTempKey;
  1101. WCHAR RegStr[MAX_CM_PATH];
  1102. PathToString(RegStr, pszDeviceID,MAX_CM_PATH);
  1103. if (RegOpenKeyEx(
  1104. hKey, RegStr, 0, KEY_READ, &hTempKey) == ERROR_SUCCESS) {
  1105. RegCloseKey(hTempKey);
  1106. return TRUE;
  1107. }
  1108. return FALSE;
  1109. } // IsDeviceMoved
  1110. CONFIGRET
  1111. MakeKeyVolatile(
  1112. IN LPCWSTR pszParentKey,
  1113. IN LPCWSTR pszChildKey
  1114. )
  1115. {
  1116. CONFIGRET Status = CR_SUCCESS;
  1117. LONG RegStatus = ERROR_SUCCESS;
  1118. WCHAR RegStr[MAX_CM_PATH], szTempKey[MAX_CM_PATH];
  1119. HKEY hParentKey = NULL, hChildKey = NULL, hKey = NULL,
  1120. hTempKey = NULL;
  1121. //---------------------------------------------------------------------
  1122. // Convert the registry key specified by pszChildKey (a subkey of
  1123. // pszParentKey) to a volatile key by copying it to a temporary key
  1124. // and recreating a volatile key, then copying the original
  1125. // registry info back. This also converts and subkeys of pszChildKey.
  1126. //---------------------------------------------------------------------
  1127. //
  1128. // Open a key to the parent
  1129. //
  1130. RegStatus = RegOpenKeyEx(
  1131. HKEY_LOCAL_MACHINE, pszParentKey, 0, KEY_ALL_ACCESS, &hParentKey);
  1132. if (RegStatus != ERROR_SUCCESS) {
  1133. goto Clean0; // nothing to convert
  1134. }
  1135. //
  1136. // open a key to the child subkey
  1137. //
  1138. RegStatus = RegOpenKeyEx(
  1139. hParentKey, pszChildKey, 0, KEY_ALL_ACCESS, &hChildKey);
  1140. if (RegStatus != ERROR_SUCCESS) {
  1141. goto Clean0; // nothing to convert
  1142. }
  1143. //
  1144. // 1. Open a unique temporary volatile key under the special Deleted Key.
  1145. // Use the parent key path to form the unique tempory key. There shouldn't
  1146. // already be such a key, but if there is then just overwrite it.
  1147. //
  1148. RegStatus = RegOpenKeyEx(
  1149. HKEY_LOCAL_MACHINE, pszRegPathCurrentControlSet, 0,
  1150. KEY_ALL_ACCESS, &hKey);
  1151. if (RegStatus != ERROR_SUCCESS) {
  1152. Status = CR_REGISTRY_ERROR;
  1153. goto Clean0;
  1154. }
  1155. wsprintf(RegStr, TEXT("%s\\%s"),
  1156. pszParentKey,
  1157. pszChildKey);
  1158. PathToString(szTempKey, RegStr,MAX_CM_PATH);
  1159. wsprintf(RegStr, TEXT("%s\\%s"),
  1160. pszRegKeyDeleted,
  1161. szTempKey);
  1162. RegStatus = RegCreateKeyEx(
  1163. hKey, RegStr, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS,
  1164. NULL, &hTempKey, NULL);
  1165. if (RegStatus != ERROR_SUCCESS) {
  1166. Status = CR_REGISTRY_ERROR;
  1167. goto Clean0;
  1168. }
  1169. //
  1170. // 2. Save the current child key (any any subkeys) to a temporary
  1171. // location
  1172. //
  1173. Status = CopyRegistryTree(hChildKey, hTempKey, REG_OPTION_VOLATILE);
  1174. if (Status != CR_SUCCESS) {
  1175. goto CleanupTempKeys;
  1176. }
  1177. RegCloseKey(hChildKey);
  1178. hChildKey = NULL;
  1179. //
  1180. // 3. Delete the current child key (and any subkeys)
  1181. //
  1182. if (!RegDeleteNode(hParentKey, pszChildKey)) {
  1183. Status = CR_REGISTRY_ERROR;
  1184. goto CleanupTempKeys;
  1185. }
  1186. //
  1187. // 4. Recreate the current child key as a volatile key
  1188. //
  1189. RegStatus = RegCreateKeyEx(
  1190. hParentKey, pszChildKey, 0, NULL, REG_OPTION_VOLATILE,
  1191. KEY_ALL_ACCESS, NULL, &hChildKey, NULL);
  1192. if (RegStatus != ERROR_SUCCESS) {
  1193. Status = CR_REGISTRY_ERROR;
  1194. goto CleanupTempKeys;
  1195. }
  1196. //
  1197. // 5. Copy the original child key (and any subkeys) back
  1198. // to the new volatile child key
  1199. //
  1200. Status = CopyRegistryTree(hTempKey, hChildKey, REG_OPTION_VOLATILE);
  1201. if (Status != CR_SUCCESS) {
  1202. goto CleanupTempKeys;
  1203. }
  1204. //
  1205. // 6. Remove the temporary volatile instance key (and any subkeys)
  1206. //
  1207. CleanupTempKeys:
  1208. if (hTempKey != NULL) {
  1209. RegCloseKey(hTempKey);
  1210. hTempKey = NULL;
  1211. }
  1212. wsprintf(RegStr, TEXT("%s\\%s"),
  1213. pszRegPathCurrentControlSet,
  1214. pszRegKeyDeleted);
  1215. RegStatus = RegOpenKeyEx(
  1216. HKEY_LOCAL_MACHINE, RegStr, 0, KEY_ALL_ACCESS, &hTempKey);
  1217. if (RegStatus != ERROR_SUCCESS) {
  1218. goto Clean0;
  1219. }
  1220. RegDeleteNode(hTempKey, szTempKey);
  1221. Clean0:
  1222. if (hParentKey != NULL) {
  1223. RegCloseKey(hParentKey);
  1224. }
  1225. if (hChildKey != NULL) {
  1226. RegCloseKey(hChildKey);
  1227. }
  1228. if (hKey != NULL) {
  1229. RegCloseKey(hKey);
  1230. }
  1231. if (hTempKey != NULL) {
  1232. RegCloseKey(hTempKey);
  1233. }
  1234. return Status;
  1235. } // MakeKeyVolatile
  1236. CONFIGRET
  1237. MakeKeyNonVolatile(
  1238. IN LPCWSTR pszParentKey,
  1239. IN LPCWSTR pszChildKey
  1240. )
  1241. {
  1242. CONFIGRET Status = CR_SUCCESS;
  1243. LONG RegStatus = ERROR_SUCCESS;
  1244. WCHAR RegStr[MAX_CM_PATH], szTempKey[MAX_CM_PATH];
  1245. HKEY hParentKey = NULL, hChildKey = NULL, hKey = NULL,
  1246. hTempKey = NULL;
  1247. //---------------------------------------------------------------------
  1248. // Convert the registry key specified by pszChildKey (a subkey of
  1249. // pszParentKey) to a non volatile key by copying it to a temporary key
  1250. // and recreating a nonvolatile key, then copying the original
  1251. // registry info back. This also converts any subkeys of pszChildKey.
  1252. //---------------------------------------------------------------------
  1253. //
  1254. // Open a key to the parent
  1255. //
  1256. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszParentKey, 0, KEY_ALL_ACCESS,
  1257. &hParentKey) != ERROR_SUCCESS) {
  1258. goto Clean0; // nothing to convert
  1259. }
  1260. //
  1261. // open a key to the child subkey
  1262. //
  1263. if (RegOpenKeyEx(hParentKey, pszChildKey, 0, KEY_ALL_ACCESS,
  1264. &hChildKey) != ERROR_SUCCESS) {
  1265. goto Clean0; // nothing to convert
  1266. }
  1267. //
  1268. // 1. Open a unique temporary volatile key under the special Deleted Key.
  1269. // Use the parent key path to form the unique tempory key. There shouldn't
  1270. // already be such a key, but if there is then just overwrite it.
  1271. //
  1272. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszRegPathCurrentControlSet, 0,
  1273. KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) {
  1274. Status = CR_REGISTRY_ERROR;
  1275. goto Clean0;
  1276. }
  1277. wsprintf(RegStr, TEXT("%s\\%s"),
  1278. pszParentKey,
  1279. pszChildKey);
  1280. PathToString(szTempKey, RegStr,MAX_CM_PATH);
  1281. wsprintf(RegStr, TEXT("%s\\%s"),
  1282. pszRegKeyDeleted,
  1283. szTempKey);
  1284. if (RegCreateKeyEx(hKey, RegStr, 0, NULL, REG_OPTION_VOLATILE,
  1285. KEY_ALL_ACCESS, NULL, &hTempKey, NULL) != ERROR_SUCCESS) {
  1286. Status = CR_REGISTRY_ERROR;
  1287. goto Clean0;
  1288. }
  1289. //
  1290. // 2. Save the current child key (and any subkeys) to a temporary
  1291. // location
  1292. //
  1293. Status = CopyRegistryTree(hChildKey, hTempKey, REG_OPTION_VOLATILE);
  1294. if (Status != CR_SUCCESS) {
  1295. goto CleanupTempKeys;
  1296. }
  1297. RegCloseKey(hChildKey);
  1298. hChildKey = NULL;
  1299. //
  1300. // 3. Delete the current child key (and any subkeys)
  1301. //
  1302. if (!RegDeleteNode(hParentKey, pszChildKey)) {
  1303. Status = CR_REGISTRY_ERROR;
  1304. goto CleanupTempKeys;
  1305. }
  1306. //
  1307. // 4. Recreate the current child key as a non-volatile key
  1308. //
  1309. if (RegCreateKeyEx(hParentKey, pszChildKey, 0, NULL,
  1310. REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
  1311. &hChildKey, NULL) != ERROR_SUCCESS) {
  1312. Status = CR_REGISTRY_ERROR;
  1313. goto CleanupTempKeys;
  1314. }
  1315. //
  1316. // 5. Copy the original child key (and any subkeys) back
  1317. // to the new volatile child key
  1318. //
  1319. Status = CopyRegistryTree(hTempKey, hChildKey, REG_OPTION_NON_VOLATILE);
  1320. if (Status != CR_SUCCESS) {
  1321. goto CleanupTempKeys;
  1322. }
  1323. //
  1324. // 6. Remove the temporary volatile instance key (and any subkeys)
  1325. //
  1326. CleanupTempKeys:
  1327. if (hTempKey != NULL) {
  1328. RegCloseKey(hTempKey);
  1329. hTempKey = NULL;
  1330. }
  1331. wsprintf(RegStr, TEXT("%s\\%s"),
  1332. pszRegPathCurrentControlSet,
  1333. pszRegKeyDeleted);
  1334. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegStr, 0, KEY_ALL_ACCESS,
  1335. &hTempKey) != ERROR_SUCCESS) {
  1336. goto Clean0;
  1337. }
  1338. RegDeleteNode(hTempKey, szTempKey);
  1339. Clean0:
  1340. if (hParentKey != NULL) {
  1341. RegCloseKey(hParentKey);
  1342. }
  1343. if (hChildKey != NULL) {
  1344. RegCloseKey(hChildKey);
  1345. }
  1346. if (hKey != NULL) {
  1347. RegCloseKey(hKey);
  1348. }
  1349. if (hTempKey != NULL) {
  1350. RegCloseKey(hTempKey);
  1351. }
  1352. return Status;
  1353. } // MakeKeyNonVolatile
  1354. CONFIGRET
  1355. OpenLogConfKey(
  1356. IN LPCWSTR pszDeviceID,
  1357. IN ULONG LogConfType,
  1358. OUT PHKEY phKey
  1359. )
  1360. {
  1361. CONFIGRET Status = CR_SUCCESS;
  1362. LONG RegStatus = ERROR_SUCCESS;
  1363. HKEY hKey = NULL;
  1364. ULONG ulSize = 0;
  1365. try {
  1366. //
  1367. // Open a key to the device ID
  1368. //
  1369. RegStatus = RegOpenKeyEx(ghEnumKey, pszDeviceID, 0,
  1370. KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY,
  1371. &hKey);
  1372. if (RegStatus != ERROR_SUCCESS) {
  1373. Status = CR_INVALID_DEVINST;
  1374. goto Clean0;
  1375. }
  1376. //
  1377. // Alloc/Filtered configs are the exception, it's stored in the volative Control
  1378. // subkey, all the other log confs are stored under the nonvolatile
  1379. // LogConf subkey.
  1380. //
  1381. if ((LogConfType == ALLOC_LOG_CONF) || (LogConfType == FILTERED_LOG_CONF)) {
  1382. //
  1383. // Try the control key first, if no alloc config value there,
  1384. // then try the log conf key.
  1385. //
  1386. RegStatus = RegCreateKeyEx(hKey, pszRegKeyDeviceControl, 0, NULL,
  1387. REG_OPTION_VOLATILE, KEY_ALL_ACCESS,
  1388. NULL, phKey, NULL);
  1389. if (RegStatus == ERROR_SUCCESS) {
  1390. if (RegQueryValueEx(*phKey, pszRegValueAllocConfig, NULL, NULL,
  1391. NULL, &ulSize) == ERROR_SUCCESS) {
  1392. goto Clean0;
  1393. }
  1394. RegCloseKey(*phKey);
  1395. }
  1396. RegStatus = RegCreateKeyEx(hKey, pszRegKeyLogConf, 0, NULL,
  1397. REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
  1398. NULL, phKey, NULL);
  1399. } else {
  1400. RegStatus = RegCreateKeyEx(hKey, pszRegKeyLogConf, 0, NULL,
  1401. REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
  1402. NULL, phKey, NULL);
  1403. }
  1404. if (RegStatus != ERROR_SUCCESS) {
  1405. Status = CR_REGISTRY_ERROR;
  1406. }
  1407. Clean0:
  1408. NOTHING;
  1409. } except(EXCEPTION_EXECUTE_HANDLER) {
  1410. Status = CR_FAILURE;
  1411. }
  1412. if (hKey != NULL) {
  1413. RegCloseKey(hKey);
  1414. }
  1415. return Status;
  1416. } // OpenLogConfKey
  1417. BOOL
  1418. GetActiveService(
  1419. IN PCWSTR pszDevice,
  1420. OUT PWSTR pszService
  1421. )
  1422. {
  1423. WCHAR RegStr[MAX_PATH];
  1424. HKEY hKey = NULL;
  1425. ULONG ulSize = MAX_SERVICE_NAME_LEN * sizeof(WCHAR);
  1426. if (pszService == NULL || pszDevice == NULL) {
  1427. return FALSE;
  1428. }
  1429. *pszService = TEXT('\0');
  1430. //
  1431. // open the volatile control key under the device instance
  1432. //
  1433. wsprintf(RegStr, TEXT("%s\\%s\\%s"),
  1434. pszRegPathEnum,
  1435. pszDevice,
  1436. pszRegKeyDeviceControl);
  1437. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegStr, 0, KEY_READ,
  1438. &hKey) != ERROR_SUCCESS) {
  1439. return FALSE;
  1440. }
  1441. //
  1442. // query the active service value
  1443. //
  1444. if (RegQueryValueEx(hKey, pszRegValueActiveService, NULL, NULL,
  1445. (LPBYTE)pszService, &ulSize) != ERROR_SUCCESS) {
  1446. RegCloseKey(hKey);
  1447. *pszService = TEXT('\0');
  1448. return FALSE;
  1449. }
  1450. RegCloseKey(hKey);
  1451. return TRUE;
  1452. } // GetActiveService
  1453. BOOL
  1454. IsDeviceIdPresent(
  1455. IN LPCWSTR pszDeviceID
  1456. )
  1457. /*++
  1458. Routine Description:
  1459. This routine determines whether the specified device instance is
  1460. considered physically present or not. This used to be based on a check
  1461. of the old "FoundAtEnum" registry setting. Now we just look for the presense
  1462. of an in-memory devnode associated with this device instance to decide whether
  1463. it's present or not.
  1464. Arguments:
  1465. pszDeviceID - device instance string to test for presense on
  1466. Return value:
  1467. The return value is TRUE if the function suceeds and FALSE if it fails.
  1468. --*/
  1469. {
  1470. ULONG ulStatus, ulProblem;
  1471. //
  1472. // If the call failed, then assume the device isn't present
  1473. //
  1474. return GetDeviceStatus(pszDeviceID, &ulStatus, &ulProblem) == CR_SUCCESS;
  1475. } // IsDeviceIdPresent
  1476. ULONG
  1477. GetDeviceConfigFlags(
  1478. IN LPCWSTR pszDeviceID,
  1479. IN HKEY hKey
  1480. )
  1481. {
  1482. HKEY hDevKey = NULL;
  1483. ULONG ulValue = 0, ulSize = sizeof(ULONG);
  1484. //
  1485. // If hKey is null, then open a key to the device instance.
  1486. //
  1487. if (hKey == NULL) {
  1488. if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ,
  1489. &hDevKey) != ERROR_SUCCESS) {
  1490. goto Clean0;
  1491. }
  1492. } else {
  1493. hDevKey = hKey;
  1494. }
  1495. //
  1496. // Retrieve the configflag value
  1497. //
  1498. if (RegQueryValueEx(hDevKey, pszRegValueConfigFlags, NULL, NULL,
  1499. (LPBYTE)&ulValue, &ulSize) != ERROR_SUCCESS) {
  1500. ulValue = 0;
  1501. }
  1502. Clean0:
  1503. if ((hKey == NULL) && (hDevKey != NULL)) {
  1504. RegCloseKey(hDevKey);
  1505. }
  1506. return ulValue;
  1507. } // GetDeviceConfigFlags
  1508. ULONG
  1509. MapNtStatusToCmError(
  1510. ULONG NtStatus
  1511. )
  1512. {
  1513. switch (NtStatus) {
  1514. case STATUS_BUFFER_TOO_SMALL:
  1515. return CR_BUFFER_SMALL;
  1516. case STATUS_NO_SUCH_DEVICE:
  1517. return CR_NO_SUCH_DEVINST;
  1518. case STATUS_INVALID_PARAMETER:
  1519. return CR_INVALID_DATA;
  1520. case STATUS_NOT_IMPLEMENTED:
  1521. return CR_CALL_NOT_IMPLEMENTED;
  1522. case STATUS_ACCESS_DENIED:
  1523. return CR_ACCESS_DENIED;
  1524. case STATUS_OBJECT_NAME_NOT_FOUND:
  1525. return CR_NO_SUCH_VALUE;
  1526. default:
  1527. return CR_FAILURE;
  1528. }
  1529. } // MapNtStatusToCmError
  1530. BOOL
  1531. VerifyKernelInitiatedEjectPermissions(
  1532. IN HANDLE UserToken OPTIONAL,
  1533. IN BOOL DockDevice
  1534. )
  1535. /*++
  1536. Routine Description:
  1537. Checks that the user has eject permissions for the specified type of
  1538. hardware.
  1539. Arguments:
  1540. UserToken - Token of the logged in console user, NULL if no console user
  1541. is logged in.
  1542. DockDevice - TRUE if a dock is being ejected, FALSE if an ordinary device
  1543. was specified.
  1544. Return Value:
  1545. TRUE if the eject should procceed, FALSE otherwise.
  1546. --*/
  1547. {
  1548. BOOL bSuccess, bResult;
  1549. PRIVILEGE_SET privilegeSet;
  1550. WCHAR regStr[MAX_CM_PATH];
  1551. HKEY hKey = NULL;
  1552. ULONG ulSize, ulResult;
  1553. if (!DockDevice) {
  1554. //
  1555. // We do not specify per device ejection security. This is not
  1556. // typically a problem as most devices are in no way secure from
  1557. // removal.
  1558. //
  1559. return TRUE;
  1560. }
  1561. //
  1562. // Not logged in, no user.
  1563. //
  1564. if (UserToken == NULL) {
  1565. //
  1566. // Open the policy key.
  1567. //
  1568. wsprintf(regStr, TEXT("%s\\%s"), pszRegPathPolicies, pszRegKeySystem);
  1569. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1570. regStr,
  1571. 0,
  1572. KEY_READ,
  1573. &hKey) != ERROR_SUCCESS) {
  1574. return FALSE;
  1575. }
  1576. //
  1577. // Snarf out the value.
  1578. //
  1579. ulSize = sizeof(ULONG);
  1580. if (RegQueryValueEx(hKey,
  1581. pszRegValueUndockWithoutLogon,
  1582. NULL,
  1583. NULL,
  1584. (LPBYTE)&ulResult,
  1585. &ulSize) != ERROR_SUCCESS) {
  1586. //
  1587. // No key means allow any undock.
  1588. //
  1589. bResult = TRUE;
  1590. } else {
  1591. //
  1592. // One means allow any undock, zero means require login.
  1593. //
  1594. bResult = (ulResult != 0);
  1595. }
  1596. RegCloseKey(hKey);
  1597. return bResult;
  1598. }
  1599. if ((gLuidUndockPrivilege.LowPart == 0) &&
  1600. (gLuidUndockPrivilege.HighPart == 0)) {
  1601. //
  1602. // Uninitialized LUID, most likely LookupPrivilegeValue failed
  1603. // during initialization, we'll pretend like they don't have
  1604. // access in this case.
  1605. //
  1606. return FALSE;
  1607. }
  1608. privilegeSet.PrivilegeCount = 1;
  1609. privilegeSet.Control = 0;
  1610. privilegeSet.Privilege[0].Luid = gLuidUndockPrivilege;
  1611. privilegeSet.Privilege[0].Attributes = 0;
  1612. if (!PrivilegeCheck(UserToken, &privilegeSet, &bResult)) {
  1613. KdPrintEx((DPFLTR_PNPMGR_ID,
  1614. DBGF_ERRORS,
  1615. "UMPNPMGR: PrivilegeCheck failed, error = %d\n",
  1616. GetLastError()));
  1617. }
  1618. return bResult;
  1619. } // VerifyKernelInitiatedEjectPermissions
  1620. DWORD
  1621. GuidFromString(
  1622. IN PCTSTR GuidString,
  1623. OUT LPGUID Guid
  1624. )
  1625. /*++
  1626. Routine Description:
  1627. This routine converts the character representation of a GUID into its binary
  1628. form (a GUID struct). The GUID is in the following form:
  1629. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
  1630. where 'x' is a hexadecimal digit.
  1631. Arguments:
  1632. GuidString - Supplies a pointer to the null-terminated GUID string. The
  1633. Guid - Supplies a pointer to the variable that receives the GUID structure.
  1634. Return Value:
  1635. If the function succeeds, the return value is NO_ERROR.
  1636. If the function fails, the return value is RPC_S_INVALID_STRING_UUID.
  1637. --*/
  1638. {
  1639. TCHAR UuidBuffer[GUID_STRING_LEN - 1];
  1640. //
  1641. // Since we're using a RPC UUID routine, we need to strip off the surrounding
  1642. // curly braces first.
  1643. //
  1644. if(*GuidString++ != TEXT('{')) {
  1645. return RPC_S_INVALID_STRING_UUID;
  1646. }
  1647. lstrcpyn(UuidBuffer, GuidString, SIZECHARS(UuidBuffer));
  1648. if((lstrlen(UuidBuffer) != GUID_STRING_LEN - 2) ||
  1649. (UuidBuffer[GUID_STRING_LEN - 3] != TEXT('}'))) {
  1650. return RPC_S_INVALID_STRING_UUID;
  1651. }
  1652. UuidBuffer[GUID_STRING_LEN - 3] = TEXT('\0');
  1653. return ((UuidFromString(UuidBuffer, Guid) == RPC_S_OK) ? NO_ERROR : RPC_S_INVALID_STRING_UUID);
  1654. } // GuidFromString
  1655. DWORD
  1656. StringFromGuid(
  1657. IN CONST GUID *Guid,
  1658. OUT PTSTR GuidString,
  1659. IN DWORD GuidStringSize
  1660. )
  1661. /*++
  1662. Routine Description:
  1663. This routine converts a GUID into a null-terminated string which represents
  1664. it. This string is of the form:
  1665. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
  1666. where x represents a hexadecimal digit.
  1667. This routine comes from ole32\common\ccompapi.cxx. It is included here to avoid linking
  1668. to ole32.dll. (The RPC version allocates memory, so it was avoided as well.)
  1669. Arguments:
  1670. Guid - Supplies a pointer to the GUID whose string representation is
  1671. to be retrieved.
  1672. GuidString - Supplies a pointer to character buffer that receives the
  1673. string. This buffer must be _at least_ 39 (GUID_STRING_LEN) characters
  1674. long.
  1675. Return Value:
  1676. If success, the return value is NO_ERROR.
  1677. if failure, the return value is
  1678. --*/
  1679. {
  1680. CONST BYTE *GuidBytes;
  1681. INT i;
  1682. if(GuidStringSize < GUID_STRING_LEN) {
  1683. return ERROR_INSUFFICIENT_BUFFER;
  1684. }
  1685. GuidBytes = (CONST BYTE *)Guid;
  1686. *GuidString++ = TEXT('{');
  1687. for(i = 0; i < sizeof(GuidMap); i++) {
  1688. if(GuidMap[i] == '-') {
  1689. *GuidString++ = TEXT('-');
  1690. } else {
  1691. *GuidString++ = szDigits[ (GuidBytes[GuidMap[i]] & 0xF0) >> 4 ];
  1692. *GuidString++ = szDigits[ (GuidBytes[GuidMap[i]] & 0x0F) ];
  1693. }
  1694. }
  1695. *GuidString++ = TEXT('}');
  1696. *GuidString = TEXT('\0');
  1697. return NO_ERROR;
  1698. } // StringFromGuid
  1699.