/*
 * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
 * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice,
 *   this list of conditions and the following disclaimer in the
 *   documentation
 *   and/or other materials provided with the distribution.
 * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
 *   nor the names of their contributors may be used to endorse or promote
 *   products derived from this software without specific prior written
 *   permission from Kernel Drivers, LLC and Your File System, Inc.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//
// File: AFSVolumeInfo.cpp
//

#include "AFSCommon.h"

NTSTATUS
AFSQueryVolumeInfo( IN PDEVICE_OBJECT LibDeviceObject,
                    IN PIRP Irp)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    AFSDeviceExt *pDeviceExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
    IO_STACK_LOCATION *pIrpSp;
    FS_INFORMATION_CLASS FsInformationClass;
    void *pBuffer = NULL;
    ULONG ulLength = 0;
    BOOLEAN bReleaseResource = FALSE;
    AFSFcb *pFcb = NULL;
    AFSVolumeCB *pVolumeCB = NULL;

    pIrpSp = IoGetCurrentIrpStackLocation( Irp);

    __try
    {

        pFcb = (AFSFcb *)pIrpSp->FileObject->FsContext;

        if( pFcb == NULL)
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "AFSQueryVolumeInfo Failing request with NULL Fcb\n");

            try_return( ntStatus = STATUS_INVALID_DEVICE_REQUEST);
        }

        pVolumeCB = pFcb->ObjectInformation->VolumeCB;

        ASSERT( pVolumeCB->ObjectInformation.FileType == AFS_FILE_TYPE_DIRECTORY &&
                pVolumeCB->ObjectInformation.FileId.Vnode == 1);

        ulLength = pIrpSp->Parameters.QueryVolume.Length;
        FsInformationClass = pIrpSp->Parameters.QueryVolume.FsInformationClass;
        pBuffer = Irp->AssociatedIrp.SystemBuffer;

        AFSAcquireExcl( pVolumeCB->VolumeLock,
                        TRUE);

        bReleaseResource = TRUE;

        //
        // Don't allow requests against IOCtl nodes
        //

        if( pFcb->Header.NodeTypeCode == AFS_IOCTL_FCB)
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "AFSQueryVolumeInfo Failing request against PIOCtl Fcb\n");

            try_return( ntStatus = STATUS_INVALID_DEVICE_REQUEST);
        }
        else if( pFcb->Header.NodeTypeCode == AFS_SPECIAL_SHARE_FCB)
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "AFSQueryVolumeInfo Failing request against SpecialShare Fcb\n");

            try_return( ntStatus = STATUS_INVALID_DEVICE_REQUEST);
        }

        //
        // Process the request
        //

        switch( FsInformationClass)
        {

            case FileFsVolumeInformation:
            {

                ntStatus = AFSQueryFsVolumeInfo( &pVolumeCB->VolumeInformation,
                                                 (PFILE_FS_VOLUME_INFORMATION)pBuffer,
                                                 &ulLength);

                break;
            }

            case FileFsSizeInformation:
            {

                ntStatus = AFSQueryFsSizeInfo( &pVolumeCB->VolumeInformation,
                                               (PFILE_FS_SIZE_INFORMATION)pBuffer,
                                               &ulLength);

                break;
            }

            case FileFsDeviceInformation:
            {

                ntStatus = AFSQueryFsDeviceInfo( &pVolumeCB->VolumeInformation,
                                                 (PFILE_FS_DEVICE_INFORMATION)pBuffer,
                                                 &ulLength);

                break;
            }

            case FileFsAttributeInformation:
            {

                ntStatus = AFSQueryFsAttributeInfo( &pVolumeCB->VolumeInformation,
                                                    (PFILE_FS_ATTRIBUTE_INFORMATION)pBuffer,
                                                    &ulLength);

                break;
            }

            case FileFsFullSizeInformation:
            {

                ntStatus = AFSQueryFsFullSizeInfo( &pVolumeCB->VolumeInformation,
                                                   (PFILE_FS_FULL_SIZE_INFORMATION)pBuffer,
                                                   &ulLength);

                break;
            }

            default:

                AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
                              AFS_TRACE_LEVEL_WARNING,
                              "AFSQueryVolumeInfo Invalid class %d\n",
                              FsInformationClass);

                ntStatus = STATUS_INVALID_PARAMETER;

                break;
        }

try_exit:

        //
        // Setup the Irp's information field to what we actually copied in.
        //

        Irp->IoStatus.Information = pIrpSp->Parameters.QueryVolume.Length - ulLength;

        if( bReleaseResource)
        {

            AFSReleaseResource( pVolumeCB->VolumeLock);
        }

        AFSCompleteRequest( Irp,
                            ntStatus);

    }
    __except( AFSExceptionFilter( GetExceptionCode(), GetExceptionInformation()) )
    {

        AFSDbgLogMsg( 0,
                      0,
                      "EXCEPTION - AFSQueryVolumeInfo\n");
    }

    return ntStatus;
}

NTSTATUS
AFSSetVolumeInfo( IN PDEVICE_OBJECT DeviceObject,
                  IN PIRP Irp)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    IO_STACK_LOCATION *pIrpSp;

    pIrpSp = IoGetCurrentIrpStackLocation( Irp);

    __try
    {

        AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
                      AFS_TRACE_LEVEL_WARNING,
                      "AFSSetVolumeInfo Entry for FO %08lX\n", pIrpSp->FileObject);

        AFSCompleteRequest( Irp,
                            ntStatus);

    }
    __except( AFSExceptionFilter( GetExceptionCode(), GetExceptionInformation()) )
    {

        AFSDbgLogMsg( 0,
                      0,
                      "EXCEPTION - AFSSetVolumeInfo\n");
    }

    return ntStatus;
}

NTSTATUS
AFSQueryFsVolumeInfo( IN AFSVolumeInfoCB *VolumeInfo,
                      IN PFILE_FS_VOLUME_INFORMATION Buffer,
                      IN OUT PULONG Length)

{

    NTSTATUS ntStatus = STATUS_BUFFER_TOO_SMALL;
    ULONG ulCopyLength;

    RtlZeroMemory( Buffer,
                   *Length);

    if( *Length >= (ULONG)sizeof( FILE_FS_VOLUME_INFORMATION))
    {

        if( *Length >= (ULONG)(FIELD_OFFSET( FILE_FS_VOLUME_INFORMATION, VolumeLabel) + (LONG)VolumeInfo->VolumeLabelLength))
        {

            ulCopyLength = (LONG)VolumeInfo->VolumeLabelLength;

            ntStatus = STATUS_SUCCESS;
        }
        else
        {

            ulCopyLength = *Length - FIELD_OFFSET( FILE_FS_VOLUME_INFORMATION, VolumeLabel);

            ntStatus = STATUS_BUFFER_OVERFLOW;
        }

        Buffer->VolumeCreationTime.QuadPart = VolumeInfo->VolumeCreationTime.QuadPart;

        Buffer->VolumeSerialNumber = VolumeInfo->VolumeID;

        Buffer->VolumeLabelLength = VolumeInfo->VolumeLabelLength;

        Buffer->SupportsObjects = FALSE;

        *Length -= FIELD_OFFSET( FILE_FS_VOLUME_INFORMATION, VolumeLabel);

        if( ulCopyLength > 0)
        {

            RtlCopyMemory( Buffer->VolumeLabel,
                           VolumeInfo->VolumeLabel,
                           ulCopyLength);

            *Length -= ulCopyLength;
        }
    }

    return ntStatus;
}

NTSTATUS
AFSQueryFsSizeInfo( IN AFSVolumeInfoCB *VolumeInfo,
                    IN PFILE_FS_SIZE_INFORMATION Buffer,
                    IN OUT PULONG Length)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;

    RtlZeroMemory( Buffer,
                   *Length);

    if( *Length >= sizeof( FILE_FS_SIZE_INFORMATION))
    {

        Buffer->TotalAllocationUnits.QuadPart = VolumeInfo->TotalAllocationUnits.QuadPart;

        Buffer->AvailableAllocationUnits.QuadPart = VolumeInfo->AvailableAllocationUnits.QuadPart;

        Buffer->SectorsPerAllocationUnit = VolumeInfo->SectorsPerAllocationUnit;

        Buffer->BytesPerSector = VolumeInfo->BytesPerSector;

        *Length -= sizeof( FILE_FS_SIZE_INFORMATION);
    }
    else
    {

        ntStatus = STATUS_BUFFER_TOO_SMALL;
    }

    return ntStatus;
}

NTSTATUS
AFSQueryFsDeviceInfo( IN AFSVolumeInfoCB *VolumeInfo,
                      IN PFILE_FS_DEVICE_INFORMATION Buffer,
                      IN OUT PULONG Length)
{
    NTSTATUS ntStatus = STATUS_SUCCESS;

    RtlZeroMemory( Buffer,
                   *Length);

    if( *Length >= (LONG)sizeof( FILE_FS_DEVICE_INFORMATION))
    {

        Buffer->DeviceType = FILE_DEVICE_DISK;

        Buffer->Characteristics = VolumeInfo->Characteristics;

        *Length -= sizeof( FILE_FS_DEVICE_INFORMATION);
    }
    else
    {

        ntStatus = STATUS_BUFFER_TOO_SMALL;
    }

    return ntStatus;
}

NTSTATUS
AFSQueryFsAttributeInfo( IN AFSVolumeInfoCB *VolumeInfo,
                         IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer,
                         IN OUT PULONG Length)
{
    NTSTATUS ntStatus = STATUS_SUCCESS;

    RtlZeroMemory( Buffer,
                   *Length);

    if( *Length >= (LONG)(sizeof( FILE_FS_ATTRIBUTE_INFORMATION)))
    {

        Buffer->FileSystemAttributes = (FILE_CASE_PRESERVED_NAMES |
                                        FILE_UNICODE_ON_DISK |
                                        FILE_SUPPORTS_REPARSE_POINTS);

        Buffer->MaximumComponentNameLength = 255;

        Buffer->FileSystemNameLength = 18;

        *Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName);

        if( *Length >= 18)
        {

            RtlCopyMemory( Buffer->FileSystemName,
                           L"AFSRDRFsd",
                           18);

            *Length -= 18;
        }
        else
        {

            ntStatus = STATUS_BUFFER_OVERFLOW;
        }
    }
    else
    {

        ntStatus = STATUS_BUFFER_TOO_SMALL;
    }

    return ntStatus;
}

NTSTATUS
AFSQueryFsFullSizeInfo( IN AFSVolumeInfoCB *VolumeInfo,
                        IN PFILE_FS_FULL_SIZE_INFORMATION Buffer,
                        IN OUT PULONG Length)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;

    RtlZeroMemory( Buffer,
                   *Length);

    if( *Length >= sizeof( FILE_FS_FULL_SIZE_INFORMATION))
    {

        Buffer->TotalAllocationUnits.QuadPart = VolumeInfo->TotalAllocationUnits.QuadPart;

        Buffer->CallerAvailableAllocationUnits.QuadPart = VolumeInfo->AvailableAllocationUnits.QuadPart;

        Buffer->ActualAvailableAllocationUnits.QuadPart = VolumeInfo->AvailableAllocationUnits.QuadPart;

        Buffer->SectorsPerAllocationUnit = VolumeInfo->SectorsPerAllocationUnit;

        Buffer->BytesPerSector = VolumeInfo->BytesPerSector;

        *Length -= sizeof( FILE_FS_FULL_SIZE_INFORMATION);
    }
    else
    {

        ntStatus = STATUS_BUFFER_TOO_SMALL;
    }

    return ntStatus;
}
