merge 37282 from amd64-branch:
[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 ExFreePool(ReturnBuffer.Buffer);
502 ReturnBuffer.Buffer = NewBuffer;
503 }
504 DPRINT("Adding symbolic link %wZ\n", &KeyName);
505 Status = RtlAppendUnicodeStringToString(&ReturnBuffer, &KeyName);
506 if (!NT_SUCCESS(Status))
507 {
508 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
509 goto cleanup;
510 }
511 /* RtlAppendUnicodeStringToString added a NULL at the end of the
512 * destination string, but didn't increase the Length field.
513 * Do it for it.
514 */
515 ReturnBuffer.Length += sizeof(WCHAR);
516
517 NextReferenceString:
518 ExFreePool(ReferenceBi);
519 ReferenceBi = NULL;
520 ExFreePool(bip);
521 bip = NULL;
522 if (ReferenceKey != INVALID_HANDLE_VALUE)
523 {
524 ZwClose(ReferenceKey);
525 ReferenceKey = INVALID_HANDLE_VALUE;
526 }
527 if (ControlKey != INVALID_HANDLE_VALUE)
528 {
529 ZwClose(ControlKey);
530 ControlKey = INVALID_HANDLE_VALUE;
531 }
532 }
533 if (FoundRightPDO)
534 {
535 /* No need to go further, as we already have found what we searched */
536 break;
537 }
538
539 ExFreePool(DeviceBi);
540 DeviceBi = NULL;
541 ZwClose(DeviceKey);
542 DeviceKey = INVALID_HANDLE_VALUE;
543 }
544
545 /* Add final NULL to ReturnBuffer */
546 if (ReturnBuffer.Length >= ReturnBuffer.MaximumLength)
547 {
548 PWSTR NewBuffer;
549 ReturnBuffer.MaximumLength += sizeof(WCHAR);
550 NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
551 if (!NewBuffer)
552 {
553 DPRINT("ExAllocatePool() failed\n");
554 Status = STATUS_INSUFFICIENT_RESOURCES;
555 goto cleanup;
556 }
557 RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
558 ExFreePool(ReturnBuffer.Buffer);
559 ReturnBuffer.Buffer = NewBuffer;
560 }
561 ReturnBuffer.Buffer[ReturnBuffer.Length / sizeof(WCHAR)] = UNICODE_NULL;
562 *SymbolicLinkList = ReturnBuffer.Buffer;
563 Status = STATUS_SUCCESS;
564
565 cleanup:
566 if (!NT_SUCCESS(Status))
567 ExFreePool(ReturnBuffer.Buffer);
568 if (InterfaceKey != INVALID_HANDLE_VALUE)
569 ZwClose(InterfaceKey);
570 if (DeviceKey != INVALID_HANDLE_VALUE)
571 ZwClose(DeviceKey);
572 if (ReferenceKey != INVALID_HANDLE_VALUE)
573 ZwClose(ReferenceKey);
574 if (ControlKey != INVALID_HANDLE_VALUE)
575 ZwClose(ControlKey);
576 ExFreePool(DeviceBi);
577 ExFreePool(ReferenceBi);
578 ExFreePool(bip);
579 return Status;
580 }
581
582 /*++
583 * @name IoRegisterDeviceInterface
584 * @implemented
585 *
586 * Registers a device interface class, if it has not been previously registered,
587 * and creates a new instance of the interface class, which a driver can
588 * subsequently enable for use by applications or other system components.
589 * Documented in WDK.
590 *
591 * @param PhysicalDeviceObject
592 * Points to an optional PDO that narrows the search to only the
593 * device interfaces of the device represented by the PDO
594 *
595 * @param InterfaceClassGuid
596 * Points to a class GUID specifying the device interface class
597 *
598 * @param ReferenceString
599 * Optional parameter, pointing to a unicode string. For a full
600 * description of this rather rarely used param (usually drivers
601 * pass NULL here) see WDK
602 *
603 * @param SymbolicLinkName
604 * Pointer to the resulting unicode string
605 *
606 * @return Usual NTSTATUS
607 *
608 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
609 * system thread
610 *
611 *--*/
612 NTSTATUS
613 NTAPI
614 IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject,
615 IN CONST GUID *InterfaceClassGuid,
616 IN PUNICODE_STRING ReferenceString OPTIONAL,
617 OUT PUNICODE_STRING SymbolicLinkName)
618 {
619 PUNICODE_STRING InstancePath;
620 UNICODE_STRING GuidString;
621 UNICODE_STRING SubKeyName;
622 UNICODE_STRING InterfaceKeyName;
623 UNICODE_STRING BaseKeyName;
624 UCHAR PdoNameInfoBuffer[sizeof(OBJECT_NAME_INFORMATION) + (256 * sizeof(WCHAR))];
625 POBJECT_NAME_INFORMATION PdoNameInfo = (POBJECT_NAME_INFORMATION)PdoNameInfoBuffer;
626 UNICODE_STRING DeviceInstance = RTL_CONSTANT_STRING(L"DeviceInstance");
627 UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
628 HANDLE ClassKey;
629 HANDLE InterfaceKey;
630 HANDLE SubKey;
631 ULONG StartIndex;
632 OBJECT_ATTRIBUTES ObjectAttributes;
633 ULONG i;
634 NTSTATUS Status;
635 PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension;
636
637 ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
638
639 DPRINT("IoRegisterDeviceInterface(): PDO %p, RefString: %wZ\n",
640 PhysicalDeviceObject, ReferenceString);
641
642 /* Parameters must pass three border of checks */
643 DeviceObjectExtension = (PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension;
644
645 /* 1st level: Presence of a Device Node */
646 if (DeviceObjectExtension->DeviceNode == NULL)
647 {
648 DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject);
649 return STATUS_INVALID_DEVICE_REQUEST;
650 }
651
652 /* 2nd level: Presence of an non-zero length InstancePath */
653 if (DeviceObjectExtension->DeviceNode->InstancePath.Length == 0)
654 {
655 DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject);
656 return STATUS_INVALID_DEVICE_REQUEST;
657 }
658
659 /* 3rd level: Optional, based on WDK documentation */
660 if (ReferenceString != NULL)
661 {
662 /* Reference string must not contain path-separator symbols */
663 for (i = 0; i < ReferenceString->Length / sizeof(WCHAR); i++)
664 {
665 if ((ReferenceString->Buffer[i] == '\\') ||
666 (ReferenceString->Buffer[i] == '/'))
667 return STATUS_INVALID_DEVICE_REQUEST;
668 }
669 }
670
671 Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
672 if (!NT_SUCCESS(Status))
673 {
674 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
675 return Status;
676 }
677
678 /* Create Pdo name: \Device\xxxxxxxx (unnamed device) */
679 Status = ObQueryNameString(
680 PhysicalDeviceObject,
681 PdoNameInfo,
682 sizeof(PdoNameInfoBuffer),
683 &i);
684 if (!NT_SUCCESS(Status))
685 {
686 DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status);
687 return Status;
688 }
689 ASSERT(PdoNameInfo->Name.Length);
690
691 /* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */
692 ASSERT(((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode);
693 InstancePath = &((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode->InstancePath;
694 BaseKeyName.Length = wcslen(BaseKeyString) * sizeof(WCHAR);
695 BaseKeyName.MaximumLength = BaseKeyName.Length
696 + GuidString.Length;
697 BaseKeyName.Buffer = ExAllocatePool(
698 PagedPool,
699 BaseKeyName.MaximumLength);
700 if (!BaseKeyName.Buffer)
701 {
702 DPRINT("ExAllocatePool() failed\n");
703 return STATUS_INSUFFICIENT_RESOURCES;
704 }
705 wcscpy(BaseKeyName.Buffer, BaseKeyString);
706 RtlAppendUnicodeStringToString(&BaseKeyName, &GuidString);
707
708 /* Create BaseKeyName key in registry */
709 InitializeObjectAttributes(
710 &ObjectAttributes,
711 &BaseKeyName,
712 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
713 NULL, /* RootDirectory */
714 NULL); /* SecurityDescriptor */
715
716 Status = ZwCreateKey(
717 &ClassKey,
718 KEY_WRITE,
719 &ObjectAttributes,
720 0, /* TileIndex */
721 NULL, /* Class */
722 REG_OPTION_VOLATILE,
723 NULL); /* Disposition */
724
725 if (!NT_SUCCESS(Status))
726 {
727 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
728 ExFreePool(BaseKeyName.Buffer);
729 return Status;
730 }
731
732 /* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */
733 InterfaceKeyName.Length = 0;
734 InterfaceKeyName.MaximumLength =
735 4 * sizeof(WCHAR) + /* 4 = size of ##?# */
736 InstancePath->Length +
737 sizeof(WCHAR) + /* 1 = size of # */
738 GuidString.Length;
739 InterfaceKeyName.Buffer = ExAllocatePool(
740 PagedPool,
741 InterfaceKeyName.MaximumLength);
742 if (!InterfaceKeyName.Buffer)
743 {
744 DPRINT("ExAllocatePool() failed\n");
745 return STATUS_INSUFFICIENT_RESOURCES;
746 }
747
748 RtlAppendUnicodeToString(&InterfaceKeyName, L"##?#");
749 StartIndex = InterfaceKeyName.Length / sizeof(WCHAR);
750 RtlAppendUnicodeStringToString(&InterfaceKeyName, InstancePath);
751 for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
752 {
753 if (InterfaceKeyName.Buffer[StartIndex + i] == '\\')
754 InterfaceKeyName.Buffer[StartIndex + i] = '#';
755 }
756 RtlAppendUnicodeToString(&InterfaceKeyName, L"#");
757 RtlAppendUnicodeStringToString(&InterfaceKeyName, &GuidString);
758
759 /* Create the interface key in registry */
760 InitializeObjectAttributes(
761 &ObjectAttributes,
762 &InterfaceKeyName,
763 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
764 ClassKey,
765 NULL); /* SecurityDescriptor */
766
767 Status = ZwCreateKey(
768 &InterfaceKey,
769 KEY_WRITE,
770 &ObjectAttributes,
771 0, /* TileIndex */
772 NULL, /* Class */
773 REG_OPTION_VOLATILE,
774 NULL); /* Disposition */
775
776 if (!NT_SUCCESS(Status))
777 {
778 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
779 ZwClose(ClassKey);
780 ExFreePool(BaseKeyName.Buffer);
781 return Status;
782 }
783
784 /* Write DeviceInstance entry. Value is InstancePath */
785 Status = ZwSetValueKey(
786 InterfaceKey,
787 &DeviceInstance,
788 0, /* TileIndex */
789 REG_SZ,
790 InstancePath->Buffer,
791 InstancePath->Length);
792 if (!NT_SUCCESS(Status))
793 {
794 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
795 ZwClose(InterfaceKey);
796 ZwClose(ClassKey);
797 ExFreePool(InterfaceKeyName.Buffer);
798 ExFreePool(BaseKeyName.Buffer);
799 return Status;
800 }
801
802 /* Create subkey. Name is #ReferenceString */
803 SubKeyName.Length = 0;
804 SubKeyName.MaximumLength = sizeof(WCHAR);
805 if (ReferenceString && ReferenceString->Length)
806 SubKeyName.MaximumLength += ReferenceString->Length;
807 SubKeyName.Buffer = ExAllocatePool(
808 PagedPool,
809 SubKeyName.MaximumLength);
810 if (!SubKeyName.Buffer)
811 {
812 DPRINT("ExAllocatePool() failed\n");
813 ZwClose(InterfaceKey);
814 ZwClose(ClassKey);
815 ExFreePool(InterfaceKeyName.Buffer);
816 ExFreePool(BaseKeyName.Buffer);
817 return STATUS_INSUFFICIENT_RESOURCES;
818 }
819 RtlAppendUnicodeToString(&SubKeyName, L"#");
820 if (ReferenceString && ReferenceString->Length)
821 RtlAppendUnicodeStringToString(&SubKeyName, ReferenceString);
822
823 /* Create SubKeyName key in registry */
824 InitializeObjectAttributes(
825 &ObjectAttributes,
826 &SubKeyName,
827 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
828 InterfaceKey, /* RootDirectory */
829 NULL); /* SecurityDescriptor */
830
831 Status = ZwCreateKey(
832 &SubKey,
833 KEY_WRITE,
834 &ObjectAttributes,
835 0, /* TileIndex */
836 NULL, /* Class */
837 REG_OPTION_VOLATILE,
838 NULL); /* Disposition */
839
840 if (!NT_SUCCESS(Status))
841 {
842 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
843 ZwClose(InterfaceKey);
844 ZwClose(ClassKey);
845 ExFreePool(InterfaceKeyName.Buffer);
846 ExFreePool(BaseKeyName.Buffer);
847 return Status;
848 }
849
850 /* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
851 SymbolicLinkName->Length = 0;
852 SymbolicLinkName->MaximumLength = SymbolicLinkName->Length
853 + 4 * sizeof(WCHAR) /* 4 = size of \??\ */
854 + InstancePath->Length
855 + sizeof(WCHAR) /* 1 = size of # */
856 + GuidString.Length
857 + sizeof(WCHAR); /* final NULL */
858 if (ReferenceString && ReferenceString->Length)
859 SymbolicLinkName->MaximumLength += sizeof(WCHAR) + ReferenceString->Length;
860 SymbolicLinkName->Buffer = ExAllocatePool(
861 PagedPool,
862 SymbolicLinkName->MaximumLength);
863 if (!SymbolicLinkName->Buffer)
864 {
865 DPRINT("ExAllocatePool() failed\n");
866 ZwClose(SubKey);
867 ZwClose(InterfaceKey);
868 ZwClose(ClassKey);
869 ExFreePool(InterfaceKeyName.Buffer);
870 ExFreePool(SubKeyName.Buffer);
871 ExFreePool(BaseKeyName.Buffer);
872 return STATUS_INSUFFICIENT_RESOURCES;
873 }
874 RtlAppendUnicodeToString(SymbolicLinkName, L"\\??\\");
875 StartIndex = SymbolicLinkName->Length / sizeof(WCHAR);
876 RtlAppendUnicodeStringToString(SymbolicLinkName, InstancePath);
877 for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
878 {
879 if (SymbolicLinkName->Buffer[StartIndex + i] == '\\')
880 SymbolicLinkName->Buffer[StartIndex + i] = '#';
881 }
882 RtlAppendUnicodeToString(SymbolicLinkName, L"#");
883 RtlAppendUnicodeStringToString(SymbolicLinkName, &GuidString);
884 if (ReferenceString && ReferenceString->Length)
885 {
886 RtlAppendUnicodeToString(SymbolicLinkName, L"\\");
887 RtlAppendUnicodeStringToString(SymbolicLinkName, ReferenceString);
888 }
889 SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0';
890
891 /* Create symbolic link */
892 DPRINT1("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName, &PdoNameInfo->Name);
893 Status = IoCreateSymbolicLink(SymbolicLinkName, &PdoNameInfo->Name);
894 if (!NT_SUCCESS(Status))
895 {
896 DPRINT("IoCreateSymbolicLink() failed with status 0x%08lx\n", Status);
897 ZwClose(SubKey);
898 ZwClose(InterfaceKey);
899 ZwClose(ClassKey);
900 ExFreePool(SubKeyName.Buffer);
901 ExFreePool(InterfaceKeyName.Buffer);
902 ExFreePool(BaseKeyName.Buffer);
903 ExFreePool(SymbolicLinkName->Buffer);
904 return Status;
905 }
906
907 /* Write symbolic link name in registry */
908 SymbolicLinkName->Buffer[1] = '\\';
909 Status = ZwSetValueKey(
910 SubKey,
911 &SymbolicLink,
912 0, /* TileIndex */
913 REG_SZ,
914 SymbolicLinkName->Buffer,
915 SymbolicLinkName->Length);
916 if (!NT_SUCCESS(Status))
917 {
918 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
919 ExFreePool(SymbolicLinkName->Buffer);
920 }
921 SymbolicLinkName->Buffer[1] = '?';
922
923 ZwClose(SubKey);
924 ZwClose(InterfaceKey);
925 ZwClose(ClassKey);
926 ExFreePool(SubKeyName.Buffer);
927 ExFreePool(InterfaceKeyName.Buffer);
928 ExFreePool(BaseKeyName.Buffer);
929
930 return Status;
931 }
932
933 /*++
934 * @name IoSetDeviceInterfaceState
935 * @implemented
936 *
937 * Enables or disables an instance of a previously registered device
938 * interface class.
939 * Documented in WDK.
940 *
941 * @param SymbolicLinkName
942 * Pointer to the string identifying instance to enable or disable
943 *
944 * @param Enable
945 * TRUE = enable, FALSE = disable
946 *
947 * @return Usual NTSTATUS
948 *
949 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
950 * system thread
951 *
952 *--*/
953 NTSTATUS
954 NTAPI
955 IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName,
956 IN BOOLEAN Enable)
957 {
958 PDEVICE_OBJECT PhysicalDeviceObject;
959 PFILE_OBJECT FileObject;
960 UNICODE_STRING GuidString;
961 PWCHAR StartPosition;
962 PWCHAR EndPosition;
963 NTSTATUS Status;
964 LPCGUID EventGuid;
965
966 if (SymbolicLinkName == NULL)
967 return STATUS_INVALID_PARAMETER_1;
968
969 DPRINT("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName, Enable);
970
971 /* Symbolic link name is \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
972 /* Get GUID from SymbolicLinkName */
973 StartPosition = wcschr(SymbolicLinkName->Buffer, L'{');
974 EndPosition = wcschr(SymbolicLinkName->Buffer, L'}');
975 if (!StartPosition ||!EndPosition || StartPosition > EndPosition)
976 {
977 DPRINT("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1\n");
978 return STATUS_INVALID_PARAMETER_1;
979 }
980 GuidString.Buffer = StartPosition;
981 GuidString.MaximumLength = GuidString.Length = (USHORT)((ULONG_PTR)(EndPosition + 1) - (ULONG_PTR)StartPosition);
982
983 /* Get pointer to the PDO */
984 Status = IoGetDeviceObjectPointer(
985 SymbolicLinkName,
986 0, /* DesiredAccess */
987 &FileObject,
988 &PhysicalDeviceObject);
989 if (!NT_SUCCESS(Status))
990 {
991 DPRINT("IoGetDeviceObjectPointer() failed with status 0x%08lx\n", Status);
992 return Status;
993 }
994
995 EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL;
996 IopNotifyPlugPlayNotification(
997 PhysicalDeviceObject,
998 EventCategoryDeviceInterfaceChange,
999 EventGuid,
1000 &GuidString,
1001 (PVOID)SymbolicLinkName);
1002
1003 ObDereferenceObject(FileObject);
1004
1005 return STATUS_SUCCESS;
1006 }
1007
1008 /* EOF */