Sync to trunk HEAD (r43416)
[reactos.git] / reactos / ntoskrnl / io / iomgr / deviface.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/iomgr/deviface.c
5 * PURPOSE: Device interface functions
6 *
7 * PROGRAMMERS: Filip Navara (xnavara@volny.cz)
8 * Matthew Brace (ismarc@austin.rr.com)
9 * Hervé Poussineau (hpoussin@reactos.org)
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ntoskrnl.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* FUNCTIONS *****************************************************************/
20
21 static PWCHAR BaseKeyString = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DeviceClasses\\";
22
23 /*++
24 * @name IoOpenDeviceInterfaceRegistryKey
25 * @unimplemented
26 *
27 * Provides a handle to the device's interface instance registry key.
28 * Documented in WDK.
29 *
30 * @param SymbolicLinkName
31 * Pointer to a string which identifies the device interface instance
32 *
33 * @param DesiredAccess
34 * Desired ACCESS_MASK used to access the key (like KEY_READ,
35 * KEY_WRITE, etc)
36 *
37 * @param DeviceInterfaceKey
38 * If a call has been succesfull, a handle to the registry key
39 * will be stored there
40 *
41 * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
42 * otherwise (see WDK for details)
43 *
44 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
45 *
46 *--*/
47 NTSTATUS
48 NTAPI
49 IoOpenDeviceInterfaceRegistryKey(IN PUNICODE_STRING SymbolicLinkName,
50 IN ACCESS_MASK DesiredAccess,
51 OUT PHANDLE DeviceInterfaceKey)
52 {
53 return STATUS_NOT_IMPLEMENTED;
54 }
55
56 /*++
57 * @name IoGetDeviceInterfaceAlias
58 * @unimplemented
59 *
60 * Returns the alias device interface of the specified device interface
61 * instance, if the alias exists.
62 * Documented in WDK.
63 *
64 * @param SymbolicLinkName
65 * Pointer to a string which identifies the device interface instance
66 *
67 * @param AliasInterfaceClassGuid
68 * See WDK
69 *
70 * @param AliasSymbolicLinkName
71 * See WDK
72 *
73 * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
74 * otherwise (see WDK for details)
75 *
76 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
77 *
78 *--*/
79 NTSTATUS
80 NTAPI
81 IoGetDeviceInterfaceAlias(IN PUNICODE_STRING SymbolicLinkName,
82 IN CONST GUID *AliasInterfaceClassGuid,
83 OUT PUNICODE_STRING AliasSymbolicLinkName)
84 {
85 return STATUS_NOT_IMPLEMENTED;
86 }
87
88 /*++
89 * @name IopOpenInterfaceKey
90 *
91 * Returns the alias device interface of the specified device interface
92 *
93 * @param InterfaceClassGuid
94 * FILLME
95 *
96 * @param DesiredAccess
97 * FILLME
98 *
99 * @param pInterfaceKey
100 * FILLME
101 *
102 * @return Usual NTSTATUS
103 *
104 * @remarks None
105 *
106 *--*/
107 static NTSTATUS
108 IopOpenInterfaceKey(IN CONST GUID *InterfaceClassGuid,
109 IN ACCESS_MASK DesiredAccess,
110 OUT HANDLE *pInterfaceKey)
111 {
112 UNICODE_STRING LocalMachine = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\");
113 UNICODE_STRING GuidString;
114 UNICODE_STRING KeyName;
115 OBJECT_ATTRIBUTES ObjectAttributes;
116 HANDLE InterfaceKey = INVALID_HANDLE_VALUE;
117 NTSTATUS Status;
118
119 GuidString.Buffer = KeyName.Buffer = NULL;
120
121 Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
122 if (!NT_SUCCESS(Status))
123 {
124 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
125 goto cleanup;
126 }
127
128 KeyName.Length = 0;
129 KeyName.MaximumLength = LocalMachine.Length + (wcslen(REGSTR_PATH_DEVICE_CLASSES) + 1) * sizeof(WCHAR) + GuidString.Length;
130 KeyName.Buffer = ExAllocatePool(PagedPool, KeyName.MaximumLength);
131 if (!KeyName.Buffer)
132 {
133 DPRINT("ExAllocatePool() failed\n");
134 Status = STATUS_INSUFFICIENT_RESOURCES;
135 goto cleanup;
136 }
137
138 Status = RtlAppendUnicodeStringToString(&KeyName, &LocalMachine);
139 if (!NT_SUCCESS(Status))
140 {
141 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
142 goto cleanup;
143 }
144 Status = RtlAppendUnicodeToString(&KeyName, REGSTR_PATH_DEVICE_CLASSES);
145 if (!NT_SUCCESS(Status))
146 {
147 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
148 goto cleanup;
149 }
150 Status = RtlAppendUnicodeToString(&KeyName, L"\\");
151 if (!NT_SUCCESS(Status))
152 {
153 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
154 goto cleanup;
155 }
156 Status = RtlAppendUnicodeStringToString(&KeyName, &GuidString);
157 if (!NT_SUCCESS(Status))
158 {
159 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
160 goto cleanup;
161 }
162
163 InitializeObjectAttributes(
164 &ObjectAttributes,
165 &KeyName,
166 OBJ_CASE_INSENSITIVE,
167 NULL,
168 NULL);
169 Status = ZwOpenKey(
170 &InterfaceKey,
171 DesiredAccess,
172 &ObjectAttributes);
173 if (!NT_SUCCESS(Status))
174 {
175 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
176 goto cleanup;
177 }
178
179 *pInterfaceKey = InterfaceKey;
180 Status = STATUS_SUCCESS;
181
182 cleanup:
183 if (!NT_SUCCESS(Status))
184 {
185 if (InterfaceKey != INVALID_HANDLE_VALUE)
186 ZwClose(InterfaceKey);
187 }
188 RtlFreeUnicodeString(&GuidString);
189 RtlFreeUnicodeString(&KeyName);
190 return Status;
191 }
192
193 /*++
194 * @name IoGetDeviceInterfaces
195 * @implemented
196 *
197 * Returns a list of device interfaces of a particular device interface class.
198 * Documented in WDK
199 *
200 * @param InterfaceClassGuid
201 * Points to a class GUID specifying the device interface class
202 *
203 * @param PhysicalDeviceObject
204 * Points to an optional PDO that narrows the search to only the
205 * device interfaces of the device represented by the PDO
206 *
207 * @param Flags
208 * Specifies flags that modify the search for device interfaces. The
209 * DEVICE_INTERFACE_INCLUDE_NONACTIVE flag specifies that the list of
210 * returned symbolic links should contain also disabled device
211 * interfaces in addition to the enabled ones.
212 *
213 * @param SymbolicLinkList
214 * Points to a character pointer that is filled in on successful return
215 * with a list of unicode strings identifying the device interfaces
216 * that match the search criteria. The newly allocated buffer contains
217 * a list of symbolic link names. Each unicode string in the list is
218 * null-terminated; the end of the whole list is marked by an additional
219 * NULL. The caller is responsible for freeing the buffer (ExFreePool)
220 * when it is no longer needed.
221 * If no device interfaces match the search criteria, this routine
222 * returns STATUS_SUCCESS and the string contains a single NULL
223 * character.
224 *
225 * @return Usual NTSTATUS
226 *
227 * @remarks None
228 *
229 *--*/
230 NTSTATUS
231 NTAPI
232 IoGetDeviceInterfaces(IN CONST GUID *InterfaceClassGuid,
233 IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL,
234 IN ULONG Flags,
235 OUT PWSTR *SymbolicLinkList)
236 {
237 UNICODE_STRING Control = RTL_CONSTANT_STRING(L"Control");
238 UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
239 HANDLE InterfaceKey = INVALID_HANDLE_VALUE;
240 HANDLE DeviceKey = INVALID_HANDLE_VALUE;
241 HANDLE ReferenceKey = INVALID_HANDLE_VALUE;
242 HANDLE ControlKey = INVALID_HANDLE_VALUE;
243 PKEY_BASIC_INFORMATION DeviceBi = NULL;
244 PKEY_BASIC_INFORMATION ReferenceBi = NULL;
245 PKEY_VALUE_PARTIAL_INFORMATION bip = NULL;
246 UNICODE_STRING KeyName;
247 OBJECT_ATTRIBUTES ObjectAttributes;
248 BOOLEAN FoundRightPDO = FALSE;
249 ULONG i = 0, j, Size;
250 UNICODE_STRING ReturnBuffer = { 0, 0, NULL };
251 NTSTATUS Status;
252
253 PAGED_CODE();
254
255 Status = IopOpenInterfaceKey(InterfaceClassGuid, KEY_ENUMERATE_SUB_KEYS, &InterfaceKey);
256 if (!NT_SUCCESS(Status))
257 {
258 DPRINT("IopOpenInterfaceKey() failed with status 0x%08lx\n", Status);
259 goto cleanup;
260 }
261
262 /* Enumerate subkeys (ie the different device objets) */
263 while (TRUE)
264 {
265 Status = ZwEnumerateKey(
266 InterfaceKey,
267 i,
268 KeyBasicInformation,
269 NULL,
270 0,
271 &Size);
272 if (Status == STATUS_NO_MORE_ENTRIES)
273 {
274 break;
275 }
276 else if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
277 {
278 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
279 goto cleanup;
280 }
281
282 DeviceBi = ExAllocatePool(PagedPool, Size);
283 if (!DeviceBi)
284 {
285 DPRINT("ExAllocatePool() failed\n");
286 Status = STATUS_INSUFFICIENT_RESOURCES;
287 goto cleanup;
288 }
289 Status = ZwEnumerateKey(
290 InterfaceKey,
291 i++,
292 KeyBasicInformation,
293 DeviceBi,
294 Size,
295 &Size);
296 if (!NT_SUCCESS(Status))
297 {
298 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
299 goto cleanup;
300 }
301
302 /* Open device key */
303 KeyName.Length = KeyName.MaximumLength = (USHORT)DeviceBi->NameLength;
304 KeyName.Buffer = DeviceBi->Name;
305 InitializeObjectAttributes(
306 &ObjectAttributes,
307 &KeyName,
308 OBJ_CASE_INSENSITIVE,
309 InterfaceKey,
310 NULL);
311 Status = ZwOpenKey(
312 &DeviceKey,
313 KEY_ENUMERATE_SUB_KEYS,
314 &ObjectAttributes);
315 if (!NT_SUCCESS(Status))
316 {
317 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
318 goto cleanup;
319 }
320
321 if (PhysicalDeviceObject)
322 {
323 /* Check if we are on the right physical device object,
324 * by reading the DeviceInstance string
325 */
326 DPRINT1("PhysicalDeviceObject != NULL. Case not implemented.\n");
327 //FoundRightPDO = TRUE;
328 Status = STATUS_NOT_IMPLEMENTED;
329 goto cleanup;
330 }
331
332 /* Enumerate subkeys (ie the different reference strings) */
333 j = 0;
334 while (TRUE)
335 {
336 Status = ZwEnumerateKey(
337 DeviceKey,
338 j,
339 KeyBasicInformation,
340 NULL,
341 0,
342 &Size);
343 if (Status == STATUS_NO_MORE_ENTRIES)
344 {
345 break;
346 }
347 else if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
348 {
349 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
350 goto cleanup;
351 }
352
353 ReferenceBi = ExAllocatePool(PagedPool, Size);
354 if (!ReferenceBi)
355 {
356 DPRINT("ExAllocatePool() failed\n");
357 Status = STATUS_INSUFFICIENT_RESOURCES;
358 goto cleanup;
359 }
360 Status = ZwEnumerateKey(
361 DeviceKey,
362 j++,
363 KeyBasicInformation,
364 ReferenceBi,
365 Size,
366 &Size);
367 if (!NT_SUCCESS(Status))
368 {
369 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
370 goto cleanup;
371 }
372
373 KeyName.Length = KeyName.MaximumLength = (USHORT)ReferenceBi->NameLength;
374 KeyName.Buffer = ReferenceBi->Name;
375 if (RtlEqualUnicodeString(&KeyName, &Control, TRUE))
376 {
377 /* Skip Control subkey */
378 goto NextReferenceString;
379 }
380
381 /* Open reference key */
382 InitializeObjectAttributes(
383 &ObjectAttributes,
384 &KeyName,
385 OBJ_CASE_INSENSITIVE,
386 DeviceKey,
387 NULL);
388 Status = ZwOpenKey(
389 &ReferenceKey,
390 KEY_QUERY_VALUE,
391 &ObjectAttributes);
392 if (!NT_SUCCESS(Status))
393 {
394 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
395 goto cleanup;
396 }
397
398 if (!(Flags & DEVICE_INTERFACE_INCLUDE_NONACTIVE))
399 {
400 /* We have to check if the interface is enabled, by
401 * reading the Linked value in the Control subkey
402 */
403 #if 0
404 InitializeObjectAttributes(
405 &ObjectAttributes,
406 &Control,
407 OBJ_CASE_INSENSITIVE,
408 ReferenceKey,
409 NULL);
410 Status = ZwOpenKey(
411 &ControlKey,
412 KEY_QUERY_VALUE,
413 &ObjectAttributes);
414 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
415 {
416 /* That's OK. The key doesn't exist (yet) because
417 * the interface is not activated.
418 */
419 goto NextReferenceString;
420 }
421 else if (!NT_SUCCESS(Status))
422 {
423 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
424 goto cleanup;
425 }
426 #endif
427 /* FIXME: Read the Linked value
428 * If it doesn't exist => ERROR
429 * If it is not a REG_DWORD or Size != sizeof(ULONG) => ERROR
430 * If its value is 0, go to NextReferenceString
431 * At the moment, do as if it is active...
432 */
433 DPRINT1("Checking if device is enabled is not implemented yet!\n");
434 }
435
436 /* Read the SymbolicLink string and add it into SymbolicLinkList */
437 Status = ZwQueryValueKey(
438 ReferenceKey,
439 &SymbolicLink,
440 KeyValuePartialInformation,
441 NULL,
442 0,
443 &Size);
444 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
445 {
446 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
447 goto cleanup;
448 }
449 bip = ExAllocatePool(PagedPool, Size);
450 if (!bip)
451 {
452 DPRINT("ExAllocatePool() failed\n");
453 Status = STATUS_INSUFFICIENT_RESOURCES;
454 goto cleanup;
455 }
456 Status = ZwQueryValueKey(
457 ReferenceKey,
458 &SymbolicLink,
459 KeyValuePartialInformation,
460 bip,
461 Size,
462 &Size);
463 if (!NT_SUCCESS(Status))
464 {
465 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
466 goto cleanup;
467 }
468 else if (bip->Type != REG_SZ)
469 {
470 DPRINT("Unexpected registry type 0x%lx (expected 0x%lx)\n", bip->Type, REG_SZ);
471 Status = STATUS_UNSUCCESSFUL;
472 goto cleanup;
473 }
474 else if (bip->DataLength < 5 * sizeof(WCHAR))
475 {
476 DPRINT("Registry string too short (length %lu, expected %lu at least)\n", bip->DataLength < 5 * sizeof(WCHAR));
477 Status = STATUS_UNSUCCESSFUL;
478 goto cleanup;
479 }
480 KeyName.Length = KeyName.MaximumLength = (USHORT)bip->DataLength - 4 * sizeof(WCHAR);
481 KeyName.Buffer = &((PWSTR)bip->Data)[4];
482 if (KeyName.Length && KeyName.Buffer[KeyName.Length / sizeof(WCHAR)] == UNICODE_NULL)
483 {
484 /* Remove trailing NULL */
485 KeyName.Length -= sizeof(WCHAR);
486 }
487
488 /* Add new symbolic link to symbolic link list */
489 if (ReturnBuffer.Length + KeyName.Length + sizeof(WCHAR) > ReturnBuffer.MaximumLength)
490 {
491 PWSTR NewBuffer;
492 ReturnBuffer.MaximumLength = max(ReturnBuffer.MaximumLength * 2, ReturnBuffer.Length + KeyName.Length + 2 * sizeof(WCHAR));
493 NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
494 if (!NewBuffer)
495 {
496 DPRINT("ExAllocatePool() failed\n");
497 Status = STATUS_INSUFFICIENT_RESOURCES;
498 goto cleanup;
499 }
500 RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
501 if (ReturnBuffer.Buffer)
502 ExFreePool(ReturnBuffer.Buffer);
503 ReturnBuffer.Buffer = NewBuffer;
504 }
505 DPRINT("Adding symbolic link %wZ\n", &KeyName);
506 Status = RtlAppendUnicodeStringToString(&ReturnBuffer, &KeyName);
507 if (!NT_SUCCESS(Status))
508 {
509 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
510 goto cleanup;
511 }
512 /* RtlAppendUnicodeStringToString added a NULL at the end of the
513 * destination string, but didn't increase the Length field.
514 * Do it for it.
515 */
516 ReturnBuffer.Length += sizeof(WCHAR);
517
518 NextReferenceString:
519 ExFreePool(ReferenceBi);
520 ReferenceBi = NULL;
521 ExFreePool(bip);
522 bip = NULL;
523 if (ReferenceKey != INVALID_HANDLE_VALUE)
524 {
525 ZwClose(ReferenceKey);
526 ReferenceKey = INVALID_HANDLE_VALUE;
527 }
528 if (ControlKey != INVALID_HANDLE_VALUE)
529 {
530 ZwClose(ControlKey);
531 ControlKey = INVALID_HANDLE_VALUE;
532 }
533 }
534 if (FoundRightPDO)
535 {
536 /* No need to go further, as we already have found what we searched */
537 break;
538 }
539
540 ExFreePool(DeviceBi);
541 DeviceBi = NULL;
542 ZwClose(DeviceKey);
543 DeviceKey = INVALID_HANDLE_VALUE;
544 }
545
546 /* Add final NULL to ReturnBuffer */
547 if (ReturnBuffer.Length >= ReturnBuffer.MaximumLength)
548 {
549 PWSTR NewBuffer;
550 ReturnBuffer.MaximumLength += sizeof(WCHAR);
551 NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
552 if (!NewBuffer)
553 {
554 DPRINT("ExAllocatePool() failed\n");
555 Status = STATUS_INSUFFICIENT_RESOURCES;
556 goto cleanup;
557 }
558 RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
559 ExFreePool(ReturnBuffer.Buffer);
560 ReturnBuffer.Buffer = NewBuffer;
561 }
562 ReturnBuffer.Buffer[ReturnBuffer.Length / sizeof(WCHAR)] = UNICODE_NULL;
563 *SymbolicLinkList = ReturnBuffer.Buffer;
564 Status = STATUS_SUCCESS;
565
566 cleanup:
567 if (!NT_SUCCESS(Status) && ReturnBuffer.Buffer)
568 ExFreePool(ReturnBuffer.Buffer);
569 if (InterfaceKey != INVALID_HANDLE_VALUE)
570 ZwClose(InterfaceKey);
571 if (DeviceKey != INVALID_HANDLE_VALUE)
572 ZwClose(DeviceKey);
573 if (ReferenceKey != INVALID_HANDLE_VALUE)
574 ZwClose(ReferenceKey);
575 if (ControlKey != INVALID_HANDLE_VALUE)
576 ZwClose(ControlKey);
577 if (DeviceBi)
578 ExFreePool(DeviceBi);
579 if (ReferenceBi)
580 ExFreePool(ReferenceBi);
581 if (bip)
582 ExFreePool(bip);
583 return Status;
584 }
585
586 /*++
587 * @name IoRegisterDeviceInterface
588 * @implemented
589 *
590 * Registers a device interface class, if it has not been previously registered,
591 * and creates a new instance of the interface class, which a driver can
592 * subsequently enable for use by applications or other system components.
593 * Documented in WDK.
594 *
595 * @param PhysicalDeviceObject
596 * Points to an optional PDO that narrows the search to only the
597 * device interfaces of the device represented by the PDO
598 *
599 * @param InterfaceClassGuid
600 * Points to a class GUID specifying the device interface class
601 *
602 * @param ReferenceString
603 * Optional parameter, pointing to a unicode string. For a full
604 * description of this rather rarely used param (usually drivers
605 * pass NULL here) see WDK
606 *
607 * @param SymbolicLinkName
608 * Pointer to the resulting unicode string
609 *
610 * @return Usual NTSTATUS
611 *
612 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
613 * system thread
614 *
615 *--*/
616 NTSTATUS
617 NTAPI
618 IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject,
619 IN CONST GUID *InterfaceClassGuid,
620 IN PUNICODE_STRING ReferenceString OPTIONAL,
621 OUT PUNICODE_STRING SymbolicLinkName)
622 {
623 PUNICODE_STRING InstancePath;
624 UNICODE_STRING GuidString;
625 UNICODE_STRING SubKeyName;
626 UNICODE_STRING InterfaceKeyName;
627 UNICODE_STRING BaseKeyName;
628 UCHAR PdoNameInfoBuffer[sizeof(OBJECT_NAME_INFORMATION) + (256 * sizeof(WCHAR))];
629 POBJECT_NAME_INFORMATION PdoNameInfo = (POBJECT_NAME_INFORMATION)PdoNameInfoBuffer;
630 UNICODE_STRING DeviceInstance = RTL_CONSTANT_STRING(L"DeviceInstance");
631 UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
632 HANDLE ClassKey;
633 HANDLE InterfaceKey;
634 HANDLE SubKey;
635 ULONG StartIndex;
636 OBJECT_ATTRIBUTES ObjectAttributes;
637 ULONG i;
638 NTSTATUS Status;
639 PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension;
640
641 ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
642
643 DPRINT("IoRegisterDeviceInterface(): PDO %p, RefString: %wZ\n",
644 PhysicalDeviceObject, ReferenceString);
645
646 /* Parameters must pass three border of checks */
647 DeviceObjectExtension = (PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension;
648
649 /* 1st level: Presence of a Device Node */
650 if (DeviceObjectExtension->DeviceNode == NULL)
651 {
652 DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject);
653 return STATUS_INVALID_DEVICE_REQUEST;
654 }
655
656 /* 2nd level: Presence of an non-zero length InstancePath */
657 if (DeviceObjectExtension->DeviceNode->InstancePath.Length == 0)
658 {
659 DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject);
660 return STATUS_INVALID_DEVICE_REQUEST;
661 }
662
663 /* 3rd level: Optional, based on WDK documentation */
664 if (ReferenceString != NULL)
665 {
666 /* Reference string must not contain path-separator symbols */
667 for (i = 0; i < ReferenceString->Length / sizeof(WCHAR); i++)
668 {
669 if ((ReferenceString->Buffer[i] == '\\') ||
670 (ReferenceString->Buffer[i] == '/'))
671 return STATUS_INVALID_DEVICE_REQUEST;
672 }
673 }
674
675 Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
676 if (!NT_SUCCESS(Status))
677 {
678 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
679 return Status;
680 }
681
682 /* Create Pdo name: \Device\xxxxxxxx (unnamed device) */
683 Status = ObQueryNameString(
684 PhysicalDeviceObject,
685 PdoNameInfo,
686 sizeof(PdoNameInfoBuffer),
687 &i);
688 if (!NT_SUCCESS(Status))
689 {
690 DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status);
691 return Status;
692 }
693 ASSERT(PdoNameInfo->Name.Length);
694
695 /* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */
696 ASSERT(((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode);
697 InstancePath = &((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode->InstancePath;
698 BaseKeyName.Length = wcslen(BaseKeyString) * sizeof(WCHAR);
699 BaseKeyName.MaximumLength = BaseKeyName.Length
700 + GuidString.Length;
701 BaseKeyName.Buffer = ExAllocatePool(
702 PagedPool,
703 BaseKeyName.MaximumLength);
704 if (!BaseKeyName.Buffer)
705 {
706 DPRINT("ExAllocatePool() failed\n");
707 return STATUS_INSUFFICIENT_RESOURCES;
708 }
709 wcscpy(BaseKeyName.Buffer, BaseKeyString);
710 RtlAppendUnicodeStringToString(&BaseKeyName, &GuidString);
711
712 /* Create BaseKeyName key in registry */
713 InitializeObjectAttributes(
714 &ObjectAttributes,
715 &BaseKeyName,
716 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
717 NULL, /* RootDirectory */
718 NULL); /* SecurityDescriptor */
719
720 Status = ZwCreateKey(
721 &ClassKey,
722 KEY_WRITE,
723 &ObjectAttributes,
724 0, /* TileIndex */
725 NULL, /* Class */
726 REG_OPTION_VOLATILE,
727 NULL); /* Disposition */
728
729 if (!NT_SUCCESS(Status))
730 {
731 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
732 ExFreePool(BaseKeyName.Buffer);
733 return Status;
734 }
735
736 /* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */
737 InterfaceKeyName.Length = 0;
738 InterfaceKeyName.MaximumLength =
739 4 * sizeof(WCHAR) + /* 4 = size of ##?# */
740 InstancePath->Length +
741 sizeof(WCHAR) + /* 1 = size of # */
742 GuidString.Length;
743 InterfaceKeyName.Buffer = ExAllocatePool(
744 PagedPool,
745 InterfaceKeyName.MaximumLength);
746 if (!InterfaceKeyName.Buffer)
747 {
748 DPRINT("ExAllocatePool() failed\n");
749 return STATUS_INSUFFICIENT_RESOURCES;
750 }
751
752 RtlAppendUnicodeToString(&InterfaceKeyName, L"##?#");
753 StartIndex = InterfaceKeyName.Length / sizeof(WCHAR);
754 RtlAppendUnicodeStringToString(&InterfaceKeyName, InstancePath);
755 for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
756 {
757 if (InterfaceKeyName.Buffer[StartIndex + i] == '\\')
758 InterfaceKeyName.Buffer[StartIndex + i] = '#';
759 }
760 RtlAppendUnicodeToString(&InterfaceKeyName, L"#");
761 RtlAppendUnicodeStringToString(&InterfaceKeyName, &GuidString);
762
763 /* Create the interface key in registry */
764 InitializeObjectAttributes(
765 &ObjectAttributes,
766 &InterfaceKeyName,
767 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
768 ClassKey,
769 NULL); /* SecurityDescriptor */
770
771 Status = ZwCreateKey(
772 &InterfaceKey,
773 KEY_WRITE,
774 &ObjectAttributes,
775 0, /* TileIndex */
776 NULL, /* Class */
777 REG_OPTION_VOLATILE,
778 NULL); /* Disposition */
779
780 if (!NT_SUCCESS(Status))
781 {
782 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
783 ZwClose(ClassKey);
784 ExFreePool(BaseKeyName.Buffer);
785 return Status;
786 }
787
788 /* Write DeviceInstance entry. Value is InstancePath */
789 Status = ZwSetValueKey(
790 InterfaceKey,
791 &DeviceInstance,
792 0, /* TileIndex */
793 REG_SZ,
794 InstancePath->Buffer,
795 InstancePath->Length);
796 if (!NT_SUCCESS(Status))
797 {
798 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
799 ZwClose(InterfaceKey);
800 ZwClose(ClassKey);
801 ExFreePool(InterfaceKeyName.Buffer);
802 ExFreePool(BaseKeyName.Buffer);
803 return Status;
804 }
805
806 /* Create subkey. Name is #ReferenceString */
807 SubKeyName.Length = 0;
808 SubKeyName.MaximumLength = sizeof(WCHAR);
809 if (ReferenceString && ReferenceString->Length)
810 SubKeyName.MaximumLength += ReferenceString->Length;
811 SubKeyName.Buffer = ExAllocatePool(
812 PagedPool,
813 SubKeyName.MaximumLength);
814 if (!SubKeyName.Buffer)
815 {
816 DPRINT("ExAllocatePool() failed\n");
817 ZwClose(InterfaceKey);
818 ZwClose(ClassKey);
819 ExFreePool(InterfaceKeyName.Buffer);
820 ExFreePool(BaseKeyName.Buffer);
821 return STATUS_INSUFFICIENT_RESOURCES;
822 }
823 RtlAppendUnicodeToString(&SubKeyName, L"#");
824 if (ReferenceString && ReferenceString->Length)
825 RtlAppendUnicodeStringToString(&SubKeyName, ReferenceString);
826
827 /* Create SubKeyName key in registry */
828 InitializeObjectAttributes(
829 &ObjectAttributes,
830 &SubKeyName,
831 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
832 InterfaceKey, /* RootDirectory */
833 NULL); /* SecurityDescriptor */
834
835 Status = ZwCreateKey(
836 &SubKey,
837 KEY_WRITE,
838 &ObjectAttributes,
839 0, /* TileIndex */
840 NULL, /* Class */
841 REG_OPTION_VOLATILE,
842 NULL); /* Disposition */
843
844 if (!NT_SUCCESS(Status))
845 {
846 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
847 ZwClose(InterfaceKey);
848 ZwClose(ClassKey);
849 ExFreePool(InterfaceKeyName.Buffer);
850 ExFreePool(BaseKeyName.Buffer);
851 return Status;
852 }
853
854 /* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
855 SymbolicLinkName->Length = 0;
856 SymbolicLinkName->MaximumLength = SymbolicLinkName->Length
857 + 4 * sizeof(WCHAR) /* 4 = size of \??\ */
858 + InstancePath->Length
859 + sizeof(WCHAR) /* 1 = size of # */
860 + GuidString.Length
861 + sizeof(WCHAR); /* final NULL */
862 if (ReferenceString && ReferenceString->Length)
863 SymbolicLinkName->MaximumLength += sizeof(WCHAR) + ReferenceString->Length;
864 SymbolicLinkName->Buffer = ExAllocatePool(
865 PagedPool,
866 SymbolicLinkName->MaximumLength);
867 if (!SymbolicLinkName->Buffer)
868 {
869 DPRINT("ExAllocatePool() failed\n");
870 ZwClose(SubKey);
871 ZwClose(InterfaceKey);
872 ZwClose(ClassKey);
873 ExFreePool(InterfaceKeyName.Buffer);
874 ExFreePool(SubKeyName.Buffer);
875 ExFreePool(BaseKeyName.Buffer);
876 return STATUS_INSUFFICIENT_RESOURCES;
877 }
878 RtlAppendUnicodeToString(SymbolicLinkName, L"\\??\\");
879 StartIndex = SymbolicLinkName->Length / sizeof(WCHAR);
880 RtlAppendUnicodeStringToString(SymbolicLinkName, InstancePath);
881 for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
882 {
883 if (SymbolicLinkName->Buffer[StartIndex + i] == '\\')
884 SymbolicLinkName->Buffer[StartIndex + i] = '#';
885 }
886 RtlAppendUnicodeToString(SymbolicLinkName, L"#");
887 RtlAppendUnicodeStringToString(SymbolicLinkName, &GuidString);
888 SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0';
889
890 /* Create symbolic link */
891 DPRINT1("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName, &PdoNameInfo->Name);
892 Status = IoCreateSymbolicLink(SymbolicLinkName, &PdoNameInfo->Name);
893 if (!NT_SUCCESS(Status) && ReferenceString == NULL)
894 {
895 DPRINT1("IoCreateSymbolicLink() failed with status 0x%08lx\n", Status);
896 ZwClose(SubKey);
897 ZwClose(InterfaceKey);
898 ZwClose(ClassKey);
899 ExFreePool(SubKeyName.Buffer);
900 ExFreePool(InterfaceKeyName.Buffer);
901 ExFreePool(BaseKeyName.Buffer);
902 ExFreePool(SymbolicLinkName->Buffer);
903 return Status;
904 }
905
906 if (ReferenceString && ReferenceString->Length)
907 {
908 RtlAppendUnicodeToString(SymbolicLinkName, L"\\");
909 RtlAppendUnicodeStringToString(SymbolicLinkName, ReferenceString);
910 }
911 SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0';
912
913 /* Write symbolic link name in registry */
914 SymbolicLinkName->Buffer[1] = '\\';
915 Status = ZwSetValueKey(
916 SubKey,
917 &SymbolicLink,
918 0, /* TileIndex */
919 REG_SZ,
920 SymbolicLinkName->Buffer,
921 SymbolicLinkName->Length);
922 if (!NT_SUCCESS(Status))
923 {
924 DPRINT1("ZwSetValueKey() failed with status 0x%08lx\n", Status);
925 ExFreePool(SymbolicLinkName->Buffer);
926 }
927 else
928 {
929 SymbolicLinkName->Buffer[1] = '?';
930 }
931
932 ZwClose(SubKey);
933 ZwClose(InterfaceKey);
934 ZwClose(ClassKey);
935 ExFreePool(SubKeyName.Buffer);
936 ExFreePool(InterfaceKeyName.Buffer);
937 ExFreePool(BaseKeyName.Buffer);
938
939 return Status;
940 }
941
942 /*++
943 * @name IoSetDeviceInterfaceState
944 * @implemented
945 *
946 * Enables or disables an instance of a previously registered device
947 * interface class.
948 * Documented in WDK.
949 *
950 * @param SymbolicLinkName
951 * Pointer to the string identifying instance to enable or disable
952 *
953 * @param Enable
954 * TRUE = enable, FALSE = disable
955 *
956 * @return Usual NTSTATUS
957 *
958 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
959 * system thread
960 *
961 *--*/
962 NTSTATUS
963 NTAPI
964 IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName,
965 IN BOOLEAN Enable)
966 {
967 PDEVICE_OBJECT PhysicalDeviceObject;
968 PFILE_OBJECT FileObject;
969 UNICODE_STRING GuidString;
970 UNICODE_STRING SymLink;
971 PWCHAR StartPosition;
972 PWCHAR EndPosition;
973 NTSTATUS Status;
974 LPCGUID EventGuid;
975
976
977 if (SymbolicLinkName == NULL)
978 return STATUS_INVALID_PARAMETER_1;
979
980 DPRINT1("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName, Enable);
981
982 /* Symbolic link name is \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
983 /* Get GUID from SymbolicLinkName */
984 StartPosition = wcschr(SymbolicLinkName->Buffer, L'{');
985 EndPosition = wcschr(SymbolicLinkName->Buffer, L'}');
986 if (!StartPosition ||!EndPosition || StartPosition > EndPosition)
987 {
988 DPRINT1("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1\n");
989 return STATUS_INVALID_PARAMETER_1;
990 }
991 GuidString.Buffer = StartPosition;
992 GuidString.MaximumLength = GuidString.Length = (USHORT)((ULONG_PTR)(EndPosition + 1) - (ULONG_PTR)StartPosition);
993
994 SymLink.Buffer = SymbolicLinkName->Buffer;
995 SymLink.MaximumLength = SymLink.Length = (USHORT)((ULONG_PTR)(EndPosition + 1) - (ULONG_PTR)SymLink.Buffer);
996 DPRINT1("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName, Enable);
997 /* Get pointer to the PDO */
998 Status = IoGetDeviceObjectPointer(
999 &SymLink,
1000 0, /* DesiredAccess */
1001 &FileObject,
1002 &PhysicalDeviceObject);
1003 if (!NT_SUCCESS(Status))
1004 {
1005 DPRINT1("IoGetDeviceObjectPointer() failed with status 0x%08lx\n", Status);
1006 return Status;
1007 }
1008
1009 EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL;
1010 IopNotifyPlugPlayNotification(
1011 PhysicalDeviceObject,
1012 EventCategoryDeviceInterfaceChange,
1013 EventGuid,
1014 &GuidString,
1015 (PVOID)SymbolicLinkName);
1016
1017 ObDereferenceObject(FileObject);
1018 DPRINT1("Status %x\n", Status);
1019 return STATUS_SUCCESS;
1020 }
1021
1022 /* EOF */