/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

    VolInfo.c

Abstract:

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

// @@BEGIN_DDKSPLIT

Author:

    Gary Kimura     [GaryKi]    12-Apr-1990

Revision History:

// @@END_DDKSPLIT

--*/

#include "FatProcs.h"

//
//  The local debug trace level
//

#define Dbg                              (DEBUG_TRACE_VOLINFO)

NTSTATUS
FatQueryFsVolumeInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PVCB Vcb,
    IN PFILE_FS_VOLUME_INFORMATION Buffer,
    IN OUT PULONG Length
    );

NTSTATUS
FatQueryFsSizeInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PVCB Vcb,
    IN PFILE_FS_SIZE_INFORMATION Buffer,
    IN OUT PULONG Length
    );

NTSTATUS
FatQueryFsDeviceInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PVCB Vcb,
    IN PFILE_FS_DEVICE_INFORMATION Buffer,
    IN OUT PULONG Length
    );

NTSTATUS
FatQueryFsAttributeInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PVCB Vcb,
    IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer,
    IN OUT PULONG Length
    );

NTSTATUS
FatQueryFsFullSizeInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PVCB Vcb,
    IN PFILE_FS_FULL_SIZE_INFORMATION Buffer,
    IN OUT PULONG Length
    );
 
NTSTATUS
FatSetFsLabelInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PVCB Vcb,
    IN PFILE_FS_LABEL_INFORMATION Buffer
    );


#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatCommonQueryVolumeInfo)
#pragma alloc_text(PAGE, FatCommonSetVolumeInfo)
#pragma alloc_text(PAGE, FatFsdQueryVolumeInformation)
#pragma alloc_text(PAGE, FatFsdSetVolumeInformation)
#pragma alloc_text(PAGE, FatQueryFsAttributeInfo)
#pragma alloc_text(PAGE, FatQueryFsDeviceInfo)
#pragma alloc_text(PAGE, FatQueryFsSizeInfo)
#pragma alloc_text(PAGE, FatQueryFsVolumeInfo)
#pragma alloc_text(PAGE, FatQueryFsFullSizeInfo)
#pragma alloc_text(PAGE, FatSetFsLabelInfo)
#endif


NTSTATUS
FatFsdQueryVolumeInformation (
    IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine implements the Fsd part of the NtQueryVolumeInformation API
    call.

Arguments:

    VolumeDeviceObject - Supplies the volume device object where the file
        being queried exists.

    Irp - Supplies the Irp being processed.

Return Value:

    NTSTATUS - The FSD status for the Irp.

--*/

{
    NTSTATUS Status;
    PIRP_CONTEXT IrpContext = NULL;

    BOOLEAN TopLevel;

    DebugTrace(+1, Dbg, "FatFsdQueryVolumeInformation\n", 0);

    //
    //  Call the common query routine, with blocking allowed if synchronous
    //

    FsRtlEnterFileSystem();

    TopLevel = FatIsIrpTopLevel( Irp );

    try {

        IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );

        Status = FatCommonQueryVolumeInfo( IrpContext, Irp );

    } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {

        //
        //  We had some trouble trying to perform the requested
        //  operation, so we'll abort the I/O request with
        //  the error status that we get back from the
        //  execption code
        //

        Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
    }

    if (TopLevel) { IoSetTopLevelIrp( NULL ); }

    FsRtlExitFileSystem();

    //
    //  And return to our caller
    //

    DebugTrace(-1, Dbg, "FatFsdQueryVolumeInformation -> %08lx\n", Status);

    UNREFERENCED_PARAMETER( VolumeDeviceObject );

    return Status;
}


NTSTATUS
FatFsdSetVolumeInformation (
    IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine implements the FSD part of the NtSetVolumeInformation API
    call.

Arguments:

    VolumeDeviceObject - Supplies the volume device object where the file
        being set exists.

    Irp - Supplies the Irp being processed.

Return Value:

    NTSTATUS - The FSD status for the Irp.

--*/

{
    NTSTATUS Status;
    PIRP_CONTEXT IrpContext = NULL;

    BOOLEAN TopLevel;

    DebugTrace(+1, Dbg, "FatFsdSetVolumeInformation\n", 0);

    //
    //  Call the common set routine
    //

    FsRtlEnterFileSystem();

    TopLevel = FatIsIrpTopLevel( Irp );

    try {

        IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );

        Status = FatCommonSetVolumeInfo( IrpContext, Irp );

    } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {

        //
        //  We had some trouble trying to perform the requested
        //  operation, so we'll abort the I/O request with
        //  the error status that we get back from the
        //  execption code
        //

        Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
    }

    if (TopLevel) { IoSetTopLevelIrp( NULL ); }

    FsRtlExitFileSystem();

    //
    //  And return to our caller
    //

    DebugTrace(-1, Dbg, "FatFsdSetVolumeInformation -> %08lx\n", Status);

    UNREFERENCED_PARAMETER( VolumeDeviceObject );

    return Status;
}


NTSTATUS
FatCommonQueryVolumeInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for querying volume information called by both
    the fsd and fsp threads.

Arguments:

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;
    PIO_STACK_LOCATION IrpSp;

    PVCB Vcb;
    PFCB Fcb;
    PCCB Ccb;

    ULONG Length;
    FS_INFORMATION_CLASS FsInformationClass;
    PVOID Buffer;

    BOOLEAN WeAcquiredVcb = FALSE;

    //
    //  Get the current stack location
    //

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    DebugTrace(+1, Dbg, "FatCommonQueryVolumeInfo...\n", 0);
    DebugTrace( 0, Dbg, "Irp                  = %08lx\n", Irp );
    DebugTrace( 0, Dbg, "->Length             = %08lx\n", IrpSp->Parameters.QueryVolume.Length);
    DebugTrace( 0, Dbg, "->FsInformationClass = %08lx\n", IrpSp->Parameters.QueryVolume.FsInformationClass);
    DebugTrace( 0, Dbg, "->Buffer             = %08lx\n", Irp->AssociatedIrp.SystemBuffer);

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

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

    //
    //  Decode the file object to get the Vcb
    //

    (VOID) FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );

    ASSERT( Vcb != NULL );

    try {

        //
        //  Make sure the vcb is in a usable condition.  This will raise
        //  and error condition if the volume is unusable
        //
        //  Also verify the Root Dcb since we need info from there.
        //

        FatVerifyFcb( IrpContext, Vcb->RootDcb );

        //
        //  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:

            //
            //  This is the only routine we need the Vcb shared because of
            //  copying the volume label.  All other routines copy fields that
            //  cannot change or are just manifest constants.
            //

            if (!FatAcquireSharedVcb( IrpContext, Vcb )) {

                DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);

                Status = FatFsdPostRequest( IrpContext, Irp );
                IrpContext = NULL;
                Irp = NULL;

            } else {

                WeAcquiredVcb = TRUE;
                
                Status = FatQueryFsVolumeInfo( IrpContext, Vcb, Buffer, &Length );
            }

            break;

        case FileFsSizeInformation:

            Status = FatQueryFsSizeInfo( IrpContext, Vcb, Buffer, &Length );
            break;

        case FileFsDeviceInformation:

            Status = FatQueryFsDeviceInfo( IrpContext, Vcb, Buffer, &Length );
            break;

        case FileFsAttributeInformation:

            Status = FatQueryFsAttributeInfo( IrpContext, Vcb, Buffer, &Length );
            break;

        case FileFsFullSizeInformation:

            Status = FatQueryFsFullSizeInfo( IrpContext, Vcb, Buffer, &Length );
            break;

        default:

            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        //
        //  Set the information field to the number of bytes actually filled in.
        //

        if (Irp != NULL) {
            
            Irp->IoStatus.Information = IrpSp->Parameters.QueryVolume.Length - Length;
        }

    } finally {

        DebugUnwind( FatCommonQueryVolumeInfo );

        if ( WeAcquiredVcb ) { FatReleaseVcb( IrpContext, Vcb ); }

        if (!AbnormalTermination()) {

            FatCompleteRequest( IrpContext, Irp, Status );
        }

        DebugTrace(-1, Dbg, "FatCommonQueryVolumeInfo -> %08lx\n", Status);
    }

    return Status;
}


NTSTATUS
FatCommonSetVolumeInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for setting Volume Information called by both
    the fsd and fsp threads.

Arguments:

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;
    PIO_STACK_LOCATION IrpSp;

    PVCB Vcb;
    PFCB Fcb;
    PCCB Ccb;
    TYPE_OF_OPEN TypeOfOpen;

    ULONG Length;
    FS_INFORMATION_CLASS FsInformationClass;
    PVOID Buffer;

    //
    //  Get the current stack location
    //

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    DebugTrace(+1, Dbg, "FatCommonSetVolumeInfo...\n", 0);
    DebugTrace( 0, Dbg, "Irp                  = %08lx\n", Irp );
    DebugTrace( 0, Dbg, "->Length             = %08lx\n", IrpSp->Parameters.SetVolume.Length);
    DebugTrace( 0, Dbg, "->FsInformationClass = %08lx\n", IrpSp->Parameters.SetVolume.FsInformationClass);
    DebugTrace( 0, Dbg, "->Buffer             = %08lx\n", Irp->AssociatedIrp.SystemBuffer);

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

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

    //
    //  Decode the file object to get the Vcb
    //

    TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );

    if (TypeOfOpen != UserVolumeOpen) {

        FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );

        DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> STATUS_ACCESS_DENIED\n", 0);

        return STATUS_ACCESS_DENIED;
    }

    //
    //  Acquire exclusive access to the Vcb and enqueue the Irp if we didn't
    //  get access
    //

    if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {

        DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);

        Status = FatFsdPostRequest( IrpContext, Irp );

        DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status );
        return Status;
    }

    try {

        //
        //  Make sure the vcb is in a usable condition.  This will raise
        //  and error condition if the volume is unusable
        //
        //  Also verify the Root Dcb since we need info from there.
        //

        FatVerifyFcb( IrpContext, Vcb->RootDcb );

        //
        //  Based on the information class we'll do different actions.  Each
        //  of the procedures that we're calling performs the action if
        //  possible and returns true if it successful and false if it couldn't
        //  wait for any I/O to complete.
        //

        switch (FsInformationClass) {

        case FileFsLabelInformation:

            Status = FatSetFsLabelInfo( IrpContext, Vcb, Buffer );
            break;

        default:

            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        FatUnpinRepinnedBcbs( IrpContext );

    } finally {

        DebugUnwind( FatCommonSetVolumeInfo );

        FatReleaseVcb( IrpContext, Vcb );

        if (!AbnormalTermination()) {

            FatCompleteRequest( IrpContext, Irp, Status );
        }

        DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status);
    }

    return Status;
}


//
//  Internal support routine
//

NTSTATUS
FatQueryFsVolumeInfo (
    IN PIRP_CONTEXT IrpContext,
    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

--*/

{
    ULONG BytesToCopy;

    NTSTATUS Status;

    DebugTrace(0, Dbg, "FatQueryFsVolumeInfo...\n", 0);

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

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

    //
    //  Check if the buffer we're given is long enough
    //

    if ( *Length >= (ULONG)Vcb->Vpb->VolumeLabelLength ) {

        BytesToCopy = Vcb->Vpb->VolumeLabelLength;

        Status = STATUS_SUCCESS;

    } else {

        BytesToCopy = *Length;

        Status = STATUS_BUFFER_OVERFLOW;
    }

    //
    //  Copy over what we can of the volume label, and adjust *Length
    //

    Buffer->VolumeLabelLength = Vcb->Vpb->VolumeLabelLength;

    RtlCopyMemory( &Buffer->VolumeLabel[0],
                   &Vcb->Vpb->VolumeLabel[0],
                   BytesToCopy );

    *Length -= BytesToCopy;

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

    UNREFERENCED_PARAMETER( IrpContext );

    return Status;
}


//
//  Internal support routine
//

NTSTATUS
FatQueryFsSizeInfo (
    IN PIRP_CONTEXT IrpContext,
    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

--*/

{
    DebugTrace(0, Dbg, "FatQueryFsSizeInfo...\n", 0);

    RtlZeroMemory( Buffer, sizeof(FILE_FS_SIZE_INFORMATION) );

    //
    //  Set the output buffer.  
    //

    Buffer->TotalAllocationUnits.LowPart =
                                    Vcb->AllocationSupport.NumberOfClusters;
    Buffer->AvailableAllocationUnits.LowPart =
                                    Vcb->AllocationSupport.NumberOfFreeClusters;
  
    Buffer->SectorsPerAllocationUnit = Vcb->Bpb.SectorsPerCluster;
    Buffer->BytesPerSector = Vcb->Bpb.BytesPerSector;

    //
    //  Adjust the length variable
    //

    *Length -= sizeof(FILE_FS_SIZE_INFORMATION);

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

    UNREFERENCED_PARAMETER( IrpContext );

    return STATUS_SUCCESS;
}


//
//  Internal support routine
//

NTSTATUS
FatQueryFsDeviceInfo (
    IN PIRP_CONTEXT IrpContext,
    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

--*/

{
    DebugTrace(0, Dbg, "FatQueryFsDeviceInfo...\n", 0);

    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
    //

    UNREFERENCED_PARAMETER( IrpContext );

    return STATUS_SUCCESS;
}


//
//  Internal support routine
//

NTSTATUS
FatQueryFsAttributeInfo (
    IN PIRP_CONTEXT IrpContext,
    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 BytesToCopy;

    NTSTATUS Status;

    DebugTrace(0, Dbg, "FatQueryFsAttributeInfo...\n", 0);

    //
    //  Set the output buffer
    //

    Buffer->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES |
                                   FILE_UNICODE_ON_DISK;

    if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED )) {
        
        SetFlag( Buffer->FileSystemAttributes, FILE_READ_ONLY_VOLUME );
    }

    Buffer->MaximumComponentNameLength = FatData.ChicagoMode ? 255 : 12;

    if (FatIsFat32(Vcb)) {

        //
        //  Determine how much of the file system name will fit.
        //
    
        if ( (*Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION,
                                      FileSystemName[0] )) >= 10 ) {
    
            BytesToCopy = 10;
            *Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION,
                                     FileSystemName[0] ) + 10;
            Status = STATUS_SUCCESS;
    
        } else {
    
            BytesToCopy = *Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION,
                                                  FileSystemName[0]);
            *Length = 0;
    
            Status = STATUS_BUFFER_OVERFLOW;
        }
    
        RtlCopyMemory( &Buffer->FileSystemName[0], L"FAT32", BytesToCopy );

    } else {

        //
        //  Determine how much of the file system name will fit.
        //
    
        if ( (*Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION,
                                      FileSystemName[0] )) >= 6 ) {
    
            BytesToCopy = 6;
            *Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION,
                                     FileSystemName[0] ) + 6;
            Status = STATUS_SUCCESS;
    
        } else {
    
            BytesToCopy = *Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION,
                                                  FileSystemName[0]);
            *Length = 0;
    
            Status = STATUS_BUFFER_OVERFLOW;
        }
    

        RtlCopyMemory( &Buffer->FileSystemName[0], L"FAT", BytesToCopy );
    }

    Buffer->FileSystemNameLength       = BytesToCopy;

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

    UNREFERENCED_PARAMETER( IrpContext );
    UNREFERENCED_PARAMETER( Vcb );

    return Status;
}


//
//  Internal support routine
//

NTSTATUS
FatQueryFsFullSizeInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PVCB Vcb,
    IN PFILE_FS_FULL_SIZE_INFORMATION Buffer,
    IN OUT PULONG Length
    )

/*++

Routine Description:

    This routine implements the query volume full 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

--*/

{
    DebugTrace(0, Dbg, "FatQueryFsSizeInfo...\n", 0);

    RtlZeroMemory( Buffer, sizeof(FILE_FS_FULL_SIZE_INFORMATION) );

    Buffer->TotalAllocationUnits.LowPart =
                                Vcb->AllocationSupport.NumberOfClusters;
    Buffer->CallerAvailableAllocationUnits.LowPart =
                                Vcb->AllocationSupport.NumberOfFreeClusters;
    Buffer->ActualAvailableAllocationUnits.LowPart =
        Buffer->CallerAvailableAllocationUnits.LowPart;
    Buffer->SectorsPerAllocationUnit = Vcb->Bpb.SectorsPerCluster;
    Buffer->BytesPerSector = Vcb->Bpb.BytesPerSector;

    //
    //  Adjust the length variable
    //

    *Length -= sizeof(FILE_FS_FULL_SIZE_INFORMATION);

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

    UNREFERENCED_PARAMETER( IrpContext );

    return STATUS_SUCCESS;
}


//
//  Internal support routine
//

NTSTATUS
FatSetFsLabelInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PVCB Vcb,
    IN PFILE_FS_LABEL_INFORMATION Buffer
    )

/*++

Routine Description:

    This routine implements the set volume label call

Arguments:

    Vcb - Supplies the Vcb being queried

    Buffer - Supplies the input where the information is stored.

Return Value:

    NTSTATUS - Returns the status for the operation

--*/

{
    NTSTATUS Status;

    PDIRENT Dirent;
    PBCB DirentBcb = NULL;
    ULONG ByteOffset;

    WCHAR TmpBuffer[11];
    UCHAR OemBuffer[11];
    OEM_STRING OemLabel;
    UNICODE_STRING UnicodeString;
    UNICODE_STRING UpcasedLabel;

    DebugTrace(+1, Dbg, "FatSetFsLabelInfo...\n", 0);

    //
    //  Setup our local variable
    //

    UnicodeString.Length = (USHORT)Buffer->VolumeLabelLength;
    UnicodeString.MaximumLength = UnicodeString.Length;
    UnicodeString.Buffer = (PWSTR) &Buffer->VolumeLabel[0];

    //
    //  Make sure the name can fit into the stack buffer
    //

    if ( UnicodeString.Length > 11*sizeof(WCHAR) ) {

        return STATUS_INVALID_VOLUME_LABEL;
    }

    //
    //  Upcase the name and convert it to the Oem code page.
    //

    OemLabel.Buffer = &OemBuffer[0];
    OemLabel.Length = 0;
    OemLabel.MaximumLength = 11;

    Status = RtlUpcaseUnicodeStringToCountedOemString( &OemLabel,
                                                       &UnicodeString,
                                                       FALSE );

    //
    //  Volume label that fits in 11 unicode character length limit
    //  is not necessary within 11 characters in OEM character set.
    //

    if (!NT_SUCCESS( Status )) {

        DebugTrace(-1, Dbg, "FatSetFsLabelInfo:  Label must be too long. %08lx\n", Status );

        return STATUS_INVALID_VOLUME_LABEL;
    }

    //
    //  Strip spaces off of the label.
    //

    if (OemLabel.Length > 0) {

        USHORT i;
        USHORT LastSpaceIndex = MAXUSHORT;

        //
        //  Check the label for illegal characters
        //

        for ( i = 0; i < (ULONG)OemLabel.Length; i += 1 ) {

            if ( FsRtlIsLeadDbcsCharacter( OemLabel.Buffer[i] ) ) {

                LastSpaceIndex = MAXUSHORT;
                i += 1;
                continue;
            }

            if (!FsRtlIsAnsiCharacterLegalFat(OemLabel.Buffer[i], FALSE) ||
                (OemLabel.Buffer[i] == '.')) {

                return STATUS_INVALID_VOLUME_LABEL;
            }

            //
            //  Watch for the last run of spaces, so we can strip them.
            //

            if (OemLabel.Buffer[i] == ' ' &&
                LastSpaceIndex == MAXUSHORT) {
                LastSpaceIndex = i;
            } else {
                LastSpaceIndex = MAXUSHORT;
            }
        }

        if (LastSpaceIndex != MAXUSHORT) {
            OemLabel.Length = LastSpaceIndex;
        }
    }

    //
    //  Get the Unicode upcased string to store in the VPB.
    //

    UpcasedLabel.Length = UnicodeString.Length;
    UpcasedLabel.MaximumLength = 11*sizeof(WCHAR);
    UpcasedLabel.Buffer = &TmpBuffer[0];

    Status = RtlOemStringToCountedUnicodeString( &UpcasedLabel,
                                                 &OemLabel,
                                                 FALSE );

    if (!NT_SUCCESS( Status )) {

        DebugTrace(-1, Dbg, "FatSetFsLabelInfo:  Label must be too long. %08lx\n", Status );

        return STATUS_INVALID_VOLUME_LABEL;
    }

    DirentBcb = NULL;

    //
    //  Make this look like a write through to disk.  This is important to
    //  avoid a unpleasant window where it looks like we have the wrong volume.
    //

    SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH );

    try {

        //
        //  Are we setting or removing the label?  Note that shaving spaces could
        //  make this different than wondering if the input buffer is non-zero length.
        //
        
        if (OemLabel.Length > 0) {

            //
            //  Locate the volume label if there already is one
            //

            FatLocateVolumeLabel( IrpContext,
                                  Vcb,
                                  &Dirent,
                                  &DirentBcb,
                                  &ByteOffset );

            //
            //  Check that we really got one, if not then we need to create
            //  a new one.  The procedure we call will raise an appropriate
            //  status if we are not able to allocate a new dirent
            //

            if (Dirent == NULL) {

                ByteOffset = FatCreateNewDirent( IrpContext,
                                                 Vcb->RootDcb,
                                                 1 );

                FatPrepareWriteDirectoryFile( IrpContext,
                                              Vcb->RootDcb,
                                              ByteOffset,
                                              sizeof(DIRENT),
                                              &DirentBcb,
                                              &Dirent,
                                              FALSE,
                                              TRUE,
                                              &Status );

                ASSERT( NT_SUCCESS( Status ));
            
            } else {

                //
                //  Just mark this guy dirty now.
                //
            
                FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE );
            }

            //
            //  Now reconstruct the volume label dirent.
            //

            FatConstructLabelDirent( IrpContext,
                                     Dirent,
                                     &OemLabel );

            //
            //  Unpin the Bcb here so that we will get any IO errors
            //  here before changing the VPB label.
            //

            FatUnpinBcb( IrpContext, DirentBcb );
            FatUnpinRepinnedBcbs( IrpContext );

            //
            //  Now set the upcased label in the VPB
            //

            RtlCopyMemory( &Vcb->Vpb->VolumeLabel[0],
                           &UpcasedLabel.Buffer[0],
                           UpcasedLabel.Length );

            Vcb->Vpb->VolumeLabelLength = UpcasedLabel.Length;

        } else {

            //
            //  Otherwise we're trying to delete the label
            //  Locate the current volume label if there already is one
            //

            FatLocateVolumeLabel( IrpContext,
                                  Vcb,
                                  &Dirent,
                                  &DirentBcb,
                                  &ByteOffset );

            //
            //  Check that we really got one
            //

            if (Dirent == NULL) {

                try_return( Status = STATUS_SUCCESS );
            }

            //
            //  Now delete the current label.
            //

            Dirent->FileName[0] = FAT_DIRENT_DELETED;

            ASSERT( (Vcb->RootDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) ||
                    RtlAreBitsSet( &Vcb->RootDcb->Specific.Dcb.FreeDirentBitmap,
                                   ByteOffset / sizeof(DIRENT),
                                   1 ) );

            RtlClearBits( &Vcb->RootDcb->Specific.Dcb.FreeDirentBitmap,
                          ByteOffset / sizeof(DIRENT),
                          1 );

            FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE );

            //
            //  Unpin the Bcb here so that we will get any IO errors
            //  here before changing the VPB label.
            //

            FatUnpinBcb( IrpContext, DirentBcb );
            FatUnpinRepinnedBcbs( IrpContext );

            //
            //  Now set the label in the VPB
            //

            Vcb->Vpb->VolumeLabelLength = 0;
        }

        Status = STATUS_SUCCESS;

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatSetFsALabelInfo );

        FatUnpinBcb( IrpContext, DirentBcb );

        DebugTrace(-1, Dbg, "FatSetFsALabelInfo -> STATUS_SUCCESS\n", 0);
    }

    return Status;
}

