NTSTATUS KspForwardIrpSynchronous( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { KEVENT Event; NTSTATUS Status; PDEVICE_EXTENSION DeviceExtension; PKSIDEVICE_HEADER DeviceHeader; ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); /* get device extension */ DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; /* get device header */ DeviceHeader = DeviceExtension->DeviceHeader; /* initialize the notification event */ KeInitializeEvent(&Event, NotificationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, KspForwardIrpSynchronousCompletion, (PVOID)&Event, TRUE, TRUE, TRUE); /* now call the driver */ Status = IoCallDriver(DeviceHeader->KsDevice.NextDeviceObject, Irp); /* did the request complete yet */ if (Status == STATUS_PENDING) { /* not yet, lets wait a bit */ KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = Irp->IoStatus.Status; } return Status; }
/* * @implemented */ VOID NTAPI IoAllocateController(IN PCONTROLLER_OBJECT ControllerObject, IN PDEVICE_OBJECT DeviceObject, IN PDRIVER_CONTROL ExecutionRoutine, IN PVOID Context) { IO_ALLOCATION_ACTION Result; ASSERT_IRQL_EQUAL(DISPATCH_LEVEL); /* Initialize the Wait Context Block */ DeviceObject->Queue.Wcb.DeviceContext = Context; DeviceObject->Queue.Wcb.DeviceRoutine = ExecutionRoutine; /* Insert the Device Queue */ if (!KeInsertDeviceQueue(&ControllerObject->DeviceWaitQueue, &DeviceObject->Queue.Wcb.WaitQueueEntry)) { /* Call the execution routine */ Result = ExecutionRoutine(DeviceObject, DeviceObject->CurrentIrp, NULL, Context); /* Free the controller if this was requested */ if (Result == DeallocateObject) IoFreeController(ControllerObject); } }
/* * See "Windows Internals" - Chapter 13, Page 49 */ NTSTATUS NTAPI PspTerminateThreadByPointer(IN PETHREAD Thread, IN NTSTATUS ExitStatus, IN BOOLEAN bSelf) { PKAPC Apc; NTSTATUS Status = STATUS_SUCCESS; ULONG Flags; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "Thread: %p ExitStatus: %d\n", Thread, ExitStatus); PSREFTRACE(Thread); /* Check if this is a Critical Thread, and Bugcheck */ if (Thread->BreakOnTermination) { /* Break to debugger */ PspCatchCriticalBreak("Terminating critical thread 0x%p (%s)\n", Thread, Thread->ThreadsProcess->ImageFileName); } /* Check if we are already inside the thread */ if ((bSelf) || (PsGetCurrentThread() == Thread)) { /* This should only happen at passive */ ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); /* Mark it as terminated */ PspSetCrossThreadFlag(Thread, CT_TERMINATED_BIT); /* Directly terminate the thread */ PspExitThread(ExitStatus); } /* This shouldn't be a system thread */ if (Thread->SystemThread) return STATUS_ACCESS_DENIED; /* Allocate the APC */ Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC); if (!Apc) return STATUS_INSUFFICIENT_RESOURCES; /* Set the Terminated Flag */ Flags = Thread->CrossThreadFlags | CT_TERMINATED_BIT; /* Set it, and check if it was already set while we were running */ if (!(InterlockedExchange((PLONG)&Thread->CrossThreadFlags, Flags) & CT_TERMINATED_BIT)) { /* Initialize a Kernel Mode APC to Kill the Thread */ KeInitializeApc(Apc, &Thread->Tcb, OriginalApcEnvironment, PsExitSpecialApc, PspExitApcRundown, PspExitNormalApc, KernelMode, (PVOID)ExitStatus); /* Insert it into the APC Queue */ if (!KeInsertQueueApc(Apc, Apc, NULL, 2)) { /* The APC was already in the queue, fail */ Status = STATUS_UNSUCCESSFUL; } else { /* Forcefully resume the thread and return */ KeForceResumeThread(&Thread->Tcb); return Status; } } /* We failed, free the APC */ ExFreePoolWithTag(Apc, TAG_TERMINATE_APC); /* Return Status */ return Status; }
/*++ * @name KiDeliverApc * @implemented @NT4 * * The KiDeliverApc routine is called from IRQL switching code if the * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are * pending. * * @param DeliveryMode * Specifies the current processor mode. * * @param ExceptionFrame * Pointer to the Exception Frame on non-i386 builds. * * @param TrapFrame * Pointer to the Trap Frame. * * @return None. * * @remarks First, Special APCs are delivered, followed by Kernel-Mode APCs and * User-Mode APCs. Note that the TrapFrame is only valid if the * delivery mode is User-Mode. * Upon entry, this routine executes at APC_LEVEL. * *--*/ VOID NTAPI KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame) { PKTHREAD Thread = KeGetCurrentThread(); PKPROCESS Process = Thread->ApcState.Process; PKTRAP_FRAME OldTrapFrame; PLIST_ENTRY ApcListEntry; PKAPC Apc; KLOCK_QUEUE_HANDLE ApcLock; PKKERNEL_ROUTINE KernelRoutine; PVOID NormalContext; PKNORMAL_ROUTINE NormalRoutine; PVOID SystemArgument1; PVOID SystemArgument2; ASSERT_IRQL_EQUAL(APC_LEVEL); /* Save the old trap frame and set current one */ OldTrapFrame = Thread->TrapFrame; Thread->TrapFrame = TrapFrame; /* Clear Kernel APC Pending */ Thread->ApcState.KernelApcPending = FALSE; /* Check if Special APCs are disabled */ if (Thread->SpecialApcDisable) goto Quickie; /* Do the Kernel APCs first */ while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) { /* Lock the APC Queue */ KiAcquireApcLockAtApcLevel(Thread, &ApcLock); /* Check if the list became empty now */ if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) { /* It is, release the lock and break out */ KiReleaseApcLock(&ApcLock); break; } /* Kernel APC is not pending anymore */ Thread->ApcState.KernelApcPending = FALSE; /* Get the next Entry */ ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink; Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/ NormalRoutine = Apc->NormalRoutine; KernelRoutine = Apc->KernelRoutine; NormalContext = Apc->NormalContext; SystemArgument1 = Apc->SystemArgument1; SystemArgument2 = Apc->SystemArgument2; /* Special APC */ if (!NormalRoutine) { /* Remove the APC from the list */ RemoveEntryList(ApcListEntry); Apc->Inserted = FALSE; /* Release the APC lock */ KiReleaseApcLock(&ApcLock); /* Call the Special APC */ KernelRoutine(Apc, &NormalRoutine, &NormalContext, &SystemArgument1, &SystemArgument2); /* Make sure it returned correctly */ if (KeGetCurrentIrql() != ApcLock.OldIrql) { KeBugCheckEx(IRQL_UNEXPECTED_VALUE, (KeGetCurrentIrql() << 16) | (ApcLock.OldIrql << 8), (ULONG_PTR)KernelRoutine, (ULONG_PTR)Apc, (ULONG_PTR)NormalRoutine); } } else { /* Normal Kernel APC, make sure it's safe to deliver */ if ((Thread->ApcState.KernelApcInProgress) || (Thread->KernelApcDisable)) { /* Release lock and return */ KiReleaseApcLock(&ApcLock); goto Quickie; } /* Dequeue the APC */ RemoveEntryList(ApcListEntry); Apc->Inserted = FALSE; /* Go back to APC_LEVEL */ KiReleaseApcLock(&ApcLock); /* Call the Kernel APC */ KernelRoutine(Apc, &NormalRoutine, &NormalContext, &SystemArgument1, &SystemArgument2); /* Make sure it returned correctly */ if (KeGetCurrentIrql() != ApcLock.OldIrql) { KeBugCheckEx(IRQL_UNEXPECTED_VALUE, (KeGetCurrentIrql() << 16) | (ApcLock.OldIrql << 8), (ULONG_PTR)KernelRoutine, (ULONG_PTR)Apc, (ULONG_PTR)NormalRoutine); } /* Check if there still is a Normal Routine */ if (NormalRoutine) { /* At Passive Level, an APC can be prempted by a Special APC */ Thread->ApcState.KernelApcInProgress = TRUE; KeLowerIrql(PASSIVE_LEVEL); /* Call and Raise IRQL back to APC_LEVEL */ NormalRoutine(NormalContext, SystemArgument1, SystemArgument2); KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql); } /* Set Kernel APC in progress to false and loop again */ Thread->ApcState.KernelApcInProgress = FALSE; } } /* Now we do the User APCs */ if ((DeliveryMode == UserMode) && !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) && (Thread->ApcState.UserApcPending)) { /* Lock the APC Queue */ KiAcquireApcLockAtApcLevel(Thread, &ApcLock); /* It's not pending anymore */ Thread->ApcState.UserApcPending = FALSE; /* Check if the list became empty now */ if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) { /* It is, release the lock and break out */ KiReleaseApcLock(&ApcLock); goto Quickie; } /* Get the actual APC object */ ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink; Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/ NormalRoutine = Apc->NormalRoutine; KernelRoutine = Apc->KernelRoutine; NormalContext = Apc->NormalContext; SystemArgument1 = Apc->SystemArgument1; SystemArgument2 = Apc->SystemArgument2; /* Remove the APC from Queue, and release the lock */ RemoveEntryList(ApcListEntry); Apc->Inserted = FALSE; KiReleaseApcLock(&ApcLock); /* Call the kernel routine */ KernelRoutine(Apc, &NormalRoutine, &NormalContext, &SystemArgument1, &SystemArgument2); /* Check if there's no normal routine */ if (!NormalRoutine) { /* Check if more User APCs are Pending */ KeTestAlertThread(UserMode); } else { /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */ KiInitializeUserApc(ExceptionFrame, TrapFrame, NormalRoutine, NormalContext, SystemArgument1, SystemArgument2); } } Quickie: /* Make sure we're still in the same process */ if (Process != Thread->ApcState.Process) { /* Erm, we got attached or something! BAD! */ KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG_PTR)Process, (ULONG_PTR)Thread->ApcState.Process, Thread->ApcStateIndex, KeGetCurrentPrcb()->DpcRoutineActive); } /* Restore the trap frame */ Thread->TrapFrame = OldTrapFrame; }
/*++ * @name IoRegisterDeviceInterface * @implemented * * Registers a device interface class, if it has not been previously registered, * and creates a new instance of the interface class, which a driver can * subsequently enable for use by applications or other system components. * Documented in WDK. * * @param PhysicalDeviceObject * Points to an optional PDO that narrows the search to only the * device interfaces of the device represented by the PDO * * @param InterfaceClassGuid * Points to a class GUID specifying the device interface class * * @param ReferenceString * Optional parameter, pointing to a unicode string. For a full * description of this rather rarely used param (usually drivers * pass NULL here) see WDK * * @param SymbolicLinkName * Pointer to the resulting unicode string * * @return Usual NTSTATUS * * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a * system thread * *--*/ NTSTATUS NTAPI IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject, IN CONST GUID *InterfaceClassGuid, IN PUNICODE_STRING ReferenceString OPTIONAL, OUT PUNICODE_STRING SymbolicLinkName) { PUNICODE_STRING InstancePath; UNICODE_STRING GuidString; UNICODE_STRING SubKeyName; UNICODE_STRING InterfaceKeyName; UNICODE_STRING BaseKeyName; UCHAR PdoNameInfoBuffer[sizeof(OBJECT_NAME_INFORMATION) + (256 * sizeof(WCHAR))]; POBJECT_NAME_INFORMATION PdoNameInfo = (POBJECT_NAME_INFORMATION)PdoNameInfoBuffer; UNICODE_STRING DeviceInstance = RTL_CONSTANT_STRING(L"DeviceInstance"); UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink"); HANDLE ClassKey; HANDLE InterfaceKey; HANDLE SubKey; ULONG StartIndex; OBJECT_ATTRIBUTES ObjectAttributes; ULONG i; NTSTATUS Status, SymLinkStatus; PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension; ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); DPRINT("IoRegisterDeviceInterface(): PDO %p, RefString: %wZ\n", PhysicalDeviceObject, ReferenceString); /* Parameters must pass three border of checks */ DeviceObjectExtension = (PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension; /* 1st level: Presence of a Device Node */ if (DeviceObjectExtension->DeviceNode == NULL) { DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject); return STATUS_INVALID_DEVICE_REQUEST; } /* 2nd level: Presence of an non-zero length InstancePath */ if (DeviceObjectExtension->DeviceNode->InstancePath.Length == 0) { DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject); return STATUS_INVALID_DEVICE_REQUEST; } /* 3rd level: Optional, based on WDK documentation */ if (ReferenceString != NULL) { /* Reference string must not contain path-separator symbols */ for (i = 0; i < ReferenceString->Length / sizeof(WCHAR); i++) { if ((ReferenceString->Buffer[i] == '\\') || (ReferenceString->Buffer[i] == '/')) return STATUS_INVALID_DEVICE_REQUEST; } } Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString); if (!NT_SUCCESS(Status)) { DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status); return Status; } /* Create Pdo name: \Device\xxxxxxxx (unnamed device) */ Status = ObQueryNameString( PhysicalDeviceObject, PdoNameInfo, sizeof(PdoNameInfoBuffer), &i); if (!NT_SUCCESS(Status)) { DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status); return Status; } ASSERT(PdoNameInfo->Name.Length); /* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */ ASSERT(((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode); InstancePath = &((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode->InstancePath; BaseKeyName.Length = (USHORT)wcslen(BaseKeyString) * sizeof(WCHAR); BaseKeyName.MaximumLength = BaseKeyName.Length + GuidString.Length; BaseKeyName.Buffer = ExAllocatePool( PagedPool, BaseKeyName.MaximumLength); if (!BaseKeyName.Buffer) { DPRINT("ExAllocatePool() failed\n"); return STATUS_INSUFFICIENT_RESOURCES; } wcscpy(BaseKeyName.Buffer, BaseKeyString); RtlAppendUnicodeStringToString(&BaseKeyName, &GuidString); /* Create BaseKeyName key in registry */ InitializeObjectAttributes( &ObjectAttributes, &BaseKeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF, NULL, /* RootDirectory */ NULL); /* SecurityDescriptor */ Status = ZwCreateKey( &ClassKey, KEY_WRITE, &ObjectAttributes, 0, /* TileIndex */ NULL, /* Class */ REG_OPTION_VOLATILE, NULL); /* Disposition */ if (!NT_SUCCESS(Status)) { DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status); ExFreePool(BaseKeyName.Buffer); return Status; } /* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */ InterfaceKeyName.Length = 0; InterfaceKeyName.MaximumLength = 4 * sizeof(WCHAR) + /* 4 = size of ##?# */ InstancePath->Length + sizeof(WCHAR) + /* 1 = size of # */ GuidString.Length; InterfaceKeyName.Buffer = ExAllocatePool( PagedPool, InterfaceKeyName.MaximumLength); if (!InterfaceKeyName.Buffer) { DPRINT("ExAllocatePool() failed\n"); return STATUS_INSUFFICIENT_RESOURCES; } RtlAppendUnicodeToString(&InterfaceKeyName, L"##?#"); StartIndex = InterfaceKeyName.Length / sizeof(WCHAR); RtlAppendUnicodeStringToString(&InterfaceKeyName, InstancePath); for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++) { if (InterfaceKeyName.Buffer[StartIndex + i] == '\\') InterfaceKeyName.Buffer[StartIndex + i] = '#'; } RtlAppendUnicodeToString(&InterfaceKeyName, L"#"); RtlAppendUnicodeStringToString(&InterfaceKeyName, &GuidString); /* Create the interface key in registry */ InitializeObjectAttributes( &ObjectAttributes, &InterfaceKeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF, ClassKey, NULL); /* SecurityDescriptor */ Status = ZwCreateKey( &InterfaceKey, KEY_WRITE, &ObjectAttributes, 0, /* TileIndex */ NULL, /* Class */ REG_OPTION_VOLATILE, NULL); /* Disposition */ if (!NT_SUCCESS(Status)) { DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status); ZwClose(ClassKey); ExFreePool(BaseKeyName.Buffer); return Status; } /* Write DeviceInstance entry. Value is InstancePath */ Status = ZwSetValueKey( InterfaceKey, &DeviceInstance, 0, /* TileIndex */ REG_SZ, InstancePath->Buffer, InstancePath->Length); if (!NT_SUCCESS(Status)) { DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status); ZwClose(InterfaceKey); ZwClose(ClassKey); ExFreePool(InterfaceKeyName.Buffer); ExFreePool(BaseKeyName.Buffer); return Status; } /* Create subkey. Name is #ReferenceString */ SubKeyName.Length = 0; SubKeyName.MaximumLength = sizeof(WCHAR); if (ReferenceString && ReferenceString->Length) SubKeyName.MaximumLength += ReferenceString->Length; SubKeyName.Buffer = ExAllocatePool( PagedPool, SubKeyName.MaximumLength); if (!SubKeyName.Buffer) { DPRINT("ExAllocatePool() failed\n"); ZwClose(InterfaceKey); ZwClose(ClassKey); ExFreePool(InterfaceKeyName.Buffer); ExFreePool(BaseKeyName.Buffer); return STATUS_INSUFFICIENT_RESOURCES; } RtlAppendUnicodeToString(&SubKeyName, L"#"); if (ReferenceString && ReferenceString->Length) RtlAppendUnicodeStringToString(&SubKeyName, ReferenceString); /* Create SubKeyName key in registry */ InitializeObjectAttributes( &ObjectAttributes, &SubKeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, InterfaceKey, /* RootDirectory */ NULL); /* SecurityDescriptor */ Status = ZwCreateKey( &SubKey, KEY_WRITE, &ObjectAttributes, 0, /* TileIndex */ NULL, /* Class */ REG_OPTION_VOLATILE, NULL); /* Disposition */ if (!NT_SUCCESS(Status)) { DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status); ZwClose(InterfaceKey); ZwClose(ClassKey); ExFreePool(InterfaceKeyName.Buffer); ExFreePool(BaseKeyName.Buffer); return Status; } /* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */ SymbolicLinkName->Length = 0; SymbolicLinkName->MaximumLength = SymbolicLinkName->Length + 4 * sizeof(WCHAR) /* 4 = size of \??\ */ + InstancePath->Length + sizeof(WCHAR) /* 1 = size of # */ + GuidString.Length + sizeof(WCHAR); /* final NULL */ if (ReferenceString && ReferenceString->Length) SymbolicLinkName->MaximumLength += sizeof(WCHAR) + ReferenceString->Length; SymbolicLinkName->Buffer = ExAllocatePool( PagedPool, SymbolicLinkName->MaximumLength); if (!SymbolicLinkName->Buffer) { DPRINT("ExAllocatePool() failed\n"); ZwClose(SubKey); ZwClose(InterfaceKey); ZwClose(ClassKey); ExFreePool(InterfaceKeyName.Buffer); ExFreePool(SubKeyName.Buffer); ExFreePool(BaseKeyName.Buffer); return STATUS_INSUFFICIENT_RESOURCES; } RtlAppendUnicodeToString(SymbolicLinkName, L"\\??\\"); StartIndex = SymbolicLinkName->Length / sizeof(WCHAR); RtlAppendUnicodeStringToString(SymbolicLinkName, InstancePath); for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++) { if (SymbolicLinkName->Buffer[StartIndex + i] == '\\') SymbolicLinkName->Buffer[StartIndex + i] = '#'; } RtlAppendUnicodeToString(SymbolicLinkName, L"#"); RtlAppendUnicodeStringToString(SymbolicLinkName, &GuidString); SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0'; /* Create symbolic link */ DPRINT("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName, &PdoNameInfo->Name); SymLinkStatus = IoCreateSymbolicLink(SymbolicLinkName, &PdoNameInfo->Name); /* If the symbolic link already exists, return an informational success status */ if (SymLinkStatus == STATUS_OBJECT_NAME_COLLISION) { /* HACK: Delete the existing symbolic link and update it to the new PDO name */ IoDeleteSymbolicLink(SymbolicLinkName); IoCreateSymbolicLink(SymbolicLinkName, &PdoNameInfo->Name); SymLinkStatus = STATUS_OBJECT_NAME_EXISTS; } if (!NT_SUCCESS(SymLinkStatus)) { DPRINT1("IoCreateSymbolicLink() failed with status 0x%08lx\n", SymLinkStatus); ZwClose(SubKey); ZwClose(InterfaceKey); ZwClose(ClassKey); ExFreePool(SubKeyName.Buffer); ExFreePool(InterfaceKeyName.Buffer); ExFreePool(BaseKeyName.Buffer); ExFreePool(SymbolicLinkName->Buffer); return SymLinkStatus; } if (ReferenceString && ReferenceString->Length) { RtlAppendUnicodeToString(SymbolicLinkName, L"\\"); RtlAppendUnicodeStringToString(SymbolicLinkName, ReferenceString); } SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0'; /* Write symbolic link name in registry */ SymbolicLinkName->Buffer[1] = '\\'; Status = ZwSetValueKey( SubKey, &SymbolicLink, 0, /* TileIndex */ REG_SZ, SymbolicLinkName->Buffer, SymbolicLinkName->Length); if (!NT_SUCCESS(Status)) { DPRINT1("ZwSetValueKey() failed with status 0x%08lx\n", Status); ExFreePool(SymbolicLinkName->Buffer); } else { SymbolicLinkName->Buffer[1] = '?'; } ZwClose(SubKey); ZwClose(InterfaceKey); ZwClose(ClassKey); ExFreePool(SubKeyName.Buffer); ExFreePool(InterfaceKeyName.Buffer); ExFreePool(BaseKeyName.Buffer); return NT_SUCCESS(Status) ? SymLinkStatus : Status; }