#include "spprecmp.h"
#pragma hdrstop



NTSTATUS
SpDeleteServiceEntry(
    IN PWCHAR ServiceKey
    )
{
    NTSTATUS Status;
    HANDLE KeyHandle;
    UNICODE_STRING UnicodeString;
    OBJECT_ATTRIBUTES Obja;

    RtlInitUnicodeString(&UnicodeString,ServiceKey);
    InitializeObjectAttributes(&Obja,&UnicodeString,OBJ_CASE_INSENSITIVE,NULL,NULL);
    Status = ZwOpenKey(&KeyHandle,KEY_WRITE|DELETE,&Obja);

    if(NT_SUCCESS(Status)) {
        Status = ZwDeleteKey(KeyHandle);
        if(!NT_SUCCESS(Status)) {
            KdPrint(("SETUP: warning: ZwDeleteKey of %ws returned %lx\n",ServiceKey,Status));
        }
    } else {
        KdPrint(("SETUP: warning: ZwOpenKey of %ws returned %lx\n",ServiceKey,Status));
    }

    return(Status);
}


NTSTATUS
SpCreateServiceEntry(
    IN  PWCHAR  ImagePath,
    OUT PWCHAR *ServiceKey
    )

/*++

Routine Description:

    Create an services entry in the registry suitable for loading
    a given device driver file.

Arguments:

    ImagePath - supplies the fully-qualified pathname of the device driver.

    ServiceKey - receives a pointer to a buffer containing the name of the
        service node created by this routine.  The caller must free this
        buffer via SpMemFree when finished.

Return Value:

    Status code indicating outcome.

--*/

{
    WCHAR KeyName[128];
    WCHAR FilePart[32];
    OBJECT_ATTRIBUTES Obja;
    UNICODE_STRING UnicodeString;
    HANDLE KeyHandle;
    ULONG u;
    NTSTATUS Status;
    PWSTR p;

    //
    // Isolate the name of the device driver file from its path.
    //
    if(p = wcsrchr(ImagePath,L'\\')) {
        p++;
    } else {
        p = ImagePath;
    }
    wcsncpy(FilePart,p,(sizeof(FilePart)/sizeof(FilePart[0]))-1);
    FilePart[(sizeof(FilePart)/sizeof(FilePart[0]))-1] = 0;
    if(p=wcsrchr(FilePart,L'.')) {
        *p = 0;
    }

    //
    // Form a unique key name in
    // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services.
    //
    swprintf(
        KeyName,
        L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\%ws",
        FilePart
        );

    //
    // Attempt to create the key for the service.
    //
    RtlInitUnicodeString(&UnicodeString,KeyName);
    InitializeObjectAttributes(&Obja,&UnicodeString,OBJ_CASE_INSENSITIVE,NULL,NULL);

    Status = ZwCreateKey(
                &KeyHandle,
                KEY_READ | KEY_WRITE,
                &Obja,
                0,
                NULL,
                REG_OPTION_VOLATILE,
                NULL
                );

    if(!NT_SUCCESS(Status)) {
        KdPrint(("SETUP: SpCreateServiceEntry: ZwCreateKey %ws returns %lx\n",KeyName,Status));
        return(Status);
    }


    //
    // Set the ImagePath value in the service key.
    //
    RtlInitUnicodeString(&UnicodeString,L"ImagePath");
    Status = ZwSetValueKey(
                KeyHandle,
                &UnicodeString,
                0,
                REG_SZ,
                ImagePath,
                (wcslen(ImagePath) + 1) * sizeof(WCHAR)
                );

    if(!NT_SUCCESS(Status)) {
        KdPrint(("SETUP: Unable to set ImagePath value in key %ws (%lx)\n",KeyName,Status));
        goto cs1;
    }

    //
    // Set the Type value in the service key.
    //
    u = SERVICE_KERNEL_DRIVER;
    RtlInitUnicodeString(&UnicodeString,L"Type");
    Status = ZwSetValueKey(
                KeyHandle,
                &UnicodeString,
                0,
                REG_DWORD,
                &u,
                sizeof(ULONG)
                );

    if(!NT_SUCCESS(Status)) {
        KdPrint(("SETUP: Unable to set Type value in key %ws (%lx)\n",KeyName,Status));
        goto cs1;
    }

    //
    // Set the Start value in the service key.
    //
    u = SERVICE_DEMAND_START;
    RtlInitUnicodeString(&UnicodeString,L"Start");
    Status = ZwSetValueKey(
                KeyHandle,
                &UnicodeString,
                0,
                REG_DWORD,
                &u,
                sizeof(ULONG)
                );

    if(!NT_SUCCESS(Status)) {
        KdPrint(("SETUP: Unable to set Start value in key %ws (%lx)\n",KeyName,Status));
        goto cs1;
    }


  cs1:

    //
    // If we were not entirely successful creating the service,
    // we'll want to clean it out here.  Otherwise duplicate the KeyName
    // string to return to the caller.
    //
    if(NT_SUCCESS(Status)) {

        if((*ServiceKey = SpDupStringW(KeyName)) == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if(!NT_SUCCESS(Status)) {

        NTSTATUS s;

        //
        // Remove the key we just created.
        //
        s = ZwDeleteKey(KeyHandle);
        if(!NT_SUCCESS(s)) {
            KdPrint(("SETUP: warning: ZwDeleteKey of %ws returned %lx\n",KeyName,s));
        }
    }

    NtClose(KeyHandle);

    return(Status);
}




NTSTATUS
SpLoadDeviceDriver(
    IN PWSTR Description,
    IN PWSTR PathPart1,
    IN PWSTR PathPart2,     OPTIONAL
    IN PWSTR PathPart3      OPTIONAL
    )

/*++

Routine Description:

    Load a device driver by creating a services entry for the driver and
    then calling the I/O subsystem.

Arguments:

    Description - supplies a human-readable description of the driver
        or hardware that the driver targets.

    PathPart1 - supplies first part of full pathname to driver file.

    PathPart2 - if specified, supplies the second part of the full pathname;
        PathPart2 will be concatenated to PathPart1. If not specified, 
        then PathPart1 is the full path.

    PathPart3 - if specified, supplies a third part of the full pathname;
        PathPart3 will be concatenated to PathPart1 and PathPart2.

Return Value:

    Status code indicating outcome.

--*/

{
    PWCHAR FullName;
    NTSTATUS Status;
    PWCHAR ServiceKey;
    UNICODE_STRING ServiceKeyU;
    PWSTR pwstr;

    SpDisplayStatusText(
        SP_STAT_LOADING_DRIVER,
        DEFAULT_STATUS_ATTRIBUTE,
        Description
        );

    pwstr = (PWSTR)TemporaryBuffer;

    //
    // Form the full name of the device driver file.
    //
    wcscpy(pwstr,PathPart1);
    if(PathPart2) {
        SpConcatenatePaths(pwstr,PathPart2);
    }
    if(PathPart3) {
        SpConcatenatePaths(pwstr,PathPart3);
    }

    FullName = SpDupStringW(pwstr);

    //
    // Create a service entry for the driver.
    //
    Status = SpCreateServiceEntry(FullName,&ServiceKey);
    if(NT_SUCCESS(Status)) {

        RtlInitUnicodeString(&ServiceKeyU,ServiceKey);

        //
        // Attempt to load the driver.
        //
        Status = ZwLoadDriver(&ServiceKeyU);
        if(!NT_SUCCESS(Status)) {
    
            KdPrint(("SETUP: ZwLoadDriver %ws returned %lx\n",FullName,Status));

            //
            // Remove the service entry we created in the registry.
            //
            SpDeleteServiceEntry(ServiceKey);
        }

        SpMemFree(ServiceKey);
    }

    SpMemFree(FullName);

    return(Status);
}