/*++

Copyright (c) 1993 Microsoft Corporation

Module Name:

    spdisk.h

Abstract:

    Hard disk manipulation support for text setup.

Author:

    Ted Miller (tedm) 27-Aug-1993

Revision History:

--*/


#include "spprecmp.h"
#pragma hdrstop
#include <ntddscsi.h>

//
// The following will be TRUE if hard disks have been determined
// successfully (ie, if SpDetermineHardDisks was successfully called).
//
BOOLEAN HardDisksDetermined = FALSE;

//
// These two globals track the hard disks attached to the computer.
//
PHARD_DISK HardDisks;
ULONG      HardDiskCount;

//
// These flags get set to TRUE if we find any disks owned
// by ATDISK or ABIOSDSK.
//
BOOLEAN AtDisksExist = FALSE;
BOOLEAN AbiosDisksExist = FALSE;

//
// Structure to track scsi ports in the system and routine to initialize
// a list of them.
//
typedef struct _MY_SCSI_PORT_INFO {

    //
    // Port number, redundant if these are stored in an array.
    //
    ULONG PortNumber;

    //
    // Port number relative to the the first port owned by the
    // adapter that owns this port.
    //
    // For example, if there are 2 Future Domain controllers and an Adaptec
    // controller, the RelativePortNumbers would be 0, 1, and 0.
    //
    ULONG RelativePortNumber;

    //
    // Name of owning miniport driver (ie, aha154x or fd8xx).
    // NULL if unknown.
    //
    PWSTR MiniportName;

} MY_SCSI_PORT_INFO, *PMY_SCSI_PORT_INFO;


//
// Disk format type strings
//
// TBD : Use the localized strings
//
WCHAR   *DiskTags[] = { DISK_TAG_TYPE_UNKNOWN,
                        DISK_TAG_TYPE_PCAT,
                        DISK_TAG_TYPE_NEC98,
                        DISK_TAG_TYPE_GPT,
                        DISK_TAG_TYPE_RAW };

VOID
SpInitializeScsiPortList(
    VOID
    );

//
// Count of scsi ports in the system.
//
ULONG ScsiPortCount;
PMY_SCSI_PORT_INFO ScsiPortInfo;

//
// Key in registry of device map
//
PCWSTR szRegDeviceMap = L"\\Registry\\Machine\\Hardware\\DeviceMap";


PWSTR
SpDetermineOwningDriver(
    IN HANDLE Handle
    );

VOID
SpGetDiskInfo(
    IN  ULONG      DiskNumber,
    IN  PVOID      SifHandle,
    IN  PWSTR      OwningDriverName,
    IN  HANDLE     Handle,
    OUT PHARD_DISK Descriptor
    );

BOOLEAN
SpGetScsiAddress(
    IN  HANDLE         Handle,
    OUT PSCSI_ADDRESS  ScsiAddress,
    OUT PWSTR         *ScsiAdapterName
    );

NTSTATUS
SpDetermineInt13Hookers(
    IN HANDLE DiskHandle,
    IN OUT PHARD_DISK Disk
    )
{
    NTSTATUS Status = STATUS_INVALID_PARAMETER;
    
    if (DiskHandle && Disk) {
        PVOID   UnalignedBuffer = SpMemAlloc(Disk->Geometry.BytesPerSector * 2);

        if (UnalignedBuffer) {
            PON_DISK_MBR    Mbr = ALIGN(UnalignedBuffer, Disk->Geometry.BytesPerSector);

            Disk->Int13Hooker = NoHooker;

            Status = SpReadWriteDiskSectors(DiskHandle,
                        0,
                        1,
                        Disk->Geometry.BytesPerSector,
                        (PVOID)Mbr,
                        FALSE);


            if (NT_SUCCESS(Status)) {
                switch (Mbr->PartitionTable[0].SystemId) {
                    case 0x54:
                        Disk->Int13Hooker = HookerOnTrackDiskManager;
                        break;
                        
                    case 0x55:
                        Disk->Int13Hooker = HookerEZDrive;
                        break;
                        
                    default:
                        break;
                }
            }                    

            SpMemFree(UnalignedBuffer);
        } else {
            Status = STATUS_NO_MEMORY;
        }            
    }

    return Status;
}

   
NTSTATUS
SpDetermineHardDisks(
    IN PVOID SifHandle
    )

/*++

Routine Description:

    Determine the hard disks attached to the computer and
    the state they are in (ie, on-line, off-line, removed, etc).

Arguments:

    SifHandle - handle to main setup information file.

Return Value:

    STATUS_SUCCESS   - operation successful.

    The global variables HardDisks and
    HardDiskCount are filled in if STATUS_SUCCESS.

--*/

{
    PCONFIGURATION_INFORMATION ConfigInfo;
    ULONG disk;
    PWSTR OwningDriverName;
    ULONG remainder;
    LARGE_INTEGER temp;
    PARTITION_INFORMATION PartitionInfo;

    CLEAR_CLIENT_SCREEN();
    SpDisplayStatusText(SP_STAT_EXAMINING_DISK_CONFIG,DEFAULT_STATUS_ATTRIBUTE);

    //
    // Determine the number of hard disks attached to the system
    // and allocate space for an array of Disk Descriptors.
    //
    ConfigInfo = IoGetConfigurationInformation();
    HardDiskCount = ConfigInfo->DiskCount;

    if ( HardDiskCount != 0 ) {
        HardDisks = SpMemAlloc(HardDiskCount * sizeof(HARD_DISK));
        RtlZeroMemory(HardDisks,HardDiskCount * sizeof(HARD_DISK));
    }

    SpInitializeScsiPortList();

    //
    // For each disk, fill in its device path in the nt namespace
    // and get information about the device.
    //

    for(disk=0; disk<HardDiskCount; disk++) {

        NTSTATUS Status;
        IO_STATUS_BLOCK IoStatusBlock;
        HANDLE Handle;
        PHARD_DISK Descriptor;
        FILE_FS_DEVICE_INFORMATION DeviceInfo;

        Descriptor = &HardDisks[disk];

        swprintf(Descriptor->DevicePath,L"\\Device\\Harddisk%u",disk);

        //
        // Assume off-line.
        //
        Descriptor->Status = DiskOffLine;

        SpFormatMessage(
            Descriptor->Description,
            sizeof(Descriptor->Description),
            SP_TEXT_UNKNOWN_DISK_0
            );

        //
        // Open partition0 of the disk.  This should succeed even if
        // there is no media in the drive.
        //
        Status = SpOpenPartition0(Descriptor->DevicePath,&Handle,FALSE);
        if(!NT_SUCCESS(Status)) {
            continue;
        }
        
        //
        // Determine device characteristics (fixed/removable).
        // If this fails, assume that the disk is fixed and off-line.
        //
        Status = ZwQueryVolumeInformationFile(
                    Handle,
                    &IoStatusBlock,
                    &DeviceInfo,
                    sizeof(DeviceInfo),
                    FileFsDeviceInformation
                    );

        if(NT_SUCCESS(Status)) {

            //
            // Save device characteristic information.
            //
            ASSERT(DeviceInfo.DeviceType == FILE_DEVICE_DISK);
            ASSERT((DeviceInfo.Characteristics & (FILE_FLOPPY_DISKETTE | FILE_REMOTE_DEVICE)) == 0);
            Descriptor->Characteristics = DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA;

        } else {
            KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: unable to determine device characteristics for %ws (%lx)\n",Descriptor->DevicePath,Status));
            ZwClose(Handle);
            continue;
        }

        //
        // Attempt to get geometry.
        // If this fails, then assume the disk is off-line.
        //
        Status = ZwDeviceIoControlFile(
                    Handle,
                    NULL,
                    NULL,
                    NULL,
                    &IoStatusBlock,
                    IOCTL_DISK_GET_DRIVE_GEOMETRY,
                    NULL,
                    0,
                    &Descriptor->Geometry,
                    sizeof(DISK_GEOMETRY)
                    );

        if(NT_SUCCESS(Status)) {

            Descriptor->CylinderCount = Descriptor->Geometry.Cylinders.QuadPart;

            //
            // Calculate some additional geometry information.
            //
            Descriptor->SectorsPerCylinder = Descriptor->Geometry.SectorsPerTrack
                                           * Descriptor->Geometry.TracksPerCylinder;

#if defined(_IA64_)            
            Status = ZwDeviceIoControlFile(
                Handle,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                IOCTL_DISK_GET_PARTITION_INFO,
                NULL,
                0,
                &PartitionInfo,
                sizeof(PARTITION_INFORMATION)
                );
            if (NT_SUCCESS(Status)) {
                Descriptor->DiskSizeSectors = (PartitionInfo.PartitionLength.QuadPart) / 
                    (Descriptor->Geometry.BytesPerSector);
            }
            else {
#endif
                Descriptor->DiskSizeSectors = RtlExtendedIntegerMultiply(
                                                    Descriptor->Geometry.Cylinders,
                                                    Descriptor->SectorsPerCylinder
                                                    ).LowPart;
#if defined(_IA64_)            
            }
#endif
            if (IsNEC_98) { //NEC98
                //
                // Used last cylinder by T&D
                //
                Descriptor->DiskSizeSectors -= Descriptor->SectorsPerCylinder;
            } //NEC98

            Descriptor->Status = DiskOnLine;

            //
            // Calculate the size of the disk in MB.
            //
            temp.QuadPart = UInt32x32To64(
                                Descriptor->DiskSizeSectors,
                                Descriptor->Geometry.BytesPerSector
                                );

            Descriptor->DiskSizeMB = RtlExtendedLargeIntegerDivide(temp,1024*1024,&remainder).LowPart;
            if(remainder >= 512) {
                Descriptor->DiskSizeMB++;
            }

            //
            // Now that we know how big the disk is, change the default disk name.
            //
            SpFormatMessage(
                Descriptor->Description,
                sizeof(Descriptor->Description),
                SP_TEXT_UNKNOWN_DISK_1,
                Descriptor->DiskSizeMB
                );

            //
            // Attempt to get the disk signature.
            //
            Status = ZwDeviceIoControlFile(
                        Handle,
                        NULL,
                        NULL,
                        NULL,
                        &IoStatusBlock,
                        IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
                        NULL,
                        0,
                        TemporaryBuffer,
                        sizeof(TemporaryBuffer)
                        );

            if(NT_SUCCESS(Status)) {
                PDRIVE_LAYOUT_INFORMATION_EX    DriveLayoutEx = 
                                (PDRIVE_LAYOUT_INFORMATION_EX)TemporaryBuffer;

                if (DriveLayoutEx->PartitionStyle == PARTITION_STYLE_MBR)                               
                    Descriptor->Signature = (( PDRIVE_LAYOUT_INFORMATION )TemporaryBuffer)->Signature;

                Descriptor->DriveLayout = *DriveLayoutEx;

                switch (DriveLayoutEx->PartitionStyle) {
                    case PARTITION_STYLE_MBR:
                        Descriptor->FormatType = DISK_FORMAT_TYPE_PCAT;

                        //
                        // Determine if any INT13 hookers are present
                        //
                        SpDetermineInt13Hookers(Handle, Descriptor);

#if defined(_IA64_)            
                        //
                        // Make sure that this is not a raw disk
                        // which is being faked as MBR disk
                        //
                        if (SpPtnIsRawDiskDriveLayout(DriveLayoutEx)) {
                            Descriptor->FormatType = DISK_FORMAT_TYPE_RAW;
                            SPPT_SET_DISK_BLANK(disk, TRUE);
                        }                                                    
#endif                    
                        
                        break;
                        
                    case PARTITION_STYLE_GPT:
                        Descriptor->FormatType = DISK_FORMAT_TYPE_GPT;
                        break;
                        
                    case PARTITION_STYLE_RAW:                    
                        Descriptor->FormatType = DISK_FORMAT_TYPE_RAW;
                        SPPT_SET_DISK_BLANK(disk, TRUE);
                        
                        break;

                    default:
                        Descriptor->FormatType = DISK_FORMAT_TYPE_UNKNOWN;
                        break;
                }
            } else {
                KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: failed to get signature for %ws (%lx)\n",Descriptor->DevicePath,Status));
                Descriptor->Signature = 0;
            }

        } else {
            KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: failed to get geometry for %ws (%lx)\n",Descriptor->DevicePath,Status));
            ZwClose(Handle);
            continue;
        }

        //
        // NEC98: force removable media to OFFLINE.
        // Because NEC98 doesnot support FLEX boot, so NT cannot boot up
        // from removable media.
        //
        if (IsNEC_98 && (Descriptor->Characteristics & FILE_REMOVABLE_MEDIA)) {
            Descriptor->Status = DiskOffLine;
            KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: found removable disk. force offline %ws\n", Descriptor->DevicePath));
        }

        //
        // Now go through the device object to determine the device driver
        // that owns this disk.
        //
        if(OwningDriverName = SpDetermineOwningDriver(Handle)) {

            SpGetDiskInfo(disk,SifHandle,OwningDriverName,Handle,Descriptor);
            SpMemFree(OwningDriverName);
        }

        ZwClose(Handle);
    }

    HardDisksDetermined = TRUE;
    return(STATUS_SUCCESS);
}


VOID
SpGetDiskInfo(
    IN  ULONG      DiskNumber,
    IN  PVOID      SifHandle,
    IN  PWSTR      OwningDriverName,
    IN  HANDLE     Handle,
    OUT PHARD_DISK Descriptor
    )
{
    PWSTR FormatString;
    PWSTR ScsiAdapterName;
    PWSTR PcCardInfoKey;
    SCSI_ADDRESS ScsiAddress;
    NTSTATUS Status;
    ULONG ValLength;
    PKEY_VALUE_PARTIAL_INFORMATION p;
    IO_STATUS_BLOCK IoStatusBlock;
    DISK_CONTROLLER_NUMBER ControllerInfo;

    PcCardInfoKey = NULL;

    //
    // Look up the driver in the map in txtsetup.sif.
    // Note that the driver could be one we don't recognize.
    //
    FormatString = SpGetSectionKeyIndex(SifHandle,SIF_DISKDRIVERMAP,OwningDriverName,0);

#ifdef _X86_
    //
    // Assume not SCSI and thus no scsi-style ARC name.
    //
    Descriptor->ArcPath[0] = 0;
    Descriptor->ScsiMiniportShortname[0] = 0;
#endif

    if(FormatString) {

        if(_wcsicmp(OwningDriverName,L"disk")) {

            //
            // Non-scsi.
            //
            SpFormatMessageText(
                Descriptor->Description,
                sizeof(Descriptor->Description),
                FormatString,
                Descriptor->DiskSizeMB
                );

            if(!_wcsicmp(OwningDriverName,L"atdisk")) {

                AtDisksExist = TRUE;

                //
                // Get controller number for atdisks.
                //
                Status = ZwDeviceIoControlFile(
                            Handle,
                            NULL,
                            NULL,
                            NULL,
                            &IoStatusBlock,
                            IOCTL_DISK_CONTROLLER_NUMBER,
                            NULL,
                            0,
                            &ControllerInfo,
                            sizeof(DISK_CONTROLLER_NUMBER)
                            );

                if(NT_SUCCESS(Status)) {

                    swprintf(
                        TemporaryBuffer,
                        L"%ws\\AtDisk\\Controller %u",
                        szRegDeviceMap,
                        ControllerInfo.ControllerNumber
                        );

                    PcCardInfoKey = SpDupStringW(TemporaryBuffer);

                } else {
                    KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to get controller number (%lx)\n",Status));
                }
            } else if(!IsNEC_98) {
                //
                // Not AT disk, might be abios disk. (NEC98 does not have ABIOS disk.)
                //
                if(!_wcsicmp(OwningDriverName,L"abiosdsk")) {
                    AbiosDisksExist = TRUE;
                }
            }

        } else {
            //
            // Scsi. Get disk address info.
            //
            if(SpGetScsiAddress(Handle,&ScsiAddress,&ScsiAdapterName)) {

                swprintf(
                    TemporaryBuffer,
                    L"%ws\\Scsi\\Scsi Port %u",
                    szRegDeviceMap,
                    ScsiAddress.PortNumber
                    );

                PcCardInfoKey = SpDupStringW(TemporaryBuffer);

                SpFormatMessageText(
                    Descriptor->Description,
                    sizeof(Descriptor->Description),
                    FormatString,
                    Descriptor->DiskSizeMB,
                    ScsiAddress.Lun,
                    ScsiAddress.TargetId,
                    ScsiAddress.PathId,
                    ScsiAdapterName
                    );

#ifdef _X86_
                //
                // Generate "secondary" arc path.
                //

                _snwprintf(
                    Descriptor->ArcPath,
                    sizeof(Descriptor->ArcPath)/sizeof(WCHAR),
                    L"scsi(%u)disk(%u)rdisk(%u)",
                    ScsiPortInfo[ScsiAddress.PortNumber].RelativePortNumber,
                    SCSI_COMBINE_BUS_TARGET(ScsiAddress.PathId, ScsiAddress.TargetId),
                    ScsiAddress.Lun
                    );

                wcsncpy(
                    Descriptor->ScsiMiniportShortname,
                    ScsiAdapterName,
                    (sizeof(Descriptor->ScsiMiniportShortname)/sizeof(WCHAR))-1
                    );
#endif

                SpMemFree(ScsiAdapterName);

            } else {

                //
                // Some drivers, like SBP2PORT (1394), don't support
                // IOCTL_SCSI_GET_ADDRESS, so just display driver name.
                //

                SpFormatMessage(
                    Descriptor->Description,
                    sizeof (Descriptor->Description),
                    SP_TEXT_UNKNOWN_DISK_2,
                    Descriptor->DiskSizeMB,
                    OwningDriverName
                    );
            }
        }
    }

    //
    // Determine whether the disk is pcmcia.
    //
    if(PcCardInfoKey) {

        Status = SpGetValueKey(
                    NULL,
                    PcCardInfoKey,
                    L"PCCARD",
                    sizeof(TemporaryBuffer),
                    (PCHAR)TemporaryBuffer,
                    &ValLength
                    );

        if(NT_SUCCESS(Status)) {

            p = (PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer;

            if((p->Type == REG_DWORD) && (p->DataLength == sizeof(ULONG)) && *(PULONG)p->Data) {

                Descriptor->PCCard = TRUE;
            }
        }

        SpMemFree(PcCardInfoKey);
    }
}


BOOLEAN
SpGetScsiAddress(
    IN  HANDLE         Handle,
    OUT PSCSI_ADDRESS  ScsiAddress,
    OUT PWSTR         *ScsiAdapterName
    )

/*++

Routine Description:

    Get scsi address information about a device.  This includes
    the port, bus, id, and lun, as well as the shortname of the miniport
    driver that owns the device.

Arguments:

    Handle - handle to open device.

    ScsiAddress - receives port, bus, id, and lun for the device described by Handle.

    ScsiAdapterName - receives pointer to buffer containing shortname
        for miniport driver that owns the device (ie, aha154x).
        The caller must free this buffer via SpMemFree().

Return Value:

    TRUE - scsi address information was determined successfully.
    FALSE - error determining scsi address information.

--*/

{
    NTSTATUS Status;
    PWSTR MiniportName = NULL;
    IO_STATUS_BLOCK IoStatusBlock;

    Status = ZwDeviceIoControlFile(
                Handle,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                IOCTL_SCSI_GET_ADDRESS,
                NULL,
                0,
                ScsiAddress,
                sizeof(SCSI_ADDRESS)
                );

    if(!NT_SUCCESS(Status)) {
        KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to get scsi address info (%lx)\n",Status));
        return(FALSE);
    }

    //
    // We can get the miniport name from the scsi port information list
    // we built earlier.
    //
    if(ScsiAddress->PortNumber < ScsiPortCount) {

        MiniportName = ScsiPortInfo[ScsiAddress->PortNumber].MiniportName;

    } else {

        //
        // This should not happen.
        //
        ASSERT(ScsiAddress->PortNumber < ScsiPortCount);

        MiniportName = TemporaryBuffer;
        SpFormatMessage(MiniportName,sizeof(TemporaryBuffer),SP_TEXT_UNKNOWN);
    }

    *ScsiAdapterName = SpDupStringW(MiniportName);

    return(TRUE);
}


PWSTR
SpDetermineOwningDriver(
    IN HANDLE Handle
    )
{
    NTSTATUS Status;
    OBJECT_HANDLE_INFORMATION HandleInfo;
    PFILE_OBJECT FileObject;
    ULONG ObjectNameLength;
    POBJECT_NAME_INFORMATION ObjectNameInfo;
    PWSTR OwningDriverName;

    //
    // Get the file object for the disk device.
    //
    Status = ObReferenceObjectByHandle(
                Handle,
                0L,
                *IoFileObjectType,
                ExGetPreviousMode(),
                &FileObject,
                &HandleInfo
                );

    if(!NT_SUCCESS(Status)) {
        KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpDetermineOwningDriver: unable to reference object (%lx)\n",Status));
        return(NULL);
    }

    //
    // Follow the links to the driver object and query the name.
    //
    ObjectNameInfo = (POBJECT_NAME_INFORMATION)TemporaryBuffer;

    Status = ObQueryNameString(
                FileObject->DeviceObject->DriverObject,
                ObjectNameInfo,
                sizeof(TemporaryBuffer),
                &ObjectNameLength
                );

    //
    // Dereference the file object now that we've got the name.
    //
    ObDereferenceObject(FileObject);

    //
    // Check the status of the name query.
    //
    if(!NT_SUCCESS(Status)) {
        KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpDetermineOwningDriver: unable to query name string (%lx)\n",Status));
        return(NULL);
    }

    //
    // Pull out the name of the owning driver.
    //
    if(OwningDriverName = wcsrchr(ObjectNameInfo->Name.Buffer,L'\\')) {
        OwningDriverName++;
    } else {
        OwningDriverName = ObjectNameInfo->Name.Buffer;
    }

    return(SpDupStringW(OwningDriverName));
}


VOID
SpInitializeScsiPortList(
    VOID
    )
{
    ULONG port;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING UnicodeString;
    IO_STATUS_BLOCK IoStatusBlock;
    NTSTATUS Status;
    HANDLE PortHandle;
    ULONG RelativeNumber;

    //
    // Get the number of scsi ports in the system.
    //
    ScsiPortCount = IoGetConfigurationInformation()->ScsiPortCount;

    //
    // Allocate an array to hold information about each port.
    //
    ScsiPortInfo = SpMemAlloc(ScsiPortCount * sizeof(MY_SCSI_PORT_INFO));
    RtlZeroMemory(ScsiPortInfo,ScsiPortCount * sizeof(MY_SCSI_PORT_INFO));

    //
    // Iterate through the ports.
    //
    for(port=0; port<ScsiPortCount; port++) {

        ScsiPortInfo[port].PortNumber = port;

        //
        // Open \device\scsiport<n> so we can determine the owning miniport.
        //
        swprintf(TemporaryBuffer,L"\\Device\\ScsiPort%u",port);

        INIT_OBJA(&ObjectAttributes,&UnicodeString,TemporaryBuffer);

        Status = ZwCreateFile(
                    &PortHandle,
                    FILE_GENERIC_READ,
                    &ObjectAttributes,
                    &IoStatusBlock,
                    NULL,
                    FILE_ATTRIBUTE_NORMAL,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    FILE_OPEN,
                    FILE_SYNCHRONOUS_IO_NONALERT,
                    NULL,
                    0
                    );

        if(NT_SUCCESS(Status)) {

            ScsiPortInfo[port].MiniportName = SpDetermineOwningDriver(PortHandle);

            ZwClose(PortHandle);

        } else {
            KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to open \\device\\scsiport%u (%lx)\n",port,Status));
        }

        //
        // Determine relative port number.  If this is port 0 or the current port owner
        // doesn't match the previous port owner, then the relative port number is 0.
        // Otherwise the relative port number is one greater than the previous relative
        // port number.
        //

        if(port && ScsiPortInfo[port-1].MiniportName && ScsiPortInfo[port].MiniportName
        && !_wcsicmp(ScsiPortInfo[port-1].MiniportName,ScsiPortInfo[port].MiniportName)) {
            RelativeNumber++;
        } else {
            RelativeNumber = 0;
        }

        ScsiPortInfo[port].RelativePortNumber = RelativeNumber;
    }
}



NTSTATUS
SpOpenPartition(
    IN  PWSTR   DiskDevicePath,
    IN  ULONG   PartitionNumber,
    OUT HANDLE *Handle,
    IN  BOOLEAN NeedWriteAccess
    )
{
    PWSTR             PartitionPath;
    UNICODE_STRING    UnicodeString;
    OBJECT_ATTRIBUTES Obja;
    NTSTATUS          Status;
    IO_STATUS_BLOCK   IoStatusBlock;

    //
    // Form the pathname of partition.
    //
    PartitionPath = SpMemAlloc((wcslen(DiskDevicePath) * sizeof(WCHAR)) + sizeof(L"\\partition000"));
    if(PartitionPath == NULL) {
        return(STATUS_NO_MEMORY);
    }

    swprintf(PartitionPath,L"%ws\\partition%u",DiskDevicePath,PartitionNumber);

    //
    // Attempt to open partition0.
    //
    INIT_OBJA(&Obja,&UnicodeString,PartitionPath);

    Status = ZwCreateFile(
                Handle,
                FILE_GENERIC_READ | (NeedWriteAccess ? FILE_GENERIC_WRITE : 0),
                &Obja,
                &IoStatusBlock,
                NULL,
                FILE_ATTRIBUTE_NORMAL,
                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                FILE_OPEN,
                FILE_SYNCHRONOUS_IO_NONALERT,
                NULL,
                0
                );

    if(!NT_SUCCESS(Status)) {
        KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to open %ws (%lx)\n",PartitionPath,Status));
    }

    SpMemFree(PartitionPath);

    return(Status);
}


NTSTATUS
SpReadWriteDiskSectors(
    IN     HANDLE  Handle,
    IN     ULONGLONG SectorNumber,
    IN     ULONG  SectorCount,
    IN     ULONG   BytesPerSector,
    IN OUT PVOID   AlignedBuffer,
    IN     BOOLEAN Write
    )

/*++

Routine Description:

    Reads or writes one or more disk sectors.

Arguments:

    Handle - supplies handle to open partition object from which
        sectors are to be read or written.  The handle must be
        opened for synchronous I/O.

Return Value:

    NTSTATUS value indicating outcome of I/O operation.

--*/

{
    LARGE_INTEGER IoOffset;
    ULONG IoSize;
    IO_STATUS_BLOCK IoStatusBlock;
    NTSTATUS Status;

    //
    // Calculate the large integer byte offset of the first sector
    // and the size of the I/O.
    //
    IoOffset.QuadPart = UInt32x32To64(SectorNumber,BytesPerSector);
    IoSize = SectorCount * BytesPerSector;

    //
    // Perform the I/O.
    //
    Status = (NTSTATUS)(

                Write

             ?
                ZwWriteFile(
                    Handle,
                    NULL,
                    NULL,
                    NULL,
                    &IoStatusBlock,
                    AlignedBuffer,
                    IoSize,
                    &IoOffset,
                    NULL
                    )
             :
                ZwReadFile(
                    Handle,
                    NULL,
                    NULL,
                    NULL,
                    &IoStatusBlock,
                    AlignedBuffer,
                    IoSize,
                    &IoOffset,
                    NULL
                    )
             );


    if(!NT_SUCCESS(Status)) {
        KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to %s %u sectors starting at sector %u\n",Write ? "write" : "read" ,SectorCount,SectorNumber));
    }

    return(Status);
}


ULONG
SpArcDevicePathToDiskNumber(
    IN PWSTR ArcPath
    )

/*++

Routine Description:

    Given an arc device path, determine which NT disk it represents.

Arguments:

    ArcPath - supplies arc path.

Return Value:

    NT disk ordinal suitable for use in generating nt device paths
    of the form \device\harddiskx.

    -1 if cannot be determined.

--*/

{
    PWSTR NtPath;
    ULONG DiskNumber;
    ULONG PrefixLength;

    //
    // Assume failure.
    //
    DiskNumber = (ULONG)(-1);
    PrefixLength = wcslen(DISK_DEVICE_NAME_BASE);

    //
    // Convert the path to an nt path.
    //
    if((NtPath = SpArcToNt(ArcPath))
    && !_wcsnicmp(NtPath,DISK_DEVICE_NAME_BASE,PrefixLength))
    {
        DiskNumber = (ULONG)SpStringToLong(NtPath+PrefixLength,NULL,10);
        SpMemFree(NtPath);
    }

    return(DiskNumber);
}


BOOLEAN
SpIsRegionBeyondCylinder1024(
    IN PDISK_REGION Region
    )

/*++

Routine Description:

    This routine figures out whether a disk region contains sectors
    that are on cylinders beyond cylinder 1024.

Arguments:

    Region - supplies the disk region for the partition to be checked.

Return Value:

    BOOLEAN - Returns TRUE if the region contains a sector located in cylinder
              1024 or greater. Otherwise returns FALSE.

--*/

{
    ULONGLONG LastSector;
    ULONGLONG LastCylinder;

    if (IsNEC_98) { //NEC98
        //
        // NEC98 has no "1024th cylinder limit".
        //
        return((BOOLEAN)FALSE);
    } //NEC98

    if (Region->DiskNumber == 0xffffffff) {
        return FALSE; //  Partition is a redirected drive
    }

    LastSector = Region->StartSector + Region->SectorCount - 1;
    LastCylinder = LastSector / HardDisks[Region->DiskNumber].SectorsPerCylinder;

    return  ((BOOLEAN)(LastCylinder > 1023));

}

VOID
SpAppendDiskTag(
    IN PHARD_DISK   Disk
    )
{
    if (Disk) {
        PWSTR   TagStart = wcsrchr(Disk->Description, DISK_TAG_START_CHAR);

        if (TagStart) {
            if (wcscmp(TagStart, DiskTags[0]) && wcscmp(TagStart, DiskTags[1]) &&
                wcscmp(TagStart, DiskTags[2]) && wcscmp(TagStart, DiskTags[3]) &&
                wcscmp(TagStart, DiskTags[4])) {

                //
                // not the tag we were looking for
                //
                TagStart = Disk->Description + wcslen(Disk->Description);
            }         
        } else {
            TagStart = Disk->Description + wcslen(Disk->Description);
            *TagStart = L' ';
            TagStart++;            
        }            

        wcscpy(TagStart, DiskTags[Disk->FormatType]);
    }   
}