/*++

Copyright (c) 2013 Minoca Corp. All Rights Reserved

Module Name:

    iohandle.c

Abstract:

    This module implements support for managing I/O handles.

Author:

    Evan Green 25-Apr-2013

Environment:

    Kernel

--*/

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

#include <minoca/kernel/kernel.h>
#include "iop.h"

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

#define IO_HANDLE_ALLOCATION_TAG 0x61486F49 // 'aHoI'
#define IO_HANDLE_MAX_REFERENCE_COUNT 0x10000000

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

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

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

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

KERNEL_API
ULONG
IoGetIoHandleAccessPermissions (
    PIO_HANDLE IoHandle
    )

/*++

Routine Description:

    This routine returns the access permissions for the given I/O handle.

Arguments:

    IoHandle - Supplies a pointer to an I/O handle.

Return Value:

    Returns the access permissions for the given I/O handle.

--*/

{

    return IoHandle->Access;
}

KERNEL_API
ULONG
IoGetIoHandleOpenFlags (
    PIO_HANDLE IoHandle
    )

/*++

Routine Description:

    This routine returns the current open flags for a given I/O handle. Some
    of these flags can change.

Arguments:

    IoHandle - Supplies a pointer to an I/O handle.

Return Value:

    Returns the current open flags for the I/O handle.

--*/

{

    return IoHandle->OpenFlags;
}

VOID
IoIoHandleAddReference (
    PIO_HANDLE IoHandle
    )

/*++

Routine Description:

    This routine increments the reference count on an I/O handle.

Arguments:

    IoHandle - Supplies a pointer to the I/O handle.

Return Value:

    None.

--*/

{

    ULONG OldValue;

    OldValue = RtlAtomicAdd32(&(IoHandle->ReferenceCount), 1);

    ASSERT((OldValue != 0) && (OldValue < IO_HANDLE_MAX_REFERENCE_COUNT));

    return;
}

KSTATUS
IoIoHandleReleaseReference (
    PIO_HANDLE IoHandle
    )

/*++

Routine Description:

    This routine decrements the reference count on an I/O handle. If the
    reference count becomes zero, the I/O handle will be destroyed.

Arguments:

    IoHandle - Supplies a pointer to the I/O handle.

Return Value:

    None.

--*/

{

    ULONG OldValue;
    KSTATUS Status;

    Status = STATUS_SUCCESS;
    OldValue = RtlAtomicAdd32(&(IoHandle->ReferenceCount), -1);

    ASSERT((OldValue != 0) && (OldValue < IO_HANDLE_MAX_REFERENCE_COUNT));

    if (OldValue == 1) {
        Status = IopClose(IoHandle);
        if (!KSUCCESS(Status)) {

            //
            // Restore the reference to the I/O handle.
            //

            RtlAtomicAdd32(&(IoHandle->ReferenceCount), 1);
            return Status;
        }

        MmFreePagedPool(IoHandle);
    }

    return Status;
}

PIMAGE_SECTION_LIST
IoGetImageSectionListFromIoHandle (
    PIO_HANDLE IoHandle
    )

/*++

Routine Description:

    This routine gets the image section list for the given I/O handle.

Arguments:

    IoHandle - Supplies a pointer to an I/O handle.

Return Value:

    Returns a pointer to the I/O handles image section list or NULL on failure.

--*/

{

    PFILE_OBJECT FileObject;

    FileObject = IoHandle->FileObject;
    return IopGetImageSectionListFromFileObject(FileObject);
}

BOOL
IoIoHandleIsCacheable (
    PIO_HANDLE IoHandle
    )

/*++

Routine Description:

    This routine determines whether or not data for the I/O object specified by
    the given handle is cached in the page cache.

Arguments:

    IoHandle - Supplies a pointer to an I/O handle.

Return Value:

    Returns TRUE if the I/O handle's object is cached or FALSE otherwise.

--*/

{

    PFILE_OBJECT FileObject;

    //
    // The I/O handle is deemed cacheable if the file object is cacheable.
    //

    FileObject = IoHandle->FileObject;
    if (IO_IS_FILE_OBJECT_CACHEABLE(FileObject) != FALSE) {
        return TRUE;
    }

    return FALSE;
}

KSTATUS
IopCreateIoHandle (
    PIO_HANDLE *Handle
    )

/*++

Routine Description:

    This routine creates a new I/O handle with a reference count of one.

Arguments:

    Handle - Supplies a pointer where a pointer to the new I/O handle will be
        returned on success.

Return Value:

    Status code.

--*/

{

    PIO_HANDLE NewHandle;
    KSTATUS Status;

    //
    // Create the I/O handle structure.
    //

    NewHandle = MmAllocatePagedPool(sizeof(IO_HANDLE),
                                    IO_HANDLE_ALLOCATION_TAG);

    if (NewHandle == NULL) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto CreateIoHandleEnd;
    }

    RtlZeroMemory(NewHandle, sizeof(IO_HANDLE));
    NewHandle->HandleType = IoHandleTypeDefault;
    NewHandle->ReferenceCount = 1;

    ASSERT(NewHandle->DeviceContext == NULL);

    Status = STATUS_SUCCESS;

CreateIoHandleEnd:
    if (!KSUCCESS(Status)) {
        if (NewHandle != NULL) {
            MmFreePagedPool(NewHandle);
            NewHandle = NULL;
        }
    }

    *Handle = NewHandle;
    return Status;
}

VOID
IopOverwriteIoHandle (
    PIO_HANDLE Destination,
    PIO_HANDLE IoSource
    )

/*++

Routine Description:

    This routine takes the contents of the given source handle and overwrites
    the destination handle with it. I/O actions performed on the destination
    handle appear as if they were done to the I/O object of the source handle.
    This replacement does not replace any information about the original path
    opened in the destination. It also does not modify the access and open
    flags in the destination. This routine is not thread safe.

Arguments:

    Destination - Supplies a pointer to the I/O handle that should magically
        redirect elsewhere.

    IoSource - Supplies a pointer to the I/O handle that contains the
        underlying I/O object the destination should interact with.

Return Value:

    None.

--*/

{

    PFILE_OBJECT OldFileObject;

    //
    // The destination I/O handle really shouldn't be handed to anyone yet,
    // since I/O might get wonky during the switch.
    //

    ASSERT(Destination->ReferenceCount == 1);

    OldFileObject = Destination->FileObject;
    Destination->FileObject = IoSource->FileObject;
    if (OldFileObject != Destination->PathPoint.PathEntry->FileObject) {
        IopFileObjectReleaseReference(OldFileObject);
    }

    IopFileObjectAddReference(IoSource->FileObject);
    return;
}

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

