/*++

Copyright (c) 1989-1993  Microsoft Corporation

Module Name:

    qsinfo.c

Abstract:

    This module contains the code to implement the NtQueryInformationFile and
    NtSetInformationFile system services for the NT I/O system.

Author:

    Darryl E. Havens (darrylh) 6-Jun-1989

Environment:

    Kernel mode only

Revision History:


--*/

#include "iomgr.h"

//
// Create local definitions for long flag names to make code slightly more
// readable.
//

#define FSIO_A  FILE_SYNCHRONOUS_IO_ALERT
#define FSIO_NA FILE_SYNCHRONOUS_IO_NONALERT

//
// Forward declarations of local routines.
//

ULONG
IopGetModeInformation(
    IN PFILE_OBJECT FileObject
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, IopGetModeInformation)
#pragma alloc_text(PAGE, NtQueryInformationFile)
#pragma alloc_text(PAGE, NtSetInformationFile)
#endif


ULONG
IopGetModeInformation(
    IN PFILE_OBJECT FileObject
    )

/*++

Routine Description:

    This encapsulates extracting and translating the mode bits from
    the passed file object, to be returned from a query information call.

Arguments:

    FileObject - Specifies the file object for which to return Mode info.

Return Value:

    The translated mode information is returned.

--*/

{
    ULONG mode = 0;

    if (FileObject->Flags & FO_WRITE_THROUGH) {
        mode = FILE_WRITE_THROUGH;
    }
    if (FileObject->Flags & FO_SEQUENTIAL_ONLY) {
        mode |= FILE_SEQUENTIAL_ONLY;
    }
    if (FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {
        mode |= FILE_NO_INTERMEDIATE_BUFFERING;
    }
    if (FileObject->Flags & FO_SYNCHRONOUS_IO) {
        if (FileObject->Flags & FO_ALERTABLE_IO) {
            mode |= FILE_SYNCHRONOUS_IO_ALERT;
        } else {
            mode |= FILE_SYNCHRONOUS_IO_NONALERT;
        }
    }
    if (FileObject->Flags & FO_DELETE_ON_CLOSE) {
        mode |= FILE_DELETE_ON_CLOSE;
    }
    return mode;
}

NTSTATUS
NtQueryInformationFile(
    IN HANDLE FileHandle,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    OUT PVOID FileInformation,
    IN ULONG Length,
    IN FILE_INFORMATION_CLASS FileInformationClass
    )

/*++

Routine Description:

    This service returns the requested information about a specified file.
    The information returned is determined by the FileInformationClass that
    is specified, and it is placed into the caller's FileInformation buffer.

Arguments:

    FileHandle - Supplies a handle to the file about which the requested
        information should be returned.

    IoStatusBlock - Address of the caller's I/O status block.

    FileInformation - Supplies a buffer to receive the requested information
        returned about the file.

    Length - Supplies the length, in bytes, of the FileInformation buffer.

    FileInformationClass - Specifies the type of information which should be
        returned about the file.

Return Value:

    The status returned is the final completion status of the operation.

--*/

{
    PIRP irp;
    NTSTATUS status;
    PFILE_OBJECT fileObject;
    PDEVICE_OBJECT deviceObject;
    PFAST_IO_DISPATCH fastIoDispatch;
    PKEVENT event = (PKEVENT) NULL;
    KPROCESSOR_MODE requestorMode;
    PIO_STACK_LOCATION irpSp;
    IO_STATUS_BLOCK localIoStatus;
    OBJECT_HANDLE_INFORMATION handleInformation;
    BOOLEAN synchronousIo;
    BOOLEAN skipDriver;
    PETHREAD CurrentThread;

    PAGED_CODE();

    //
    // Get the previous mode;  i.e., the mode of the caller.
    //

    CurrentThread = PsGetCurrentThread ();
    requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);

    if (requestorMode != KernelMode) {

        //
        // Ensure that the FileInformationClass parameter is legal for querying
        // information about the file.
        //

        if ((ULONG) FileInformationClass >= FileMaximumInformation ||
            !IopQueryOperationLength[FileInformationClass]) {
            return STATUS_INVALID_INFO_CLASS;
        }

        //
        // Ensure that the supplied buffer is large enough to contain the
        // information associated with the specified set operation that is
        // to be performed.
        //

        if (Length < (ULONG) IopQueryOperationLength[FileInformationClass]) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        //
        // The caller's access mode is not kernel so probe each of the arguments
        // and capture them as necessary.  If any failures occur, the condition
        // handler will be invoked to handle them.  It will simply cleanup and
        // return an access violation status code back to the system service
        // dispatcher.
        //

        try {

            //
            // The IoStatusBlock parameter must be writeable by the caller.
            //

            ProbeForWriteIoStatus( IoStatusBlock );

            //
            // The FileInformation buffer must be writeable by the caller.
            //

#if defined(_X86_)
            ProbeForWrite( FileInformation, Length, sizeof( ULONG ) );
#elif defined(_WIN64)

            //
            // If we are a wow64 process, follow the X86 rules
            //

            if (PsGetCurrentProcessByThread(CurrentThread)->Wow64Process) {
                ProbeForWrite( FileInformation, Length, sizeof( ULONG ) );
            } else {
                ProbeForWrite( FileInformation,
                               Length,
                               IopQuerySetAlignmentRequirement[FileInformationClass] );
            }
#else
            ProbeForWrite( FileInformation,
                           Length,
                           IopQuerySetAlignmentRequirement[FileInformationClass] );
#endif

        } except(EXCEPTION_EXECUTE_HANDLER) {

            //
            // An exception was incurred while probing the caller's
            // parameters.  Simply return an appropriate error status
            // code.
            //


            return GetExceptionCode();
        }

#if DBG

    } else {

        //
        // The caller's mode is kernel.  Ensure that at least the information
        // class and lengths are appropriate.
        //

        if ((ULONG) FileInformationClass >= FileMaximumInformation ||
            !IopQueryOperationLength[FileInformationClass]) {
            return STATUS_INVALID_INFO_CLASS;
        }

        if (Length < (ULONG) IopQueryOperationLength[FileInformationClass]) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

#endif // DBG

    }

    //
    // There were no blatant errors so far, so reference the file object so
    // the target device object can be found.  Note that if the handle does
    // not refer to a file object, or if the caller does not have the required
    // access to the file, then it will fail.
    //

    status = ObReferenceObjectByHandle( FileHandle,
                                        IopQueryOperationAccess[FileInformationClass],
                                        IoFileObjectType,
                                        requestorMode,
                                        (PVOID *) &fileObject,
                                        &handleInformation);

    if (!NT_SUCCESS( status )) {
        return status;
    }

    //
    // Get the address of the target device object.  If this file represents
    // a device that was opened directly, then simply use the device or its
    // attached device(s) directly.  Also get the address of the Fast Io
    // dispatch structure.
    //

    if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
        deviceObject = IoGetRelatedDeviceObject( fileObject );
    } else {
        deviceObject = IoGetAttachedDevice( fileObject->DeviceObject );
    }
    fastIoDispatch = deviceObject->DriverObject->FastIoDispatch;

    //
    // Make a special check here to determine whether this is a synchronous
    // I/O operation.  If it is, then wait here until the file is owned by
    // the current thread.  If this is not a (serialized) synchronous I/O
    // operation, then allocate and initialize the local event.
    //

    if (fileObject->Flags & FO_SYNCHRONOUS_IO) {

        BOOLEAN interrupted;

        if (!IopAcquireFastLock( fileObject )) {
            status = IopAcquireFileObjectLock( fileObject,
                                               requestorMode,
                                               (BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
                                               &interrupted );
            if (interrupted) {
                ObDereferenceObject( fileObject );
                return status;
            }
        }

        //
        // Make a special check here to determine whether or not the caller
        // is attempting to query the file position pointer.  If so, then
        // return it immediately and get out.
        //

        if (FileInformationClass == FilePositionInformation) {

            //
            // The caller has requested the current file position context
            // information.  This is a relatively frequent call, so it is
            // optimized here to cut through the normal IRP path.
            //
            // Begin by establishing a condition handler and attempting to
            // return both the file position information as well as the I/O
            // status block.  If writing the output buffer fails, then return
            // an appropriate error status code.  If writing the I/O status
            // block fails, then ignore the error.  This is what would
            // normally happen were everything to go through normal special
            // kernel APC processing.
            //

            BOOLEAN writingBuffer = TRUE;
            PFILE_POSITION_INFORMATION fileInformation = FileInformation;

            try {

                //
                // Return the current position information.
                //

                fileInformation->CurrentByteOffset = fileObject->CurrentByteOffset;
                writingBuffer = FALSE;

                //
                // Write the I/O status block.
                //

                IoStatusBlock->Status = STATUS_SUCCESS;
                IoStatusBlock->Information = sizeof( FILE_POSITION_INFORMATION );

            } except( EXCEPTION_EXECUTE_HANDLER ) {

                //
                // One of writing the caller's buffer or writing the I/O
                // status block failed.  Set the final status appropriately.
                //

                if (writingBuffer) {
                    status = GetExceptionCode();
                }

            }

            //
            // Note that the state of the event in the file object has not yet
            // been reset, so it need not be set either.  Therefore, simply
            // cleanup and return.
            //

            IopReleaseFileObjectLock( fileObject );
            ObDereferenceObject( fileObject );
            return status;

        //
        // Also do a special check if the caller it doing a query for basic or
        // standard information and if so then try the fast query calls if they
        // exist.
        //

        } else if (fastIoDispatch &&
                   (((FileInformationClass == FileBasicInformation) &&
                     fastIoDispatch->FastIoQueryBasicInfo) ||
                    ((FileInformationClass == FileStandardInformation) &&
                     fastIoDispatch->FastIoQueryStandardInfo))) {

            IO_STATUS_BLOCK localIoStatus;
            BOOLEAN queryResult = FALSE;
            BOOLEAN writingStatus = FALSE;

            //
            // Do the query and setting of the IoStatusBlock inside an exception
            // handler.  Note that if an exception occurs, other than writing
            // the status back, then the IRP route will be taken.  If an error
            // occurs attempting to write the status back to the caller's buffer
            // then it will be ignored, just as it would be on the long path.
            //

            try {

                if (FileInformationClass == FileBasicInformation) {
                    queryResult = fastIoDispatch->FastIoQueryBasicInfo( fileObject,
                                                                        TRUE,
                                                                        FileInformation,
                                                                        &localIoStatus,
                                                                        deviceObject );
                } else {
                    queryResult = fastIoDispatch->FastIoQueryStandardInfo( fileObject,
                                                                           TRUE,
                                                                           FileInformation,
                                                                           &localIoStatus,
                                                                           deviceObject );
                }

                if (queryResult) {
                    status = localIoStatus.Status;
                    writingStatus = TRUE;
                    *IoStatusBlock = localIoStatus;
                }

            } except( EXCEPTION_EXECUTE_HANDLER ) {

                //
                // If the result of the preceeding block is an exception that
                // occurred after the Fast I/O path itself, then the query
                // actually succeeded so everything is done already, but the
                // user's I/O status buffer is not writable.  This case is
                // ignored to be consistent w/the long path.
                //

                if (!writingStatus) {
                    status = GetExceptionCode();
                }
            }

            //
            // If the results of the preceeding statement block is true, then
            // the fast query call succeeeded, so simply cleanup and return.
            //

            if (queryResult) {

                //
                // Note that once again, the event in the file object has not
                // yet been set reset, so it need not be set to the Signaled
                // state, so simply cleanup and return.
                //

                IopReleaseFileObjectLock( fileObject );
                ObDereferenceObject( fileObject );
                return status;
            }
        }
        synchronousIo = TRUE;
    } else {

        //
        // This is a synchronous API being invoked for a file that is opened
        // for asynchronous I/O.  This means that this system service is
        // to synchronize the completion of the operation before returning
        // to the caller.  A local event is used to do this.
        //

        event = ExAllocatePool( NonPagedPool, sizeof( KEVENT ) );
        if (event == NULL) {
            ObDereferenceObject( fileObject );
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        KeInitializeEvent( event, SynchronizationEvent, FALSE );
        synchronousIo = FALSE;
    }

    //
    // Set the file object to the Not-Signaled state.
    //

    KeClearEvent( &fileObject->Event );

    //
    // Allocate and initialize the I/O Request Packet (IRP) for this operation.
    // The allocation is performed with an exception handler in case the
    // caller does not have enough quota to allocate the packet.
    //

    irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
    if (!irp) {

        //
        // An IRP could not be allocated.  Cleanup and return an appropriate
        // error status code.
        //

        if (!(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
            ExFreePool( event );
        }

        IopAllocateIrpCleanup( fileObject, (PKEVENT) NULL );

        return STATUS_INSUFFICIENT_RESOURCES;
    }
    irp->Tail.Overlay.OriginalFileObject = fileObject;
    irp->Tail.Overlay.Thread = CurrentThread;
    irp->RequestorMode = requestorMode;

    //
    // Fill in the service independent parameters in the IRP.
    //

    if (synchronousIo) {
        irp->UserEvent = (PKEVENT) NULL;
        irp->UserIosb = IoStatusBlock;
    } else {
        irp->UserEvent = event;
        irp->UserIosb = &localIoStatus;
        irp->Flags = IRP_SYNCHRONOUS_API;
    }
    irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;

    //
    // Get a pointer to the stack location for the first driver.  This will be
    // used to pass the original function codes and parameters.
    //

    irpSp = IoGetNextIrpStackLocation( irp );
    irpSp->MajorFunction = IRP_MJ_QUERY_INFORMATION;
    irpSp->FileObject = fileObject;

    //
    // Allocate a buffer which should be used to put the information into by
    // the driver.  This will be copied back to the caller's buffer when the
    // service completes.  This is done by setting the flag which says that
    // this is an input operation.
    //

    irp->UserBuffer = FileInformation;
    irp->AssociatedIrp.SystemBuffer = (PVOID) NULL;
    irp->MdlAddress = (PMDL) NULL;

    try {

        //
        // Allocate the system buffer using an exception handler so that
        // errors can be caught and handled.
        //

        irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuota( NonPagedPool,
                                                                   Length );
    } except(EXCEPTION_EXECUTE_HANDLER) {

        //
        // An exception was incurred by attempting to allocate the intermediary
        // system buffer.  Cleanup everything and return an appropriate error
        // status code.
        //

        IopExceptionCleanup( fileObject,
                             irp,
                             (PKEVENT) NULL,
                             event );

        return GetExceptionCode();
    }

    irp->Flags |= IRP_BUFFERED_IO |
                  IRP_DEALLOCATE_BUFFER |
                  IRP_INPUT_OPERATION |
                  IRP_DEFER_IO_COMPLETION;

    //
    // Copy the caller's parameters to the service-specific portion of the
    // IRP.
    //

    irpSp->Parameters.QueryFile.Length = Length;
    irpSp->Parameters.QueryFile.FileInformationClass = FileInformationClass;

    //
    // Insert the packet at the head of the IRP list for the thread.
    //

    IopQueueThreadIrp( irp );

    //
    // Update the operation count statistic for the current process for
    // operations other than read and write.
    //

    IopUpdateOtherOperationCount();

    //
    // Everything is now set to invoke the device driver with this request.
    // However, it is possible that the information that the caller wants
    // is device independent.  If this is the case, then the request can
    // be satisfied here without having to have all of the drivers implement
    // the same code.  Note that having the IRP is still necessary since
    // the I/O completion code requires it.
    //

    skipDriver = FALSE;

    if (FileInformationClass == FileAccessInformation) {

        PFILE_ACCESS_INFORMATION accessBuffer = irp->AssociatedIrp.SystemBuffer;

        //
        // Return the access information for this file.
        //

        accessBuffer->AccessFlags = handleInformation.GrantedAccess;

        //
        // Complete the I/O operation.
        //

        irp->IoStatus.Information = sizeof( FILE_ACCESS_INFORMATION );
        skipDriver = TRUE;

    } else if (FileInformationClass == FileModeInformation) {

        PFILE_MODE_INFORMATION modeBuffer = irp->AssociatedIrp.SystemBuffer;

        //
        // Return the mode information for this file.
        //

        modeBuffer->Mode = IopGetModeInformation( fileObject );

        //
        // Complete the I/O operation.
        //

        irp->IoStatus.Information = sizeof( FILE_MODE_INFORMATION );
        skipDriver = TRUE;

    } else if (FileInformationClass == FileAlignmentInformation) {

        PFILE_ALIGNMENT_INFORMATION alignmentInformation = irp->AssociatedIrp.SystemBuffer;

        //
        // Return the alignment information for this file.
        //

        alignmentInformation->AlignmentRequirement = deviceObject->AlignmentRequirement;

        //
        // Complete the I/O operation.
        //

        irp->IoStatus.Information = sizeof( FILE_ALIGNMENT_INFORMATION );
        skipDriver = TRUE;

    } else if (FileInformationClass == FileAllInformation) {

        PFILE_ALL_INFORMATION allInformation = irp->AssociatedIrp.SystemBuffer;

        //
        // The caller has requested all of the information about the file.
        // This request is handled specially because the service will fill
        // in the Access and Mode and Alignment information in the buffer
        // and then pass the buffer to the driver to fill in the remainder.
        //
        // Begin by returning the Access information for the file.
        //

        allInformation->AccessInformation.AccessFlags =
            handleInformation.GrantedAccess;

        //
        // Return the mode information for this file.
        //

        allInformation->ModeInformation.Mode =
            IopGetModeInformation( fileObject );

        //
        // Return the alignment information for this file.
        //

        allInformation->AlignmentInformation.AlignmentRequirement =
            deviceObject->AlignmentRequirement;

        //
        // Finally, set the information field of the IoStatus block in the IRP
        // to account for the amount information already filled in and invoke
        // the driver to fill in the remainder.
        //

        irp->IoStatus.Information = sizeof( FILE_ACCESS_INFORMATION ) +
                                    sizeof( FILE_MODE_INFORMATION ) +
                                    sizeof( FILE_ALIGNMENT_INFORMATION );
    }

    if (skipDriver) {

        //
        // The requested operation has already been performed.  Simply
        // set the final status in the packet and the return state.
        //

        status = STATUS_SUCCESS;
        irp->IoStatus.Status = STATUS_SUCCESS;

    } else {

        //
        // This is not a request that can be [completely] performed here, so
        // invoke the driver at its appropriate dispatch entry with the IRP.
        //

        status = IoCallDriver( deviceObject, irp );
    }

    //
    // If this operation was a synchronous I/O operation, check the return
    // status to determine whether or not to wait on the file object.  If
    // the file object is to be waited on, wait for the operation to complete
    // and obtain the final status from the file object itself.
    //

    if (status == STATUS_PENDING) {

        if (synchronousIo) {

            status = KeWaitForSingleObject( &fileObject->Event,
                                            Executive,
                                            requestorMode,
                                            (BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
                                            (PLARGE_INTEGER) NULL );

            if (status == STATUS_ALERTED || status == STATUS_USER_APC) {

                //
                // The wait request has ended either because the thread was
                // alerted or an APC was queued to this thread, because of
                // thread rundown or CTRL/C processing.  In either case, try
                // to bail out of this I/O request carefully so that the IRP
                // completes before this routine exists so that synchronization
                // with the file object will remain intact.
                //

                IopCancelAlertedRequest( &fileObject->Event, irp );

            }

            status = fileObject->FinalStatus;

            IopReleaseFileObjectLock( fileObject );

        } else {

            //
            // This is a normal synchronous I/O operation, as opposed to a
            // serialized synchronous I/O operation.  For this case, wait for
            // the local event and copy the final status information back to
            // the caller.
            //

            status = KeWaitForSingleObject( event,
                                            Executive,
                                            requestorMode,
                                            FALSE,
                                            (PLARGE_INTEGER) NULL );

            if (status == STATUS_ALERTED || status == STATUS_USER_APC) {

                //
                // The wait request has ended either because the thread was
                // alerted or an APC was queued to this thread, because of
                // thread rundown or CTRL/C processing.  In either case, try
                // to bail out of this I/O request carefully so that the IRP
                // completes before this routine exists or the event will not
                // be around to set to the Signaled state.
                //

                IopCancelAlertedRequest( event, irp );

            }

            status = localIoStatus.Status;

            try {

                *IoStatusBlock = localIoStatus;

            } except(EXCEPTION_EXECUTE_HANDLER) {

                //
                // An exception occurred attempting to write the caller's I/O
                // status block.  Simply change the final status of the operation
                // to the exception code.
                //

                status = GetExceptionCode();
            }

            ExFreePool( event );

        }

    } else {

        //
        // The I/O operation finished without return a status of pending.
        // This means that the operation has not been through I/O completion,
        // so it must be done here.
        //

        PKNORMAL_ROUTINE normalRoutine;
        PVOID normalContext;
        KIRQL irql;

        if (!synchronousIo) {

            //
            // This is not a synchronous I/O operation, it is a synchronous
            // I/O API to a file opened for asynchronous I/O.  Since this
            // code path need never wait on the allocated and supplied event,
            // get rid of it so that it doesn't have to be set to the
            // Signaled state by the I/O completion code.
            //

            irp->UserEvent = (PKEVENT) NULL;
            ExFreePool( event );
        }

        irp->UserIosb = IoStatusBlock;
        KeRaiseIrql( APC_LEVEL, &irql );
        IopCompleteRequest( &irp->Tail.Apc,
                            &normalRoutine,
                            &normalContext,
                            (PVOID *) &fileObject,
                            &normalContext );
        KeLowerIrql( irql );

        if (synchronousIo) {
            IopReleaseFileObjectLock( fileObject );
        }
    }

    return status;
}

NTSTATUS
NtSetInformationFile(
    IN HANDLE FileHandle,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    IN PVOID FileInformation,
    IN ULONG Length,
    IN FILE_INFORMATION_CLASS FileInformationClass
    )

/*++

Routine Description:

    This service changes the provided information about a specified file.  The
    information that is changed is determined by the FileInformationClass that
    is specified.  The new information is taken from the FileInformation buffer.

Arguments:

    FileHandle - Supplies a handle to the file whose information should be
        changed.

    IoStatusBlock - Address of the caller's I/O status block.

    FileInformation - Supplies a buffer containing the information which should
        be changed on the file.

    Length - Supplies the length, in bytes, of the FileInformation buffer.

    FileInformationClass - Specifies the type of information which should be
        changed about the file.

Return Value:

    The status returned is the final completion status of the operation.

--*/

{
    PIRP irp;
    NTSTATUS status;
    PFILE_OBJECT fileObject;
    PDEVICE_OBJECT deviceObject;
    PKEVENT event = (PKEVENT) NULL;
    KPROCESSOR_MODE requestorMode;
    PIO_STACK_LOCATION irpSp;
    IO_STATUS_BLOCK localIoStatus;
    HANDLE targetHandle = (HANDLE) NULL;
    BOOLEAN synchronousIo;
    PETHREAD CurrentThread;

    PAGED_CODE();

    //
    // Get the previous mode;  i.e., the mode of the caller.
    //

    CurrentThread = PsGetCurrentThread ();
    requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);

    if (requestorMode != KernelMode) {

        //
        // Ensure that the FileInformationClass parameter is legal for setting
        // information about the file.
        //

        if ((ULONG) FileInformationClass >= FileMaximumInformation ||
            !IopSetOperationLength[FileInformationClass]) {
            return STATUS_INVALID_INFO_CLASS;
        }

        //
        // Ensure that the supplied buffer is large enough to contain the
        // information associated with the specified set operation that is
        // to be performed.
        //

        if (Length < (ULONG) IopSetOperationLength[FileInformationClass]) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        //
        // The caller's access mode is user, so probe each of the arguments
        // and capture them as necessary.  If any failures occur, the condition
        // handler will be invoked to handle them.  It will simply cleanup and
        // return an access violation status code back to the system service
        // dispatcher.
        //

        try {

            //
            // The IoStatusBlock parameter must be writeable by the caller.
            //

            ProbeForWriteIoStatus( IoStatusBlock );

            //
            // The FileInformation buffer must be readable by the caller.
            //

#if defined(_X86_)
            ProbeForRead( FileInformation,
                          Length,
                          Length == sizeof( BOOLEAN ) ? sizeof( BOOLEAN ) : sizeof( ULONG ) );
#elif defined(_WIN64)
            // If we are a wow64 process, follow the X86 rules
            if (PsGetCurrentProcessByThread(CurrentThread)->Wow64Process) {
                ProbeForRead( FileInformation,
                              Length,
                              Length == sizeof( BOOLEAN ) ? sizeof( BOOLEAN ) : sizeof( ULONG ) );
            }
            else {
                ProbeForRead( FileInformation,
                              Length,
                              IopQuerySetAlignmentRequirement[FileInformationClass] );
            }
#else
            ProbeForRead( FileInformation,
                          Length,
                          IopQuerySetAlignmentRequirement[FileInformationClass] );
#endif

        } except(EXCEPTION_EXECUTE_HANDLER) {

            //
            // An exception was incurred while probing the caller's parameters.
            // Simply return an appropriate error status code.
            //

            return GetExceptionCode();

        }

#if DBG

    } else {

        //
        // The caller's mode is kernel.  Ensure that at least the information
        // class and lengths are appropriate.
        //

        if ((ULONG) FileInformationClass >= FileMaximumInformation ||
            !IopSetOperationLength[FileInformationClass]) {
            return STATUS_INVALID_INFO_CLASS;
        }

        if (Length < (ULONG) IopSetOperationLength[FileInformationClass]) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

#endif // DBG

    }

    //
    // There were no blatant errors so far, so reference the file object so
    // the target device object can be found.  Note that if the handle does
    // not refer to a file object, or if the caller does not have the required
    // access to the file, then it will fail.
    //

    status = ObReferenceObjectByHandle( FileHandle,
                                        IopSetOperationAccess[FileInformationClass],
                                        IoFileObjectType,
                                        requestorMode,
                                        (PVOID *) &fileObject,
                                        NULL );
    if (!NT_SUCCESS( status )) {
        return status;
    }

    //
    // Get the address of the target device object.  If this file represents
    // a device that was opened directly, then simply use the device or its
    // attached device(s) directly.
    //

    if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
        deviceObject = IoGetRelatedDeviceObject( fileObject );
    } else {
        deviceObject = IoGetAttachedDevice( fileObject->DeviceObject );
    }

    //
    // Make a special check here to determine whether this is a synchronous
    // I/O operation.  If it is, then wait here until the file is owned by
    // the current thread.  If this is not a (serialized) synchronous I/O
    // operation, then allocate and initialize the local event.
    //

    if (fileObject->Flags & FO_SYNCHRONOUS_IO) {

        BOOLEAN interrupted;

        if (!IopAcquireFastLock( fileObject )) {
            status = IopAcquireFileObjectLock( fileObject,
                                               requestorMode,
                                               (BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
                                               &interrupted );
            if (interrupted) {
                ObDereferenceObject( fileObject );
                return status;
            }
        }

        //
        // Make a special check here to determine whether or not the caller
        // is attempting to set the file position pointer information.  If so,
        // then set it immediately and get out.
        //

        if (FileInformationClass == FilePositionInformation) {

            //
            // The caller has requested setting the current file position
            // context information.  This is a relatively frequent call, so
            // it is optimized here to cut through the normal IRP path.
            //
            // Begin by checking to see whether the file was opened with no
            // intermediate buffering.  If so, then the file pointer must be
            // set in a manner consistent with the alignment requirement of
            // read and write operations to a non-buffered file.
            //

            PFILE_POSITION_INFORMATION fileInformation = FileInformation;
            LARGE_INTEGER currentByteOffset;

            try {

                //
                // Attempt to read the position information from the buffer.
                //

                currentByteOffset.QuadPart = fileInformation->CurrentByteOffset.QuadPart;

            } except( EXCEPTION_EXECUTE_HANDLER ) {

                IopReleaseFileObjectLock( fileObject );
                ObDereferenceObject( fileObject );
                return GetExceptionCode();
            }

            if ((fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING &&
                 (deviceObject->SectorSize &&
                 (currentByteOffset.LowPart &
                 (deviceObject->SectorSize - 1)))) ||
                 currentByteOffset.HighPart < 0) {

                    status = STATUS_INVALID_PARAMETER;

            } else {

                //
                // Set the current file position information.
                //

                fileObject->CurrentByteOffset.QuadPart = currentByteOffset.QuadPart;

                try {

                    //
                    // Write the I/O status block.
                    //

                    IoStatusBlock->Status = STATUS_SUCCESS;
                    IoStatusBlock->Information = 0;

                } except( EXCEPTION_EXECUTE_HANDLER ) {

                    //
                    // Writes to I/O status blocks are ignored since the
                    // operation succeeded.
                    //

                    NOTHING;

                }

            }

            //
            // Update the transfer count statistic for the current process for
            // operations other than read and write.
            //
        
            IopUpdateOtherTransferCount( Length );

            //
            // Note that the file object's event has not yet been reset,
            // so it is not necessary to set it to the Signaled state, since
            // that is it's state at this point by definition.  Therefore,
            // simply cleanup and return.
            //

            IopReleaseFileObjectLock( fileObject );
            ObDereferenceObject( fileObject );
            return status;
        }
        synchronousIo = TRUE;
    } else {

        //
        // This is a synchronous API being invoked for a file that is opened
        // for asynchronous I/O.  This means that this system service is
        // to synchronize the completion of the operation before returning
        // to the caller.  A local event is used to do this.
        //

        event = ExAllocatePool( NonPagedPool, sizeof( KEVENT ) );
        if (event == NULL) {
            ObDereferenceObject( fileObject );
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        KeInitializeEvent( event, SynchronizationEvent, FALSE );
        synchronousIo = FALSE;
    }

    //
    // Set the file object to the Not-Signaled state.
    //

    KeClearEvent( &fileObject->Event );

    //
    // If a link is being tracked, handle this out-of-line.
    //

    if (FileInformationClass == FileTrackingInformation) {
        status = IopTrackLink( fileObject,
                               &localIoStatus,
                               FileInformation,
                               Length,
                               synchronousIo ? &fileObject->Event : event,
                               requestorMode );
        if (NT_SUCCESS( status )) {
            try {
                IoStatusBlock->Information = 0;
                IoStatusBlock->Status = status;
            } except(EXCEPTION_EXECUTE_HANDLER) {
                NOTHING;
            }
        }

        if (synchronousIo) {
            IopReleaseFileObjectLock( fileObject );
        } else {
            ExFreePool( event );
        }
        ObDereferenceObject( fileObject );
        return status;
    }

    //
    // Allocate and initialize the I/O Request Packet (IRP) for this operation.
    // The allocation is performed with an exception handler in case the
    // caller does not have enough quota to allocate the packet.

    irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
    if (!irp) {

        //
        // An IRP could not be allocated.  Cleanup and return an appropriate
        // error status code.
        //

        if (!(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
            ExFreePool( event );
        }

        IopAllocateIrpCleanup( fileObject, (PKEVENT) NULL );

        return STATUS_INSUFFICIENT_RESOURCES;
    }
    irp->Tail.Overlay.OriginalFileObject = fileObject;
    irp->Tail.Overlay.Thread = CurrentThread;
    irp->RequestorMode = requestorMode;

    //
    // Fill in the service independent parameters in the IRP.
    //

    if (synchronousIo) {
        irp->UserEvent = (PKEVENT) NULL;
        irp->UserIosb = IoStatusBlock;
    } else {
        irp->UserEvent = event;
        irp->UserIosb = &localIoStatus;
        irp->Flags = IRP_SYNCHRONOUS_API;
    }
    irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;

    //
    // Get a pointer to the stack location for the first driver.  This will
    // be used to pass the original function codes and parameters.
    //

    irpSp = IoGetNextIrpStackLocation( irp );
    irpSp->MajorFunction = IRP_MJ_SET_INFORMATION;
    irpSp->FileObject = fileObject;

    //
    // Allocate a buffer and copy the information that is to be set on the
    // file into it.  Also, set the flags so that the completion code will
    // properly handle getting rid of the buffer and will not attempt to
    // copy data.
    //

    irp->AssociatedIrp.SystemBuffer = (PVOID) NULL;
    irp->MdlAddress = (PMDL) NULL;

    try {

        PVOID systemBuffer;

        systemBuffer =
        irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuota( NonPagedPool,
                                                                   Length );
        RtlCopyMemory( irp->AssociatedIrp.SystemBuffer,
                       FileInformation,
                       Length );

        //
        //  Negative file offsets are illegal.
        //

        ASSERT((FIELD_OFFSET(FILE_END_OF_FILE_INFORMATION, EndOfFile) |
                FIELD_OFFSET(FILE_ALLOCATION_INFORMATION, AllocationSize) |
                FIELD_OFFSET(FILE_POSITION_INFORMATION, CurrentByteOffset)) == 0);

        if (((FileInformationClass == FileEndOfFileInformation) ||
             (FileInformationClass == FileAllocationInformation) ||
             (FileInformationClass == FilePositionInformation)) &&
            (((PFILE_POSITION_INFORMATION)systemBuffer)->CurrentByteOffset.HighPart < 0)) {

            ExRaiseStatus(STATUS_INVALID_PARAMETER);
        }



    } except(EXCEPTION_EXECUTE_HANDLER) {

        //
        // An exception was incurred while allocating the intermediary
        // system buffer or while copying the caller's data into the
        // buffer. Cleanup and return an appropriate error status code.
        //

        IopExceptionCleanup( fileObject,
                             irp,
                             (PKEVENT) NULL,
                             event );

        return GetExceptionCode();

    }

    irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_DEFER_IO_COMPLETION;

    //
    // Copy the caller's parameters to the service-specific portion of the
    // IRP.
    //

    irpSp->Parameters.SetFile.Length = Length;
    irpSp->Parameters.SetFile.FileInformationClass = FileInformationClass;

    //
    // Insert the packet at the head of the IRP list for the thread.
    //

    IopQueueThreadIrp( irp );

    //
    // Update the operation count statistic for the current process for
    // operations other than read and write.
    //

    IopUpdateOtherOperationCount();


    //
    // Everything is now set to invoke the device driver with this request.
    // However, it is possible that the information that the caller wants
    // to set is device independent.  If this is the case, then the request
    // can be satisfied here without having to have all of the drivers
    // implement the same code.  Note that having the IRP is still necessary
    // since the I/O completion code requires it.
    //

    if (FileInformationClass == FileModeInformation) {

        PFILE_MODE_INFORMATION modeBuffer = irp->AssociatedIrp.SystemBuffer;

        //
        // Set the various flags in the mode field for the file object, if
        // they are reasonable.  There are 4 different invalid combinations
        // that the caller may not specify:
        //
        //     1)  An invalid flag was set in the mode field.  Not all Create/
        //         Open options may be changed.
        //
        //     2)  The caller set one of the synchronous I/O flags (alert or
        //         nonalert), but the file is not opened for synchronous I/O.
        //
        //     3)  The file is opened for synchronous I/O but the caller did
        //         not set either of the synchronous I/O flags (alert or non-
        //         alert).
        //
        //     4)  The caller set both of the synchronous I/O flags (alert and
        //         nonalert).
        //

        if ((modeBuffer->Mode & ~FILE_VALID_SET_FLAGS) ||
            ((modeBuffer->Mode & (FSIO_A | FSIO_NA)) && (!(fileObject->Flags & FO_SYNCHRONOUS_IO))) ||
            ((!(modeBuffer->Mode & (FSIO_A | FSIO_NA))) && (fileObject->Flags & FO_SYNCHRONOUS_IO)) ||
            (((modeBuffer->Mode & FSIO_A) && (modeBuffer->Mode & FSIO_NA) ))) {
            status = STATUS_INVALID_PARAMETER;

        } else {

            //
            // Set or clear the appropriate flags in the file object.
            //

            if (!(fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)) {
                if (modeBuffer->Mode & FILE_WRITE_THROUGH) {
                    fileObject->Flags |= FO_WRITE_THROUGH;
                } else {
                    fileObject->Flags &= ~FO_WRITE_THROUGH;
                }
            }

            if (modeBuffer->Mode & FILE_SEQUENTIAL_ONLY) {
                fileObject->Flags |= FO_SEQUENTIAL_ONLY;
            } else {
                fileObject->Flags &= ~FO_SEQUENTIAL_ONLY;
            }

            if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
                if (modeBuffer->Mode & FSIO_A) {
                    fileObject->Flags |= FO_ALERTABLE_IO;
                } else {
                    fileObject->Flags &= ~FO_ALERTABLE_IO;
                }
            }

            status = STATUS_SUCCESS;
        }

        //
        // Complete the I/O operation.
        //

        irp->IoStatus.Status = status;
        irp->IoStatus.Information = 0L;

    } else if (FileInformationClass == FileRenameInformation ||
               FileInformationClass == FileLinkInformation ||
               FileInformationClass == FileMoveClusterInformation) {

        //
        // Note that following code depends on the fact that the rename
        // information, link information and copy-on-write information
        // structures look exactly the same.
        //

        PFILE_RENAME_INFORMATION renameBuffer = irp->AssociatedIrp.SystemBuffer;

        //
        // The information being set is a variable-length structure with
        // embedded size information.  Walk the structure to ensure that
        // it is valid so the driver does not walk off the end and incur
        // an access violation in kernel mode.
        //
 
        if (renameBuffer->FileNameLength <= 0 || (renameBuffer->FileNameLength & (sizeof(WCHAR) -1))) {
            status = STATUS_INVALID_PARAMETER;
            irp->IoStatus.Status = status;
        } else if ((ULONG) (Length - FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName[0] )) < renameBuffer->FileNameLength) {
            status = STATUS_INVALID_PARAMETER;
            irp->IoStatus.Status = status;

        } else {

            //
            // Copy the value of the replace BOOLEAN (or the ClusterCount field)
            // from the caller's buffer to the I/O stack location parameter
            // field where it is expected by file systems.
            //

            if (FileInformationClass == FileMoveClusterInformation) {
                irpSp->Parameters.SetFile.ClusterCount =
                    ((FILE_MOVE_CLUSTER_INFORMATION *) renameBuffer)->ClusterCount;
            } else {
                irpSp->Parameters.SetFile.ReplaceIfExists = renameBuffer->ReplaceIfExists;
            }

            //
            // Check to see whether or not a fully qualified pathname was
            // supplied.  If so, then more processing is required.
            //

            if (renameBuffer->FileName[0] == (WCHAR) OBJ_NAME_PATH_SEPARATOR ||
                renameBuffer->RootDirectory) {

                //
                // A fully qualified file name was specified as the target of
                // the rename operation.  Attempt to open the target file and
                // ensure that the replacement policy for the file is consistent
                // with the caller's request, and ensure that the file is on the
                // same volume.
                //

                status = IopOpenLinkOrRenameTarget( &targetHandle,
                                                    irp,
                                                    renameBuffer,
                                                    fileObject );
                if (!NT_SUCCESS( status )) {
                    irp->IoStatus.Status = status;

                } else {

                    //
                    // The fully qualified file name specifies a file on the
                    // same volume and if it exists, then the caller specified
                    // that it should be replaced.
                    //

                    status = IoCallDriver( deviceObject, irp );

                }

            } else {

                //
                // This is a simple rename operation, so call the driver and
                // let it perform the rename operation within the same directory
                // as the source file.
                //

                status = IoCallDriver( deviceObject, irp );

            }
        }

    } else if (FileInformationClass == FileShortNameInformation) {

        PFILE_NAME_INFORMATION shortnameBuffer = irp->AssociatedIrp.SystemBuffer;

        //
        // The information being set is a variable-length structure with
        // embedded size information.  Walk the structure to ensure that
        // it is valid so the driver does not walk off the end and incur
        // an access violation in kernel mode.
        //
 
        if (shortnameBuffer->FileNameLength <= 0) {
            status = STATUS_INVALID_PARAMETER;
            irp->IoStatus.Status = status;
        } else if ((ULONG) (Length - FIELD_OFFSET( FILE_NAME_INFORMATION, FileName[0] )) < shortnameBuffer->FileNameLength) {
            status = STATUS_INVALID_PARAMETER;
            irp->IoStatus.Status = status;

        //
        // The short name must not begin with a separator character.
        //

        } else if (shortnameBuffer->FileName[0] == (WCHAR) OBJ_NAME_PATH_SEPARATOR) {

            status = STATUS_INVALID_PARAMETER;
            irp->IoStatus.Status = status;

        //
        // Pass the request to the driver below.
        //

        } else {

            status = IoCallDriver( deviceObject, irp );
        }

    } else if (FileInformationClass == FileDispositionInformation) {

        PFILE_DISPOSITION_INFORMATION disposition = irp->AssociatedIrp.SystemBuffer;

        //
        // Check to see whether the disposition delete field has been set to
        // TRUE and, if so, copy the handle being used to do this to the IRP
        // stack location parameter.
        //

        if (disposition->DeleteFile) {
            irpSp->Parameters.SetFile.DeleteHandle = FileHandle;
        }

        //
        // Simply invoke the driver to perform the (un)delete operation.
        //

        status = IoCallDriver( deviceObject, irp );

    } else if (FileInformationClass == FileCompletionInformation) {

        PFILE_COMPLETION_INFORMATION completion = irp->AssociatedIrp.SystemBuffer;
        PIO_COMPLETION_CONTEXT context;
        PVOID portObject;

        //
        // It is an error if this file object already has an LPC port associated
        // with it.
        //

        if (fileObject->CompletionContext || fileObject->Flags & FO_SYNCHRONOUS_IO) {

            status = STATUS_INVALID_PARAMETER;

        } else {

            //
            // Attempt to reference the port object by its handle and convert it
            // into a pointer to the port object itself.
            //

            status = ObReferenceObjectByHandle( completion->Port,
                                                IO_COMPLETION_MODIFY_STATE,
                                                IoCompletionObjectType,
                                                requestorMode,
                                                (PVOID *) &portObject,
                                                NULL );
            if (NT_SUCCESS( status )) {

                //
                // Allocate the memory to be associated w/this file object
                //

                context = ExAllocatePoolWithTag( PagedPool,
                                                 sizeof( IO_COMPLETION_CONTEXT ),
                                                 'cCoI' );
                if (!context) {

                    ObDereferenceObject( portObject );
                    status = STATUS_INSUFFICIENT_RESOURCES;

                } else {

                    //
                    // Everything was successful.  Capture the completion port
                    // and the key.
                    //

                    context->Port = portObject;
                    context->Key = completion->Key;

                    if (!InterlockedCompareExchangePointer( &fileObject->CompletionContext, context, NULL )) {

                        status = STATUS_SUCCESS;

                    } else {

                        //
                        // Someone set the completion context after the check.
                        // Simply drop everything on the floor and return an
                        // error.
                        //

                        ExFreePool( context );
                        ObDereferenceObject( portObject );
                        status = STATUS_INVALID_PARAMETER;
                    }
                }
            }
        }

        //
        // Complete the I/O operation.
        //

        irp->IoStatus.Status = status;
        irp->IoStatus.Information = 0;

    } else {

        //
        // This is not a request that can be performed here, so invoke the
        // driver at its appropriate dispatch entry with the IRP.
        //

        status = IoCallDriver( deviceObject, irp );
    }

    //
    // If this operation was a synchronous I/O operation, check the return
    // status to determine whether or not to wait on the file object.  If
    // the file object is to be waited on, wait for the operation to complete
    // and obtain the final status from the file object itself.
    //

    if (status == STATUS_PENDING) {

        if (synchronousIo) {

            status = KeWaitForSingleObject( &fileObject->Event,
                                            Executive,
                                            requestorMode,
                                            (BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
                                            (PLARGE_INTEGER) NULL );

            if (status == STATUS_ALERTED || status == STATUS_USER_APC) {

                //
                // The wait request has ended either because the thread was
                // alerted or an APC was queued to this thread, because of
                // thread rundown or CTRL/C processing.  In either case, try
                // to bail out of this I/O request carefully so that the IRP
                // completes before this routine exists so that synchronization
                // with the file object will remain intact.
                //

                IopCancelAlertedRequest( &fileObject->Event, irp );

            }

            status = fileObject->FinalStatus;

            IopReleaseFileObjectLock( fileObject );

        } else {

            //
            // This is a normal synchronous I/O operation, as opposed to a
            // serialized synchronous I/O operation.  For this case, wait for
            // the local event and copy the final status information back to
            // the caller.
            //

            status = KeWaitForSingleObject( event,
                                            Executive,
                                            requestorMode,
                                            FALSE,
                                            (PLARGE_INTEGER) NULL );

            if (status == STATUS_ALERTED || status == STATUS_USER_APC) {

                //
                // The wait request has ended either because the thread was
                // alerted or an APC was queued to this thread, because of
                // thread rundown or CTRL/C processing.  In either case, try
                // to bail out of this I/O request carefully so that the IRP
                // completes before this routine exists or the event will not
                // be around to set to the Signaled state.
                //

                IopCancelAlertedRequest( event, irp );

            }

            status = localIoStatus.Status;

            try {

                *IoStatusBlock = localIoStatus;

            } except(EXCEPTION_EXECUTE_HANDLER) {

                //
                // An exception occurred attempting to write the caller's I/O
                // status block.  Simply change the final status of the
                // operation to the exception code.
                //

                status = GetExceptionCode();
            }

            ExFreePool( event );

        }

    } else {

        //
        // The I/O operation finished without return a status of pending.
        // This means that the operation has not been through I/O completion,
        // so it must be done here.
        //

        PKNORMAL_ROUTINE normalRoutine;
        PVOID normalContext;
        KIRQL irql;

        if (!synchronousIo) {

            //
            // This is not a synchronous I/O operation, it is a synchronous
            // I/O API to a file opened for asynchronous I/O.  Since this
            // code path need never wait on the allocated and supplied event,
            // get rid of it so that it doesn't have to be set to the
            // Signaled state by the I/O completion code.
            //

            irp->UserEvent = (PKEVENT) NULL;
            ExFreePool( event );
        }

        irp->UserIosb = IoStatusBlock;
        KeRaiseIrql( APC_LEVEL, &irql );
        IopCompleteRequest( &irp->Tail.Apc,
                            &normalRoutine,
                            &normalContext,
                            (PVOID *) &fileObject,
                            &normalContext );
        KeLowerIrql( irql );

        if (synchronousIo) {
            IopReleaseFileObjectLock( fileObject );
        }

    }

    //
    // If there was a target handle generated because of a rename operation,
    // close it now.
    //

    if (targetHandle) {
        ObCloseHandle( targetHandle, KernelMode );
    }

    return status;
}