/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    ntfs_rec.c

Abstract:

    This module contains the mini-file system recognizer for NTFS.

Author:

    Darryl E. Havens (darrylh) 8-dec-1992

Environment:

    Kernel mode, local to I/O system

Revision History:


--*/

#include "fs_rec.h"
#include "ntfs_rec.h"

//
//  The local debug trace level
//

#define Dbg                              (FSREC_DEBUG_LEVEL_NTFS)

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,NtfsRecFsControl)
#pragma alloc_text(PAGE,IsNtfsVolume)
#endif // ALLOC_PRAGMA


NTSTATUS
NtfsRecFsControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This function performs the mount and driver reload functions for this mini-
    file system recognizer driver.

Arguments:

    DeviceObject - Pointer to this driver's device object.

    Irp - Pointer to the I/O Request Packet (IRP) representing the function to
        be performed.

Return Value:

    The function value is the final status of the operation.


--*/

{
    NTSTATUS status;
    PIO_STACK_LOCATION irpSp;
    PDEVICE_EXTENSION deviceExtension;
    PDEVICE_OBJECT targetDevice;
    PPACKED_BOOT_SECTOR buffer;
    LARGE_INTEGER byteOffset;
    LARGE_INTEGER secondByteOffset;
    LARGE_INTEGER lastByteOffset;
    UNICODE_STRING driverName;
    ULONG bytesPerSector;
    LARGE_INTEGER numberOfSectors;

    PAGED_CODE();

    //
    // Begin by determining what function that is to be performed.
    //

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    irpSp = IoGetCurrentIrpStackLocation( Irp );

    switch ( irpSp->MinorFunction ) {

    case IRP_MN_MOUNT_VOLUME:

        //
        // Attempt to mount a volume:  Determine whether or not the volume in
        // question is an NTFS volume and, if so, let the I/O system know that it
        // is by returning a special status code so that this driver can get
        // called back to load the NTFS file system.
        //

        status = STATUS_UNRECOGNIZED_VOLUME;

        //
        // Attempt to determine whether or not the target volume being mounted
        // is an NTFS volume.
        //

        targetDevice = irpSp->Parameters.MountVolume.DeviceObject;

        if (FsRecGetDeviceSectorSize( targetDevice,
                                      &bytesPerSector ) &&
            FsRecGetDeviceSectors( targetDevice,
                                   bytesPerSector,
                                   &numberOfSectors )) {

            byteOffset.QuadPart = 0;
            buffer = NULL;
            secondByteOffset.QuadPart = numberOfSectors.QuadPart >> 1;
            secondByteOffset.QuadPart *= (LONG) bytesPerSector;
            lastByteOffset.QuadPart = (numberOfSectors.QuadPart - 1) * (LONG) bytesPerSector;

            if (FsRecReadBlock( targetDevice,
                                &byteOffset,
                                sizeof( PACKED_BOOT_SECTOR ),
                                bytesPerSector,
                                (PVOID *)&buffer,
                                NULL ))
            {

                if (IsNtfsVolume( buffer, bytesPerSector, &numberOfSectors )) {
                    status = STATUS_FS_DRIVER_REQUIRED;
                }

            } else {

                if (FsRecReadBlock( targetDevice,
                                    &secondByteOffset,
                                    sizeof( PACKED_BOOT_SECTOR ),
                                    bytesPerSector,
                                    (PVOID *)&buffer,
                                    NULL ) &&
                    IsNtfsVolume( buffer, bytesPerSector, &numberOfSectors )) {

                    status = STATUS_FS_DRIVER_REQUIRED;

                } else {

                    if (FsRecReadBlock( targetDevice,
                                        &lastByteOffset,
                                        sizeof( PACKED_BOOT_SECTOR ),
                                        bytesPerSector,
                                        (PVOID *)&buffer,
                                        NULL ) &&
                        IsNtfsVolume( buffer, bytesPerSector, &numberOfSectors )) {

                        status = STATUS_FS_DRIVER_REQUIRED;
                    }
                }
            }

            if (buffer != NULL) {
                ExFreePool( buffer );
            }
        }

        break;

    case IRP_MN_LOAD_FILE_SYSTEM:

        status = FsRecLoadFileSystem( DeviceObject,
                                      L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Ntfs" );
        break;

    default:
        status = STATUS_INVALID_DEVICE_REQUEST;

    }

    //
    // Finally, complete the request and return the same status code to the
    // caller.
    //

    Irp->IoStatus.Status = status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return status;
}


BOOLEAN
IsNtfsVolume(
    IN PPACKED_BOOT_SECTOR BootSector,
    IN ULONG BytesPerSector,
    IN PLARGE_INTEGER NumberOfSectors
    )

/*++

Routine Description:

    This routine looks at the buffer passed in which contains the NTFS boot
    sector and determines whether or not it represents an NTFS volume.

Arguments:

    BootSector - Pointer to buffer containing a potential NTFS boot sector.

    BytesPerSector - Supplies the number of bytes per sector for the drive.

    NumberOfSectors - Supplies the number of sectors on the partition.

Return Value:

    The function returns TRUE if the buffer contains a recognizable NTFS boot
    sector, otherwise it returns FALSE.

--*/

{
    PAGED_CODE();

    //
    // Now perform all the checks, starting with the Name and Checksum.
    // The remaining checks should be obvious, including some fields which
    // must be 0 and other fields which must be a small power of 2.
    //

    if (BootSector->Oem[0] == 'N' &&
        BootSector->Oem[1] == 'T' &&
        BootSector->Oem[2] == 'F' &&
        BootSector->Oem[3] == 'S' &&
        BootSector->Oem[4] == ' ' &&
        BootSector->Oem[5] == ' ' &&
        BootSector->Oem[6] == ' ' &&
        BootSector->Oem[7] == ' '

            &&

        //
        // Check number of bytes per sector.  The low order byte of this
        // number must be zero (smallest sector size = 0x100) and the
        // high order byte shifted must equal the bytes per sector gotten
        // from the device and stored in the Vcb.  And just to be sure,
        // sector size must be less than page size.
        //

        BootSector->PackedBpb.BytesPerSector[0] == 0

            &&

        ((ULONG) (BootSector->PackedBpb.BytesPerSector[1] << 8) == BytesPerSector)

            &&

        BootSector->PackedBpb.BytesPerSector[1] << 8 <= PAGE_SIZE

            &&

        //
        //  Sectors per cluster must be a power of 2.
        //

        (BootSector->PackedBpb.SectorsPerCluster[0] == 0x1 ||
         BootSector->PackedBpb.SectorsPerCluster[0] == 0x2 ||
         BootSector->PackedBpb.SectorsPerCluster[0] == 0x4 ||
         BootSector->PackedBpb.SectorsPerCluster[0] == 0x8 ||
         BootSector->PackedBpb.SectorsPerCluster[0] == 0x10 ||
         BootSector->PackedBpb.SectorsPerCluster[0] == 0x20 ||
         BootSector->PackedBpb.SectorsPerCluster[0] == 0x40 ||
         BootSector->PackedBpb.SectorsPerCluster[0] == 0x80)

            &&

        //
        //  These fields must all be zero.  For both Fat and HPFS, some of
        //  these fields must be nonzero.
        //

        BootSector->PackedBpb.ReservedSectors[0] == 0 &&
        BootSector->PackedBpb.ReservedSectors[1] == 0 &&
        BootSector->PackedBpb.Fats[0] == 0 &&
        BootSector->PackedBpb.RootEntries[0] == 0 &&
        BootSector->PackedBpb.RootEntries[1] == 0 &&
        BootSector->PackedBpb.Sectors[0] == 0 &&
        BootSector->PackedBpb.Sectors[1] == 0 &&
        BootSector->PackedBpb.SectorsPerFat[0] == 0 &&
        BootSector->PackedBpb.SectorsPerFat[1] == 0 &&
        BootSector->PackedBpb.LargeSectors[0] == 0 &&
        BootSector->PackedBpb.LargeSectors[1] == 0 &&
        BootSector->PackedBpb.LargeSectors[2] == 0 &&
        BootSector->PackedBpb.LargeSectors[3] == 0

            &&

        //
        //  Number of Sectors cannot be greater than the number of sectors
        //  on the partition.
        //

        !( BootSector->NumberSectors.QuadPart > NumberOfSectors->QuadPart )

            &&

        //
        //  Check that both Lcn values are for sectors within the partition.
        //

        !( BootSector->MftStartLcn.QuadPart *
                    BootSector->PackedBpb.SectorsPerCluster[0] >
                NumberOfSectors->QuadPart )

            &&

        !( BootSector->Mft2StartLcn.QuadPart *
                    BootSector->PackedBpb.SectorsPerCluster[0] >
                NumberOfSectors->QuadPart )

            &&

        //
        //  Clusters per file record segment and default clusters for Index
        //  Allocation Buffers must be a power of 2.  A negative number indicates
        //  a shift value to get the actual size of the structure.
        //

        ((BootSector->ClustersPerFileRecordSegment >= -31 &&
          BootSector->ClustersPerFileRecordSegment <= -9) ||
         BootSector->ClustersPerFileRecordSegment == 0x1 ||
         BootSector->ClustersPerFileRecordSegment == 0x2 ||
         BootSector->ClustersPerFileRecordSegment == 0x4 ||
         BootSector->ClustersPerFileRecordSegment == 0x8 ||
         BootSector->ClustersPerFileRecordSegment == 0x10 ||
         BootSector->ClustersPerFileRecordSegment == 0x20 ||
         BootSector->ClustersPerFileRecordSegment == 0x40)

            &&

        ((BootSector->DefaultClustersPerIndexAllocationBuffer >= -31 &&
          BootSector->DefaultClustersPerIndexAllocationBuffer <= -9) ||
         BootSector->DefaultClustersPerIndexAllocationBuffer == 0x1 ||
         BootSector->DefaultClustersPerIndexAllocationBuffer == 0x2 ||
         BootSector->DefaultClustersPerIndexAllocationBuffer == 0x4 ||
         BootSector->DefaultClustersPerIndexAllocationBuffer == 0x8 ||
         BootSector->DefaultClustersPerIndexAllocationBuffer == 0x10 ||
         BootSector->DefaultClustersPerIndexAllocationBuffer == 0x20 ||
         BootSector->DefaultClustersPerIndexAllocationBuffer == 0x40)) {

        return TRUE;

    } else {

        //
        // This does not appear to be an NTFS volume.
        //

        return FALSE;
    }
}