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.

975 lines
30 KiB

  1. /*++
  2. Copyright (c) 1995-2000 Microsoft Corporation
  3. Module Name:
  4. devices.c
  5. Abstract:
  6. Plug and Play Manager routines dealing with device manipulation/registration.
  7. Author:
  8. Lonny McMichael (lonnym) 02/14/95
  9. Revision History:
  10. --*/
  11. #include "pnpmgrp.h"
  12. #pragma hdrstop
  13. typedef struct {
  14. BOOLEAN Add;
  15. } PROCESS_DRIVER_CONTEXT, *PPROCESS_DRIVER_CONTEXT;
  16. typedef NTSTATUS (*PDEVICE_SERVICE_ITERATOR_ROUTINE)(
  17. IN PUNICODE_STRING DeviceInstancePath,
  18. IN PUNICODE_STRING ServiceName,
  19. IN ULONG ServiceType,
  20. IN PVOID Context
  21. );
  22. typedef struct {
  23. PUNICODE_STRING DeviceInstancePath;
  24. PDEVICE_SERVICE_ITERATOR_ROUTINE Iterator;
  25. PVOID Context;
  26. } DEVICE_SERVICE_ITERATOR_CONTEXT, *PDEVICE_SERVICE_ITERATOR_CONTEXT;
  27. //
  28. // Prototype utility functions internal to this file.
  29. //
  30. NTSTATUS
  31. PiFindDevInstMatch(
  32. IN HANDLE ServiceEnumHandle,
  33. IN PUNICODE_STRING DeviceInstanceName,
  34. OUT PULONG InstanceCount,
  35. OUT PUNICODE_STRING MatchingValueName
  36. );
  37. NTSTATUS PiProcessDriverInstance(
  38. IN PUNICODE_STRING DeviceInstancePath,
  39. IN PUNICODE_STRING ServiceName,
  40. IN ULONG ServiceType,
  41. IN PPROCESS_DRIVER_CONTEXT Context
  42. );
  43. NTSTATUS
  44. PpForEachDeviceInstanceDriver(
  45. PUNICODE_STRING DeviceInstancePath,
  46. PDEVICE_SERVICE_ITERATOR_ROUTINE IteratorRoutine,
  47. PVOID Context
  48. );
  49. NTSTATUS
  50. PiForEachDriverQueryRoutine(
  51. IN PWSTR ValueName,
  52. IN ULONG ValueType,
  53. IN PVOID ValueData,
  54. IN ULONG ValueLength,
  55. IN PDEVICE_SERVICE_ITERATOR_CONTEXT InternalContext,
  56. IN ULONG ServiceType
  57. );
  58. #ifdef ALLOC_PRAGMA
  59. #pragma alloc_text(PAGE, PpDeviceRegistration)
  60. #pragma alloc_text(PAGE, PiDeviceRegistration)
  61. #pragma alloc_text(PAGE, PiProcessDriverInstance)
  62. #pragma alloc_text(PAGE, PiFindDevInstMatch)
  63. #pragma alloc_text(PAGE, PpForEachDeviceInstanceDriver)
  64. #pragma alloc_text(PAGE, PiForEachDriverQueryRoutine)
  65. #endif // ALLOC_PRAGMA
  66. NTSTATUS
  67. PpDeviceRegistration(
  68. IN PUNICODE_STRING DeviceInstancePath,
  69. IN BOOLEAN Add,
  70. IN PUNICODE_STRING ServiceKeyName OPTIONAL
  71. )
  72. /*++
  73. Routine Description:
  74. If Add is set to TRUE, this Plug and Play Manager API creates (if necessary)
  75. and populates the volatile Enum subkey of a device's service list entry, based
  76. on the device instance path specified. If Add is set to FALSE, the specified
  77. device instance will be removed from the volatile Enum subkey of a device's
  78. service list entry.
  79. For example, if there is a device in the Enum tree as follows:
  80. HKLM\System\Enum\PCI
  81. \foo
  82. \0000
  83. Service = REG_SZ bar
  84. \0001
  85. Service = REG_SZ other
  86. The result of the call, PpDeviceRegistration("PCI\foo\0000", Add = TRUE), would be:
  87. HKLM\CurrentControlSet\Services
  88. \bar
  89. \Enum
  90. Count = REG_DWORD 1
  91. 0 = REG_SZ PCI\foo\0000
  92. Arguments:
  93. DeviceInstancePath - Supplies the path in the registry (relative to
  94. HKLM\CCS\System\Enum) of the device to be registered/deregistered.
  95. This path must point to an instance subkey.
  96. Add - Supplies a BOOLEAN value to indicate the operation is for addition or removal.
  97. ServiceKeyName - Optionally, supplies the address of a unicode string to
  98. receive the name of the registry key for this device
  99. instance's service (if one exists). The caller must
  100. release the space once done with it.
  101. Return Value:
  102. NTSTATUS code indicating whether or not the function was successful
  103. --*/
  104. {
  105. NTSTATUS Status;
  106. PAGED_CODE();
  107. //
  108. // Acquire PnP device-specific registry resource for exclusive (read/write) access.
  109. //
  110. PiLockPnpRegistry(TRUE);
  111. Status = PiDeviceRegistration(DeviceInstancePath,
  112. Add,
  113. ServiceKeyName
  114. );
  115. PiUnlockPnpRegistry();
  116. return Status;
  117. }
  118. NTSTATUS
  119. PiDeviceRegistration(
  120. IN PUNICODE_STRING DeviceInstancePath,
  121. IN BOOLEAN Add,
  122. IN PUNICODE_STRING ServiceKeyName OPTIONAL
  123. )
  124. /*++
  125. Routine Description:
  126. If Add is set to TRUE, this Plug and Play Manager API creates (if necessary)
  127. and populates the volatile Enum subkey of a device's service list entry, based
  128. on the device instance path specified. If Add is set to FALSE, the specified
  129. device instance will be removed from the volatile Enum subkey of a device's
  130. service list entry.
  131. For example, if there is a device in the Enum tree as follows:
  132. HKLM\System\Enum\PCI
  133. \foo
  134. \0000
  135. Service = REG_SZ bar
  136. \0001
  137. Service = REG_SZ other
  138. The result of the call, PpDeviceRegistration("PCI\foo\0000", Add = TRUE), would be:
  139. HKLM\CurrentControlSet\Services
  140. \bar
  141. \Enum
  142. Count = REG_DWORD 1
  143. 0 = REG_SZ PCI\foo\0000
  144. Arguments:
  145. DeviceInstancePath - Supplies the path in the registry (relative to
  146. HKLM\CCS\System\Enum) of the device to be registered/deregistered.
  147. This path must point to an instance subkey.
  148. Add - Supplies a BOOLEAN value to indicate the operation is for addition or removal.
  149. ServiceKeyName - Optionally, supplies the address of a unicode string to
  150. receive the name of the registry key for this device
  151. instance's service (if one exists). The caller must
  152. release the space once done with it.
  153. Return Value:
  154. NTSTATUS code indicating whether or not the function was successful
  155. --*/
  156. {
  157. NTSTATUS Status;
  158. UNICODE_STRING ServiceName;
  159. PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
  160. HANDLE TempKeyHandle;
  161. HANDLE DeviceInstanceHandle = NULL;
  162. PAGED_CODE();
  163. //
  164. // Assume successful completion.
  165. //
  166. Status = STATUS_SUCCESS;
  167. if (ServiceKeyName) {
  168. PiWstrToUnicodeString(ServiceKeyName, NULL);
  169. }
  170. //
  171. // 'Normalize' the DeviceInstancePath by stripping off a trailing
  172. // backslash (if present)
  173. //
  174. if (DeviceInstancePath->Length <= sizeof(WCHAR)) {
  175. Status = STATUS_INVALID_PARAMETER;
  176. goto PrepareForReturn1;
  177. }
  178. if (DeviceInstancePath->Buffer[CB_TO_CWC(DeviceInstancePath->Length) - 1] ==
  179. OBJ_NAME_PATH_SEPARATOR) {
  180. DeviceInstancePath->Length -= sizeof(WCHAR);
  181. }
  182. //
  183. // Open HKLM\System\CurrentControlSet\Enum
  184. //
  185. Status = IopOpenRegistryKeyEx( &TempKeyHandle,
  186. NULL,
  187. &CmRegistryMachineSystemCurrentControlSetEnumName,
  188. KEY_READ
  189. );
  190. if(!NT_SUCCESS(Status)) {
  191. goto PrepareForReturn1;
  192. }
  193. //
  194. // Open the specified device instance key under HKLM\CCS\System\Enum
  195. //
  196. Status = IopOpenRegistryKeyEx( &DeviceInstanceHandle,
  197. TempKeyHandle,
  198. DeviceInstancePath,
  199. KEY_READ
  200. );
  201. ZwClose(TempKeyHandle);
  202. if(!NT_SUCCESS(Status)) {
  203. goto PrepareForReturn1;
  204. }
  205. //
  206. // Read Service= value entry of the specified device instance key.
  207. //
  208. Status = IopGetRegistryValue(DeviceInstanceHandle,
  209. REGSTR_VALUE_SERVICE,
  210. &KeyValueInformation
  211. );
  212. ZwClose(DeviceInstanceHandle);
  213. if (NT_SUCCESS(Status)) {
  214. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  215. if (KeyValueInformation->Type == REG_SZ) {
  216. if (KeyValueInformation->DataLength > sizeof(UNICODE_NULL)) {
  217. IopRegistryDataToUnicodeString(&ServiceName,
  218. (PWSTR)KEY_VALUE_DATA(KeyValueInformation),
  219. KeyValueInformation->DataLength
  220. );
  221. Status = STATUS_SUCCESS;
  222. if (ServiceKeyName) {
  223. //
  224. // If need to return ServiceKeyName, make a copy now.
  225. //
  226. if (!PipConcatenateUnicodeStrings( ServiceKeyName,
  227. &ServiceName,
  228. NULL)) {
  229. Status = STATUS_INSUFFICIENT_RESOURCES;
  230. }
  231. }
  232. }
  233. }
  234. ExFreePool(KeyValueInformation);
  235. } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
  236. //
  237. // The device instance key may have no Service value entry if the device
  238. // is raw capable.
  239. //
  240. Status = STATUS_SUCCESS;
  241. goto PrepareForReturn1;
  242. }
  243. if (NT_SUCCESS(Status)) {
  244. PROCESS_DRIVER_CONTEXT context;
  245. context.Add = Add;
  246. Status = PpForEachDeviceInstanceDriver(
  247. DeviceInstancePath,
  248. (PDEVICE_SERVICE_ITERATOR_ROUTINE) PiProcessDriverInstance,
  249. &context);
  250. if(!NT_SUCCESS(Status) && Add) {
  251. context.Add = FALSE;
  252. PpForEachDeviceInstanceDriver(DeviceInstancePath,
  253. PiProcessDriverInstance,
  254. &context);
  255. }
  256. }
  257. PrepareForReturn1:
  258. if (!NT_SUCCESS(Status)) {
  259. if (ServiceKeyName) {
  260. if (ServiceKeyName->Length != 0) {
  261. ExFreePool(ServiceKeyName->Buffer);
  262. ServiceKeyName->Buffer = NULL;
  263. ServiceKeyName->Length = ServiceKeyName->MaximumLength = 0;
  264. }
  265. }
  266. }
  267. return Status;
  268. }
  269. NTSTATUS
  270. PiProcessDriverInstance(
  271. IN PUNICODE_STRING DeviceInstancePath,
  272. IN PUNICODE_STRING ServiceName,
  273. IN ULONG ServiceType,
  274. IN PPROCESS_DRIVER_CONTEXT Context
  275. )
  276. {
  277. NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
  278. PKEY_VALUE_FULL_INFORMATION KeyValueInformation = NULL;
  279. HANDLE ServiceEnumHandle;
  280. UNICODE_STRING MatchingDeviceInstance;
  281. UNICODE_STRING TempUnicodeString;
  282. CHAR UnicodeBuffer[20];
  283. BOOLEAN UpdateCount = FALSE;
  284. ULONG i, j, Count, junk, maxCount;
  285. UNREFERENCED_PARAMETER( ServiceType );
  286. PAGED_CODE();
  287. ASSERT(Context != NULL);
  288. //
  289. // Next, open the service entry, and volatile Enum subkey
  290. // under HKLM\System\CurrentControlSet\Services (creating it if it
  291. // doesn't exist)
  292. //
  293. Status = PipOpenServiceEnumKeys(ServiceName,
  294. KEY_ALL_ACCESS,
  295. NULL,
  296. &ServiceEnumHandle,
  297. TRUE
  298. );
  299. if(!NT_SUCCESS(Status)) {
  300. goto PrepareForReturn2;
  301. }
  302. //
  303. // Now, search through the service's existing list of device instances, to see
  304. // if this instance has previously been registered.
  305. //
  306. Status = PiFindDevInstMatch(ServiceEnumHandle,
  307. DeviceInstancePath,
  308. &Count,
  309. &MatchingDeviceInstance);
  310. if (!NT_SUCCESS(Status)) {
  311. goto PrepareForReturn3;
  312. }
  313. if (!MatchingDeviceInstance.Buffer) {
  314. //
  315. // If we didn't find a match and caller wants to register the device, then we add
  316. // this instance to the service's Enum list.
  317. //
  318. if (Context->Add) {
  319. PWSTR instancePathBuffer;
  320. ULONG instancePathLength;
  321. BOOLEAN freeBuffer = FALSE;
  322. //
  323. // Create the value entry and update NextInstance= for the madeup key
  324. //
  325. if (DeviceInstancePath->Buffer[DeviceInstancePath->Length / sizeof(WCHAR) - 1] !=
  326. UNICODE_NULL) {
  327. instancePathLength = DeviceInstancePath->Length + sizeof(WCHAR);
  328. instancePathBuffer = (PWSTR)ExAllocatePool(PagedPool, instancePathLength);
  329. if (instancePathBuffer) {
  330. RtlCopyMemory(instancePathBuffer,
  331. DeviceInstancePath->Buffer,
  332. DeviceInstancePath->Length
  333. );
  334. instancePathBuffer[DeviceInstancePath->Length / sizeof(WCHAR)] = UNICODE_NULL;
  335. freeBuffer = TRUE;
  336. }
  337. }
  338. if (!freeBuffer) {
  339. instancePathBuffer = DeviceInstancePath->Buffer;
  340. instancePathLength = DeviceInstancePath->Length;
  341. }
  342. PiUlongToUnicodeString(&TempUnicodeString, UnicodeBuffer, 20, Count);
  343. Status = ZwSetValueKey(
  344. ServiceEnumHandle,
  345. &TempUnicodeString,
  346. TITLE_INDEX_VALUE,
  347. REG_SZ,
  348. instancePathBuffer,
  349. instancePathLength
  350. );
  351. if (freeBuffer) {
  352. ExFreePool(instancePathBuffer);
  353. }
  354. Count++;
  355. UpdateCount = TRUE;
  356. }
  357. } else {
  358. //
  359. // If we did find a match and caller wants to deregister the device, then we remove
  360. // this instance from the service's Enum list.
  361. //
  362. if (Context->Add == FALSE) {
  363. ZwDeleteValueKey(ServiceEnumHandle, &MatchingDeviceInstance);
  364. Count--;
  365. UpdateCount = TRUE;
  366. //
  367. // Finally, if Count is not zero we need to physically reorganize the
  368. // instances under the ServiceKey\Enum key to make them contiguous.
  369. //
  370. if (Count != 0) {
  371. KEY_FULL_INFORMATION keyInfo;
  372. ULONG tmp;
  373. Status = ZwQueryKey(ServiceEnumHandle, KeyFullInformation, &keyInfo, sizeof(keyInfo), &tmp);
  374. if (NT_SUCCESS(Status) && keyInfo.Values) {
  375. maxCount = keyInfo.Values;
  376. } else {
  377. maxCount = 0x200;
  378. }
  379. i = j = 0;
  380. while (j < Count && i < maxCount) {
  381. PiUlongToUnicodeString(&TempUnicodeString, UnicodeBuffer, 20, i);
  382. Status = ZwQueryValueKey( ServiceEnumHandle,
  383. &TempUnicodeString,
  384. KeyValueFullInformation,
  385. (PVOID) NULL,
  386. 0,
  387. &junk);
  388. if ((Status != STATUS_OBJECT_NAME_NOT_FOUND) && (Status != STATUS_OBJECT_PATH_NOT_FOUND)) {
  389. if (i != j) {
  390. //
  391. // Need to change the instance i to instance j
  392. //
  393. Status = IopGetRegistryValue(ServiceEnumHandle,
  394. TempUnicodeString.Buffer,
  395. &KeyValueInformation
  396. );
  397. if (NT_SUCCESS(Status)) {
  398. ZwDeleteValueKey(ServiceEnumHandle, &TempUnicodeString);
  399. PiUlongToUnicodeString(&TempUnicodeString, UnicodeBuffer, 20, j);
  400. ZwSetValueKey (ServiceEnumHandle,
  401. &TempUnicodeString,
  402. TITLE_INDEX_VALUE,
  403. REG_SZ,
  404. (PVOID)KEY_VALUE_DATA(KeyValueInformation),
  405. KeyValueInformation->DataLength
  406. );
  407. ExFreePool(KeyValueInformation);
  408. KeyValueInformation = NULL;
  409. } else {
  410. IopDbgPrint((IOP_WARNING_LEVEL,
  411. "PpDeviceRegistration: Fail to rearrange device instances %x\n",
  412. Status));
  413. break;
  414. }
  415. }
  416. j++;
  417. }
  418. i++;
  419. }
  420. }
  421. }
  422. }
  423. if (UpdateCount) {
  424. PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_COUNT);
  425. ZwSetValueKey(
  426. ServiceEnumHandle,
  427. &TempUnicodeString,
  428. TITLE_INDEX_VALUE,
  429. REG_DWORD,
  430. &Count,
  431. sizeof(Count)
  432. );
  433. PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_NEXT_INSTANCE);
  434. ZwSetValueKey(
  435. ServiceEnumHandle,
  436. &TempUnicodeString,
  437. TITLE_INDEX_VALUE,
  438. REG_DWORD,
  439. &Count,
  440. sizeof(Count)
  441. );
  442. }
  443. //
  444. // Need to release the matching device value name
  445. //
  446. if (MatchingDeviceInstance.Buffer) {
  447. RtlFreeUnicodeString(&MatchingDeviceInstance);
  448. }
  449. Status = STATUS_SUCCESS;
  450. PrepareForReturn3:
  451. ZwClose(ServiceEnumHandle);
  452. PrepareForReturn2:
  453. if (KeyValueInformation) {
  454. ExFreePool(KeyValueInformation);
  455. }
  456. return Status;
  457. }
  458. NTSTATUS
  459. PiFindDevInstMatch(
  460. IN HANDLE ServiceEnumHandle,
  461. IN PUNICODE_STRING DeviceInstanceName,
  462. OUT PULONG Count,
  463. OUT PUNICODE_STRING MatchingValueName
  464. )
  465. /*++
  466. Routine Description:
  467. This routine searches through the specified Service\Enum values entries
  468. for a device instance matching the one specified by KeyInformation.
  469. If a matching is found, the MatchingValueName is returned and caller must
  470. free the unicode string when done with it.
  471. Arguments:
  472. ServiceEnumHandle - Supplies a handle to service enum key.
  473. DeviceInstanceName - Supplies a pointer to a unicode string specifying the
  474. name of the device instance key to search for.
  475. InstanceCount - Supplies a pointer to a ULONG variable to receive the device
  476. instance count under the service enum key.
  477. MatchingNameFound - Supplies a pointer to a UNICODE_STRING to receive the value
  478. name of the matched device instance.
  479. Return Value:
  480. A NTSTATUS code. if a matching is found, the MatchingValueName is the unicode
  481. string of the value name. Otherwise its length and Buffer will be set to empty.
  482. --*/
  483. {
  484. NTSTATUS status;
  485. ULONG i, instanceCount, length = 256, junk;
  486. UNICODE_STRING valueName, unicodeValue;
  487. PWCHAR unicodeBuffer;
  488. PKEY_VALUE_FULL_INFORMATION keyValueInformation = NULL;
  489. PAGED_CODE();
  490. //
  491. // Find out how many instances are referenced in the service's Enum key.
  492. //
  493. MatchingValueName->Length = 0;
  494. MatchingValueName->Buffer = NULL;
  495. *Count = instanceCount = 0;
  496. status = IopGetRegistryValue(ServiceEnumHandle,
  497. REGSTR_VALUE_COUNT,
  498. &keyValueInformation
  499. );
  500. if (NT_SUCCESS(status)) {
  501. if((keyValueInformation->Type == REG_DWORD) &&
  502. (keyValueInformation->DataLength >= sizeof(ULONG))) {
  503. instanceCount = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
  504. *Count = instanceCount;
  505. }
  506. ExFreePool(keyValueInformation);
  507. } else if(status != STATUS_OBJECT_NAME_NOT_FOUND) {
  508. return status;
  509. } else {
  510. //
  511. // If 'Count' value entry not found, consider this to mean there are simply
  512. // no device instance controlled by this service. Thus we don't have a match.
  513. //
  514. return STATUS_SUCCESS;
  515. }
  516. keyValueInformation = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool(
  517. PagedPool, length);
  518. if (!keyValueInformation) {
  519. return STATUS_INSUFFICIENT_RESOURCES;
  520. }
  521. //
  522. // Allocate heap to store value name
  523. //
  524. unicodeBuffer = (PWSTR)ExAllocatePool(PagedPool, 10 * sizeof(WCHAR));
  525. if (!unicodeBuffer) {
  526. ExFreePool(keyValueInformation);
  527. return STATUS_INSUFFICIENT_RESOURCES;
  528. }
  529. //
  530. // Next scan thru each value key to find a match
  531. //
  532. for (i = 0; i < instanceCount ; i++) {
  533. PiUlongToUnicodeString(&valueName, unicodeBuffer, 20, i);
  534. status = ZwQueryValueKey (
  535. ServiceEnumHandle,
  536. &valueName,
  537. KeyValueFullInformation,
  538. keyValueInformation,
  539. length,
  540. &junk
  541. );
  542. if (!NT_SUCCESS(status)) {
  543. if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL) {
  544. ExFreePool(keyValueInformation);
  545. length = junk;
  546. keyValueInformation = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool(
  547. PagedPool, length);
  548. if (!keyValueInformation) {
  549. ExFreePool(unicodeBuffer);
  550. return STATUS_INSUFFICIENT_RESOURCES;
  551. }
  552. i--;
  553. }
  554. continue;
  555. }
  556. if (keyValueInformation->Type == REG_SZ) {
  557. if (keyValueInformation->DataLength > sizeof(UNICODE_NULL)) {
  558. IopRegistryDataToUnicodeString(&unicodeValue,
  559. (PWSTR)KEY_VALUE_DATA(keyValueInformation),
  560. keyValueInformation->DataLength
  561. );
  562. } else {
  563. continue;
  564. }
  565. } else {
  566. continue;
  567. }
  568. if (RtlEqualUnicodeString(&unicodeValue,
  569. DeviceInstanceName,
  570. TRUE)) {
  571. //
  572. // We found a match.
  573. //
  574. *MatchingValueName= valueName;
  575. break;
  576. }
  577. }
  578. if (keyValueInformation) {
  579. ExFreePool(keyValueInformation);
  580. }
  581. if (MatchingValueName->Length == 0) {
  582. //
  583. // If we did not find a match, we need to release the buffer. Otherwise
  584. // it is caller's responsibility to release the buffer.
  585. //
  586. ExFreePool(unicodeBuffer);
  587. }
  588. return STATUS_SUCCESS;
  589. }
  590. NTSTATUS
  591. PpForEachDeviceInstanceDriver(
  592. PUNICODE_STRING DeviceInstancePath,
  593. PDEVICE_SERVICE_ITERATOR_ROUTINE IteratorRoutine,
  594. PVOID Context
  595. )
  596. /*++
  597. Routine Description:
  598. This routine will call the iterator routine once for each driver listed
  599. for this particular device instance. It will walk through any class
  600. filter drivers and device filter drivers, as well as the service, in the
  601. order they will be added to the PDO. If the iterator routine returns
  602. a failure status at any point the iteration will be terminated.
  603. Arguments:
  604. DeviceInstancePath - the registry path (relative to CCS\Enum)
  605. IteratorRoutine - the routine to be called for each service. This routine
  606. will be passed:
  607. * The device instance path
  608. * The type of driver that this is (filter, service, etc.)
  609. * the Context value passed in
  610. * The name of the service
  611. Context - an arbitrary context passed into the iterator routine
  612. Return Value:
  613. STATUS_SUCCCESS if everything was run across properly
  614. status if an error occurred opening critical keys or if the iterator
  615. routine returns an error.
  616. --*/
  617. {
  618. HANDLE enumKey,instanceKey, classKey, controlKey;
  619. PKEY_VALUE_FULL_INFORMATION keyValueInformation;
  620. DEVICE_SERVICE_ITERATOR_CONTEXT internalContext;
  621. RTL_QUERY_REGISTRY_TABLE queryTable[4];
  622. NTSTATUS status;
  623. UNICODE_STRING unicodeClassGuid;
  624. PAGED_CODE();
  625. //
  626. // Open the HKLM\System\CCS\Enum key.
  627. //
  628. status = IopOpenRegistryKeyEx( &enumKey,
  629. NULL,
  630. &CmRegistryMachineSystemCurrentControlSetEnumName,
  631. KEY_READ
  632. );
  633. if (!NT_SUCCESS(status)) {
  634. return status;
  635. }
  636. //
  637. // Open the instance key for this devnode
  638. //
  639. status = IopOpenRegistryKeyEx( &instanceKey,
  640. enumKey,
  641. DeviceInstancePath,
  642. KEY_READ
  643. );
  644. ZwClose(enumKey);
  645. if(!NT_SUCCESS(status)) {
  646. return status;
  647. }
  648. classKey = NULL;
  649. status = IopGetRegistryValue(instanceKey,
  650. REGSTR_VALUE_CLASSGUID,
  651. &keyValueInformation);
  652. if(NT_SUCCESS(status)) {
  653. if ( keyValueInformation->Type == REG_SZ &&
  654. keyValueInformation->DataLength) {
  655. IopRegistryDataToUnicodeString(
  656. &unicodeClassGuid,
  657. (PWSTR) KEY_VALUE_DATA(keyValueInformation),
  658. keyValueInformation->DataLength);
  659. //
  660. // Open the class key
  661. //
  662. status = IopOpenRegistryKeyEx( &controlKey,
  663. NULL,
  664. &CmRegistryMachineSystemCurrentControlSetControlClass,
  665. KEY_READ
  666. );
  667. if(NT_SUCCESS(status)) {
  668. status = IopOpenRegistryKeyEx( &classKey,
  669. controlKey,
  670. &unicodeClassGuid,
  671. KEY_READ
  672. );
  673. ZwClose(controlKey);
  674. }
  675. }
  676. ExFreePool(keyValueInformation);
  677. keyValueInformation = NULL;
  678. }
  679. //
  680. // For each type of filter driver we want to query for the list and
  681. // call into our callback routine. We should do this in order from
  682. // bottom to top.
  683. //
  684. internalContext.Context = Context;
  685. internalContext.DeviceInstancePath = DeviceInstancePath;
  686. internalContext.Iterator = IteratorRoutine;
  687. //
  688. // First get all the information we have to out of the instance key and
  689. // the device node.
  690. //
  691. if(classKey != NULL) {
  692. RtlZeroMemory(queryTable, sizeof(queryTable));
  693. queryTable[0].QueryRoutine =
  694. (PRTL_QUERY_REGISTRY_ROUTINE) PiForEachDriverQueryRoutine;
  695. queryTable[0].Name = REGSTR_VAL_LOWERFILTERS;
  696. queryTable[0].EntryContext = (PVOID) 0;
  697. status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
  698. (PWSTR) classKey,
  699. queryTable,
  700. &internalContext,
  701. NULL);
  702. if(!NT_SUCCESS(status)) {
  703. goto PrepareForReturn;
  704. }
  705. }
  706. RtlZeroMemory(queryTable, sizeof(queryTable));
  707. queryTable[0].QueryRoutine =
  708. (PRTL_QUERY_REGISTRY_ROUTINE) PiForEachDriverQueryRoutine;
  709. queryTable[0].Name = REGSTR_VAL_LOWERFILTERS;
  710. queryTable[0].EntryContext = (PVOID) 1;
  711. queryTable[1].QueryRoutine =
  712. (PRTL_QUERY_REGISTRY_ROUTINE) PiForEachDriverQueryRoutine;
  713. queryTable[1].Name = REGSTR_VAL_SERVICE;
  714. queryTable[1].EntryContext = (PVOID) 2;
  715. queryTable[2].QueryRoutine =
  716. (PRTL_QUERY_REGISTRY_ROUTINE) PiForEachDriverQueryRoutine;
  717. queryTable[2].Name = REGSTR_VAL_UPPERFILTERS;
  718. queryTable[2].EntryContext = (PVOID) 3;
  719. status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
  720. (PWSTR) instanceKey,
  721. queryTable,
  722. &internalContext,
  723. NULL);
  724. if(!NT_SUCCESS(status)) {
  725. goto PrepareForReturn;
  726. }
  727. if(classKey != NULL) {
  728. RtlZeroMemory(queryTable, sizeof(queryTable));
  729. queryTable[0].QueryRoutine =
  730. (PRTL_QUERY_REGISTRY_ROUTINE) PiForEachDriverQueryRoutine;
  731. queryTable[0].Name = REGSTR_VAL_UPPERFILTERS;
  732. queryTable[0].EntryContext = (PVOID) 4;
  733. status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
  734. (PWSTR) classKey,
  735. queryTable,
  736. &internalContext,
  737. NULL);
  738. if(!NT_SUCCESS(status)) {
  739. goto PrepareForReturn;
  740. }
  741. }
  742. PrepareForReturn:
  743. if(classKey != NULL) {
  744. ZwClose(classKey);
  745. }
  746. ZwClose(instanceKey);
  747. return status;
  748. }
  749. NTSTATUS
  750. PiForEachDriverQueryRoutine(
  751. IN PWSTR ValueName,
  752. IN ULONG ValueType,
  753. IN PVOID ValueData,
  754. IN ULONG ValueLength,
  755. IN PDEVICE_SERVICE_ITERATOR_CONTEXT InternalContext,
  756. IN ULONG ServiceType
  757. )
  758. {
  759. UNICODE_STRING ServiceName;
  760. UNREFERENCED_PARAMETER( ValueName );
  761. if (ValueType != REG_SZ) {
  762. return STATUS_SUCCESS;
  763. }
  764. //
  765. // Make sure the string is a reasonable length.
  766. // copied directly from IopCallDriverAddDeviceQueryRoutine
  767. //
  768. if (ValueLength <= sizeof(WCHAR)) {
  769. return STATUS_SUCCESS;
  770. }
  771. RtlInitUnicodeString(&ServiceName, ValueData);
  772. return InternalContext->Iterator(
  773. InternalContext->DeviceInstancePath,
  774. &ServiceName,
  775. ServiceType,
  776. InternalContext->Context);
  777. }