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.
2629 lines
78 KiB
2629 lines
78 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
netboot.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code to initialize network boot.
|
|
|
|
Author:
|
|
|
|
Chuck Lenzmeier (chuckl) December 27, 1996
|
|
|
|
Environment:
|
|
|
|
Kernel mode, system initialization code
|
|
|
|
Revision History:
|
|
|
|
Colin Watson (colinw) November 1997 Add CSC support
|
|
|
|
--*/
|
|
|
|
#include "iop.h"
|
|
#pragma hdrstop
|
|
|
|
#include <regstrp.h>
|
|
|
|
#include <ntddip.h>
|
|
#include <nbtioctl.h>
|
|
#include <ntddnfs.h>
|
|
#include <ntddbrow.h>
|
|
#include <ntddtcp.h>
|
|
#include <setupblk.h>
|
|
#include <remboot.h>
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma const_seg("INITCONST")
|
|
#endif
|
|
#include <oscpkt.h>
|
|
#include <windef.h>
|
|
#include <tdiinfo.h>
|
|
|
|
#ifndef NT
|
|
#define NT
|
|
#include <ipinfo.h>
|
|
#undef NT
|
|
#else
|
|
#include <ipinfo.h>
|
|
#endif
|
|
|
|
#include <devguid.h>
|
|
|
|
extern BOOLEAN ExpInTextModeSetup;
|
|
|
|
BOOLEAN IopRemoteBootCardInitialized = FALSE;
|
|
|
|
|
|
//
|
|
// TCP/IP definitions
|
|
//
|
|
|
|
#define DEFAULT_DEST 0
|
|
#define DEFAULT_DEST_MASK 0
|
|
#define DEFAULT_METRIC 1
|
|
|
|
NTSTATUS
|
|
IopWriteIpAddressToRegistry(
|
|
HANDLE handle,
|
|
PWCHAR regkey,
|
|
PUCHAR value
|
|
);
|
|
|
|
NTSTATUS
|
|
IopTCPQueryInformationEx(
|
|
IN HANDLE TCPHandle,
|
|
IN TDIObjectID FAR *ID,
|
|
OUT void FAR *Buffer,
|
|
IN OUT DWORD FAR *BufferSize,
|
|
IN OUT BYTE FAR *Context
|
|
);
|
|
|
|
NTSTATUS
|
|
IopTCPSetInformationEx(
|
|
IN HANDLE TCPHandle,
|
|
IN TDIObjectID FAR *ID,
|
|
IN void FAR *Buffer,
|
|
IN DWORD FAR BufferSize
|
|
);
|
|
|
|
NTSTATUS
|
|
IopSetDefaultGateway(
|
|
IN ULONG GatewayAddress
|
|
);
|
|
|
|
NTSTATUS
|
|
IopCacheNetbiosNameForIpAddress(
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock
|
|
);
|
|
|
|
VOID
|
|
IopAssignNetworkDriveLetter (
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock
|
|
);
|
|
|
|
|
|
//
|
|
// The following allows the I/O system's initialization routines to be
|
|
// paged out of memory.
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
__inline long
|
|
htonl(long x);
|
|
#pragma alloc_text(INIT,IopAddRemoteBootValuesToRegistry)
|
|
#pragma alloc_text(INIT,IopStartNetworkForRemoteBoot)
|
|
#pragma alloc_text(INIT,IopStartTcpIpForRemoteBoot)
|
|
#pragma alloc_text(INIT,IopIsRemoteBootCard)
|
|
#pragma alloc_text(INIT,IopSetupRemoteBootCard)
|
|
#pragma alloc_text(INIT,IopAssignNetworkDriveLetter)
|
|
#pragma alloc_text(INIT,IopWriteIpAddressToRegistry)
|
|
#pragma alloc_text(INIT,IopSetDefaultGateway)
|
|
#pragma alloc_text(INIT,htonl)
|
|
#pragma alloc_text(INIT,IopCacheNetbiosNameForIpAddress)
|
|
#pragma alloc_text(INIT,IopTCPQueryInformationEx)
|
|
#pragma alloc_text(INIT,IopTCPSetInformationEx)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
IopAddRemoteBootValuesToRegistry (
|
|
PLOADER_PARAMETER_BLOCK LoaderBlock
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
HANDLE handle;
|
|
HANDLE serviceHandle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING string;
|
|
CHAR addressA[16];
|
|
WCHAR addressW[16];
|
|
STRING addressStringA;
|
|
UNICODE_STRING addressStringW;
|
|
PUCHAR addressPointer;
|
|
PUCHAR p;
|
|
PUCHAR q;
|
|
PUCHAR ntName;
|
|
PWCHAR imagePath;
|
|
STRING ansiString;
|
|
UNICODE_STRING unicodeString;
|
|
UNICODE_STRING dnsNameString;
|
|
UNICODE_STRING netbiosNameString;
|
|
ULONG tmpValue;
|
|
ULONG bufferLength;
|
|
PUCHAR buffer = NULL;
|
|
|
|
//
|
|
// allocate a scratch buffer, making it as large as we'll need.
|
|
//
|
|
bufferLength = sizeof("\\Device\\LanmanRedirector") + strlen(LoaderBlock->NtBootPathName);
|
|
|
|
if (LoaderBlock->SetupLoaderBlock->Flags & SETUPBLK_FLAGS_IS_TEXTMODE) {
|
|
bufferLength = max(
|
|
bufferLength,
|
|
sizeof("\\Device\\LanmanRedirector") + strlen(LoaderBlock->SetupLoaderBlock->MachineDirectoryPath));
|
|
}
|
|
|
|
bufferLength = max(
|
|
bufferLength,
|
|
sizeof(L"system32\\drivers\\") + wcslen(LoaderBlock->SetupLoaderBlock->NetbootCardDriverName)*sizeof(WCHAR));
|
|
|
|
buffer = ExAllocatePoolWithTag( NonPagedPool, bufferLength, 'bRoI' );
|
|
if (buffer == NULL) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to allocate buffer\n"));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
ntName = buffer;
|
|
imagePath = (PWCHAR)buffer;
|
|
|
|
if (LoaderBlock->SetupLoaderBlock->ComputerName[0] != 0) {
|
|
|
|
//
|
|
// Convert the name to a Netbios name.
|
|
//
|
|
|
|
_wcsupr( LoaderBlock->SetupLoaderBlock->ComputerName );
|
|
|
|
RtlInitUnicodeString( &dnsNameString, LoaderBlock->SetupLoaderBlock->ComputerName );
|
|
|
|
status = RtlDnsHostNameToComputerName(
|
|
&netbiosNameString,
|
|
&dnsNameString,
|
|
TRUE); // allocate netbiosNameString
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Failed RtlDnsHostNameToComputerName: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Add a value for the computername.
|
|
//
|
|
|
|
IopWstrToUnicodeString( &string, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName\\ComputerName" );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = NtOpenKey( &handle, KEY_ALL_ACCESS, &objectAttributes );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to open ComputerName key: %x\n", status ));
|
|
RtlFreeUnicodeString( &netbiosNameString );
|
|
goto cleanup;
|
|
}
|
|
|
|
IopWstrToUnicodeString( &string, L"ComputerName" );
|
|
|
|
status = NtSetValueKey(
|
|
handle,
|
|
&string,
|
|
0,
|
|
REG_SZ,
|
|
netbiosNameString.Buffer,
|
|
netbiosNameString.Length + sizeof(WCHAR)
|
|
);
|
|
NtClose( handle );
|
|
RtlFreeUnicodeString( &netbiosNameString );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to set ComputerName value: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Add a value for the host name.
|
|
//
|
|
|
|
IopWstrToUnicodeString( &string, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters" );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = NtOpenKey( &handle, KEY_ALL_ACCESS, &objectAttributes );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to open Tcpip\\Parameters key: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
_wcslwr( LoaderBlock->SetupLoaderBlock->ComputerName );
|
|
|
|
IopWstrToUnicodeString( &string, L"Hostname" );
|
|
|
|
status = NtSetValueKey(
|
|
handle,
|
|
&string,
|
|
0,
|
|
REG_SZ,
|
|
LoaderBlock->SetupLoaderBlock->ComputerName,
|
|
(wcslen(LoaderBlock->SetupLoaderBlock->ComputerName) + 1) * sizeof(WCHAR)
|
|
);
|
|
NtClose( handle );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to set Hostname value: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the UNC path to the system files is supplied then store it in the registry.
|
|
//
|
|
|
|
ASSERT( _stricmp(LoaderBlock->ArcBootDeviceName,"net(0)") == 0 );
|
|
|
|
IopWstrToUnicodeString( &string, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control" );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = NtOpenKey( &handle, KEY_ALL_ACCESS, &objectAttributes );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to open Control key: %x\n", status ));
|
|
goto skiproot;
|
|
}
|
|
|
|
p = strrchr( LoaderBlock->NtBootPathName, '\\' ); // find last separator
|
|
if ( (p != NULL) && (*(p+1) == 0) ) {
|
|
|
|
//
|
|
// NtBootPathName ends with a backslash, so we need to back up
|
|
// to the previous backslash.
|
|
//
|
|
|
|
q = p;
|
|
*q = 0;
|
|
p = strrchr( LoaderBlock->NtBootPathName, '\\' ); // find last separator
|
|
*q = '\\';
|
|
}
|
|
if ( p == NULL ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: malformed NtBootPathName: %s\n", LoaderBlock->NtBootPathName ));
|
|
NtClose( handle );
|
|
goto skiproot;
|
|
}
|
|
*p = 0; // terminate \server\share\images\machine
|
|
|
|
strcpy( ntName, "\\Device\\LanmanRedirector");
|
|
strcat( ntName, LoaderBlock->NtBootPathName ); // append \server\share\images\machine
|
|
*p = '\\';
|
|
|
|
RtlInitAnsiString( &ansiString, ntName );
|
|
|
|
status = RtlAnsiStringToUnicodeString( &unicodeString, &ansiString, TRUE );
|
|
if (!NT_SUCCESS(status)) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to convert RemoteBootRoot value: %x\n", status ));
|
|
} else {
|
|
|
|
IopWstrToUnicodeString( &string, L"RemoteBootRoot" );
|
|
|
|
status = NtSetValueKey(
|
|
handle,
|
|
&string,
|
|
0,
|
|
REG_SZ,
|
|
unicodeString.Buffer,
|
|
unicodeString.Length + sizeof(WCHAR)
|
|
);
|
|
|
|
RtlFreeUnicodeString( &unicodeString );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to set RemoteBootRoot value: %x\n", status ));
|
|
}
|
|
}
|
|
|
|
if ((LoaderBlock->SetupLoaderBlock->Flags & SETUPBLK_FLAGS_IS_TEXTMODE) != 0) {
|
|
|
|
strcpy( ntName, "\\Device\\LanmanRedirector");
|
|
strcat( ntName, LoaderBlock->SetupLoaderBlock->MachineDirectoryPath );
|
|
RtlInitAnsiString( &ansiString, ntName );
|
|
status = RtlAnsiStringToUnicodeString( &unicodeString, &ansiString, TRUE );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to convert RemoteBootMachineDirectory value: %x\n", status ));
|
|
} else {
|
|
|
|
IopWstrToUnicodeString( &string, L"RemoteBootMachineDirectory" );
|
|
|
|
status = NtSetValueKey(
|
|
handle,
|
|
&string,
|
|
0,
|
|
REG_SZ,
|
|
unicodeString.Buffer,
|
|
unicodeString.Length + sizeof(WCHAR)
|
|
);
|
|
|
|
RtlFreeUnicodeString( &unicodeString );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to set RemoteBootMachineDirectory value: %x\n", status ));
|
|
}
|
|
}
|
|
}
|
|
|
|
NtClose( handle );
|
|
|
|
skiproot:
|
|
|
|
//
|
|
// Add registry values for the IP address and subnet mask received
|
|
// from DHCP. These are stored under the Tcpip service key and are
|
|
// read by both Tcpip and Netbt. The adapter name used is the known
|
|
// GUID for the NetbootCard.
|
|
//
|
|
|
|
IopWstrToUnicodeString( &string, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\{54C7D140-09EF-11D1-B25A-F5FE627ED95E}" );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = NtOpenKey( &handle, KEY_ALL_ACCESS, &objectAttributes );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to open Tcpip\\Parameters\\Interfaces\\{54C7D140-09EF-11D1-B25A-F5FE627ED95E} key: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
status = IopWriteIpAddressToRegistry(handle,
|
|
L"DhcpIPAddress",
|
|
(PUCHAR)&(LoaderBlock->SetupLoaderBlock->IpAddress)
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status)) {
|
|
NtClose(handle);
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to write DhcpIPAddress: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
status = IopWriteIpAddressToRegistry(handle,
|
|
L"DhcpSubnetMask",
|
|
(PUCHAR)&(LoaderBlock->SetupLoaderBlock->SubnetMask)
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status)) {
|
|
NtClose(handle);
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to write DhcpSubnetMask: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
status = IopWriteIpAddressToRegistry(handle,
|
|
L"DhcpDefaultGateway",
|
|
(PUCHAR)&(LoaderBlock->SetupLoaderBlock->DefaultRouter)
|
|
);
|
|
|
|
NtClose(handle);
|
|
|
|
if ( !NT_SUCCESS(status)) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to write DhcpDefaultGateway: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Create the service key for the netboot card. We need to have
|
|
// the Type value there or the card won't be initialized.
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx(&handle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetServices,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to open CurrentControlSet\\Services: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString(&string, LoaderBlock->SetupLoaderBlock->NetbootCardServiceName);
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
handle,
|
|
(PSECURITY_DESCRIPTOR)NULL
|
|
);
|
|
|
|
status = ZwCreateKey(&serviceHandle,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes,
|
|
0,
|
|
(PUNICODE_STRING)NULL,
|
|
0,
|
|
&tmpValue // disposition
|
|
);
|
|
|
|
ZwClose(handle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to open/create netboot card service key: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Store the image path.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&string, L"ImagePath");
|
|
wcscpy(imagePath, L"system32\\drivers\\");
|
|
wcscat(imagePath, LoaderBlock->SetupLoaderBlock->NetbootCardDriverName);
|
|
|
|
status = ZwSetValueKey(serviceHandle,
|
|
&string,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
imagePath,
|
|
(wcslen(imagePath) + 1) * sizeof(WCHAR)
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
NtClose(serviceHandle);
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to write ImagePath: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Store the type.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&string, L"Type");
|
|
tmpValue = 1;
|
|
|
|
status = ZwSetValueKey(serviceHandle,
|
|
&string,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
|
|
NtClose(serviceHandle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
KdPrint(( "IopAddRemoteBootValuesToRegistry: Unable to write Type: %x\n", status ));
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (buffer) {
|
|
ExFreePool( buffer );
|
|
buffer = NULL;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopStartNetworkForRemoteBoot (
|
|
PLOADER_PARAMETER_BLOCK LoaderBlock
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE dgHandle;
|
|
HANDLE keyHandle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
UNICODE_STRING string;
|
|
UNICODE_STRING computerName;
|
|
UNICODE_STRING domainName;
|
|
PUCHAR buffer;
|
|
ULONG bufferLength;
|
|
PLMR_REQUEST_PACKET rrp;
|
|
PLMDR_REQUEST_PACKET drrp;
|
|
WKSTA_INFO_502 wkstaConfig;
|
|
WKSTA_TRANSPORT_INFO_0 wkstaTransportInfo;
|
|
LARGE_INTEGER interval;
|
|
ULONG length;
|
|
PKEY_VALUE_PARTIAL_INFORMATION keyValue;
|
|
BOOLEAN startDatagramReceiver;
|
|
ULONG enumerateAttempts;
|
|
HANDLE RdrHandle;
|
|
HANDLE eventHandle;
|
|
|
|
//
|
|
// Initialize for cleanup.
|
|
//
|
|
|
|
buffer = NULL;
|
|
computerName.Buffer = NULL;
|
|
domainName.Buffer = NULL;
|
|
dgHandle = NULL;
|
|
RdrHandle = NULL;
|
|
eventHandle = NULL;
|
|
|
|
//
|
|
// Allocate a temporary buffer. It has to be big enough for all the
|
|
// various FSCTLs we send down.
|
|
//
|
|
|
|
bufferLength = max(sizeof(LMR_REQUEST_PACKET) + (MAX_PATH + 1) * sizeof(WCHAR) +
|
|
(DNLEN + 1) * sizeof(WCHAR),
|
|
max(sizeof(LMDR_REQUEST_PACKET),
|
|
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + MAX_PATH));
|
|
bufferLength = max(bufferLength, sizeof(LMMR_RI_INITIALIZE_SECRET));
|
|
|
|
buffer = ExAllocatePoolWithTag( NonPagedPool, bufferLength, 'bRoI' );
|
|
if (buffer == NULL) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to allocate buffer\n"));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
rrp = (PLMR_REQUEST_PACKET)buffer;
|
|
drrp = (PLMDR_REQUEST_PACKET)buffer;
|
|
|
|
//
|
|
// Open the redirector and the datagram receiver.
|
|
//
|
|
|
|
IopWstrToUnicodeString( &string, L"\\Device\\LanmanRedirector" );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = NtCreateFile(
|
|
&RdrHandle,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to open redirector: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
IopWstrToUnicodeString( &string, DD_BROWSER_DEVICE_NAME_U );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = NtCreateFile(
|
|
&dgHandle,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to open datagram receiver: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
status = NtCreateEvent(
|
|
&eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to createevent redirector: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// If the setup loader block has a disk secret in it provided by the
|
|
// loader, pass this down to the redirector (do this before sending
|
|
// the LMR_START, since that uses this information).
|
|
//
|
|
|
|
{
|
|
PLMMR_RI_INITIALIZE_SECRET RbInit = (PLMMR_RI_INITIALIZE_SECRET)buffer;
|
|
|
|
ASSERT(LoaderBlock->SetupLoaderBlock->NetBootSecret != NULL);
|
|
RtlCopyMemory(
|
|
&RbInit->Secret,
|
|
LoaderBlock->SetupLoaderBlock->NetBootSecret,
|
|
sizeof(RI_SECRET));
|
|
|
|
status = NtFsControlFile(
|
|
RdrHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
FSCTL_LMMR_RI_INITIALIZE_SECRET,
|
|
buffer,
|
|
sizeof(LMMR_RI_INITIALIZE_SECRET),
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
NtWaitForSingleObject(
|
|
eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
NtClearEvent( eventHandle );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to FSCTL(RB initialize) redirector: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Read the computer name and domain name from the registry so we
|
|
// can give them to the datagram receiver. During textmode setup
|
|
// the domain name will not be there, so we won't start the datagram
|
|
// receiver, which is fine.
|
|
//
|
|
IopWstrToUnicodeString( &string, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName\\ComputerName" );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = NtOpenKey( &keyHandle, KEY_ALL_ACCESS, &objectAttributes );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to open ComputerName key: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
IopWstrToUnicodeString( &string, L"ComputerName" );
|
|
|
|
keyValue = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
|
|
RtlZeroMemory(buffer, bufferLength);
|
|
|
|
status = NtQueryValueKey(
|
|
keyHandle,
|
|
&string,
|
|
KeyValuePartialInformation,
|
|
keyValue,
|
|
bufferLength,
|
|
&length);
|
|
|
|
NtClose( keyHandle );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to query ComputerName value: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !RtlCreateUnicodeString(&computerName, (PWSTR)keyValue->Data) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to create ComputerName string\n" ));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
domainName.Length = 0;
|
|
|
|
IopWstrToUnicodeString( &string, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName\\DomainName" );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = NtOpenKey( &keyHandle, KEY_ALL_ACCESS, &objectAttributes );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to open DomainName key: %x\n", status ));
|
|
startDatagramReceiver = FALSE;
|
|
} else {
|
|
|
|
IopWstrToUnicodeString( &string, L"DomainName" );
|
|
|
|
keyValue = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
|
|
RtlZeroMemory(buffer, bufferLength);
|
|
|
|
status = NtQueryValueKey(
|
|
keyHandle,
|
|
&string,
|
|
KeyValuePartialInformation,
|
|
keyValue,
|
|
bufferLength,
|
|
&length);
|
|
|
|
NtClose( keyHandle );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to query Domain value: %x\n", status ));
|
|
startDatagramReceiver = FALSE;
|
|
} else {
|
|
if ( !RtlCreateUnicodeString(&domainName, (PWSTR)keyValue->Data) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to create DomainName string\n" ));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
startDatagramReceiver = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tell the redir to start.
|
|
//
|
|
|
|
rrp->Type = ConfigInformation;
|
|
rrp->Version = REQUEST_PACKET_VERSION;
|
|
|
|
rrp->Parameters.Start.RedirectorNameLength = computerName.Length;
|
|
RtlCopyMemory(rrp->Parameters.Start.RedirectorName,
|
|
computerName.Buffer,
|
|
computerName.Length);
|
|
|
|
rrp->Parameters.Start.DomainNameLength = domainName.Length;
|
|
RtlCopyMemory(((PUCHAR)rrp->Parameters.Start.RedirectorName) + computerName.Length,
|
|
domainName.Buffer,
|
|
domainName.Length);
|
|
|
|
RtlFreeUnicodeString(&computerName);
|
|
RtlFreeUnicodeString(&domainName);
|
|
|
|
wkstaConfig.wki502_char_wait = 3600;
|
|
wkstaConfig.wki502_maximum_collection_count = 16;
|
|
wkstaConfig.wki502_collection_time = 250;
|
|
wkstaConfig.wki502_keep_conn = 600;
|
|
wkstaConfig.wki502_max_cmds = 5;
|
|
wkstaConfig.wki502_sess_timeout = 45;
|
|
wkstaConfig.wki502_siz_char_buf = 512;
|
|
wkstaConfig.wki502_max_threads = 17;
|
|
wkstaConfig.wki502_lock_quota = 6144;
|
|
wkstaConfig.wki502_lock_increment = 10;
|
|
wkstaConfig.wki502_lock_maximum = 500;
|
|
wkstaConfig.wki502_pipe_increment = 10;
|
|
wkstaConfig.wki502_pipe_maximum = 500;
|
|
wkstaConfig.wki502_cache_file_timeout = 40;
|
|
wkstaConfig.wki502_dormant_file_limit = 45;
|
|
wkstaConfig.wki502_read_ahead_throughput = MAXULONG;
|
|
wkstaConfig.wki502_num_mailslot_buffers = 3;
|
|
wkstaConfig.wki502_num_srv_announce_buffers = 20;
|
|
wkstaConfig.wki502_max_illegal_datagram_events = 5;
|
|
wkstaConfig.wki502_illegal_datagram_event_reset_frequency = 60;
|
|
wkstaConfig.wki502_log_election_packets = FALSE;
|
|
wkstaConfig.wki502_use_opportunistic_locking = TRUE;
|
|
wkstaConfig.wki502_use_unlock_behind = TRUE;
|
|
wkstaConfig.wki502_use_close_behind = TRUE;
|
|
wkstaConfig.wki502_buf_named_pipes = TRUE;
|
|
wkstaConfig.wki502_use_lock_read_unlock = TRUE;
|
|
wkstaConfig.wki502_utilize_nt_caching = TRUE;
|
|
wkstaConfig.wki502_use_raw_read = TRUE;
|
|
wkstaConfig.wki502_use_raw_write = TRUE;
|
|
wkstaConfig.wki502_use_write_raw_data = TRUE;
|
|
wkstaConfig.wki502_use_encryption = TRUE;
|
|
wkstaConfig.wki502_buf_files_deny_write = TRUE;
|
|
wkstaConfig.wki502_buf_read_only_files = TRUE;
|
|
wkstaConfig.wki502_force_core_create_mode = TRUE;
|
|
wkstaConfig.wki502_use_512_byte_max_transfer = FALSE;
|
|
|
|
status = NtFsControlFile(
|
|
RdrHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
FSCTL_LMR_START | 0x80000000,
|
|
rrp,
|
|
sizeof(LMR_REQUEST_PACKET) +
|
|
rrp->Parameters.Start.RedirectorNameLength +
|
|
rrp->Parameters.Start.DomainNameLength,
|
|
&wkstaConfig,
|
|
sizeof(wkstaConfig)
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
NtWaitForSingleObject(
|
|
eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to FSCTL(start) redirector: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
NtClearEvent( eventHandle );
|
|
|
|
if (startDatagramReceiver) {
|
|
|
|
//
|
|
// Tell the datagram receiver to start.
|
|
//
|
|
|
|
drrp->Version = LMDR_REQUEST_PACKET_VERSION;
|
|
|
|
drrp->Parameters.Start.NumberOfMailslotBuffers = 16;
|
|
drrp->Parameters.Start.NumberOfServerAnnounceBuffers = 20;
|
|
drrp->Parameters.Start.IllegalDatagramThreshold = 5;
|
|
drrp->Parameters.Start.EventLogResetFrequency = 60;
|
|
drrp->Parameters.Start.LogElectionPackets = FALSE;
|
|
|
|
drrp->Parameters.Start.IsLanManNt = FALSE;
|
|
|
|
status = NtDeviceIoControlFile(
|
|
dgHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_LMDR_START,
|
|
drrp,
|
|
sizeof(LMDR_REQUEST_PACKET),
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
NtWaitForSingleObject(
|
|
eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
NtClearEvent( eventHandle );
|
|
|
|
NtClose( dgHandle );
|
|
dgHandle = NULL;
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to IOCTL(start) datagram receiver: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
} else {
|
|
|
|
NtClose( dgHandle );
|
|
dgHandle = NULL;
|
|
|
|
//
|
|
// Tell the redir to bind to the transports.
|
|
//
|
|
// Note: In the current redirector implementation, this call just
|
|
// tells the redirector to register for TDI PnP notifications.
|
|
// Starting the datagram receiver also does this, so we only issue
|
|
// this FSCTL if we're not starting the datagram receiver.
|
|
//
|
|
|
|
status = NtFsControlFile(
|
|
RdrHandle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
FSCTL_LMR_BIND_TO_TRANSPORT | 0x80000000,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
NtWaitForSingleObject(
|
|
eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
NtClearEvent( eventHandle );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Unable to FSCTL(bind) redirector: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
{
|
|
|
|
//
|
|
// Loop until the redirector is bound to the transport. It may take a
|
|
// while because TDI defers notification of binding to a worker thread.
|
|
// We start with a half a second wait and double it each time, trying
|
|
// five times total.
|
|
//
|
|
|
|
interval.QuadPart = -500 * 1000 * 10; // 1/2 second, relative
|
|
enumerateAttempts = 0;
|
|
|
|
while (TRUE) {
|
|
|
|
KeDelayExecutionThread(KernelMode, FALSE, &interval);
|
|
|
|
RtlZeroMemory(rrp, sizeof(LMR_REQUEST_PACKET));
|
|
|
|
rrp->Type = EnumerateTransports;
|
|
rrp->Version = REQUEST_PACKET_VERSION;
|
|
|
|
status = NtFsControlFile(
|
|
RdrHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
FSCTL_LMR_ENUMERATE_TRANSPORTS,
|
|
rrp,
|
|
sizeof(LMR_REQUEST_PACKET),
|
|
&wkstaTransportInfo,
|
|
sizeof(wkstaTransportInfo)
|
|
);
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
if ( !NT_SUCCESS(status) ) {
|
|
//KdPrint(( "IopStartNetworkForRemoteBoot: Unable to FSCTL(enumerate) redirector: %x\n", status ));
|
|
} else if (rrp->Parameters.Get.TotalBytesNeeded == 0) {
|
|
//KdPrint(( "IopStartNetworkForRemoteBoot: FSCTL(enumerate) returned 0 entries\n" ));
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
++enumerateAttempts;
|
|
|
|
if (enumerateAttempts == 5) {
|
|
KdPrint(( "IopStartNetworkForRemoteBoot: Redirector didn't start\n" ));
|
|
status = STATUS_REDIRECTOR_NOT_STARTED;
|
|
goto cleanup;
|
|
}
|
|
|
|
interval.QuadPart *= 2;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Prime the transport.
|
|
//
|
|
IopSetDefaultGateway(LoaderBlock->SetupLoaderBlock->DefaultRouter);
|
|
IopCacheNetbiosNameForIpAddress(LoaderBlock);
|
|
|
|
IopAssignNetworkDriveLetter(LoaderBlock);
|
|
|
|
cleanup:
|
|
|
|
RtlFreeUnicodeString( &computerName );
|
|
RtlFreeUnicodeString( &domainName );
|
|
if ( buffer != NULL ) {
|
|
ExFreePool( buffer );
|
|
}
|
|
|
|
if ( dgHandle != NULL ) {
|
|
NtClose( dgHandle );
|
|
}
|
|
|
|
if (eventHandle) {
|
|
NtClose( eventHandle );
|
|
}
|
|
|
|
if (RdrHandle) {
|
|
NtClose( RdrHandle );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
IopAssignNetworkDriveLetter (
|
|
PLOADER_PARAMETER_BLOCK LoaderBlock
|
|
)
|
|
{
|
|
PUCHAR p;
|
|
PUCHAR q;
|
|
PUCHAR ntName;
|
|
STRING ansiString;
|
|
UNICODE_STRING unicodeString;
|
|
UNICODE_STRING unicodeString2;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Create the symbolic link of X: to the redirector. We do this
|
|
// after the redirector has loaded, but before AssignDriveLetters
|
|
// is called the first time in textmode setup (once that has
|
|
// happened, the drive letters will stick).
|
|
//
|
|
// Note that we use X: for the textmode setup phase of a remote
|
|
// installation. But for a true remote boot, we use C:.
|
|
//
|
|
|
|
if ((LoaderBlock->SetupLoaderBlock->Flags & (SETUPBLK_FLAGS_REMOTE_INSTALL |
|
|
SETUPBLK_FLAGS_SYSPREP_INSTALL)) != 0) {
|
|
IopWstrToUnicodeString( &unicodeString2, L"\\DosDevices\\X:");
|
|
} else {
|
|
IopWstrToUnicodeString( &unicodeString2, L"\\DosDevices\\C:");
|
|
}
|
|
|
|
//
|
|
// If this is a remote boot setup boot, NtBootPathName is of the
|
|
// form \<server>\<share>\setup\<install-directory>\<platform>.
|
|
// We want the root of the X: drive to be the root of the install
|
|
// directory.
|
|
//
|
|
// If this is a normal remote boot, NtBootPathName is of the form
|
|
// \<server>\<share>\images\<machine>\winnt. We want the root of
|
|
// the X: drive to be the root of the machine directory.
|
|
//
|
|
// Thus in either case, we need to remove the last element of the
|
|
// path.
|
|
//
|
|
|
|
p = strrchr( LoaderBlock->NtBootPathName, '\\' ); // find last separator
|
|
if ( (p != NULL) && (*(p+1) == 0) ) {
|
|
|
|
//
|
|
// NtBootPathName ends with a backslash, so we need to back up
|
|
// to the previous backslash.
|
|
//
|
|
|
|
q = p;
|
|
*q = 0;
|
|
p = strrchr( LoaderBlock->NtBootPathName, '\\' ); // find last separator
|
|
*q = '\\';
|
|
}
|
|
if ( p == NULL ) {
|
|
KdPrint(( "IopAssignNetworkDriveLetter: malformed NtBootPathName: %s\n", LoaderBlock->NtBootPathName ));
|
|
KeBugCheck( ASSIGN_DRIVE_LETTERS_FAILED );
|
|
}
|
|
*p = 0; // terminate \server\share\images\machine
|
|
|
|
ntName = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof("\\Device\\LanmanRedirector") + strlen(LoaderBlock->NtBootPathName),
|
|
'bRoI' );
|
|
|
|
if (!ntName) {
|
|
KdPrint(( "IopAssignNetworkDriveLetter: unable to alloc memory" ));
|
|
KeBugCheck( ASSIGN_DRIVE_LETTERS_FAILED );
|
|
}
|
|
|
|
strcpy( ntName, "\\Device\\LanmanRedirector");
|
|
strcat( ntName, LoaderBlock->NtBootPathName ); // append \server\share\images\machine
|
|
|
|
RtlInitAnsiString( &ansiString, ntName );
|
|
|
|
status = RtlAnsiStringToUnicodeString( &unicodeString, &ansiString, TRUE );
|
|
if (!NT_SUCCESS(status)) {
|
|
KdPrint(( "IopAssignNetworkDriveLetter: unable to convert DOS link for redirected boot drive: %x\n", status ));
|
|
KeBugCheck( ASSIGN_DRIVE_LETTERS_FAILED );
|
|
}
|
|
|
|
status = IoCreateSymbolicLink(&unicodeString2, &unicodeString);
|
|
if (!NT_SUCCESS(status)) {
|
|
KdPrint(( "IopAssignNetworkDriveLetter: unable to create DOS link for redirected boot drive: %x\n", status ));
|
|
KeBugCheck( ASSIGN_DRIVE_LETTERS_FAILED );
|
|
}
|
|
// DbgPrint("IopAssignNetworkDriveLetter: assigned %wZ to %wZ\n", &unicodeString2, &unicodeString);
|
|
|
|
RtlFreeUnicodeString( &unicodeString );
|
|
|
|
*p = '\\'; // restore string
|
|
|
|
ExFreePool( ntName );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IopStartTcpIpForRemoteBoot (
|
|
PLOADER_PARAMETER_BLOCK LoaderBlock
|
|
)
|
|
{
|
|
UNICODE_STRING IpString;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
HANDLE handle = NULL;
|
|
HANDLE eventHandle = NULL;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
IP_SET_ADDRESS_REQUEST IpRequest;
|
|
|
|
RtlInitUnicodeString( &IpString, DD_IP_DEVICE_NAME );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&IpString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
RtlZeroMemory(&IpRequest, sizeof(IpRequest));
|
|
IpRequest.Context = (USHORT)2;
|
|
IpRequest.Address = LoaderBlock->SetupLoaderBlock->IpAddress;
|
|
IpRequest.SubnetMask = LoaderBlock->SetupLoaderBlock->SubnetMask;
|
|
|
|
status = NtCreateFile(
|
|
&handle,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartTcpIpForRemoteBoot: Unable to open IP: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
status = NtCreateEvent(
|
|
&eventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
if ( !NT_SUCCESS(status)) {
|
|
KdPrint(( "IopStartTcpIpForRemoteBoot: Unable to create event: %x\n", status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
status = NtDeviceIoControlFile(
|
|
handle,
|
|
eventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_IP_SET_ADDRESS,
|
|
&IpRequest,
|
|
sizeof(IP_SET_ADDRESS_REQUEST),
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if ( status == STATUS_PENDING ) {
|
|
NtWaitForSingleObject(
|
|
eventHandle,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopStartTcpIpForRemoteBoot: Unable to IOCTL IP: %x\n", status ));
|
|
|
|
//
|
|
// if we got a duplicate name error this means that there is another
|
|
// network node with the same address as us. This is a fatal
|
|
// error, and we bugcheck with the ip address as a parameter so that
|
|
// the network administrator can determine how they assigned duplicate
|
|
// addresses on the network.
|
|
//
|
|
if (status == STATUS_DUPLICATE_NAME) {
|
|
KeBugCheckEx(
|
|
NETWORK_BOOT_DUPLICATE_ADDRESS,
|
|
LoaderBlock->SetupLoaderBlock->IpAddress,
|
|
0,
|
|
0,
|
|
0 );
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (eventHandle != NULL) {
|
|
NtClose( handle );
|
|
}
|
|
|
|
if (handle != NULL) {
|
|
NtClose( handle );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
IopIsRemoteBootCard(
|
|
IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements,
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
|
IN PWCHAR HwIds
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function determines if the card described by the hwIds is the
|
|
remote boot network card. It checks against the hardware ID for the
|
|
card that is stored in the setup loader block.
|
|
|
|
THIS ASSUMES THAT IOREMOTEBOOTCLIENT IS TRUE AND THAT LOADERBLOCK
|
|
IS VALID.
|
|
|
|
Arguments:
|
|
|
|
DeviceNode - Device node for the card in question.
|
|
|
|
LoaderBlock - Supplies a pointer to the loader parameter block that was
|
|
created by the OS Loader.
|
|
|
|
HwIds - The hardware IDs for the device in question.
|
|
|
|
Return Value:
|
|
|
|
TRUE or FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSETUP_LOADER_BLOCK setupLoaderBlock;
|
|
PWCHAR curHwId;
|
|
|
|
//
|
|
// setupLoaderBlock will always be non-NULL if we are
|
|
// remote booting, even if we are not in setup.
|
|
//
|
|
|
|
setupLoaderBlock = LoaderBlock->SetupLoaderBlock;
|
|
|
|
//
|
|
// Scan through the HwIds for a match.
|
|
//
|
|
|
|
curHwId = HwIds;
|
|
|
|
while (*curHwId != L'\0') {
|
|
|
|
#if defined (_IA64_)
|
|
|
|
//
|
|
// On IA64, PXE may not have told the BINL service enough details about
|
|
// the NIC hardware, so the PNP ID may not be exact. We should test against
|
|
// the PNP ID of the that BINL gave setupldr and then to us.
|
|
//
|
|
if (_wcsnicmp(curHwId, setupLoaderBlock->NetbootCardHardwareId, wcslen(setupLoaderBlock->NetbootCardHardwareId)) == 0) {
|
|
#else
|
|
if (wcscmp(curHwId, setupLoaderBlock->NetbootCardHardwareId) == 0) {
|
|
#endif
|
|
|
|
ULONG BusNumber, DeviceNumber, FunctionNumber;
|
|
|
|
//
|
|
// PCI's encoding is this: fff ddddd
|
|
// PXE's encoding is this: ddddd fff
|
|
//
|
|
|
|
BusNumber = (ULONG)((((PNET_CARD_INFO)setupLoaderBlock->NetbootCardInfo)->pci.BusDevFunc) >> 8);
|
|
DeviceNumber = (ULONG)(((((PNET_CARD_INFO)setupLoaderBlock->NetbootCardInfo)->pci.BusDevFunc) & 0xf8) >> 3);
|
|
FunctionNumber = (ULONG)(((((PNET_CARD_INFO)setupLoaderBlock->NetbootCardInfo)->pci.BusDevFunc) & 0x3));
|
|
|
|
KdPrint(("IopIsRemoteBootCard: FOUND %ws\n", setupLoaderBlock->NetbootCardHardwareId));
|
|
if ((ResourceRequirements->BusNumber != BusNumber) ||
|
|
((ResourceRequirements->SlotNumber & 0x1f) != DeviceNumber) ||
|
|
(((ResourceRequirements->SlotNumber >> 5) & 0x3) != FunctionNumber)) {
|
|
KdPrint(("IopIsRemoteBootCard: ignoring non-matching card:\n"));
|
|
KdPrint((" devnode bus %d, busdevfunc bus %d\n",
|
|
ResourceRequirements->BusNumber,
|
|
BusNumber));
|
|
KdPrint((" devnode slot %d (%d %d), busdevfunc slot %d (%d %d)\n",
|
|
ResourceRequirements->SlotNumber,
|
|
(ResourceRequirements->SlotNumber & 0x1f),
|
|
((ResourceRequirements->SlotNumber >> 5) & 0x3),
|
|
(ULONG)(((PNET_CARD_INFO)setupLoaderBlock->NetbootCardInfo)->pci.BusDevFunc),
|
|
DeviceNumber,
|
|
FunctionNumber));
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
curHwId += (wcslen(curHwId) + 1);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopSetupRemoteBootCard(
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
|
IN HANDLE UniqueIdHandle,
|
|
IN PUNICODE_STRING UnicodeDeviceInstance
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function modifies the registry to set up the netboot card.
|
|
We must do this here since the card is needed to boot, we can't
|
|
wait for the class installer to run.
|
|
|
|
THIS ASSUMES THAT IOREMOTEBOOTCLIENT IS TRUE.
|
|
|
|
Arguments:
|
|
|
|
LoaderBlock - Supplies a pointer to the loader parameter block that was
|
|
created by the OS Loader.
|
|
|
|
UniqueIdHandle - A handle to the device's unique node under the
|
|
Enum key.
|
|
|
|
UnicodeDeviceInstance - The device instance assigned to the device.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSETUP_LOADER_BLOCK setupLoaderBlock;
|
|
UNICODE_STRING unicodeName, pnpInstanceId, keyName;
|
|
HANDLE tmpHandle;
|
|
HANDLE parametersHandle = NULL;
|
|
HANDLE currentControlSetHandle = NULL;
|
|
HANDLE remoteBootHandle = NULL;
|
|
HANDLE instanceHandle = NULL;
|
|
PWCHAR componentIdBuffer, curComponentIdLoc;
|
|
PCHAR registryList;
|
|
ULONG componentIdLength;
|
|
WCHAR tempNameBuffer[32];
|
|
WCHAR tempValueBuffer[128];
|
|
NTSTATUS status;
|
|
ULONG tmpValue, length;
|
|
PKEY_VALUE_PARTIAL_INFORMATION keyValue;
|
|
PKEY_VALUE_BASIC_INFORMATION keyValueBasic;
|
|
UCHAR dataBuffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + 128];
|
|
ULONG enumerateIndex;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
ULONG disposition;
|
|
|
|
//
|
|
// If we already think we have initialized a remote boot card, then
|
|
// exit (should not really happen once we identify cards using the
|
|
// bus/slot.
|
|
//
|
|
|
|
if (IopRemoteBootCardInitialized) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// setupLoaderBlock will always be non-NULL if we are
|
|
// remote booting, even if we are not in setup.
|
|
//
|
|
|
|
setupLoaderBlock = LoaderBlock->SetupLoaderBlock;
|
|
|
|
//
|
|
// Open the current control set.
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx(¤tControlSetHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSet,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the Control\RemoteBoot key, which may not exist.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"Control\\RemoteBoot");
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
currentControlSetHandle,
|
|
(PSECURITY_DESCRIPTOR)NULL
|
|
);
|
|
|
|
status = ZwCreateKey(&remoteBootHandle,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes,
|
|
0,
|
|
(PUNICODE_STRING)NULL,
|
|
0,
|
|
&disposition
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the key where the netui code stores information about the cards.
|
|
// During textmode setup this will fail because the Control\Network
|
|
// key is not there. After that it should work, although we may need
|
|
// to create the last node in the path.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\{54C7D140-09EF-11D1-B25A-F5FE627ED95E}");
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
currentControlSetHandle,
|
|
(PSECURITY_DESCRIPTOR)NULL
|
|
);
|
|
|
|
status = ZwCreateKey(&instanceHandle,
|
|
KEY_ALL_ACCESS,
|
|
&objectAttributes,
|
|
0,
|
|
(PUNICODE_STRING)NULL,
|
|
0,
|
|
&disposition
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// If the PnpInstanceID of the first netboot card matches the one
|
|
// for this device node, and the NET_CARD_INFO that the loader
|
|
// found is the same as the one we saved, then this is the same
|
|
// card with the same instance ID as before, so we don't need to
|
|
// do anything.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"PnPInstanceID");
|
|
keyValue = (PKEY_VALUE_PARTIAL_INFORMATION)dataBuffer;
|
|
RtlZeroMemory(dataBuffer, sizeof(dataBuffer));
|
|
|
|
status = ZwQueryValueKey(
|
|
instanceHandle,
|
|
&unicodeName,
|
|
KeyValuePartialInformation,
|
|
keyValue,
|
|
sizeof(dataBuffer),
|
|
&length);
|
|
|
|
//
|
|
// Check that it matches. We can init the string because we zeroed
|
|
// the dataBuffer before reading the key, so even if the
|
|
// registry value had no NULL at the end that is OK.
|
|
//
|
|
|
|
if ((NT_SUCCESS(status)) &&
|
|
(keyValue->Type == REG_SZ)) {
|
|
|
|
RtlInitUnicodeString(&pnpInstanceId, (PWSTR)(keyValue->Data));
|
|
|
|
if (RtlEqualUnicodeString(UnicodeDeviceInstance, &pnpInstanceId, TRUE)) {
|
|
|
|
//
|
|
// Instance ID matched, see if the NET_CARD_INFO matches.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"NetCardInfo");
|
|
RtlZeroMemory(dataBuffer, sizeof(dataBuffer));
|
|
|
|
status = ZwQueryValueKey(
|
|
remoteBootHandle,
|
|
&unicodeName,
|
|
KeyValuePartialInformation,
|
|
keyValue,
|
|
sizeof(dataBuffer),
|
|
&length);
|
|
|
|
if ((NT_SUCCESS(status)) &&
|
|
(keyValue->Type == REG_BINARY) &&
|
|
(keyValue->DataLength == sizeof(NET_CARD_INFO)) &&
|
|
(memcmp(keyValue->Data, setupLoaderBlock->NetbootCardInfo, sizeof(NET_CARD_INFO)) == 0)) {
|
|
|
|
//
|
|
// Everything matched, so no need to do any setup.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto cleanup;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// We come through here if the saved registry data was missing or
|
|
// not correct. Write all the relevant values to the registry.
|
|
//
|
|
|
|
|
|
//
|
|
// Service name is in the loader block.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, REGSTR_VALUE_SERVICE);
|
|
status = ZwSetValueKey(UniqueIdHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
setupLoaderBlock->NetbootCardServiceName,
|
|
(wcslen(setupLoaderBlock->NetbootCardServiceName) + 1) * sizeof(WCHAR)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// ClassGUID is the known net card GUID.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, REGSTR_VALUE_CLASSGUID);
|
|
status = ZwSetValueKey(UniqueIdHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
L"{4D36E972-E325-11CE-BFC1-08002BE10318}",
|
|
sizeof(L"{4D36E972-E325-11CE-BFC1-08002BE10318}")
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Driver is the first net card.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, REGSTR_VALUE_DRIVER);
|
|
status = ZwSetValueKey(UniqueIdHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
L"{4D36E972-E325-11CE-BFC1-08002BE10318}\\0000",
|
|
sizeof(L"{4D36E972-E325-11CE-BFC1-08002BE10318}\\0000")
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Open a handle for card parameters. We write RemoteBootCard plus
|
|
// whatever the BINL server told us to write.
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx(&tmpHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetControlClass,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"{4D36E972-E325-11CE-BFC1-08002BE10318}\\0000");
|
|
|
|
status = IopOpenRegistryKeyEx(¶metersHandle,
|
|
tmpHandle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
|
|
ZwClose(tmpHandle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// We know that this is a different NIC, so remove all the old parameters.
|
|
//
|
|
|
|
keyValueBasic = (PKEY_VALUE_BASIC_INFORMATION)dataBuffer;
|
|
enumerateIndex = 0;
|
|
|
|
while (TRUE) {
|
|
|
|
RtlZeroMemory(dataBuffer, sizeof(dataBuffer));
|
|
|
|
status = ZwEnumerateValueKey(
|
|
parametersHandle,
|
|
enumerateIndex,
|
|
KeyValueBasicInformation,
|
|
keyValueBasic,
|
|
sizeof(dataBuffer),
|
|
&length
|
|
);
|
|
if (status == STATUS_NO_MORE_ENTRIES) {
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// We don't delete "NetCfgInstanceID", it won't change and
|
|
// its presence signifies to the net class installer that
|
|
// this is a replacement not a clean install.
|
|
//
|
|
|
|
if (_wcsicmp(keyValueBasic->Name, L"NetCfgInstanceID") != 0) {
|
|
|
|
RtlInitUnicodeString(&keyName, keyValueBasic->Name);
|
|
status = ZwDeleteValueKey(
|
|
parametersHandle,
|
|
&keyName
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
} else {
|
|
|
|
enumerateIndex = 1; // leave NetCfgInstanceID at index 0
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Write a parameter called RemoteBootCard set to TRUE, this
|
|
// is primarily so NDIS can recognize this as such.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"RemoteBootCard");
|
|
tmpValue = 1;
|
|
status = ZwSetValueKey(parametersHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Store any other parameters sent from the server.
|
|
//
|
|
|
|
registryList = setupLoaderBlock->NetbootCardRegistry;
|
|
|
|
if (registryList != NULL) {
|
|
|
|
STRING aString;
|
|
UNICODE_STRING uString, uString2;
|
|
|
|
//
|
|
// The registry list is a series of name\0type\0value\0, with
|
|
// a final \0 at the end. It is in ANSI, not UNICODE.
|
|
//
|
|
// All values are stored under parametersHandle. Type is 1 for
|
|
// DWORD and 2 for SZ.
|
|
//
|
|
|
|
uString.Buffer = tempNameBuffer;
|
|
uString.MaximumLength = sizeof(tempNameBuffer);
|
|
|
|
while (*registryList != '\0') {
|
|
|
|
//
|
|
// First the name.
|
|
//
|
|
|
|
RtlInitString(&aString, registryList);
|
|
status = RtlAnsiStringToUnicodeString(&uString, &aString, FALSE);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Now the type.
|
|
//
|
|
|
|
registryList += (strlen(registryList) + 1);
|
|
|
|
if (*registryList == '1') {
|
|
|
|
//
|
|
// A DWORD, parse it.
|
|
//
|
|
|
|
registryList += 2; // skip "1\0"
|
|
tmpValue = 0;
|
|
|
|
while (*registryList != '\0') {
|
|
tmpValue = (tmpValue * 10) + (*registryList - '0');
|
|
++registryList;
|
|
}
|
|
|
|
status = ZwSetValueKey(parametersHandle,
|
|
&uString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
registryList += (strlen(registryList) + 1);
|
|
|
|
} else if (*registryList == '2') {
|
|
|
|
//
|
|
// An SZ, convert to Unicode.
|
|
//
|
|
|
|
registryList += 2; // skip "2\0"
|
|
|
|
uString2.Buffer = tempValueBuffer;
|
|
uString2.MaximumLength = sizeof(tempValueBuffer);
|
|
RtlInitAnsiString(&aString, registryList);
|
|
status = RtlAnsiStringToUnicodeString(&uString2, &aString, FALSE);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
status = ZwSetValueKey(parametersHandle,
|
|
&uString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
uString2.Buffer,
|
|
uString2.Length + sizeof(WCHAR)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
registryList += (strlen(registryList) + 1);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Not "1" or "2", so stop processing registryList.
|
|
//
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Save the NET_CARD_INFO so we can check it next time.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"NetCardInfo");
|
|
|
|
status = ZwSetValueKey(remoteBootHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_BINARY,
|
|
setupLoaderBlock->NetbootCardInfo,
|
|
sizeof(NET_CARD_INFO)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Save the hardware ID, driver name, and service name,
|
|
// so the loader can read those if the server is down
|
|
// on subsequent boots.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"HardwareId");
|
|
status = ZwSetValueKey(remoteBootHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
setupLoaderBlock->NetbootCardHardwareId,
|
|
(wcslen(setupLoaderBlock->NetbootCardHardwareId) + 1) * sizeof(WCHAR)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"DriverName");
|
|
status = ZwSetValueKey(remoteBootHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
setupLoaderBlock->NetbootCardDriverName,
|
|
(wcslen(setupLoaderBlock->NetbootCardDriverName) + 1) * sizeof(WCHAR)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"ServiceName");
|
|
status = ZwSetValueKey(remoteBootHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
setupLoaderBlock->NetbootCardServiceName,
|
|
(wcslen(setupLoaderBlock->NetbootCardServiceName) + 1) * sizeof(WCHAR)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Save the device instance, in case we need to ID the card later.
|
|
//
|
|
|
|
IopWstrToUnicodeString(&unicodeName, L"DeviceInstance");
|
|
status = ZwSetValueKey(remoteBootHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
UnicodeDeviceInstance->Buffer,
|
|
UnicodeDeviceInstance->Length + sizeof(WCHAR)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Make sure we only pick one card to setup this way!
|
|
//
|
|
|
|
IopRemoteBootCardInitialized = TRUE;
|
|
|
|
|
|
cleanup:
|
|
if (instanceHandle != NULL) {
|
|
ZwClose(instanceHandle);
|
|
}
|
|
if (remoteBootHandle != NULL) {
|
|
ZwClose(remoteBootHandle);
|
|
}
|
|
if (parametersHandle != NULL) {
|
|
ZwClose(parametersHandle);
|
|
}
|
|
if (currentControlSetHandle != NULL) {
|
|
ZwClose(currentControlSetHandle);
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IopWriteIpAddressToRegistry(
|
|
HANDLE handle,
|
|
PWCHAR regkey,
|
|
PUCHAR value
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING string;
|
|
CHAR addressA[17];
|
|
WCHAR addressW[17];
|
|
STRING addressStringA;
|
|
UNICODE_STRING addressStringW;
|
|
|
|
RtlInitUnicodeString( &string, regkey );
|
|
|
|
RtlZeroMemory(addressW,sizeof(addressW));
|
|
|
|
//
|
|
// note that value is PUCHAR, therefore the largest
|
|
// each %d could expand to is 255, which means that our
|
|
// buffer cannot overflow.
|
|
//
|
|
sprintf(addressA, "%d.%d.%d.%d",
|
|
value[0],
|
|
value[1],
|
|
value[2],
|
|
value[3]);
|
|
|
|
RtlInitAnsiString(&addressStringA, addressA);
|
|
addressStringW.Buffer = addressW;
|
|
addressStringW.MaximumLength = sizeof(addressW);
|
|
|
|
status = RtlAnsiStringToUnicodeString(&addressStringW, &addressStringA, FALSE);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopWriteIpAddressToRegistry: Unable to convert %ws value: %x\n", regkey, status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// we are setting a REG_MULTI_SZ, this has an extra NULL at the
|
|
// end of it. Our buffer has an extra character in it to account
|
|
// for this.
|
|
//
|
|
status = NtSetValueKey(
|
|
handle,
|
|
&string,
|
|
0,
|
|
REG_MULTI_SZ,
|
|
addressW,
|
|
addressStringW.Length + sizeof(WCHAR)
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
KdPrint(( "IopWriteIpAddressToRegistry: Unable to set %ws value: %x\n", regkey, status ));
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IopSetDefaultGateway(
|
|
IN ULONG GatewayAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function adds a default gateway entry from the router table.
|
|
|
|
Arguments:
|
|
|
|
GatewayAddress - Address of the default gateway.
|
|
|
|
Return Value:
|
|
|
|
Error Code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
HANDLE Handle = NULL;
|
|
BYTE Context[CONTEXT_SIZE];
|
|
TDIObjectID ID;
|
|
DWORD Size;
|
|
IPSNMPInfo IPStats;
|
|
IPAddrEntry *AddrTable = NULL;
|
|
DWORD NumReturned;
|
|
DWORD Type;
|
|
DWORD i;
|
|
DWORD MatchIndex;
|
|
IPRouteEntry RouteEntry;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING NameString;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
if (GatewayAddress == 0) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlInitUnicodeString( &NameString, DD_TCP_DEVICE_NAME );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&NameString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtCreateFile(
|
|
&Handle,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
KdPrint(( "IopSetDefaultGateway: Unable to open TCPIP: %x\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get the NetAddr info, to find an interface index for the gateway.
|
|
//
|
|
|
|
ID.toi_entity.tei_entity = CL_NL_ENTITY;
|
|
ID.toi_entity.tei_instance = 0;
|
|
ID.toi_class = INFO_CLASS_PROTOCOL;
|
|
ID.toi_type = INFO_TYPE_PROVIDER;
|
|
ID.toi_id = IP_MIB_STATS_ID;
|
|
|
|
Size = sizeof(IPStats);
|
|
memset(&IPStats, 0x0, Size);
|
|
memset(Context, 0x0, CONTEXT_SIZE);
|
|
|
|
Status = IopTCPQueryInformationEx(
|
|
Handle,
|
|
&ID,
|
|
&IPStats,
|
|
&Size,
|
|
Context);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(( "IopSetDefaultGateway: Unable to query TCPIP(1): %x\n", Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Size = IPStats.ipsi_numaddr * sizeof(IPAddrEntry);
|
|
AddrTable = ExAllocatePoolWithTag(PagedPool, Size, 'bRoI');
|
|
|
|
if (AddrTable == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ID.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID;
|
|
memset(Context, 0x0, CONTEXT_SIZE);
|
|
|
|
Status = IopTCPQueryInformationEx(
|
|
Handle,
|
|
&ID,
|
|
AddrTable,
|
|
&Size,
|
|
Context);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(( "IopSetDefaultGateway: Unable to query TCPIP(2): %x\n", Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
NumReturned = Size/sizeof(IPAddrEntry);
|
|
|
|
//
|
|
// We've got the address table. Loop through it. If we find an exact
|
|
// match for the gateway, then we're adding or deleting a direct route
|
|
// and we're done. Otherwise try to find a match on the subnet mask,
|
|
// and remember the first one we find.
|
|
//
|
|
|
|
Type = IRE_TYPE_INDIRECT;
|
|
for (i = 0, MatchIndex = 0xffff; i < NumReturned; i++) {
|
|
|
|
if( AddrTable[i].iae_addr == GatewayAddress ) {
|
|
|
|
//
|
|
// Found an exact match.
|
|
//
|
|
|
|
MatchIndex = i;
|
|
Type = IRE_TYPE_DIRECT;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The next hop is on the same subnet as this address. If
|
|
// we haven't already found a match, remember this one.
|
|
//
|
|
|
|
if ( (MatchIndex == 0xffff) &&
|
|
(AddrTable[i].iae_addr != 0) &&
|
|
(AddrTable[i].iae_mask != 0) &&
|
|
((AddrTable[i].iae_addr & AddrTable[i].iae_mask) ==
|
|
(GatewayAddress & AddrTable[i].iae_mask)) ) {
|
|
|
|
MatchIndex = i;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We've looked at all of the entries. See if we found a match.
|
|
//
|
|
|
|
if (MatchIndex == 0xffff) {
|
|
//
|
|
// Didn't find a match.
|
|
//
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
KdPrint(( "IopSetDefaultGateway: Unable to find match for gateway\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// We've found a match. Fill in the route entry, and call the
|
|
// Set API.
|
|
//
|
|
|
|
RouteEntry.ire_dest = DEFAULT_DEST;
|
|
RouteEntry.ire_index = AddrTable[MatchIndex].iae_index;
|
|
RouteEntry.ire_metric1 = DEFAULT_METRIC;
|
|
RouteEntry.ire_metric2 = (DWORD)(-1);
|
|
RouteEntry.ire_metric3 = (DWORD)(-1);
|
|
RouteEntry.ire_metric4 = (DWORD)(-1);
|
|
RouteEntry.ire_nexthop = GatewayAddress;
|
|
RouteEntry.ire_type = Type;
|
|
RouteEntry.ire_proto = IRE_PROTO_NETMGMT;
|
|
RouteEntry.ire_age = 0;
|
|
RouteEntry.ire_mask = DEFAULT_DEST_MASK;
|
|
RouteEntry.ire_metric5 = (DWORD)(-1);
|
|
RouteEntry.ire_context = 0;
|
|
|
|
Size = sizeof(RouteEntry);
|
|
|
|
ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID;
|
|
|
|
Status = IopTCPSetInformationEx(
|
|
Handle,
|
|
&ID,
|
|
&RouteEntry,
|
|
Size );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(( "IopSetDefaultGateway: Unable to set default gateway: %x\n", Status ));
|
|
}
|
|
|
|
NtClose(Handle);
|
|
|
|
Handle = NULL;
|
|
|
|
Cleanup:
|
|
|
|
if (Handle != NULL) {
|
|
NtClose(Handle);
|
|
}
|
|
|
|
if( AddrTable != NULL ) {
|
|
ExFreePool( AddrTable );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
__inline long
|
|
htonl(long x)
|
|
{
|
|
return((((x) >> 24) & 0x000000FFL) |
|
|
(((x) >> 8) & 0x0000FF00L) |
|
|
(((x) << 8) & 0x00FF0000L) |
|
|
(((x) << 24) & 0xFF000000L));
|
|
}
|
|
|
|
NTSTATUS
|
|
IopCacheNetbiosNameForIpAddress(
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function takes an IP address, and submits it to NetBt for name resolution.
|
|
|
|
Arguments:
|
|
|
|
IpAddress - Address to resolve
|
|
|
|
Return Value:
|
|
|
|
Error Code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Handle = NULL;
|
|
BYTE Context[CONTEXT_SIZE];
|
|
DWORD Size;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING NameString;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
tREMOTE_CACHE cacheInfo;
|
|
PCHAR serverName;
|
|
PCHAR endOfServerName;
|
|
HANDLE EventHandle;
|
|
|
|
//
|
|
// Open NetBT.
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
&NameString,
|
|
L"\\Device\\NetBT_Tcpip_{54C7D140-09EF-11D1-B25A-F5FE627ED95E}"
|
|
);
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&NameString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtCreateFile(
|
|
&Handle,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
KdPrint(( "IopCacheNetbiosNameForIpAddress: Unable to open NETBT: %x\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get the server's name.
|
|
//
|
|
// If this is a remote boot setup boot, NtBootPathName is of the
|
|
// form \<server>\<share>\setup\<install-directory>\<platform>.
|
|
// If this is a normal remote boot, NtBootPathName is of the form
|
|
// \<server>\<share>\images\<machine>\winnt.
|
|
//
|
|
// Thus in either case, we need to isolate the first element of the
|
|
// path.
|
|
//
|
|
|
|
serverName = LoaderBlock->NtBootPathName;
|
|
if ( *serverName == '\\' ) {
|
|
serverName++;
|
|
}
|
|
endOfServerName = strchr( serverName, '\\' );
|
|
if ( endOfServerName == NULL ) {
|
|
endOfServerName = strchr( serverName, '\0' );
|
|
}
|
|
|
|
//
|
|
// Fill in the tREMOTE_CACHE structure.
|
|
//
|
|
|
|
memset(&cacheInfo, 0x0, sizeof(cacheInfo));
|
|
|
|
memset(cacheInfo.name, ' ', NETBIOS_NAMESIZE);
|
|
ASSERT(NETBIOS_NAMESIZE >= (ULONG)(endOfServerName - serverName));
|
|
memcpy(
|
|
cacheInfo.name,
|
|
serverName,
|
|
min(
|
|
(ULONG)(endOfServerName - serverName),
|
|
NETBIOS_NAMESIZE));
|
|
cacheInfo.IpAddress = htonl(LoaderBlock->SetupLoaderBlock->ServerIpAddress);
|
|
cacheInfo.Ttl = MAXULONG;
|
|
|
|
//
|
|
// Submit the IOCTL.
|
|
//
|
|
|
|
Status = NtCreateEvent(
|
|
&EventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
KdPrint(( "IopCacheNetbiosNameForIpAddress: Unable to createevent: %x\n", Status ));
|
|
NtClose(Handle);
|
|
return(Status);
|
|
}
|
|
|
|
Status = NtDeviceIoControlFile(
|
|
Handle,
|
|
EventHandle,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_NETBT_ADD_TO_REMOTE_TABLE,
|
|
&cacheInfo,
|
|
sizeof(cacheInfo),
|
|
Context,
|
|
sizeof(Context)
|
|
);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
NtWaitForSingleObject(
|
|
EventHandle,
|
|
FALSE,
|
|
NULL);
|
|
|
|
Status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
Status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
KdPrint(( "IopCacheNetbiosNameForIpAddress: Adapter status failed: %x\n", Status ));
|
|
}
|
|
|
|
NtClose(Handle);
|
|
NtClose(EventHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IopTCPQueryInformationEx(
|
|
IN HANDLE TCPHandle,
|
|
IN TDIObjectID FAR *ID,
|
|
OUT void FAR *Buffer,
|
|
IN OUT DWORD FAR *BufferSize,
|
|
IN OUT BYTE FAR *Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine provides the interface to the TDI QueryInformationEx
|
|
facility of the TCP/IP stack on NT. Someday, this facility will be
|
|
part of TDI.
|
|
|
|
Arguments:
|
|
|
|
TCPHandle - Open handle to the TCP driver
|
|
ID - The TDI Object ID to query
|
|
Buffer - Data buffer to contain the query results
|
|
BufferSize - Pointer to the size of the results buffer. Filled in
|
|
with the amount of results data on return.
|
|
Context - Context value for the query. Should be zeroed for a
|
|
new query. It will be filled with context
|
|
information for linked enumeration queries.
|
|
|
|
Return Value:
|
|
|
|
An NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCP_REQUEST_QUERY_INFORMATION_EX queryBuffer;
|
|
DWORD queryBufferSize;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
HANDLE EventHandle = NULL;
|
|
|
|
|
|
if (TCPHandle == NULL) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
queryBufferSize = sizeof(TCP_REQUEST_QUERY_INFORMATION_EX);
|
|
memcpy(&(queryBuffer.ID), ID, sizeof(TDIObjectID));
|
|
memcpy(&(queryBuffer.Context), Context, CONTEXT_SIZE);
|
|
|
|
status = NtCreateEvent(
|
|
&EventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return(status);
|
|
}
|
|
|
|
status = NtDeviceIoControlFile(
|
|
TCPHandle, // Driver handle
|
|
EventHandle, // Event
|
|
NULL, // APC Routine
|
|
NULL, // APC context
|
|
&ioStatusBlock, // Status block
|
|
IOCTL_TCP_QUERY_INFORMATION_EX, // Control code
|
|
&queryBuffer, // Input buffer
|
|
queryBufferSize, // Input buffer size
|
|
Buffer, // Output buffer
|
|
*BufferSize // Output buffer size
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
NtWaitForSingleObject(
|
|
EventHandle,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
//
|
|
// Copy the return context to the caller's context buffer
|
|
//
|
|
memcpy(Context, &(queryBuffer.Context), CONTEXT_SIZE);
|
|
*BufferSize = (ULONG)ioStatusBlock.Information;
|
|
status = ioStatusBlock.Status;
|
|
} else {
|
|
*BufferSize = 0;
|
|
}
|
|
|
|
NtClose(EventHandle);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IopTCPSetInformationEx(
|
|
IN HANDLE TCPHandle,
|
|
IN TDIObjectID FAR *ID,
|
|
IN void FAR *Buffer,
|
|
IN DWORD FAR BufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine provides the interface to the TDI SetInformationEx
|
|
facility of the TCP/IP stack on NT. Someday, this facility will be
|
|
part of TDI.
|
|
|
|
Arguments:
|
|
|
|
TCPHandle - Open handle to the TCP driver
|
|
ID - The TDI Object ID to set
|
|
Buffer - Data buffer containing the information to be set
|
|
BufferSize - The size of the set data buffer.
|
|
|
|
Return Value:
|
|
|
|
An NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTCP_REQUEST_SET_INFORMATION_EX setBuffer;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
DWORD setBufferSize;
|
|
HANDLE EventHandle;
|
|
|
|
|
|
if (TCPHandle == NULL) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
setBufferSize = FIELD_OFFSET(TCP_REQUEST_SET_INFORMATION_EX, Buffer) + BufferSize;
|
|
|
|
setBuffer = ExAllocatePoolWithTag(PagedPool, setBufferSize, 'bRoI');
|
|
|
|
if (setBuffer == NULL) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
setBuffer->BufferSize = BufferSize;
|
|
|
|
memcpy(&(setBuffer->ID), ID, sizeof(TDIObjectID));
|
|
|
|
memcpy(&(setBuffer->Buffer[0]), Buffer, BufferSize);
|
|
|
|
status = NtCreateEvent(
|
|
&EventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(setBuffer);
|
|
return(status);
|
|
}
|
|
|
|
status = NtDeviceIoControlFile(
|
|
TCPHandle, // Driver handle
|
|
EventHandle, // Event
|
|
NULL, // APC Routine
|
|
NULL, // APC context
|
|
&ioStatusBlock, // Status block
|
|
IOCTL_TCP_SET_INFORMATION_EX, // Control code
|
|
setBuffer, // Input buffer
|
|
setBufferSize, // Input buffer size
|
|
NULL, // Output buffer
|
|
0 // Output buffer size
|
|
);
|
|
|
|
|
|
if (status == STATUS_PENDING) {
|
|
NtWaitForSingleObject(
|
|
EventHandle,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
status = ioStatusBlock.Status;
|
|
}
|
|
|
|
NtClose(EventHandle);
|
|
|
|
ExFreePool(setBuffer);
|
|
|
|
return(status);
|
|
}
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma const_seg()
|
|
#endif
|
|
|