/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    VolInfo.c

Abstract:

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

Author:

    Gary Kimura     [GaryKi]    28-Dec-1989

Revision History:

--*/

#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 recieves 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 recieves 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_EX PartitionInformation;

    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 strucures,
    //  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;

    } else {

        //
        //  Query the partition table
        //

        KeResetEvent( &Event );

        Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_GET_PARTITION_INFO_EX,
                                             RealDevice,
                                             NULL,
                                             0,
                                             &PartitionInformation,
                                             sizeof(PARTITION_INFORMATION_EX),
                                             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;
        }
    }

    //
    //  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 recieves 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 recieves 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;
}