/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    VolInfo.c

Abstract:

    This module implements the volume information routines for Raw called by
    the dispatch driver.

--*/

#include "RawProcs.h"

NTSTATUS
RawQueryFsVolumeInfo (
    IN PVCB Vcb,
    IN PFILE_FS_VOLUME_INFORMATION Buffer,
    IN OUT PULONG Length
    );

NTSTATUS
RawQueryFsSizeInfo (
    IN PVCB Vcb,
    IN PFILE_FS_SIZE_INFORMATION Buffer,
    IN OUT PULONG Length
    );

NTSTATUS
RawQueryFsDeviceInfo (
    IN PVCB Vcb,
    IN PFILE_FS_DEVICE_INFORMATION Buffer,
    IN OUT PULONG Length
    );

NTSTATUS
RawQueryFsAttributeInfo (
    IN PVCB Vcb,
    IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer,
    IN OUT PULONG Length
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RawQueryVolumeInformation)
#pragma alloc_text(PAGE, RawQueryFsVolumeInfo)
#pragma alloc_text(PAGE, RawQueryFsSizeInfo)
#pragma alloc_text(PAGE, RawQueryFsDeviceInfo)
#pragma alloc_text(PAGE, RawQueryFsAttributeInfo)
#endif


NTSTATUS
RawQueryVolumeInformation (
    IN PVCB Vcb,
    IN PIRP Irp,
    IN PIO_STACK_LOCATION IrpSp
    )

/*++

Routine Description:

    This routine implements the NtQueryVolumeInformation API call.

Arguments:

    Vcb - Supplies the volume being queried.

    Irp - Supplies the Irp being processed.

    IrpSp - Supplies parameters describing the read

Return Value:

    NTSTATUS - The status for the Irp.

--*/

{
    NTSTATUS Status;

    ULONG Length;
    FS_INFORMATION_CLASS FsInformationClass;
    PVOID Buffer;

    PAGED_CODE();

    //
    //  Reference our input parameters to make things easier
    //

    Length = IrpSp->Parameters.QueryVolume.Length;
    FsInformationClass = IrpSp->Parameters.QueryVolume.FsInformationClass;
    Buffer = Irp->AssociatedIrp.SystemBuffer;

    //
    //  Based on the information class we'll do different actions.  Each
    //  of the procedures that we're calling fills up the output buffer
    //  if possible and returns true if it successfully filled the buffer
    //  and false if it couldn't wait for any I/O to complete.
    //

    switch (FsInformationClass) {

    case FileFsVolumeInformation:

        Status = RawQueryFsVolumeInfo( Vcb, Buffer, &Length );
        break;

    case FileFsSizeInformation:

        Status = RawQueryFsSizeInfo( Vcb, Buffer, &Length );
        break;

    case FileFsDeviceInformation:

        Status = RawQueryFsDeviceInfo( Vcb, Buffer, &Length );
        break;

    case FileFsAttributeInformation:

        Status = RawQueryFsAttributeInfo( Vcb, Buffer, &Length );
        break;

    default:

        Status = STATUS_INVALID_PARAMETER;
        break;
    }

    //
    //  Set the information field to the number of bytes actually filled in,
    //  and complete the request.
    //

    Irp->IoStatus.Information = IrpSp->Parameters.QueryVolume.Length - Length;

    RawCompleteRequest( Irp, Status );

    return Status;
}


//
//  Internal support routine
//

NTSTATUS
RawQueryFsVolumeInfo (
    IN PVCB Vcb,
    IN PFILE_FS_VOLUME_INFORMATION Buffer,
    IN OUT PULONG Length
    )

/*++

Routine Description:

    This routine implements the query volume info call

Arguments:

    Vcb - Supplies the Vcb being queried

    Buffer - Supplies a pointer to the output buffer where the information
        is to be returned

    Length - Supplies the length of the buffer in byte.  This variable
        upon return receives the remaining bytes free in the buffer

Return Value:

    NTSTATUS - Returns the status for the query

--*/

{
    PAGED_CODE();

    //
    //  Zero out the buffer, then extract and fill up the non zero fields.
    //

    RtlZeroMemory( Buffer, sizeof(FILE_FS_VOLUME_INFORMATION) );

    Buffer->VolumeSerialNumber = Vcb->Vpb->SerialNumber;

    Buffer->SupportsObjects = FALSE;

    Buffer->VolumeLabelLength = 0;

    *Length -= FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel[0]);

    //
    //  Set our status and return to our caller
    //

    return STATUS_SUCCESS;
}


//
//  Internal support routine
//

NTSTATUS
RawQueryFsSizeInfo (
    IN PVCB Vcb,
    IN PFILE_FS_SIZE_INFORMATION Buffer,
    IN OUT PULONG Length
    )

/*++

Routine Description:

    This routine implements the query volume size call

Arguments:

    Vcb - Supplies the Vcb being queried

    Buffer - Supplies a pointer to the output buffer where the information
        is to be returned

    Length - Supplies the length of the buffer in byte.  This variable
        upon return receives the remaining bytes free in the buffer

Return Value:

    Status - Returns the status for the query

--*/

{
    PIRP Irp;
    KEVENT Event;
    NTSTATUS Status;
    IO_STATUS_BLOCK Iosb;
    PDEVICE_OBJECT RealDevice;

    DISK_GEOMETRY DiskGeometry;
    PARTITION_INFORMATION PartitionInformation;
    GET_LENGTH_INFORMATION GetLengthInformation;

    BOOLEAN DriveIsPartitioned;

    PAGED_CODE();

    //
    //  Make sure the buffer is large enough
    //

    if (*Length < sizeof(FILE_FS_SIZE_INFORMATION)) {

        return STATUS_BUFFER_OVERFLOW;
    }

    RtlZeroMemory( Buffer, sizeof(FILE_FS_SIZE_INFORMATION) );

    //
    //  Prepare for our device control below.  The device drivers only
    //  have to copy geometry and partition info from in-memory structures,
    //  so it is OK to make these calls even when we can't wait.
    //

    KeInitializeEvent( &Event, NotificationEvent, FALSE );
    RealDevice = Vcb->Vpb->RealDevice;

    //
    //  Query the disk geometry
    //

    Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_GET_DRIVE_GEOMETRY,
                                         RealDevice,
                                         NULL,
                                         0,
                                         &DiskGeometry,
                                         sizeof(DISK_GEOMETRY),
                                         FALSE,
                                         &Event,
                                         &Iosb );

    if ( Irp == NULL ) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    if ( (Status = IoCallDriver( RealDevice, Irp )) == STATUS_PENDING ) {

        (VOID) KeWaitForSingleObject( &Event,
                                      Executive,
                                      KernelMode,
                                      FALSE,
                                      (PLARGE_INTEGER)NULL );

        Status = Iosb.Status;
    }

    //
    //  If this call didn't succeed, the drive hasn't even been low-level
    //  formatted, and thus geometry information is undefined.
    //

    if (!NT_SUCCESS( Status )) {

        *Length = 0;
        return Status;
    }

    //
    //  See if we have to check the partition information (floppy disks are
    //  the only type that can't have partitions )
    //

    if ( FlagOn( RealDevice->Characteristics, FILE_FLOPPY_DISKETTE )) {

        DriveIsPartitioned = FALSE;
        PartitionInformation.PartitionLength.QuadPart = 0;

    } else {

        //
        //  Query the length info.
        //

        KeResetEvent( &Event );

        Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_GET_LENGTH_INFO,
                                             RealDevice,
                                             NULL,
                                             0,
                                             &GetLengthInformation,
                                             sizeof(GET_LENGTH_INFORMATION),
                                             FALSE,
                                             &Event,
                                             &Iosb );

        if ( Irp == NULL ) {
           return STATUS_INSUFFICIENT_RESOURCES;
        }

        if ( (Status = IoCallDriver( RealDevice, Irp )) == STATUS_PENDING ) {

            (VOID) KeWaitForSingleObject( &Event,
                                          Executive,
                                          KernelMode,
                                          FALSE,
                                          (PLARGE_INTEGER)NULL );

            Status = Iosb.Status;
        }

        PartitionInformation.PartitionLength = GetLengthInformation.Length;

        if ( !NT_SUCCESS (Status) ) {

            //
            //  Query the partition table
            //

            KeResetEvent( &Event );

            Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_GET_PARTITION_INFO,
                                                 RealDevice,
                                                 NULL,
                                                 0,
                                                 &PartitionInformation,
                                                 sizeof(PARTITION_INFORMATION),
                                                 FALSE,
                                                 &Event,
                                                 &Iosb );

            if ( Irp == NULL ) {
               return STATUS_INSUFFICIENT_RESOURCES;
            }

            if ( (Status = IoCallDriver( RealDevice, Irp )) == STATUS_PENDING ) {

                (VOID) KeWaitForSingleObject( &Event,
                                              Executive,
                                              KernelMode,
                                              FALSE,
                                              (PLARGE_INTEGER)NULL );

                Status = Iosb.Status;
            }

            //
            //  If we get back invalid device request, the disk is not partitioned
            //

            if ( !NT_SUCCESS (Status) ) {

                DriveIsPartitioned = FALSE;

            } else {

                DriveIsPartitioned = TRUE;
            }

        } else {

            DriveIsPartitioned = TRUE;
        }
    }

    //
    //  Set the output buffer
    //

    Buffer->BytesPerSector = DiskGeometry.BytesPerSector;

    Buffer->SectorsPerAllocationUnit = 1;

    //
    //  Now, based on whether the disk is partitioned, compute the
    //  total number of sectors on this disk.
    //

    Buffer->TotalAllocationUnits =
    Buffer->AvailableAllocationUnits = ( DriveIsPartitioned == TRUE ) ?

        RtlExtendedLargeIntegerDivide( PartitionInformation.PartitionLength,
                                       DiskGeometry.BytesPerSector,
                                       NULL )

                                        :

        RtlExtendedIntegerMultiply( DiskGeometry.Cylinders,
                                    DiskGeometry.TracksPerCylinder *
                                    DiskGeometry.SectorsPerTrack );

    //
    //  Adjust the length variable
    //

    *Length -= sizeof(FILE_FS_SIZE_INFORMATION);

    //
    //  And return success to our caller
    //

    return STATUS_SUCCESS;
}


//
//  Internal support routine
//

NTSTATUS
RawQueryFsDeviceInfo (
    IN PVCB Vcb,
    IN PFILE_FS_DEVICE_INFORMATION Buffer,
    IN OUT PULONG Length
    )

/*++

Routine Description:

    This routine implements the query volume device call

Arguments:

    Vcb - Supplies the Vcb being queried

    Buffer - Supplies a pointer to the output buffer where the information
        is to be returned

    Length - Supplies the length of the buffer in byte.  This variable
        upon return receives the remaining bytes free in the buffer

Return Value:

    Status - Returns the status for the query

--*/

{
    PAGED_CODE();

    //
    //  Make sure the buffer is large enough
    //

    if (*Length < sizeof(FILE_FS_DEVICE_INFORMATION)) {

        return STATUS_BUFFER_OVERFLOW;
    }

    RtlZeroMemory( Buffer, sizeof(FILE_FS_DEVICE_INFORMATION) );

    //
    //  Set the output buffer
    //

    Buffer->DeviceType = FILE_DEVICE_DISK;

    Buffer->Characteristics = Vcb->TargetDeviceObject->Characteristics;

    //
    //  Adjust the length variable
    //

    *Length -= sizeof(FILE_FS_DEVICE_INFORMATION);

    //
    //  And return success to our caller
    //

    return STATUS_SUCCESS;
}


//
//  Internal support routine
//

NTSTATUS
RawQueryFsAttributeInfo (
    IN PVCB Vcb,
    IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer,
    IN OUT PULONG Length
    )

/*++

Routine Description:

    This routine implements the query volume attribute call

Arguments:

    Vcb - Supplies the Vcb being queried

    Buffer - Supplies a pointer to the output buffer where the information
        is to be returned

    Length - Supplies the length of the buffer in byte.  This variable
        upon return receives the remaining bytes free in the buffer

Return Value:

    Status - Returns the status for the query

--*/

{
    ULONG LengthUsed;

    UNREFERENCED_PARAMETER( Vcb );

    PAGED_CODE();

    //
    //  Check if the buffer we're given is long enough to contain "Raw"
    //

    LengthUsed = FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName[0]) + 6;

    if (*Length < LengthUsed) {

        return STATUS_BUFFER_OVERFLOW;
    }

    //
    //  Set the output buffer
    //

    Buffer->FileSystemAttributes       = 0;
    Buffer->MaximumComponentNameLength = 0;
    Buffer->FileSystemNameLength       = 6;
    RtlCopyMemory( &Buffer->FileSystemName[0], L"RAW", 6 );

    //
    //  Adjust the length variable
    //

    *Length -= LengthUsed;

    //
    //  And return success to our caller
    //

    return STATUS_SUCCESS;
}

