/*++

Copyright (c) 2013 Minoca Corp. All Rights Reserved

Module Name:

    env.c

Abstract:

    This module implements environment support for user mode programs.

Author:

    Evan Green 7-Mar-2013

Environment:

    User Mode

--*/

//
// ------------------------------------------------------------------- Includes
//

#include "osbasep.h"

//
// ---------------------------------------------------------------- Definitions
//

#define ENVIRONMENT_ALLOCATION_TAG 0x21766E45

//
// ------------------------------------------------------ Data Type Definitions
//

//
// ----------------------------------------------- Internal Function Prototypes
//

//
// -------------------------------------------------------------------- Globals
//

//
// Store a pointer to the environment.
//

PPROCESS_ENVIRONMENT OsEnvironment = NULL;

//
// ------------------------------------------------------------------ Functions
//

OS_API
PPROCESS_ENVIRONMENT
OsCreateEnvironment (
    PSTR ImagePath,
    ULONG ImagePathLength,
    PSTR *ArgumentValues,
    ULONG ArgumentValuesTotalLength,
    ULONG ArgumentCount,
    PSTR *EnvironmentValues,
    ULONG EnvironmentValuesTotalLength,
    ULONG EnvironmentCount
    )

/*++

Routine Description:

    This routine creates an environment that can be passed to the kernel for
    execution of an image. This routine will use the heap.

Arguments:

    ImagePath - Supplies a pointer to the name of the image that will be
        executed.

    ImagePathLength - Supplies the length of the image path buffer in bytes,
        including the null terminator.

    ArgumentValues - Supplies an array of pointers to arguments to pass to the
        image.

    ArgumentValuesTotalLength - Supplies the total length of all arguments, in
        bytes, including their null terminators.

    ArgumentCount - Supplies the number of arguments in the argument values
        array.

    EnvironmentValues - Supplies an array of pointers to environment variables
        to set in the new environment. Environment variables take the form
        "name=value".

    EnvironmentValuesTotalLength - Supplies the total length of all environment
        variables, in bytes, including their null terminators.

    EnvironmentCount - Supplies the number of environment variable strings
        present in the environment values array. This should not include any
        null terminating entry in the array.

Return Value:

    Returns a pointer to a heap allocated environment, suitable for sending to
    the execute image system call.

    NULL on failure.

--*/

{

    ULONG AllocationSize;
    ULONG BufferSize;
    PSTR CurrentBuffer;
    ULONG ElementIndex;
    PPROCESS_ENVIRONMENT Environment;
    BOOL Result;
    ULONG StringSize;
    ULONG TerminatedEnvironmentCount;

    Result = FALSE;
    TerminatedEnvironmentCount = EnvironmentCount;
    if ((EnvironmentCount == 0) ||
        (EnvironmentValues[EnvironmentCount - 1] != NULL)) {

        TerminatedEnvironmentCount += 1;
    }

    //
    // Allocate the beast. There's a null pointer at the end of the list too
    // (hence the argument count plus one).
    //

    AllocationSize = sizeof(PROCESS_ENVIRONMENT) +
                     ALIGN_RANGE_UP(ImagePathLength, sizeof(PVOID)) +
                     ((ArgumentCount + 1) * sizeof(PSTR)) +
                     ALIGN_RANGE_UP(ArgumentValuesTotalLength, sizeof(PVOID)) +
                     (TerminatedEnvironmentCount * sizeof(PSTR)) +
                     EnvironmentValuesTotalLength;

    Environment = OsHeapAllocate(AllocationSize, ENVIRONMENT_ALLOCATION_TAG);
    if (Environment == NULL) {
        return NULL;
    }

    RtlZeroMemory(Environment, sizeof(PROCESS_ENVIRONMENT));

    //
    // Copy the image path.
    //

    Environment->ImageName = (PSTR)(Environment + 1);
    RtlStringCopy(Environment->ImageName, ImagePath, ImagePathLength);
    Environment->ImageNameLength = ImagePathLength;

    //
    // Make room for the arguments and its buffer.
    //

    Environment->Arguments =
                      (PSTR *)((PVOID)Environment->ImageName +
                               ALIGN_RANGE_UP(ImagePathLength, sizeof(PVOID)));

    Environment->ArgumentCount = ArgumentCount;
    Environment->ArgumentsBuffer = (PVOID)Environment->Arguments +
                                   ((ArgumentCount + 1) * sizeof(PSTR));

    Environment->ArgumentsBufferLength = ArgumentValuesTotalLength;

    //
    // Make room for the environment and its buffer.
    //

    Environment->Environment =
            (PSTR *)(Environment->ArgumentsBuffer +
                     ALIGN_RANGE_UP(ArgumentValuesTotalLength, sizeof(PVOID)));

    Environment->EnvironmentCount = EnvironmentCount;
    Environment->EnvironmentBuffer =
                                   (PVOID)Environment->Environment +
                                   (TerminatedEnvironmentCount * sizeof(PSTR));

    Environment->EnvironmentBufferLength = EnvironmentValuesTotalLength;

    //
    // Copy over each argument. The first is always the image name.
    //

    BufferSize = ArgumentValuesTotalLength;
    CurrentBuffer = Environment->ArgumentsBuffer;
    for (ElementIndex = 0; ElementIndex < ArgumentCount; ElementIndex += 1) {
        StringSize = RtlStringCopy(CurrentBuffer,
                                   ArgumentValues[ElementIndex],
                                   BufferSize);

        if (StringSize == 0) {
            goto CreateEnvironmentEnd;
        }

        Environment->Arguments[ElementIndex] = CurrentBuffer;
        CurrentBuffer += StringSize;
        BufferSize -= StringSize;
    }

    //
    // Copy over each environment variable.
    //

    BufferSize = EnvironmentValuesTotalLength;
    CurrentBuffer = Environment->EnvironmentBuffer;
    for (ElementIndex = 0; ElementIndex < EnvironmentCount; ElementIndex += 1) {

        //
        // Protect against a supplied environment that was null-terminated.
        //

        if ((ElementIndex == (EnvironmentCount - 1)) &&
            (EnvironmentValues[ElementIndex] == NULL)) {

            break;
        }

        StringSize = RtlStringCopy(CurrentBuffer,
                                   EnvironmentValues[ElementIndex],
                                   BufferSize);

        if (StringSize == 0) {
            goto CreateEnvironmentEnd;
        }

        Environment->Environment[ElementIndex] = CurrentBuffer;
        CurrentBuffer += StringSize;
        BufferSize -= StringSize;
    }

    //
    // NULL out the last one.
    //

    ASSERT(ElementIndex == TerminatedEnvironmentCount - 1);

    Environment->Environment[ElementIndex] = NULL;
    Result = TRUE;

CreateEnvironmentEnd:
    if (Result == FALSE) {
        if (Environment != NULL) {
            OsHeapFree(Environment);
            Environment = NULL;
        }
    }

    return Environment;
}

OS_API
VOID
OsDestroyEnvironment (
    PPROCESS_ENVIRONMENT Environment
    )

/*++

Routine Description:

    This routine destroys an environment created with the create environment
    function.

Arguments:

    Environment - Supplies a pointer to the environment to destroy.

Return Value:

    None.

--*/

{

    OsHeapFree(Environment);
    return;
}

OS_API
PPROCESS_ENVIRONMENT
OsGetCurrentEnvironment (
    VOID
    )

/*++

Routine Description:

    This routine gets the environment for the current process.

Arguments:

    None.

Return Value:

    Returns a pointer to the current environment. This is shared memory, and
    should not be altered by the caller.

--*/

{

    return OsEnvironment;
}

//
// --------------------------------------------------------- Internal Functions
//

