/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    filehops.c

Abstract:

    This module implements Win32 file handle APIs

Author:

    Mark Lucovsky (markl) 25-Sep-1990

Revision History:

--*/

#include "basedll.h"

HANDLE
WINAPI
GetStdHandle(
    DWORD nStdHandle
    )
{
    PPEB Peb;
    HANDLE rv;


    Peb = NtCurrentPeb();
    switch( nStdHandle ) {
        case STD_INPUT_HANDLE:
            rv = Peb->ProcessParameters->StandardInput;
            break;

        case STD_OUTPUT_HANDLE:
            rv = Peb->ProcessParameters->StandardOutput;
            break;

        case STD_ERROR_HANDLE:
            rv = Peb->ProcessParameters->StandardError;
            break;
        default:
            rv = INVALID_HANDLE_VALUE;
            break;
    }
    if ( rv == INVALID_HANDLE_VALUE ) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        }
    return rv;
}

BOOL
WINAPI
SetStdHandle(
    DWORD nStdHandle,
    HANDLE hHandle
    )
{
    PPEB Peb;

    Peb = NtCurrentPeb();
    switch( nStdHandle ) {
        case STD_INPUT_HANDLE:
            Peb->ProcessParameters->StandardInput = hHandle;
            break;

        case STD_OUTPUT_HANDLE:
            Peb->ProcessParameters->StandardOutput = hHandle;
            break;

        case STD_ERROR_HANDLE:
            Peb->ProcessParameters->StandardError = hHandle;
            break;

        default:
            BaseSetLastNTError(STATUS_INVALID_HANDLE);
            return FALSE;
    }

    return( TRUE );
}

DWORD
WINAPI
GetFileType(
    HANDLE hFile
    )

/*++

Routine Description:

    GetFileType is used to determine the file type of the specified file.

Arguments:

    hFile - Supplies an open handle to a file whose type is to be
        determined

Return Value:

    FILE_TYPE_UNKNOWN - The type of the specified file is unknown.

    FILE_TYPE_DISK - The specified file is a disk file.

    FILE_TYPE_CHAR - The specified file is a character file (LPT,
        console...)

    FILE_TYPE_PIPE - The specified file is a pipe (either a named pipe or
        a pipe created by CreatePipe).

--*/

{
    NTSTATUS Status;
    FILE_FS_DEVICE_INFORMATION DeviceInformation;
    IO_STATUS_BLOCK IoStatusBlock;
    PPEB Peb;

    Peb = NtCurrentPeb();

    switch( HandleToUlong(hFile) ) {
        case STD_INPUT_HANDLE:
            hFile = Peb->ProcessParameters->StandardInput;
            break;
        case STD_OUTPUT_HANDLE:
            hFile = Peb->ProcessParameters->StandardOutput;
            break;
        case STD_ERROR_HANDLE:
            hFile = Peb->ProcessParameters->StandardError;
            break;
    }

    if (CONSOLE_HANDLE(hFile) && VerifyConsoleIoHandle(hFile)) {
        return( FILE_TYPE_CHAR );
    }

    if (hFile == NULL) {
        BaseSetLastNTError( STATUS_INVALID_HANDLE );
        return( FILE_TYPE_UNKNOWN );    
    }

    //
    // If handle cannot be a real kernel handle we will fail
    // the call instead of calling with a bogus value NtQuery.
    //

    if (((ULONG_PTR)hFile & 0x01)) {

        BaseSetLastNTError( STATUS_INVALID_HANDLE );
        return( FILE_TYPE_UNKNOWN );
    }

    Status = NtQueryVolumeInformationFile( hFile,
                                           &IoStatusBlock,
                                           &DeviceInformation,
                                           sizeof( DeviceInformation ),
                                           FileFsDeviceInformation
                                         );

    if (!NT_SUCCESS( Status )) {
        BaseSetLastNTError( Status );
        return( FILE_TYPE_UNKNOWN );
    }

    switch( DeviceInformation.DeviceType ) {

    case FILE_DEVICE_SCREEN:
    case FILE_DEVICE_KEYBOARD:
    case FILE_DEVICE_MOUSE:
    case FILE_DEVICE_PARALLEL_PORT:
    case FILE_DEVICE_PRINTER:
    case FILE_DEVICE_SERIAL_PORT:
    case FILE_DEVICE_MODEM:
    case FILE_DEVICE_SOUND:
    case FILE_DEVICE_NULL:
        return( FILE_TYPE_CHAR );

    case FILE_DEVICE_CD_ROM:
    case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
    case FILE_DEVICE_CONTROLLER:
    case FILE_DEVICE_DATALINK:
    case FILE_DEVICE_DFS:
    case FILE_DEVICE_DISK:
    case FILE_DEVICE_DISK_FILE_SYSTEM:
    case FILE_DEVICE_VIRTUAL_DISK:
        return( FILE_TYPE_DISK );

    case FILE_DEVICE_NAMED_PIPE:
        return( FILE_TYPE_PIPE );

    case FILE_DEVICE_NETWORK:
    case FILE_DEVICE_NETWORK_FILE_SYSTEM:
    case FILE_DEVICE_PHYSICAL_NETCARD:
    case FILE_DEVICE_TAPE:
    case FILE_DEVICE_TAPE_FILE_SYSTEM:
    case FILE_DEVICE_TRANSPORT:
        // FIX, FIX - how to handle tapes, network devices, etc.

    case FILE_DEVICE_UNKNOWN:
    default:
        SetLastError( NO_ERROR );
        return( FILE_TYPE_UNKNOWN );
    }
}

BOOL
WINAPI
ReadFile(
    HANDLE hFile,
    LPVOID lpBuffer,
    DWORD nNumberOfBytesToRead,
    LPDWORD lpNumberOfBytesRead,
    LPOVERLAPPED lpOverlapped
    )

/*++

Routine Description:

    Data can be read from a file using ReadFile.

    This API is used to read data from a file.  Data is read from the
    file from the position indicated by the file pointer.  After the
    read completes, the file pointer is adjusted by the number of bytes
    actually read.  A return value of TRUE coupled with a bytes read of
    0 indicates that the file pointer was beyond the current end of the
    file at the time of the read.

Arguments:

    hFile - Supplies an open handle to a file that is to be read.  The
        file handle must have been created with GENERIC_READ access to
        the file.

    lpBuffer - Supplies the address of a buffer to receive the data read
        from the file.

    nNumberOfBytesToRead - Supplies the number of bytes to read from the
        file.

    lpNumberOfBytesRead - Returns the number of bytes read by this call.
        This parameter is always set to 0 before doing any IO or error
        checking.

    lpOverlapped - Optionally points to an OVERLAPPED structure to be used with the
    request. If NULL then the transfer starts at the current file position
    and ReadFile will not return until the operation completes.

    If the handle hFile was created without specifying FILE_FLAG_OVERLAPPED
    the file pointer is moved to the specified offset plus
    lpNumberOfBytesRead before ReadFile returns. ReadFile will wait for the
    request to complete before returning (it will not return
    ERROR_IO_PENDING).

    When FILE_FLAG_OVERLAPPED is specified, ReadFile may return
    ERROR_IO_PENDING to allow the calling function to continue processing
    while the operation completes. The event (or hFile if hEvent is NULL) will
    be set to the signalled state upon completion of the request.

    When the handle is created with FILE_FLAG_OVERLAPPED and lpOverlapped
    is set to NULL, ReadFile will return ERROR_INVALID_PARAMTER because
    the file offset is required.


Return Value:

    TRUE - The operation was successul.

    FALSE - The operation failed.  Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    PPEB Peb;
    DWORD InputMode;

    if ( ARGUMENT_PRESENT(lpNumberOfBytesRead) ) {
        *lpNumberOfBytesRead = 0;
        }

    Peb = NtCurrentPeb();

    switch( HandleToUlong(hFile) ) {
        case STD_INPUT_HANDLE:  hFile = Peb->ProcessParameters->StandardInput;
                                break;
        case STD_OUTPUT_HANDLE: hFile = Peb->ProcessParameters->StandardOutput;
                                break;
        case STD_ERROR_HANDLE:  hFile = Peb->ProcessParameters->StandardError;
                                break;
        }

    if (CONSOLE_HANDLE(hFile)) {
        if (ReadConsoleA(hFile,
                        lpBuffer,
                        nNumberOfBytesToRead,
                        lpNumberOfBytesRead,
                        lpOverlapped
                       )
           ) {
            Status = STATUS_SUCCESS;
            if (!GetConsoleMode( hFile, &InputMode )) {
                InputMode = 0;
                }

            if (InputMode & ENABLE_PROCESSED_INPUT) {
                try {
                    if (*(PCHAR)lpBuffer == 0x1A) {
                        *lpNumberOfBytesRead = 0;
                        }
                    }
                except( EXCEPTION_EXECUTE_HANDLER ) {
                    Status = GetExceptionCode();
                    }
                }

            if (NT_SUCCESS(Status)) {
                return TRUE;
                }
            else {
                BaseSetLastNTError(Status);
                return FALSE;
                }
            }
        else {
            return FALSE;
            }
        }

    if ( ARGUMENT_PRESENT( lpOverlapped ) ) {
        LARGE_INTEGER Li;

        lpOverlapped->Internal = (DWORD)STATUS_PENDING;
        Li.LowPart = lpOverlapped->Offset;
        Li.HighPart = lpOverlapped->OffsetHigh;
        Status = NtReadFile(
                hFile,
                lpOverlapped->hEvent,
                NULL,
                (ULONG_PTR)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped,
                (PIO_STATUS_BLOCK)&lpOverlapped->Internal,
                lpBuffer,
                nNumberOfBytesToRead,
                &Li,
                NULL
                );


        if ( NT_SUCCESS(Status) && Status != STATUS_PENDING) {
            if ( ARGUMENT_PRESENT(lpNumberOfBytesRead) ) {
                try {
                    *lpNumberOfBytesRead = (DWORD)lpOverlapped->InternalHigh;
                    }
                except(EXCEPTION_EXECUTE_HANDLER) {
                    *lpNumberOfBytesRead = 0;
                    }
                }
            return TRUE;
            }
        else
        if (Status == STATUS_END_OF_FILE) {
            if ( ARGUMENT_PRESENT(lpNumberOfBytesRead) ) {
                *lpNumberOfBytesRead = 0;
                }
            BaseSetLastNTError(Status);
            return FALSE;
            }
        else {
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
    else
        {
        Status = NtReadFile(
                hFile,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                lpBuffer,
                nNumberOfBytesToRead,
                NULL,
                NULL
                );

        if ( Status == STATUS_PENDING) {
            // Operation must complete before return & IoStatusBlock destroyed
            Status = NtWaitForSingleObject( hFile, FALSE, NULL );
            if ( NT_SUCCESS(Status)) {
                Status = IoStatusBlock.Status;
                }
            }

        if ( NT_SUCCESS(Status) ) {
            *lpNumberOfBytesRead = (DWORD)IoStatusBlock.Information;
            return TRUE;
            }
        else
        if (Status == STATUS_END_OF_FILE) {
            *lpNumberOfBytesRead = 0;
            return TRUE;
            }
        else {
            if ( NT_WARNING(Status) ) {
                *lpNumberOfBytesRead = (DWORD)IoStatusBlock.Information;
                }
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
}


BOOL
WINAPI
WriteFile(
    HANDLE hFile,
    LPCVOID lpBuffer,
    DWORD nNumberOfBytesToWrite,
    LPDWORD lpNumberOfBytesWritten,
    LPOVERLAPPED lpOverlapped
    )

/*++

Routine Description:

    Data can be written to a file using WriteFile.

    This API is used to write data to a file.  Data is written to the
    file from the position indicated by the file pointer.  After the
    write completes, the file pointer is adjusted by the number of bytes
    actually written.

    Unlike DOS, a NumberOfBytesToWrite value of zero does not truncate
    or extend the file.  If this function is required, SetEndOfFile
    should be used.

Arguments:

    hFile - Supplies an open handle to a file that is to be written.  The
        file handle must have been created with GENERIC_WRITE access to
        the file.

    lpBuffer - Supplies the address of the data that is to be written to
        the file.

    nNumberOfBytesToWrite - Supplies the number of bytes to write to the
        file. Unlike DOS, a value of zero is interpreted a null write.

    lpNumberOfBytesWritten - Returns the number of bytes written by this
        call. Before doing any work or error processing, the API sets this
        to zero.


    lpOverlapped - Optionally points to an OVERLAPPED structure to be
        used with the request. If NULL then the transfer starts at the
        current file position and WriteFile will not return until the
        operation completes.

        If the handle <hFile> was created without specifying
        FILE_FLAG_OVERLAPPED the file pointer is moved to the specified
        offset plus lpNumberOfBytesWritten before WriteFile returns.
        WriteFile will wait for the request to complete before returning
        (it will not set ERROR_IO_PENDING).

        When FILE_FLAG_OVERLAPPED is specified, WriteFile may return
        ERROR_IO_PENDING to allow the calling function to continue processing
        while the operation completes. The event (or hFile if hEvent is NULL) will
        be set to the signalled state upon completion of the request.

        When the handle is created with FILE_FLAG_OVERLAPPED and lpOverlapped
        is set to NULL, WriteFile will return ERROR_INVALID_PARAMTER because
        the file offset is required.

Return Value:

    TRUE - The operation was a success.

    FALSE - The operation failed.  Extended error status is
        available using GetLastError.

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    PPEB Peb;

    if ( ARGUMENT_PRESENT(lpNumberOfBytesWritten) ) {
        *lpNumberOfBytesWritten = 0;
        }

    Peb = NtCurrentPeb();
    switch( HandleToUlong(hFile) ) {
        case STD_INPUT_HANDLE:  hFile = Peb->ProcessParameters->StandardInput;
                                break;
        case STD_OUTPUT_HANDLE: hFile = Peb->ProcessParameters->StandardOutput;
                                break;
        case STD_ERROR_HANDLE:  hFile = Peb->ProcessParameters->StandardError;
                                break;
        }

    if (CONSOLE_HANDLE(hFile)) {
        return WriteConsoleA(hFile,
                            (LPVOID)lpBuffer,
                            nNumberOfBytesToWrite,
                            lpNumberOfBytesWritten,
                            lpOverlapped
                           );
        }

    if ( ARGUMENT_PRESENT( lpOverlapped ) ) {
        LARGE_INTEGER Li;

        lpOverlapped->Internal = (DWORD)STATUS_PENDING;
        Li.LowPart = lpOverlapped->Offset;
        Li.HighPart = lpOverlapped->OffsetHigh;
        Status = NtWriteFile(
                hFile,
                lpOverlapped->hEvent,
                NULL,
                (ULONG_PTR)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped,
                (PIO_STATUS_BLOCK)&lpOverlapped->Internal,
                (PVOID)lpBuffer,
                nNumberOfBytesToWrite,
                &Li,
                NULL
                );

        if ( !NT_ERROR(Status) && Status != STATUS_PENDING) {
            if ( ARGUMENT_PRESENT(lpNumberOfBytesWritten) ) {
                try {
                    *lpNumberOfBytesWritten = (DWORD)lpOverlapped->InternalHigh;
                    }
                except(EXCEPTION_EXECUTE_HANDLER) {
                    *lpNumberOfBytesWritten = 0;
                    }
                }
            return TRUE;
            }
        else  {
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
    else {
        Status = NtWriteFile(
                hFile,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                (PVOID)lpBuffer,
                nNumberOfBytesToWrite,
                NULL,
                NULL
                );

        if ( Status == STATUS_PENDING) {
            // Operation must complete before return & IoStatusBlock destroyed
            Status = NtWaitForSingleObject( hFile, FALSE, NULL );
            if ( NT_SUCCESS(Status)) {
                Status = IoStatusBlock.Status;
                }
            }

        if ( NT_SUCCESS(Status)) {
            *lpNumberOfBytesWritten = (DWORD)IoStatusBlock.Information;
            return TRUE;
            }
        else {
            if ( NT_WARNING(Status) ) {
                *lpNumberOfBytesWritten = (DWORD)IoStatusBlock.Information;
                }
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
}

BOOL
WINAPI
SetEndOfFile(
    HANDLE hFile
    )

/*++

Routine Description:

    The end of file position of an open file can be set to the current
    file pointer using SetEndOfFile.

    This API is used to set the end of file position of a file to the
    same value as the current file pointer.  This has the effect of
    truncating or extending a file.  This functionality is similar to
    DOS (int 21h, function 40H with CX=0).

Arguments:

    hFile - Supplies an open handle to a file that is to be extended or
        truncated.  The file handle must have been created with
        GENERIC_WRITE access to the file.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{

    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    FILE_POSITION_INFORMATION CurrentPosition;
    FILE_END_OF_FILE_INFORMATION EndOfFile;
    FILE_ALLOCATION_INFORMATION Allocation;

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
        }

    //
    // Get the current position of the file pointer
    //

    Status = NtQueryInformationFile(
                hFile,
                &IoStatusBlock,
                &CurrentPosition,
                sizeof(CurrentPosition),
                FilePositionInformation
                );
    if ( !NT_SUCCESS(Status) ) {
        BaseSetLastNTError(Status);
        return FALSE;
        }

    //
    // Set the end of file based on the current file position
    //

    EndOfFile.EndOfFile = CurrentPosition.CurrentByteOffset;

    Status = NtSetInformationFile(
                hFile,
                &IoStatusBlock,
                &EndOfFile,
                sizeof(EndOfFile),
                FileEndOfFileInformation
                );
    if ( !NT_SUCCESS(Status) ) {
        BaseSetLastNTError(Status);
        return FALSE;
        }

    //
    // Set the allocation based on the current file size
    //

    Allocation.AllocationSize = CurrentPosition.CurrentByteOffset;

    Status = NtSetInformationFile(
                hFile,
                &IoStatusBlock,
                &Allocation,
                sizeof(Allocation),
                FileAllocationInformation
                );
    if ( NT_SUCCESS(Status) ) {
        return TRUE;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }
}

DWORD
WINAPI
SetFilePointer(
    HANDLE hFile,
    LONG lDistanceToMove,
    PLONG lpDistanceToMoveHigh,
    DWORD dwMoveMethod
    )

/*++

Routine Description:

    An open file's file pointer can be set using SetFilePointer.

    The purpose of this function is to update the current value of a
    file's file pointer.  Care should be taken in multi-threaded
    applications that have multiple threads sharing a file handle with
    each thread updating the file pointer and then doing a read.  This
    sequence should be treated as a critical section of code and should
    be protected using either a critical section object or a mutex
    object.

    This API provides the same functionality as DOS (int 21h, function
    42h) and OS/2's DosSetFilePtr.

Arguments:

    hFile - Supplies an open handle to a file whose file pointer is to be
        moved.  The file handle must have been created with
        GENERIC_READ or GENERIC_WRITE access to the file.

    lDistanceToMove - Supplies the number of bytes to move the file
        pointer.  A positive value moves the pointer forward in the file
        and a negative value moves backwards in the file.

    lpDistanceToMoveHigh - An optional parameter that if specified
        supplies the high order 32-bits of the 64-bit distance to move.
        If the value of this parameter is NULL, this API can only
        operate on files whose maximum size is (2**32)-2.  If this
        parameter is specified, than the maximum file size is (2**64)-2.
        This value also returns the high order 32-bits of the new value
        of the file pointer.  If this value, and the return value
        are 0xffffffff, then an error is indicated.

    dwMoveMethod - Supplies a value that specifies the starting point
        for the file pointer move.

        FILE_BEGIN - The starting point is zero or the beginning of the
            file.  If FILE_BEGIN is specified, then DistanceToMove is
            interpreted as an unsigned location for the new
            file pointer.

        FILE_CURRENT - The current value of the file pointer is used as
            the starting point.

        FILE_END - The current end of file position is used as the
            starting point.


Return Value:

    Not -1 - Returns the low order 32-bits of the new value of the file
        pointer.

    0xffffffff - If the value of lpDistanceToMoveHigh was NULL, then The
        operation failed.  Extended error status is available using
        GetLastError.  Otherwise, this is the low order 32-bits of the
        new value of the file pointer.

--*/

{

    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    FILE_POSITION_INFORMATION CurrentPosition;
    FILE_STANDARD_INFORMATION StandardInfo;
    LARGE_INTEGER Large;

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return (DWORD)-1;
        }

    if (ARGUMENT_PRESENT(lpDistanceToMoveHigh)) {
        Large.HighPart = *lpDistanceToMoveHigh;
        Large.LowPart = lDistanceToMove;
        }
    else {
        Large.QuadPart = lDistanceToMove;
        }
    switch (dwMoveMethod) {
        case FILE_BEGIN :
            CurrentPosition.CurrentByteOffset = Large;
                break;

        case FILE_CURRENT :

            //
            // Get the current position of the file pointer
            //

            Status = NtQueryInformationFile(
                        hFile,
                        &IoStatusBlock,
                        &CurrentPosition,
                        sizeof(CurrentPosition),
                        FilePositionInformation
                        );
            if ( !NT_SUCCESS(Status) ) {
                BaseSetLastNTError(Status);
                return (DWORD)-1;
                }
            CurrentPosition.CurrentByteOffset.QuadPart += Large.QuadPart;
            break;

        case FILE_END :
            Status = NtQueryInformationFile(
                        hFile,
                        &IoStatusBlock,
                        &StandardInfo,
                        sizeof(StandardInfo),
                        FileStandardInformation
                        );
            if ( !NT_SUCCESS(Status) ) {
                BaseSetLastNTError(Status);
                return (DWORD)-1;
                }
            CurrentPosition.CurrentByteOffset.QuadPart =
                                StandardInfo.EndOfFile.QuadPart + Large.QuadPart;
            break;

        default:
            SetLastError(ERROR_INVALID_PARAMETER);
            return (DWORD)-1;
            break;
        }

    //
    // If the resulting file position is negative, or if the app is not
    // prepared for greater than
    // then 32 bits than fail
    //

    if ( CurrentPosition.CurrentByteOffset.QuadPart < 0 ) {
        SetLastError(ERROR_NEGATIVE_SEEK);
        return (DWORD)-1;
        }
    if ( !ARGUMENT_PRESENT(lpDistanceToMoveHigh) &&
        (CurrentPosition.CurrentByteOffset.HighPart & MAXLONG) ) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (DWORD)-1;
        }


    //
    // Set the current file position
    //

    Status = NtSetInformationFile(
                hFile,
                &IoStatusBlock,
                &CurrentPosition,
                sizeof(CurrentPosition),
                FilePositionInformation
                );
    if ( NT_SUCCESS(Status) ) {
        if (ARGUMENT_PRESENT(lpDistanceToMoveHigh)){
            *lpDistanceToMoveHigh = CurrentPosition.CurrentByteOffset.HighPart;
            }
        if ( CurrentPosition.CurrentByteOffset.LowPart == -1 ) {
            SetLastError(0);
            }
        return CurrentPosition.CurrentByteOffset.LowPart;
        }
    else {
        BaseSetLastNTError(Status);
        if (ARGUMENT_PRESENT(lpDistanceToMoveHigh)){
            *lpDistanceToMoveHigh = -1;
            }
        return (DWORD)-1;
        }
}


BOOL
WINAPI
SetFilePointerEx(
    HANDLE hFile,
    LARGE_INTEGER liDistanceToMove,
    PLARGE_INTEGER lpNewFilePointer,
    DWORD dwMoveMethod
    )

/*++

Routine Description:

    An open file's file pointer can be set using SetFilePointer.

    The purpose of this function is to update the current value of a
    file's file pointer.  Care should be taken in multi-threaded
    applications that have multiple threads sharing a file handle with
    each thread updating the file pointer and then doing a read.  This
    sequence should be treated as a critical section of code and should
    be protected using either a critical section object or a mutex
    object.

    This API provides the same functionality as DOS (int 21h, function
    42h) and OS/2's DosSetFilePtr.

Arguments:

    hFile - Supplies an open handle to a file whose file pointer is to be
        moved.  The file handle must have been created with
        GENERIC_READ or GENERIC_WRITE access to the file.

    liDistanceToMove - Supplies the number of bytes to move the file
        pointer.  A positive value moves the pointer forward in the file
        and a negative value moves backwards in the file.

    lpNewFilePointer - An optional parameter that if specified returns
        the new file pointer

    dwMoveMethod - Supplies a value that specifies the starting point
        for the file pointer move.

        FILE_BEGIN - The starting point is zero or the beginning of the
            file.  If FILE_BEGIN is specified, then DistanceToMove is
            interpreted as an unsigned location for the new
            file pointer.

        FILE_CURRENT - The current value of the file pointer is used as
            the starting point.

        FILE_END - The current end of file position is used as the
            starting point.


Return Value:

    TRUE - The operation was successful

    FALSE - The operation failed. Extended error status is available using
        GetLastError.

--*/

{

    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    FILE_POSITION_INFORMATION CurrentPosition;
    FILE_STANDARD_INFORMATION StandardInfo;
    LARGE_INTEGER Large;

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
        }
    Large = liDistanceToMove;

    switch (dwMoveMethod) {
        case FILE_BEGIN :
            CurrentPosition.CurrentByteOffset = Large;
                break;

        case FILE_CURRENT :

            //
            // Get the current position of the file pointer
            //

            Status = NtQueryInformationFile(
                        hFile,
                        &IoStatusBlock,
                        &CurrentPosition,
                        sizeof(CurrentPosition),
                        FilePositionInformation
                        );
            if ( !NT_SUCCESS(Status) ) {
                BaseSetLastNTError(Status);
                return FALSE;
                }
            CurrentPosition.CurrentByteOffset.QuadPart += Large.QuadPart;
            break;

        case FILE_END :
            Status = NtQueryInformationFile(
                        hFile,
                        &IoStatusBlock,
                        &StandardInfo,
                        sizeof(StandardInfo),
                        FileStandardInformation
                        );
            if ( !NT_SUCCESS(Status) ) {
                BaseSetLastNTError(Status);
                return FALSE;
                }
            CurrentPosition.CurrentByteOffset.QuadPart =
                                StandardInfo.EndOfFile.QuadPart + Large.QuadPart;
            break;

        default:
            SetLastError(ERROR_INVALID_PARAMETER);
            return FALSE;
            break;
        }

    //
    // If the resulting file position is negative fail
    //

    if ( CurrentPosition.CurrentByteOffset.QuadPart < 0 ) {
        SetLastError(ERROR_NEGATIVE_SEEK);
        return FALSE;
        }


    //
    // Set the current file position
    //

    Status = NtSetInformationFile(
                hFile,
                &IoStatusBlock,
                &CurrentPosition,
                sizeof(CurrentPosition),
                FilePositionInformation
                );
    if ( NT_SUCCESS(Status) ) {
        if (ARGUMENT_PRESENT(lpNewFilePointer)){
            *lpNewFilePointer = CurrentPosition.CurrentByteOffset;
            }
        return TRUE;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }
}



BOOL
WINAPI
GetFileInformationByHandle(
    HANDLE hFile,
    LPBY_HANDLE_FILE_INFORMATION lpFileInformation
    )

/*++

Routine Description:


Arguments:

    hFile - Supplies an open handle to a file whose modification date and
        times are to be read.  The file handle must have been created with
        GENERIC_READ access to the file.

    lpCreationTime - An optional parameter that if specified points to
        the location to return the date and time the file was created.
        A returned time of all zero indicates that the file system
        containing the file does not support this time value.


Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    BY_HANDLE_FILE_INFORMATION LocalFileInformation;
    FILE_ALL_INFORMATION FileInformation;
    FILE_FS_VOLUME_INFORMATION VolumeInfo;

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
        }

    Status = NtQueryVolumeInformationFile(
                hFile,
                &IoStatusBlock,
                &VolumeInfo,
                sizeof(VolumeInfo),
                FileFsVolumeInformation
                );
    if ( !NT_ERROR(Status) ) {
        LocalFileInformation.dwVolumeSerialNumber = VolumeInfo.VolumeSerialNumber;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }


    Status = NtQueryInformationFile(
                hFile,
                &IoStatusBlock,
                &FileInformation,
                sizeof(FileInformation),
                FileAllInformation
                );

    //
    // we really plan for buffer overflow
    //

    if ( !NT_ERROR(Status) ) {
        LocalFileInformation.dwFileAttributes = FileInformation.BasicInformation.FileAttributes;
        LocalFileInformation.ftCreationTime = *(LPFILETIME)&FileInformation.BasicInformation.CreationTime;
        LocalFileInformation.ftLastAccessTime = *(LPFILETIME)&FileInformation.BasicInformation.LastAccessTime;
        LocalFileInformation.ftLastWriteTime = *(LPFILETIME)&FileInformation.BasicInformation.LastWriteTime;
        LocalFileInformation.nFileSizeHigh = FileInformation.StandardInformation.EndOfFile.HighPart;
        LocalFileInformation.nFileSizeLow = FileInformation.StandardInformation.EndOfFile.LowPart;
        LocalFileInformation.nNumberOfLinks = FileInformation.StandardInformation.NumberOfLinks;
        LocalFileInformation.nFileIndexHigh = FileInformation.InternalInformation.IndexNumber.HighPart;
        LocalFileInformation.nFileIndexLow = FileInformation.InternalInformation.IndexNumber.LowPart;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }
    *lpFileInformation = LocalFileInformation;
    return TRUE;
}

BOOL
APIENTRY
GetFileTime(
    HANDLE hFile,
    LPFILETIME lpCreationTime,
    LPFILETIME lpLastAccessTime,
    LPFILETIME lpLastWriteTime
    )

/*++

Routine Description:

    The date and time that a file was created, last accessed or last
    modified can be read using GetFileTime.  File time stamps are
    returned as 64-bit values, that represent the number of 100
    nanoseconds since January 1st, 1601.  This date was chosen because
    it is the start of a new quadricentury.  At 100ns resolution 32 bits
    is good for about 429 seconds (or 7 minutes) and a 63-bit integer is
    good for about 29,247 years, or around 10,682,247 days.

    This API provides the same functionality as DOS (int 21h, function
    47H with AL=0), and provides a subset of OS/2's DosQueryFileInfo.

Arguments:

    hFile - Supplies an open handle to a file whose modification date and
        times are to be read.  The file handle must have been created with
        GENERIC_READ access to the file.

    lpCreationTime - An optional parameter that if specified points to
        the location to return the date and time the file was created.
        A returned time of all zero indicates that the file system
        containing the file does not support this time value.

    lpLastAccessTime - An optional parameter that if specified points to
        the location to return the date and time the file was last accessed.
        A returned time of all zero indicates that the file system
        containing the file does not support this time value.

    lpLastWriteTime - An optional parameter that if specified points to
        the location to return the date and time the file was last written.
        A file system must support this time and thus a valid value will
        always be returned for this time value.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    FILE_BASIC_INFORMATION BasicInfo;

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
        }

    //
    // Get the attributes
    //

    Status = NtQueryInformationFile(
                hFile,
                &IoStatusBlock,
                &BasicInfo,
                sizeof(BasicInfo),
                FileBasicInformation
                );

    if ( NT_SUCCESS(Status) ) {
        if (ARGUMENT_PRESENT( lpCreationTime )) {
            *lpCreationTime = *(LPFILETIME)&BasicInfo.CreationTime;
            }

        if (ARGUMENT_PRESENT( lpLastAccessTime )) {
            *lpLastAccessTime = *(LPFILETIME)&BasicInfo.LastAccessTime;
            }

        if (ARGUMENT_PRESENT( lpLastWriteTime )) {
            *lpLastWriteTime = *(LPFILETIME)&BasicInfo.LastWriteTime;
            }
        return TRUE;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }
}

BOOL
WINAPI
SetFileTime(
    HANDLE hFile,
    CONST FILETIME *lpCreationTime,
    CONST FILETIME *lpLastAccessTime,
    CONST FILETIME *lpLastWriteTime
    )

/*++

Routine Description:

    The date and time that a file was created, last accessed or last
    modified can be modified using SetFileTime.  File time stamps are
    returned as 64-bit values, that represent the number of 100
    nanoseconds since January 1st, 1601.  This date was chosen because
    it is the start of a new quadricentury.  At 100ns resolution 32 bits
    is good for about 429 seconds (or 7 minutes) and a 63-bit integer is
    good for about 29,247 years, or around 10,682,247 days.

    This API provides the same functionality as DOS (int 21h, function
    47H with AL=1), and provides a subset of OS/2's DosSetFileInfo.

Arguments:

    hFile - Supplies an open handle to a file whose modification date and
        times are to be written.  The file handle must have been created
        with GENERIC_WRITE access to the file.

    lpCreationTime - An optional parameter, that if specified supplies
        the new creation time for the file.  Some file system's do not
        support this time value, so this parameter may be ignored.

    lpLastAccessTime - An optional parameter, that if specified supplies
        the new last access time for the file.  Some file system's do
        not support this time value, so this parameter may be ignored.

    lpLastWriteTime - An optional parameter, that if specified supplies
        the new last write time for the file.  A file system must support
        this time value.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    FILE_BASIC_INFORMATION BasicInfo;

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
        }

    //
    // Zero all the time values we can set.
    //

    RtlZeroMemory(&BasicInfo,sizeof(BasicInfo));

    //
    // For each time value that is specified, copy it to the I/O system
    // record.
    //
    if (ARGUMENT_PRESENT( lpCreationTime )) {
        BasicInfo.CreationTime.LowPart = lpCreationTime->dwLowDateTime;
        BasicInfo.CreationTime.HighPart = lpCreationTime->dwHighDateTime;
        }

    if (ARGUMENT_PRESENT( lpLastAccessTime )) {
        BasicInfo.LastAccessTime.LowPart = lpLastAccessTime->dwLowDateTime;
        BasicInfo.LastAccessTime.HighPart = lpLastAccessTime->dwHighDateTime;
        }

    if (ARGUMENT_PRESENT( lpLastWriteTime )) {
        BasicInfo.LastWriteTime.LowPart = lpLastWriteTime->dwLowDateTime;
        BasicInfo.LastWriteTime.HighPart = lpLastWriteTime->dwHighDateTime;
        }

    //
    // Set the requested times.
    //

    Status = NtSetInformationFile(
                hFile,
                &IoStatusBlock,
                &BasicInfo,
                sizeof(BasicInfo),
                FileBasicInformation
                );

    if ( NT_SUCCESS(Status) ) {
        return TRUE;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }
}

BOOL
WINAPI
FlushFileBuffers(
    HANDLE hFile
    )

/*++

Routine Description:

    Buffered data may be flushed out to the file using the
    FlushFileBuffers service.

    The FlushFileBuffers service causes all buffered data to be written
    to the specified file.

Arguments:

    hFile - Supplies an open handle to a file whose buffers are to be
        flushed.  The file handle must have been created with
        GENERIC_WRITE access to the file.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    PPEB Peb;

    Peb = NtCurrentPeb();

    switch( HandleToUlong(hFile) ) {
        case STD_INPUT_HANDLE:  hFile = Peb->ProcessParameters->StandardInput;
                                break;
        case STD_OUTPUT_HANDLE: hFile = Peb->ProcessParameters->StandardOutput;
                                break;
        case STD_ERROR_HANDLE:  hFile = Peb->ProcessParameters->StandardError;
                                break;
        }

    if (CONSOLE_HANDLE(hFile)) {
        return( FlushConsoleInputBuffer( hFile ) );
        }

    Status = NtFlushBuffersFile(hFile,&IoStatusBlock);

    if ( NT_SUCCESS(Status) ) {
        return TRUE;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }
}

BOOL
WINAPI
LockFile(
    HANDLE hFile,
    DWORD dwFileOffsetLow,
    DWORD dwFileOffsetHigh,
    DWORD nNumberOfBytesToLockLow,
    DWORD nNumberOfBytesToLockHigh
    )

/*++

Routine Description:

    A byte range within an open file may be locked for exclusive access
    using LockFile.

    Locking a region of a file is used to aquire exclusive access to the
    specified region of the file.  File locks are not inherited by the
    new process during process creation.

    Locking a portion of a file denies all other processes both read and
    write access to the specified region of the file.  Locking a region
    that goes beyond the current end-of-file position is not an error.

    Locks may not overlap an existing locked region of the file.

    For DOS based systems running share.exe the lock semantics work as
    described above.  Without share.exe, all attempts to lock or unlock
    a file will fail.

Arguments:

    hFile - Supplies an open handle to a file that is to have a range of
        bytes locked for exclusive access.  The handle must have been
        created with either GENERIC_READ or GENERIC_WRITE access to the
        file.

    dwFileOffsetLow - Supplies the low order 32-bits of the starting
        byte offset of the file where the lock should begin.

    dwFileOffsetHigh - Supplies the high order 32-bits of the starting
        byte offset of the file where the lock should begin.

    nNumberOfBytesToLockLow - Supplies the low order 32-bits of the length
        of the byte range to be locked.

    nNumberOfBytesToLockHigh - Supplies the high order 32-bits of the length
        of the byte range to be locked.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    LARGE_INTEGER ByteOffset;
    LARGE_INTEGER Length;
    IO_STATUS_BLOCK IoStatusBlock;

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
        }

    ByteOffset.LowPart = dwFileOffsetLow;
    ByteOffset.HighPart = dwFileOffsetHigh;

    Length.LowPart = nNumberOfBytesToLockLow;
    Length.HighPart = nNumberOfBytesToLockHigh;

    Status = NtLockFile( hFile,
                         NULL,
                         NULL,
                         NULL,
                         &IoStatusBlock,
                         &ByteOffset,
                         &Length,
                         0,
                         TRUE,
                         TRUE
                       );

    if (Status == STATUS_PENDING) {

        Status = NtWaitForSingleObject( hFile, FALSE, NULL );
        if (NT_SUCCESS( Status )) {
            Status = IoStatusBlock.Status;
            }
        }

    if ( NT_SUCCESS(Status) ) {
        return TRUE;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }
}


BOOL
WINAPI
LockFileEx(
    HANDLE hFile,
    DWORD dwFlags,
    DWORD dwReserved,
    DWORD nNumberOfBytesToLockLow,
    DWORD nNumberOfBytesToLockHigh,
    LPOVERLAPPED lpOverlapped
    )

/*++

Routine Description:

    A byte range within an open file may be locked for shared or
    exclusive access using LockFileEx.

    Locking a region of a file is used to aquire shared or exclusive
    access to the specified region of the file.  File locks are not
    inherited by the new process during process creation.

    Locking a portion of a file for exclusive access denies all other
    processes both read and write access to the specified region of the
    file.  Locking a region that goes beyond the current end-of-file
    position is not an error.

    Locking a portion of a file for shared access denies all other
    processes write access to the specified region of the file, but
    allows other processes to read the locked region.

    If requesting an exclusive lock for a file that is already locked
    shared or exclusively by other threads, then this call will wait
    until the lock is granted unless the LOCKFILE_FAIL_IMMEDIATELY
    flag is specified.

    Locks may not overlap an existing locked region of the file.

Arguments:

    hFile - Supplies an open handle to a file that is to have a range of
        bytes locked for exclusive access.  The handle must have been
        created with either GENERIC_READ or GENERIC_WRITE access to the
        file.

    dwFlags - Supplies flag bits that modify the behavior of this function.

        LOCKFILE_FAIL_IMMEDIATELY - if set, then this function will return
            immediately if it is unable to acquire the requested lock.
            Otherwise it will wait.

        LOCKFILE_EXCLUSIVE_LOCK - if set, then this function requests an
            exclusive lock, otherwise it requested a shared lock.

    dwReserved - Reserved parameter that must be zero.

    nNumberOfBytesToLockLow - Supplies the low order 32-bits of the length
        of the byte range to be locked.

    nNumberOfBytesToLockHigh - Supplies the high order 32-bits of the length
        of the byte range to be locked.

    lpOverlapped - Required pointer to an OVERLAPPED structure to be
        used with the request.  It contains the file offset of the
        beginning of the lock range.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/


{
    NTSTATUS Status;
    LARGE_INTEGER ByteOffset;
    LARGE_INTEGER Length;

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
        }

    if (dwReserved != 0) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
        }

    ByteOffset.LowPart = lpOverlapped->Offset;
    ByteOffset.HighPart = lpOverlapped->OffsetHigh;

    Length.LowPart = nNumberOfBytesToLockLow;
    Length.HighPart = nNumberOfBytesToLockHigh;
    lpOverlapped->Internal = (DWORD)STATUS_PENDING;

    Status = NtLockFile( hFile,
                         lpOverlapped->hEvent,
                         NULL,
                         (ULONG_PTR)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped,
                         (PIO_STATUS_BLOCK)&lpOverlapped->Internal,
                         &ByteOffset,
                         &Length,
                         0,
                         (BOOLEAN)((dwFlags & LOCKFILE_FAIL_IMMEDIATELY) ? TRUE : FALSE),
                         (BOOLEAN)((dwFlags & LOCKFILE_EXCLUSIVE_LOCK) ? TRUE : FALSE)
                       );

    if ( NT_SUCCESS(Status) && Status != STATUS_PENDING) {
        return TRUE;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }
}


BOOL
WINAPI
UnlockFile(
    HANDLE hFile,
    DWORD dwFileOffsetLow,
    DWORD dwFileOffsetHigh,
    DWORD nNumberOfBytesToUnlockLow,
    DWORD nNumberOfBytesToUnlockHigh
    )

/*++

Routine Description:

    A previously locked byte range within an open file may be Unlocked
    using UnlockFile.

    Unlocking a region of a file is used release a previously aquired
    lock on a file.  The region to unlock must exactly correspond to an
    existing locked region.  Two adjacent regions of a file can not be
    locked seperately and then be unlocked using a single region that
    spans both locked regions.

    If a process terminates with a portion of a file locked, or closes a
    file that has outstanding locks, the behavior is not specified.

    For DOS based systems running share.exe the lock semantics work as
    described above.  Without share.exe, all attempts to lock or unlock
    a file will fail.

Arguments:

    hFile - Supplies an open handle to a file that is to have an
        existing locked region unlocked.  The handle must have been
        created with either GENERIC_READ or GENERIC_WRITE access to the
        file.

    dwFileOffsetLow - Supplies the low order 32-bits of an existing
        locked region to be unlocked.

    dwFileOffsetHigh - Supplies the high order 32-bits of an existing
        locked region to be unlocked.

    nNumberOfBytesToUnlockLow - Supplies the low order 32-bits of the
        length of the byte range to be unlocked.

    nNumberOfBytesToUnlockHigh - Supplies the high order 32-bits of the
        length of the byte range to be unlocked.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    BOOL bResult;
    OVERLAPPED Overlapped;
    NTSTATUS Status;

    Overlapped.Offset = dwFileOffsetLow;
    Overlapped.OffsetHigh = dwFileOffsetHigh;
    bResult = UnlockFileEx( hFile,
                            0,
                            nNumberOfBytesToUnlockLow,
                            nNumberOfBytesToUnlockHigh,
                            &Overlapped
                          );
    if (!bResult && GetLastError() == ERROR_IO_PENDING) {
        Status = NtWaitForSingleObject( hFile, FALSE, NULL );
        if (NT_SUCCESS( Status )) {
            Status = (NTSTATUS)Overlapped.Internal;
            }

        if ( NT_SUCCESS(Status) ) {
            return TRUE;
            }
        else {
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
    else {
        return bResult;
        }
}



BOOL
WINAPI
UnlockFileEx(
    HANDLE hFile,
    DWORD dwReserved,
    DWORD nNumberOfBytesToUnlockLow,
    DWORD nNumberOfBytesToUnlockHigh,
    LPOVERLAPPED lpOverlapped
    )

/*++

Routine Description:

    A previously locked byte range within an open file may be Unlocked
    using UnlockFile.

    Unlocking a region of a file is used release a previously aquired
    lock on a file.  The region to unlock must exactly correspond to an
    existing locked region.  Two adjacent regions of a file can not be
    locked seperately and then be unlocked using a single region that
    spans both locked regions.

    If a process terminates with a portion of a file locked, or closes a
    file that has outstanding locks, the behavior is not specified.

Arguments:

    hFile - Supplies an open handle to a file that is to have an
        existing locked region unlocked.  The handle must have been
        created with either GENERIC_READ or GENERIC_WRITE access to the
        file.

    dwReserved - Reserved parameter that must be zero.

    nNumberOfBytesToUnlockLow - Supplies the low order 32-bits of the
        length of the byte range to be unlocked.

    nNumberOfBytesToUnlockHigh - Supplies the high order 32-bits of the
        length of the byte range to be unlocked.

    lpOverlapped - Required pointer to an OVERLAPPED structure to be
        used with the request.  It contains the file offset of the
        beginning of the lock range.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/


{
    NTSTATUS Status;
    LARGE_INTEGER ByteOffset;
    LARGE_INTEGER Length;

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
        }

    if (dwReserved != 0) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
        }

    ByteOffset.LowPart = lpOverlapped->Offset;
    ByteOffset.HighPart = lpOverlapped->OffsetHigh;

    Length.LowPart = nNumberOfBytesToUnlockLow;
    Length.HighPart = nNumberOfBytesToUnlockHigh;

    Status = NtUnlockFile(
                hFile,
                (PIO_STATUS_BLOCK)&lpOverlapped->Internal,
                &ByteOffset,
                &Length,
                0
                );

    if ( NT_SUCCESS(Status) ) {
        return TRUE;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }
}

UINT
WINAPI
SetHandleCount(
    UINT uNumber
    )

/*++

Routine Description:

    This function changes the number of file handles available to a
    process.  For DOS based Win32, the default maximum number of file
    handles available to a process is 20.  For NT/Win32 systems, this
    API has no effect.

Arguments:

    uNumber - Specifies the number of file handles needed by the
        application.  The maximum is 255.

Return Value:

    The return value specifies the number of file handles actually
    available to the application.  It may be less than the number
    specified by the wNumber parameter.

--*/

{
    return uNumber;
}

DWORD
WINAPI
GetFileSize(
    HANDLE hFile,
    LPDWORD lpFileSizeHigh
    )

/*++

Routine Description:

    This function returns the size of the file specified by
    hFile. It is capable of returning 64-bits worth of file size.

    The return value contains the low order 32-bits of the file's size.
    The optional lpFileSizeHigh returns the high order 32-bits of the
    file's size.

Arguments:

    hFile - Supplies an open handle to a file whose size is to be
        returned.  The handle must have been created with either
        GENERIC_READ or GENERIC_WRITE access to the file.

    lpFileSizeHigh - An optional parameter, that if specified, returns
        the high order 64-bits of the file's size.


Return Value:

    Not -1 - Returns the low order 32-bits of the specified file's size.


    0xffffffff - If the value of size of the file cannot be determined,
        or an invalid handle or handle with inappropriate access, or a
        handle to a non-file is specified, this error is returned.  If
        the file's size (low 32-bits) is -1, then this value is
        returned, and GetLastError() will return 0.  Extended error
        status is available using GetLastError.


--*/

{
    BOOL b;
    LARGE_INTEGER Li;

    b = GetFileSizeEx(hFile,&Li);

    if ( b ) {

        if ( ARGUMENT_PRESENT(lpFileSizeHigh) ) {
            *lpFileSizeHigh = (DWORD)Li.HighPart;
            }
        if (Li.LowPart == -1 ) {
            SetLastError(0);
            }
        }
    else {
        Li.LowPart = -1;
        }

    return Li.LowPart;
}

BOOL
WINAPI
GetFileSizeEx(
    HANDLE hFile,
    PLARGE_INTEGER lpFileSize
    )

/*++

Routine Description:

    This function returns the size of the file specified by
    hFile. It is capable of returning 64-bits worth of file size.

Arguments:

    hFile - Supplies an open handle to a file whose size is to be
        returned.  The handle must have been created with either
        GENERIC_READ or GENERIC_WRITE access to the file.

    lpFileSize - Returns the files size


Return Value:

    TRUE - The operation was successful


    FALSE - The operation failed. Extended error
        status is available using GetLastError.


--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    FILE_STANDARD_INFORMATION StandardInfo;

    Status = NtQueryInformationFile(
                hFile,
                &IoStatusBlock,
                &StandardInfo,
                sizeof(StandardInfo),
                FileStandardInformation
                );
    if ( !NT_SUCCESS(Status) ) {
        BaseSetLastNTError(Status);
        return FALSE;
        }
    else {
        *lpFileSize = StandardInfo.EndOfFile;
        return TRUE;
        }
}

VOID
WINAPI
BasepIoCompletion(
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    DWORD Reserved
    )


/*++

Routine Description:

    This procedure is called to complete ReadFileEx and WriteFileEx
    asynchronous I/O. Its primary function is to extract the
    appropriate information from the passed IoStatusBlock and call the
    users completion routine.

    The users completion routine is called as:

        Routine Description:

            When an outstanding I/O completes with a callback, this
            function is called.  This function is only called while the
            thread is in an alertable wait (SleepEx,
            WaitForSingleObjectEx, or WaitForMultipleObjectsEx with the
            bAlertable flag set to TRUE).  Returning from this function
            allows another pendiong I/O completion callback to be
            processed.  If this is the case, this callback is entered
            before the termination of the thread's wait with a return
            code of WAIT_IO_COMPLETION.

            Note that each time your completion routine is called, the
            system uses some of your stack.  If you code your completion
            logic to do additional ReadFileEx's and WriteFileEx's within
            your completion routine, AND you do alertable waits in your
            completion routine, you may grow your stack without ever
            trimming it back.

        Arguments:

            dwErrorCode - Supplies the I/O completion status for the
                related I/O.  A value of 0 indicates that the I/O was
                successful.  Note that end of file is indicated by a
                non-zero dwErrorCode value of ERROR_HANDLE_EOF.

            dwNumberOfBytesTransfered - Supplies the number of bytes
                transfered during the associated I/O.  If an error
                occured, a value of 0 is supplied.

            lpOverlapped - Supplies the address of the OVERLAPPED
                structure used to initiate the associated I/O.  The
                hEvent field of this structure is not used by the system
                and may be used by the application to provide additional
                I/O context.  Once a completion routine is called, the
                system will not use the OVERLAPPED structure.  The
                completion routine is free to deallocate the overlapped
                structure.

Arguments:

    ApcContext - Supplies the users completion routine. The format of
        this routine is an LPOVERLAPPED_COMPLETION_ROUTINE.

    IoStatusBlock - Supplies the address of the IoStatusBlock that
        contains the I/O completion status. The IoStatusBlock is
        contained within the OVERLAPPED structure.

    Reserved - Not used; reserved for future use.

Return Value:

    None.

--*/

{
    PBASE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK ActivationBlock;
    LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine;
    DWORD dwErrorCode;
    DWORD dwNumberOfBytesTransfered;
    LPOVERLAPPED lpOverlapped;
    NTSTATUS Status;
    RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActivationFrame = { sizeof(ActivationFrame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
    PACTIVATION_CONTEXT ActivationContext = NULL;

    if ( NT_ERROR(IoStatusBlock->Status) ) {
        dwErrorCode = RtlNtStatusToDosError(IoStatusBlock->Status);
        dwNumberOfBytesTransfered = 0;
    } else {
        dwErrorCode = 0;
        dwNumberOfBytesTransfered = (DWORD)IoStatusBlock->Information;
    }

    ActivationBlock = (PBASE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK) ApcContext;
    ActivationContext = ActivationBlock->ActivationContext;
    CompletionRoutine = (LPOVERLAPPED_COMPLETION_ROUTINE) ActivationBlock->CallbackFunction;
    lpOverlapped = (LPOVERLAPPED) CONTAINING_RECORD(IoStatusBlock, OVERLAPPED, Internal);

    if (!(ActivationBlock->Flags & BASE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK_FLAG_DO_NOT_FREE_AFTER_CALLBACK))
        BasepFreeActivationContextActivationBlock(ActivationBlock);

    RtlActivateActivationContextUnsafeFast(&ActivationFrame, ActivationContext);
    __try {
        (*CompletionRoutine)(dwErrorCode, dwNumberOfBytesTransfered, lpOverlapped);
    } __finally {
        RtlDeactivateActivationContextUnsafeFast(&ActivationFrame);
    }

    Reserved;
}

VOID
WINAPI
BasepIoCompletionSimple(
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    DWORD Reserved
    )


/*++

Routine Description:

    This procedure is called to complete ReadFileEx and WriteFileEx
    asynchronous I/O. Its primary function is to extract the
    appropriate information from the passed IoStatusBlock and call the
    users completion routine.

    The users completion routine is called as:

        Routine Description:

            When an outstanding I/O completes with a callback, this
            function is called.  This function is only called while the
            thread is in an alertable wait (SleepEx,
            WaitForSingleObjectEx, or WaitForMultipleObjectsEx with the
            bAlertable flag set to TRUE).  Returning from this function
            allows another pendiong I/O completion callback to be
            processed.  If this is the case, this callback is entered
            before the termination of the thread's wait with a return
            code of WAIT_IO_COMPLETION.

            Note that each time your completion routine is called, the
            system uses some of your stack.  If you code your completion
            logic to do additional ReadFileEx's and WriteFileEx's within
            your completion routine, AND you do alertable waits in your
            completion routine, you may grow your stack without ever
            trimming it back.

        Arguments:

            dwErrorCode - Supplies the I/O completion status for the
                related I/O.  A value of 0 indicates that the I/O was
                successful.  Note that end of file is indicated by a
                non-zero dwErrorCode value of ERROR_HANDLE_EOF.

            dwNumberOfBytesTransfered - Supplies the number of bytes
                transfered during the associated I/O.  If an error
                occured, a value of 0 is supplied.

            lpOverlapped - Supplies the address of the OVERLAPPED
                structure used to initiate the associated I/O.  The
                hEvent field of this structure is not used by the system
                and may be used by the application to provide additional
                I/O context.  Once a completion routine is called, the
                system will not use the OVERLAPPED structure.  The
                completion routine is free to deallocate the overlapped
                structure.

Arguments:

    ApcContext - Supplies the users completion routine. The format of
        this routine is an LPOVERLAPPED_COMPLETION_ROUTINE.

    IoStatusBlock - Supplies the address of the IoStatusBlock that
        contains the I/O completion status. The IoStatusBlock is
        contained within the OVERLAPPED structure.

    Reserved - Not used; reserved for future use.

Return Value:

    None.

--*/

{
    LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine;
    DWORD dwErrorCode;
    DWORD dwNumberOfBytesTransfered;
    LPOVERLAPPED lpOverlapped;

    dwErrorCode = 0;

    if ( NT_ERROR(IoStatusBlock->Status) ) {
        dwErrorCode = RtlNtStatusToDosError(IoStatusBlock->Status);
        dwNumberOfBytesTransfered = 0;
        }
    else {
        dwErrorCode = 0;
        dwNumberOfBytesTransfered = (DWORD)IoStatusBlock->Information;
        }

    CompletionRoutine = (LPOVERLAPPED_COMPLETION_ROUTINE)ApcContext;
    lpOverlapped = (LPOVERLAPPED)CONTAINING_RECORD(IoStatusBlock,OVERLAPPED,Internal);

    (CompletionRoutine)(dwErrorCode,dwNumberOfBytesTransfered,lpOverlapped);

    Reserved;
}

BOOL
WINAPI
ReadFileEx(
    HANDLE hFile,
    LPVOID lpBuffer,
    DWORD nNumberOfBytesToRead,
    LPOVERLAPPED lpOverlapped,
    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
    )

/*++

Routine Description:

    Data can be read from a file using ReadFileEx.

    This API reports its completion status asynchronously by calling the
    specified lpCompletionRoutine.

    The caller of this routine uses the lpOverlappedStructure to specify
    the byte offset within the file where the read is to begin from.
    For files that do not support this concept (pipes...), the starting
    file offset is ignored.

    Upon successful completion of this API (return value of TRUE), the
    calling thread has an I/O outstanding.  When the I/O completes, and
    the thread is blocked in an alertable wait, the lpCompletionRoutine
    will be called and the wait will return with a return code of
    WAIT_IO_COMPLETION.  If the I/O completes, but the thread issuing
    the I/O is not in an alertable wait, the call to the completion
    routine is queued until the thread executes an alertable wait.

    If this API fails (by returning FALSE), GetLastError can be used to
    get additional error information.  If this call fails because the
    thread issued a read beyond the end of file, GetLastError will
    return a value of ERROR_HANDLE_EOF.

Arguments:

    hFile - Supplies an open handle to a file that is to be read.  The
        file handle must have been created with GENERIC_READ access to
        the file.  The file must have been created with the
        FILE_FLAG_OVERLAPPED flag.

    lpBuffer - Supplies the address of a buffer to receive the data read
        from the file.

    nNumberOfBytesToRead - Supplies the number of bytes to read from the
        file.

    lpOverlapped - Supplies the address of an OVERLAPPED structure to be
        used with the request.  The caller of this function must specify
        a starting byte offset within the file to start the read from.
        It does this using the Offset and OffsetHigh fields of the
        overlapped structure.  This call does not use or modify the
        hEvent field of the overlapped structure.  The caller may use
        this field for any purpose.  This API does use the Internal and
        InternalHigh fields of the overlapped structure, the thread
        should not manipulate this.  The lpOverlapped structure must
        remain valid for the duration of the I/O.  It is not a good idea
        to make it a local variable and then possibly returning from the
        routine with the I/O that is using this structure still pending.

Return Value:

    TRUE - The operation was successul.  Completion status will be
        propagated to the caller using the completion callback
        mechanism.  Note that this information is only made available to
        the thread that issued the I/O, and only when the I/O completes,
        and the thread is executing in an alertable wait.

    FALSE - The operation failed.  Extended error status is available
        using GetLastError. Note that end of file is treated as a failure
        with an error code of ERROR_HANDLE_EOF.

--*/
{
    NTSTATUS Status;
    LARGE_INTEGER Li;
    PBASE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK ActivationBlock = NULL;
    PIO_APC_ROUTINE IoApcRoutine = &BasepIoCompletionSimple;
    PVOID ApcContext = lpCompletionRoutine;

    Li.LowPart = lpOverlapped->Offset;
    Li.HighPart = lpOverlapped->OffsetHigh;

    // If there's an APC routine to call we need to allocate a little chunk of heap
    // to pass the activation context to the APC callback.
    if (lpCompletionRoutine != NULL) {
        Status = BasepAllocateActivationContextActivationBlock(
            BASEP_ALLOCATE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK_FLAG_DO_NOT_ALLOCATE_IF_PROCESS_DEFAULT,
            lpCompletionRoutine,
            lpOverlapped,
            &ActivationBlock);
        if (!NT_SUCCESS(Status)) {
            BaseSetLastNTError(Status);
            return FALSE;
        }

        // If there's nothing to do, call the simpler one that doesn't try to do activation context stuff
        if (ActivationBlock != NULL) {
            IoApcRoutine = &BasepIoCompletion;
            ApcContext = ActivationBlock;
        }
    }

    Status = NtReadFile(
                hFile,
                NULL,
                IoApcRoutine,
                ApcContext,
                (PIO_STATUS_BLOCK) &lpOverlapped->Internal,
                lpBuffer,
                nNumberOfBytesToRead,
                &Li,
                NULL
                );
    if ( NT_ERROR(Status) ) {
        if (ActivationBlock != NULL)
            BasepFreeActivationContextActivationBlock(ActivationBlock);
        BaseSetLastNTError(Status);
        return FALSE;
    }

    return TRUE;
}

BOOL
WINAPI
WriteFileEx(
    HANDLE hFile,
    LPCVOID lpBuffer,
    DWORD nNumberOfBytesToWrite,
    LPOVERLAPPED lpOverlapped,
    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
    )

/*++

Routine Description:

    Data can be written to a file using WriteFileEx.

    This API reports its completion status asynchronously by calling the
    specified lpCompletionRoutine.

    The caller of this routine uses the lpOverlappedStructure to specify
    the byte offset within the file where the write is to begin.
    For files that do not support this concept (pipes...), the starting
    file offset is ignored.

    Upon successful completion of this API (return value of TRUE), the
    calling thread has an I/O outstanding.  When the I/O completes, and
    the thread is blocked in an alertable wait, the lpCompletionRoutine
    will be called and the wait will return with a return code of
    WAIT_IO_COMPLETION.  If the I/O completes, but the thread issuing
    the I/O is not in an alertable wait, the call to the completion
    routine is queued until the thread executes an alertable wait.

    If this API fails (by returning FALSE), GetLastError can be used to
    get additional error information.

    Unlike DOS, a NumberOfBytesToWrite value of zero does not truncate
    or extend the file.  If this function is required, SetEndOfFile
    should be used.

Arguments:

    hFile - Supplies an open handle to a file that is to be written.  The
        file handle must have been created with GENERIC_WRITE access to
        the file.

    lpBuffer - Supplies the address of the data that is to be written to
        the file.

    nNumberOfBytesToWrite - Supplies the number of bytes to write to the
        file. Unlike DOS, a value of zero is interpreted a null write.

    lpOverlapped - Supplies the address of an OVERLAPPED structure to be
        used with the request.  The caller of this function must specify
        a starting byte offset within the file to start the write to.
        It does this using the Offset and OffsetHigh fields of the
        overlapped structure.  This call does not use or modify the
        hEvent field of the overlapped structure.  The caller may use
        this field for any purpose.  This API does use the Internal and
        InternalHigh fields of the overlapped structure, the thread
        should not manipulate this.  The lpOverlapped structure must
        remain valid for the duration of the I/O.  It is not a good idea
        to make it a local variable and then possibly returning from the
        routine with the I/O that is using this structure still pending.

Return Value:

    TRUE - The operation was successul.  Completion status will be
        propagated to the caller using the completion callback
        mechanism.  Note that this information is only made available to
        the thread that issued the I/O, and only when the I/O completes,
        and the thread is executing in an alertable wait.

    FALSE - The operation failed.  Extended error status is available
        using GetLastError. Note that end of file is treated as a failure
        with an error code of ERROR_HANDLE_EOF.

--*/
{
    NTSTATUS Status;
    LARGE_INTEGER Li;
    PBASE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK ActivationBlock = NULL;
    PIO_APC_ROUTINE IoApcRoutine = &BasepIoCompletionSimple;
    PVOID ApcContext = lpCompletionRoutine;

    Li.LowPart = lpOverlapped->Offset;
    Li.HighPart = lpOverlapped->OffsetHigh;

    // If there's an APC routine to call we may need to allocate a little chunk of heap
    // to pass to the APC callback.
    //
    // we'll replace the parameters to the common NtWriteFile call below so that
    // the control flow is obvious.
    //
    if (lpCompletionRoutine != NULL) {
        Status = BasepAllocateActivationContextActivationBlock(
            BASEP_ALLOCATE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK_FLAG_DO_NOT_ALLOCATE_IF_PROCESS_DEFAULT,
            lpCompletionRoutine,
            lpOverlapped,
            &ActivationBlock);
        if (!NT_SUCCESS(Status)) {
            BaseSetLastNTError(Status);
            return FALSE;
        }

        // If there's nothing to do, call the simpler one that doesn't try to do activation context stuff
        if (ActivationBlock != NULL) {
            IoApcRoutine = &BasepIoCompletion;
            ApcContext = ActivationBlock;
        }
    }

    Status = NtWriteFile(
                hFile,
                NULL,
                IoApcRoutine,
                ApcContext,
                (PIO_STATUS_BLOCK)&lpOverlapped->Internal,
                (LPVOID)lpBuffer,
                nNumberOfBytesToWrite,
                &Li,
                NULL
                );
    if ( NT_ERROR(Status) ) {
        if (ActivationBlock != NULL) {
            BasepFreeActivationContextActivationBlock(ActivationBlock);
        }
        BaseSetLastNTError(Status);
        return FALSE;
        }

    return TRUE;
}

BOOL
WINAPI
DeviceIoControl(
    HANDLE hDevice,
    DWORD dwIoControlCode,
    LPVOID lpInBuffer,
    DWORD nInBufferSize,
    LPVOID lpOutBuffer,
    DWORD nOutBufferSize,
    LPDWORD lpBytesReturned,
    LPOVERLAPPED lpOverlapped
    )

/*++

Routine Description:

    An operation on a device may be performed by calling the device driver
    directly using the DeviceIoContrl function.

    The device driver must first be opened to get a valid handle.

Arguments:

    hDevice - Supplies an open handle a device on which the operation is to
        be performed.

    dwIoControlCode - Supplies the control code for the operation. This
        control code determines on which type of device the operation must
        be performed and determines exactly what operation is to be
        performed.

    lpInBuffer - Suplies an optional pointer to an input buffer that contains
        the data required to perform the operation.  Whether or not the
        buffer is actually optional is dependent on the IoControlCode.

    nInBufferSize - Supplies the length of the input buffer in bytes.

    lpOutBuffer - Suplies an optional pointer to an output buffer into which
        the output data will be copied. Whether or not the buffer is actually
        optional is dependent on the IoControlCode.

    nOutBufferSize - Supplies the length of the output buffer in bytes.

    lpBytesReturned - Supplies a pointer to a dword which will receive the
        actual length of the data returned in the output buffer.

    lpOverlapped - An optional parameter that supplies an overlap structure to
        be used with the request. If NULL or the handle was created without
        FILE_FLAG_OVERLAPPED then the DeviceIoControl will not return until
        the operation completes.

        When lpOverlapped is supplied and FILE_FLAG_OVERLAPPED was specified
        when the handle was created, DeviceIoControl may return
        ERROR_IO_PENDING to allow the caller to continue processing while the
        operation completes. The event (or File handle if hEvent == NULL) will
        be set to the not signalled state before ERROR_IO_PENDING is
        returned. The event will be set to the signalled state upon completion
        of the request. GetOverlappedResult is used to determine the result
        when ERROR_IO_PENDING is returned.

Return Value:

    TRUE -- The operation was successful.

    FALSE -- The operation failed. Extended error status is available using
        GetLastError.

--*/
{

    NTSTATUS Status;
    BOOLEAN DevIoCtl;

    if ( dwIoControlCode >> 16 == FILE_DEVICE_FILE_SYSTEM ) {
        DevIoCtl = FALSE;
        }
    else {
        DevIoCtl = TRUE;
        }

    if ( ARGUMENT_PRESENT( lpOverlapped ) ) {
        lpOverlapped->Internal = (DWORD)STATUS_PENDING;

        if ( DevIoCtl ) {

            Status = NtDeviceIoControlFile(
                        hDevice,
                        lpOverlapped->hEvent,
                        NULL,             // APC routine
                        (ULONG_PTR)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped,
                        (PIO_STATUS_BLOCK)&lpOverlapped->Internal,
                        dwIoControlCode,  // IoControlCode
                        lpInBuffer,       // Buffer for data to the FS
                        nInBufferSize,
                        lpOutBuffer,      // OutputBuffer for data from the FS
                        nOutBufferSize    // OutputBuffer Length
                        );
            }
        else {

            Status = NtFsControlFile(
                        hDevice,
                        lpOverlapped->hEvent,
                        NULL,             // APC routine
                        (ULONG_PTR)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped,
                        (PIO_STATUS_BLOCK)&lpOverlapped->Internal,
                        dwIoControlCode,  // IoControlCode
                        lpInBuffer,       // Buffer for data to the FS
                        nInBufferSize,
                        lpOutBuffer,      // OutputBuffer for data from the FS
                        nOutBufferSize    // OutputBuffer Length
                        );

            }

        // handle warning value STATUS_BUFFER_OVERFLOW somewhat correctly
        if ( !NT_ERROR(Status) && ARGUMENT_PRESENT(lpBytesReturned) ) {
            try {
                *lpBytesReturned = (DWORD)lpOverlapped->InternalHigh;
                }
            except(EXCEPTION_EXECUTE_HANDLER) {
                *lpBytesReturned = 0;
                }
            }
        if ( NT_SUCCESS(Status) && Status != STATUS_PENDING) {
            return TRUE;
            }
        else {
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
    else
        {
        IO_STATUS_BLOCK Iosb;

        if ( DevIoCtl ) {
            Status = NtDeviceIoControlFile(
                        hDevice,
                        NULL,
                        NULL,             // APC routine
                        NULL,             // APC Context
                        &Iosb,
                        dwIoControlCode,  // IoControlCode
                        lpInBuffer,       // Buffer for data to the FS
                        nInBufferSize,
                        lpOutBuffer,      // OutputBuffer for data from the FS
                        nOutBufferSize    // OutputBuffer Length
                        );
            }
        else {
            Status = NtFsControlFile(
                        hDevice,
                        NULL,
                        NULL,             // APC routine
                        NULL,             // APC Context
                        &Iosb,
                        dwIoControlCode,  // IoControlCode
                        lpInBuffer,       // Buffer for data to the FS
                        nInBufferSize,
                        lpOutBuffer,      // OutputBuffer for data from the FS
                        nOutBufferSize    // OutputBuffer Length
                        );
            }

        if ( Status == STATUS_PENDING) {
            // Operation must complete before return & Iosb destroyed
            Status = NtWaitForSingleObject( hDevice, FALSE, NULL );
            if ( NT_SUCCESS(Status)) {
                Status = Iosb.Status;
                }
            }

        if ( NT_SUCCESS(Status) ) {
            *lpBytesReturned = (DWORD)Iosb.Information;
            return TRUE;
            }
        else {
            // handle warning value STATUS_BUFFER_OVERFLOW somewhat correctly
            if ( !NT_ERROR(Status) ) {
                *lpBytesReturned = (DWORD)Iosb.Information;
            }
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
}

BOOL
WINAPI
CancelIo(
    HANDLE hFile
    )

/*++

Routine Description:

    This routine cancels all of the outstanding I/O for the specified handle
    for the specified file.

Arguments:

    hFile - Supplies the handle to the file whose pending I/O is to be
        canceled.

Return Value:

    TRUE -- The operation was successful.

    FALSE -- The operation failed.  Extended error status is available using
        GetLastError.

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;

    //
    // Simply cancel the I/O for the specified file.
    //

    Status = NtCancelIoFile(hFile, &IoStatusBlock);

    if ( NT_SUCCESS(Status) ) {
        return TRUE;
        }
    else {
        BaseSetLastNTError(Status);
        return FALSE;
        }

}

BOOL
WINAPI
ReadFileScatter(
    HANDLE hFile,
    FILE_SEGMENT_ELEMENT aSegementArray[],
    DWORD nNumberOfBytesToRead,
    LPDWORD lpReserved,
    LPOVERLAPPED lpOverlapped
    )
/*++

Routine Description:

    Data can be read from a file using ReadFileScatter.  The data
    is then scatter to specified buffer segements.

    This API is used to read data from a file.  Data is read from the
    file from the position indicated by the file pointer.  After the
    read completes, the file pointer is adjusted by the number of bytes
    actually read.  A return value of TRUE coupled with a bytes read of
    0 indicates that the file pointer was beyond the current end of the
    file at the time of the read.

Arguments:

    hFile - Supplies an open handle to a file that is to be read.  The
        file handle must have been created with GENERIC_READ access to
        the file.

    aSegementArray - Supplies a pointer an array of virtual segments.
        A virtual segment is a memory buffer where part of the transferred data
        should be placed.  Segments are have a fix size of PAGE_SIZE
        and must be aligned on a PAGE_SIZE boundary.

    nNumberOfBytesToRead - Supplies the number of bytes to read from the file.

    lpReserved - Reserved for now.

    lpOverlapped - Optionally points to an OVERLAPPED structure to be used with the
    request. If NULL then the transfer starts at the current file position
    and ReadFile will not return until the operation completes.

    If the handle hFile was created without specifying FILE_FLAG_OVERLAPPED
    the file pointer is moved to the specified offset plus
    lpNumberOfBytesRead before ReadFile returns. ReadFile will wait for the
    request to complete before returning (it will not return
    ERROR_IO_PENDING).

    When FILE_FLAG_OVERLAPPED is specified, ReadFile may return
    ERROR_IO_PENDING to allow the calling function to continue processing
    while the operation completes. The event (or hFile if hEvent is NULL) will
    be set to the signalled state upon completion of the request.

    When the handle is created with FILE_FLAG_OVERLAPPED and lpOverlapped
    is set to NULL, ReadFile will return ERROR_INVALID_PARAMTER because
    the file offset is required.


Return Value:

    TRUE - The operation was successul.

    FALSE - The operation failed.  Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    LPDWORD lpNumberOfBytesRead = NULL;

    if ( ARGUMENT_PRESENT(lpReserved) ||
         !ARGUMENT_PRESENT( lpOverlapped )) {

        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;

        }

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
        }

    if ( ARGUMENT_PRESENT(lpNumberOfBytesRead) ) {
        *lpNumberOfBytesRead = 0;
        }

    if ( ARGUMENT_PRESENT( lpOverlapped ) ) {
        LARGE_INTEGER Li;

        lpOverlapped->Internal = (DWORD)STATUS_PENDING;
        Li.LowPart = lpOverlapped->Offset;
        Li.HighPart = lpOverlapped->OffsetHigh;
        Status = NtReadFileScatter(
                hFile,
                lpOverlapped->hEvent,
                NULL,
                (ULONG_PTR)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped,
                (PIO_STATUS_BLOCK)&lpOverlapped->Internal,
                aSegementArray,
                nNumberOfBytesToRead,
                &Li,
                NULL
                );


        if ( NT_SUCCESS(Status) && Status != STATUS_PENDING) {
            if ( ARGUMENT_PRESENT(lpNumberOfBytesRead) ) {
                try {
                    *lpNumberOfBytesRead = (DWORD)lpOverlapped->InternalHigh;
                    }
                except(EXCEPTION_EXECUTE_HANDLER) {
                    *lpNumberOfBytesRead = 0;
                    }
                }
            return TRUE;
            }
        else
        if (Status == STATUS_END_OF_FILE) {
            if ( ARGUMENT_PRESENT(lpNumberOfBytesRead) ) {
                *lpNumberOfBytesRead = 0;
                }
            BaseSetLastNTError(Status);
            return FALSE;
            }
        else {
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
    else
        {
        Status = NtReadFileScatter(
                hFile,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                aSegementArray,
                nNumberOfBytesToRead,
                NULL,
                NULL
                );

        if ( Status == STATUS_PENDING) {
            // Operation must complete before return & IoStatusBlock destroyed
            Status = NtWaitForSingleObject( hFile, FALSE, NULL );
            if ( NT_SUCCESS(Status)) {
                Status = IoStatusBlock.Status;
                }
            }

        if ( NT_SUCCESS(Status) ) {
            *lpNumberOfBytesRead = (DWORD)IoStatusBlock.Information;
            return TRUE;
            }
        else
        if (Status == STATUS_END_OF_FILE) {
            *lpNumberOfBytesRead = 0;
            return TRUE;
            }
        else {
            if ( NT_WARNING(Status) ) {
                *lpNumberOfBytesRead = (DWORD)IoStatusBlock.Information;
                }
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
}


BOOL
WINAPI
WriteFileGather(
    HANDLE hFile,
    FILE_SEGMENT_ELEMENT aSegementArray[],
    DWORD nNumberOfBytesToWrite,
    LPDWORD lpReserved,
    LPOVERLAPPED lpOverlapped
    )

/*++

Routine Description:

    Data can be written to a file using WriteFileGather.  The data can
    be in multple file segement buffers.

    This API is used to write data to a file.  Data is written to the
    file from the position indicated by the file pointer.  After the
    write completes, the file pointer is adjusted by the number of bytes
    actually written.

    Unlike DOS, a NumberOfBytesToWrite value of zero does not truncate
    or extend the file.  If this function is required, SetEndOfFile
    should be used.

Arguments:

    hFile - Supplies an open handle to a file that is to be written.  The
        file handle must have been created with GENERIC_WRITE access to
        the file.

    aSegementArray - Supplies a pointer an array of virtual segments.
        A virtual segment is a memory buffer where part of the transferred data
        should be placed.  Segments are have a fix size of PAGE_SIZE
        and must be aligned on a PAGE_SIZE boundary. The number of
        entries in the array must be equal to nNumberOfBytesToRead /
        PAGE_SIZE.

    nNumberOfBytesToWrite - Supplies the number of bytes to write to the
        file. Unlike DOS, a value of zero is interpreted a null write.

    lpReserved - Unused for now.

    lpOverlapped - Optionally points to an OVERLAPPED structure to be
        used with the request. If NULL then the transfer starts at the
        current file position and WriteFileGather will not return until the
        operation completes.

        If the handle <hFile> was created without specifying
        FILE_FLAG_OVERLAPPED the file pointer is moved to the specified
        offset plus lpNumberOfBytesWritten before WriteFile returns.
        WriteFile will wait for the request to complete before returning
        (it will not set ERROR_IO_PENDING).

        When FILE_FLAG_OVERLAPPED is specified, WriteFile may return
        ERROR_IO_PENDING to allow the calling function to continue processing
        while the operation completes. The event (or hFile if hEvent is NULL) will
        be set to the signalled state upon completion of the request.

        When the handle is created with FILE_FLAG_OVERLAPPED and lpOverlapped
        is set to NULL, WriteFile will return ERROR_INVALID_PARAMTER because
        the file offset is required.

Return Value:

    TRUE - The operation was a success.

    FALSE - The operation failed.  Extended error status is
        available using GetLastError.

--*/

{

    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    LPDWORD lpNumberOfBytesWritten = NULL;

    if ( ARGUMENT_PRESENT(lpReserved) ||
         !ARGUMENT_PRESENT( lpOverlapped )) {

        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;

        }

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
        }

    if ( ARGUMENT_PRESENT(lpNumberOfBytesWritten) ) {
        *lpNumberOfBytesWritten = 0;
        }

    if ( ARGUMENT_PRESENT( lpOverlapped ) ) {
        LARGE_INTEGER Li;

        lpOverlapped->Internal = (DWORD)STATUS_PENDING;
        Li.LowPart = lpOverlapped->Offset;
        Li.HighPart = lpOverlapped->OffsetHigh;
        Status = NtWriteFileGather(
                hFile,
                lpOverlapped->hEvent,
                NULL,
                (ULONG_PTR)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped,
                (PIO_STATUS_BLOCK)&lpOverlapped->Internal,
                aSegementArray,
                nNumberOfBytesToWrite,
                &Li,
                NULL
                );

        if ( !NT_ERROR(Status) && Status != STATUS_PENDING) {
            if ( ARGUMENT_PRESENT(lpNumberOfBytesWritten) ) {
                try {
                    *lpNumberOfBytesWritten = (DWORD)lpOverlapped->InternalHigh;
                    }
                except(EXCEPTION_EXECUTE_HANDLER) {
                    *lpNumberOfBytesWritten = 0;
                    }
                }
            return TRUE;
            }
        else  {
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
    else {
        Status = NtWriteFileGather(
                hFile,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                aSegementArray,
                nNumberOfBytesToWrite,
                NULL,
                NULL
                );

        if ( Status == STATUS_PENDING) {
            // Operation must complete before return & IoStatusBlock destroyed
            Status = NtWaitForSingleObject( hFile, FALSE, NULL );
            if ( NT_SUCCESS(Status)) {
                Status = IoStatusBlock.Status;
                }
            }

        if ( NT_SUCCESS(Status)) {
            *lpNumberOfBytesWritten = (DWORD)IoStatusBlock.Information;
            return TRUE;
            }
        else {
            if ( NT_WARNING(Status) ) {
                *lpNumberOfBytesWritten = (DWORD)IoStatusBlock.Information;
                }
            BaseSetLastNTError(Status);
            return FALSE;
            }
        }
}


BOOL
APIENTRY
SetFileValidData(
    IN HANDLE hFile,
    IN LONGLONG ValidDataLength
    )

/*++

Routine Description:

    SetFileValidData is used to set the valid data length for the given file.

Arguments:

    hFile - Supplies an open handle to a file whose type valid data
        length is to be set

    ValidDataLength - Supplies the desired valid data length

Return Value:

    TRUE - The operation was a success.

    FALSE - The operation failed.  Extended error status is
        available using GetLastError.

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    FILE_VALID_DATA_LENGTH_INFORMATION ValidDataInfo;

    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
    }

    ValidDataInfo.ValidDataLength.QuadPart = ValidDataLength;

    Status = NtSetInformationFile(
        hFile,
        &IoStatusBlock,
        &ValidDataInfo,
        sizeof(FILE_VALID_DATA_LENGTH_INFORMATION),
        FileValidDataLengthInformation
        );
    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    return TRUE;
}


BOOL
APIENTRY
SetFileShortNameW(
    IN HANDLE hFile,
    IN LPCWSTR lpShortName
    )

/*++

Routine Description:

    SetFileShortNameW is used to set the short name for the given file.

Arguments:

    hFile - Supplies an open handle to a file whose short name is to be changed

    lpShortName - Supplies the desired short name

Return Value:

    TRUE - The operation was a success.

    FALSE - The operation failed.  Extended error status is
        available using GetLastError.

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    PFILE_NAME_INFORMATION FileNameInfo;
    DWORD FileNameInfoSize;
    DWORD FileInformationClass;


    if (CONSOLE_HANDLE(hFile)) {
        BaseSetLastNTError(STATUS_INVALID_HANDLE);
        return FALSE;
    }

    if (!ARGUMENT_PRESENT(lpShortName)) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    FileNameInfoSize = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + ((wcslen(lpShortName)+1)*sizeof(WCHAR));
    FileNameInfo = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG(TMP_TAG), FileNameInfoSize );
    if (!FileNameInfo) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    FileNameInfo->FileNameLength = wcslen(lpShortName) * sizeof(WCHAR);
    wcscpy( FileNameInfo->FileName, lpShortName );

    Status = NtSetInformationFile(
        hFile,
        &IoStatusBlock,
        FileNameInfo,
        FileNameInfoSize,
        FileShortNameInformation
        );

    RtlFreeHeap( RtlProcessHeap(), 0, FileNameInfo );

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    FileInformationClass = FileShortNameInformation;

    if ((FileInformationClass == FileEndOfFileInformation) ||
         (FileInformationClass == FileAllocationInformation) ||
         (FileInformationClass == FilePositionInformation))
    {
        return FALSE;
    }



    return TRUE;
}


BOOL
APIENTRY
SetFileShortNameA(
    IN HANDLE hFile,
    IN LPCSTR lpShortName
    )

/*++

Routine Description:

    SetFileShortNameW is used to set the short name for the given file.

Arguments:

    hFile - Supplies an open handle to a file whose short name is to be changed

    lpShortName - Supplies the desired short name

Return Value:

    TRUE - The operation was a success.

    FALSE - The operation failed.  Extended error status is
        available using GetLastError.

--*/

{
    PUNICODE_STRING Unicode;

    Unicode = Basep8BitStringToStaticUnicodeString( lpShortName );
    if (Unicode == NULL) {
        return FALSE;
    }

    return ( SetFileShortNameW(
                hFile,
                (LPCWSTR)Unicode->Buffer
                )
            );
}
