Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3892 lines
110 KiB

/*++
Copyright (c) 1990-1998 Microsoft Corporation
Module Name:
hanfnc.c
Abstract:
default handlers for hal functions which don't get handlers
installed by the hal
Author:
Ken Reneris (kenr) 19-July-1994
Revision History:
G.Chrysanthakopoulos (georgioc)
Added support for removable disk with a BPB,instead of a partition table.
All changes in HalIoReadParitionTable. Started 01-June-1996
--*/
#include "ntos.h"
#include "zwapi.h"
#include "hal.h"
#include "ntdddisk.h"
#include "haldisp.h"
#include "ntddft.h"
#include "mountmgr.h"
#include "stdio.h"
#include <setupblk.h>
#include "drivesup.h"
#include "fstub.h"
//
// Macro definitions
//
#define GET_STARTING_SECTOR( p ) ( \
(ULONG) (p->StartingSectorLsb0) + \
(ULONG) (p->StartingSectorLsb1 << 8) + \
(ULONG) (p->StartingSectorMsb0 << 16) + \
(ULONG) (p->StartingSectorMsb1 << 24) )
#define GET_PARTITION_LENGTH( p ) ( \
(ULONG) (p->PartitionLengthLsb0) + \
(ULONG) (p->PartitionLengthLsb1 << 8) + \
(ULONG) (p->PartitionLengthMsb0 << 16) + \
(ULONG) (p->PartitionLengthMsb1 << 24) )
//
// Structure for determing if an 0xaa55 marked sector has a BPB in it.
//
typedef struct _BOOT_SECTOR_INFO {
UCHAR JumpByte[1];
UCHAR Ignore1[2];
UCHAR OemData[8];
UCHAR BytesPerSector[2];
UCHAR Ignore2[6];
UCHAR NumberOfSectors[2];
UCHAR MediaByte[1];
UCHAR Ignore3[2];
UCHAR SectorsPerTrack[2];
UCHAR NumberOfHeads[2];
} BOOT_SECTOR_INFO, *PBOOT_SECTOR_INFO;
typedef struct _PARTITION_TABLE {
PARTITION_INFORMATION PartitionEntry[4];
} PARTITION_TABLE, *PPARTITION_TABLE;
typedef struct _DISK_LAYOUT {
ULONG TableCount;
ULONG Signature;
PARTITION_TABLE PartitionTable[1];
} DISK_LAYOUT, *PDISK_LAYOUT;
typedef struct _PTE {
UCHAR ActiveFlag; // Bootable or not
UCHAR StartingTrack; // Not used
USHORT StartingCylinder; // Not used
UCHAR PartitionType; // 12 bit FAT, 16 bit FAT etc.
UCHAR EndingTrack; // Not used
USHORT EndingCylinder; // Not used
ULONG StartingSector; // Hidden sectors
ULONG PartitionLength; // Sectors in this partition
} PTE;
typedef PTE UNALIGNED *PPTE;
//
// Strings definitions
//
static PUCHAR DiskPartitionName = "\\Device\\Harddisk%d\\Partition%d";
static PUCHAR RegistryKeyName = DISK_REGISTRY_KEY;
VOID
HalpCalculateChsValues(
IN PLARGE_INTEGER PartitionOffset,
IN PLARGE_INTEGER PartitionLength,
IN CCHAR ShiftCount,
IN ULONG SectorsPerTrack,
IN ULONG NumberOfTracks,
IN ULONG ConventionalCylinders,
OUT PPARTITION_DESCRIPTOR PartitionDescriptor
);
NTSTATUS
HalpQueryPartitionType(
IN PUNICODE_STRING DeviceName,
IN PDRIVE_LAYOUT_INFORMATION DriveLayout,
OUT PULONG PartitionType
);
NTSTATUS
HalpQueryDriveLayout(
IN PUNICODE_STRING DeviceName,
OUT PDRIVE_LAYOUT_INFORMATION* DriveLayout
);
VOID
FASTCALL
xHalGetPartialGeometry(
IN PDEVICE_OBJECT DeviceObject,
IN PULONG ConventionalCylinders,
IN PLONGLONG DiskSize
);
NTSTATUS
HalpGetFullGeometry(
IN PDEVICE_OBJECT DeviceObject,
IN PDISK_GEOMETRY Geometry,
OUT PULONGLONG RealSectorCount
);
BOOLEAN
HalpIsValidPartitionEntry(
PPARTITION_DESCRIPTOR Entry,
ULONGLONG MaxOffset,
ULONGLONG MaxSector
);
NTSTATUS
HalpNextMountLetter(
IN PUNICODE_STRING DeviceName,
OUT PUCHAR DriveLetter
);
UCHAR
HalpNextDriveLetter(
IN PUNICODE_STRING DeviceName,
IN PSTRING NtDeviceName,
OUT PUCHAR NtSystemPath,
IN BOOLEAN UseHardLinksIfNecessary
);
VOID
HalpEnableAutomaticDriveLetterAssignment(
);
NTSTATUS
HalpSetMountLetter(
IN PUNICODE_STRING DeviceName,
IN UCHAR DriveLetter
);
BOOLEAN
HalpIsOldStyleFloppy(
IN PUNICODE_STRING DeviceName
);
PULONG
IopComputeHarddiskDerangements(
IN ULONG DiskCount
);
VOID
FstubFixupEfiPartition(
IN PPARTITION_DESCRIPTOR Entry,
IN ULONGLONG MaxSector
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, HalpCalculateChsValues)
#pragma alloc_text(PAGE, HalpQueryPartitionType)
#pragma alloc_text(PAGE, HalpQueryDriveLayout)
#pragma alloc_text(PAGE, HalpNextMountLetter)
#pragma alloc_text(PAGE, HalpNextDriveLetter)
#pragma alloc_text(PAGE, HalpEnableAutomaticDriveLetterAssignment)
#pragma alloc_text(PAGE, HalpSetMountLetter)
#pragma alloc_text(PAGE, IoAssignDriveLetters)
#pragma alloc_text(PAGE, IoReadPartitionTable)
#pragma alloc_text(PAGE, IoSetPartitionInformation)
#pragma alloc_text(PAGE, IoWritePartitionTable)
#pragma alloc_text(PAGE, HalpIsValidPartitionEntry)
#pragma alloc_text(PAGE, HalpGetFullGeometry)
#pragma alloc_text(PAGE, HalpIsOldStyleFloppy)
#pragma alloc_text(PAGE, IopComputeHarddiskDerangements)
#pragma alloc_text(PAGE, FstubFixupEfiPartition)
#endif
VOID
FASTCALL
HalExamineMBR(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONG MBRTypeIdentifier,
OUT PVOID *Buffer
)
/*++
Routine Description:
Given a master boot record type (MBR - the zero'th sector on the disk),
read the master boot record of a disk. If the MBR is found to be of that
type, allocate a structure whose layout is dependant upon that partition
type, fill with the appropriate values, and return a pointer to that buffer
in the output parameter.
The best example for a use of this routine is to support Ontrack
systems DiskManager software. Ontrack software lays down a special
partition describing the entire drive. The special partition type
(0x54) will be recognized and a couple of longwords of data will
be passed back in a buffer for a disk driver to act upon.
Arguments:
DeviceObject - The device object describing the entire drive.
SectorSize - The minimum number of bytes that an IO operation can
fetch.
MBRIndentifier - A value that will be searched for in the
in the MBR. This routine will understand
the semantics implied by this value.
Buffer - Pointer to a buffer that returns data according to the
type of MBR searched for. If the MBR is not of the
type asked for, the buffer will not be allocated and this
pointer will be NULL. It is the responsibility of the
caller of HalExamineMBR to deallocate the buffer. The
caller should deallocate the memory ASAP.
Return Value:
None.
--*/
{
LARGE_INTEGER partitionTableOffset;
PUCHAR readBuffer = (PUCHAR) NULL;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PIRP irp;
PPARTITION_DESCRIPTOR partitionTableEntry;
NTSTATUS status = STATUS_SUCCESS;
ULONG readSize;
*Buffer = NULL;
//
// Determine the size of a read operation to ensure that at least 512
// bytes are read. This will guarantee that enough data is read to
// include an entire partition table. Note that this code assumes that
// the actual sector size of the disk (if less than 512 bytes) is a
// multiple of 2, a fairly reasonable assumption.
//
if (SectorSize >= 512) {
readSize = SectorSize;
} else {
readSize = 512;
}
//
// Start at sector 0 of the device.
//
partitionTableOffset = RtlConvertUlongToLargeInteger( 0 );
//
// Allocate a buffer that will hold the reads.
//
readBuffer = ExAllocatePoolWithTag(
NonPagedPoolCacheAligned,
PAGE_SIZE>readSize?PAGE_SIZE:readSize,
'btsF'
);
if (readBuffer == NULL) {
return;
}
//
// Read record containing partition table.
//
// Create a notification event object to be used while waiting for
// the read request to complete.
//
KeInitializeEvent( &event, NotificationEvent, FALSE );
irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
DeviceObject,
readBuffer,
readSize,
&partitionTableOffset,
&event,
&ioStatus );
if (!irp) {
ExFreePool(readBuffer);
return;
} else {
PIO_STACK_LOCATION irpStack;
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
status = IoCallDriver( DeviceObject, irp );
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL);
status = ioStatus.Status;
}
if (!NT_SUCCESS( status )) {
ExFreePool(readBuffer);
return;
}
//
// Check for Boot Record signature.
//
if (((PUSHORT) readBuffer)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE) {
ExFreePool(readBuffer);
return;
}
//
// Check for DM type partition.
//
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) readBuffer)[PARTITION_TABLE_OFFSET]);
if (partitionTableEntry->PartitionType != MBRTypeIdentifier) {
//
// The partition type isn't what the caller cares about.
//
ExFreePool(readBuffer);
} else {
if (partitionTableEntry->PartitionType == 0x54) {
//
// Rather than allocate a new piece of memory to return
// the data - just use the memory allocated for the buffer.
// We can assume the caller will delete this shortly.
//
((PULONG)readBuffer)[0] = 63;
*Buffer = readBuffer;
} else if (partitionTableEntry->PartitionType == 0x55) {
//
// EzDrive Parititon. Simply return the pointer to non-null
// There is no skewing here.
//
*Buffer = readBuffer;
} else {
ASSERT(partitionTableEntry->PartitionType == 0x55);
}
}
}
VOID
FASTCALL
xHalGetPartialGeometry(
IN PDEVICE_OBJECT DeviceObject,
IN PULONG ConventionalCylinders,
IN PLONGLONG DiskSize
)
/*++
Routine Description:
We need this routine to get the number of cylinders that the disk driver
thinks is on the drive. We will need this to calculate CHS values
when we fill in the partition table entries.
Arguments:
DeviceObject - The device object describing the entire drive.
ConventionalCylinders - Number of cylinders on the drive.
Return Value:
None.
--*/
{
PIRP localIrp;
PDISK_GEOMETRY diskGeometry;
PIO_STATUS_BLOCK iosb;
PKEVENT eventPtr;
NTSTATUS status;
*ConventionalCylinders = 0UL;
*DiskSize = 0UL;
diskGeometry = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(DISK_GEOMETRY),
'btsF'
);
if (!diskGeometry) {
return;
}
iosb = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(IO_STATUS_BLOCK),
'btsF'
);
if (!iosb) {
ExFreePool(diskGeometry);
return;
}
eventPtr = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(KEVENT),
'btsF'
);
if (!eventPtr) {
ExFreePool(iosb);
ExFreePool(diskGeometry);
return;
}
KeInitializeEvent(
eventPtr,
NotificationEvent,
FALSE
);
localIrp = IoBuildDeviceIoControlRequest(
IOCTL_DISK_GET_DRIVE_GEOMETRY,
DeviceObject,
NULL,
0UL,
diskGeometry,
sizeof(DISK_GEOMETRY),
FALSE,
eventPtr,
iosb
);
if (!localIrp) {
ExFreePool(eventPtr);
ExFreePool(iosb);
ExFreePool(diskGeometry);
return;
}
//
// Call the lower level driver, wait for the opertion
// to finish.
//
status = IoCallDriver(
DeviceObject,
localIrp
);
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject(
eventPtr,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL
);
status = iosb->Status;
}
if (NT_SUCCESS(status)) {
//
// The operation completed successfully. Get the cylinder
// count of the drive.
//
*ConventionalCylinders = diskGeometry->Cylinders.LowPart;
//
// If the count is less than 1024 we can pass that back. Otherwise
// send back the 1024
//
if (diskGeometry->Cylinders.QuadPart >= (LONGLONG)1024) {
*ConventionalCylinders = 1024;
}
//
// Calculate disk size from gemotry information
//
*DiskSize = diskGeometry->Cylinders.QuadPart *
diskGeometry->TracksPerCylinder *
diskGeometry->SectorsPerTrack *
diskGeometry->BytesPerSector;
}
ExFreePool(eventPtr);
ExFreePool(iosb);
ExFreePool(diskGeometry);
return;
}
VOID
HalpCalculateChsValues(
IN PLARGE_INTEGER PartitionOffset,
IN PLARGE_INTEGER PartitionLength,
IN CCHAR ShiftCount,
IN ULONG SectorsPerTrack,
IN ULONG NumberOfTracks,
IN ULONG ConventionalCylinders,
OUT PPARTITION_DESCRIPTOR PartitionDescriptor
)
/*++
Routine Description:
This routine will determine the cylinder, head, and sector (CHS) values
that should be placed in a partition table entry, given the partition's
location on the disk and its size. The values calculated are packed into
int13 format -- the high two bits of the sector byte contain bits 8 and 9
of the 10 bit cylinder value, the low 6 bits of the sector byte contain
the 6 bit sector value; the cylinder byte contains the low 8 bits
of the cylinder value; and the head byte contains the 8-bit head value.
Both the start and end CHS values are calculated.
Arguments:
PartitionOffset - Byte offset of the partition, relative to the entire
physical disk.
PartitionLength - Size in bytes of the partition.
ShiftCount - Shift count to convert from byte counts to sector counts.
SectorsPerTrack - Number of sectors in a track on the media on which
the partition resides.
NumberOfTracks - Number of tracks in a cylinder on the media on which
the partition resides.
ConventionalCylinders - The "normalized" disk cylinders. We will never
set the cylinders greater than this.
PartitionDescriptor - Structure to be filled in with the start and
end CHS values. Other fields in the structure are not referenced
or modified.
Return Value:
None.
Note:
The Cylinder and Head values are 0-based but the Sector value is 1-based.
If the start or end cylinder overflows 10 bits (ie, > 1023), CHS values
will be set to all 1's.
No checking is done on the SectorsPerTrack and NumberOfTrack values.
--*/
{
ULONG startSector, sectorCount, endSector;
ULONG sectorsPerCylinder;
ULONG remainder;
ULONG startC, startH, startS, endC, endH, endS;
LARGE_INTEGER tempInt;
PAGED_CODE();
//
// Calculate the number of sectors in a cylinder. This is the
// number of heads multiplied by the number of sectors per track.
//
sectorsPerCylinder = SectorsPerTrack * NumberOfTracks;
//
// Convert byte offset/count to sector offset/count.
//
tempInt.QuadPart = PartitionOffset->QuadPart >> ShiftCount;
startSector = tempInt.LowPart;
tempInt.QuadPart = PartitionLength->QuadPart >> ShiftCount;
sectorCount = tempInt.LowPart;
endSector = startSector + sectorCount - 1;
startC = startSector / sectorsPerCylinder;
endC = endSector / sectorsPerCylinder;
if (!ConventionalCylinders) {
ConventionalCylinders = 1024;
}
//
// Set these values so that win95 is happy.
//
if (startC >= ConventionalCylinders) {
startC = ConventionalCylinders - 1;
}
if (endC >= ConventionalCylinders) {
endC = ConventionalCylinders - 1;
}
//
// Calculate the starting track and sector.
//
remainder = startSector % sectorsPerCylinder;
startH = remainder / SectorsPerTrack;
startS = remainder % SectorsPerTrack;
//
// Calculate the ending track and sector.
//
remainder = endSector % sectorsPerCylinder;
endH = remainder / SectorsPerTrack;
endS = remainder % SectorsPerTrack;
//
// Pack the result into the caller's structure.
//
// low 8 bits of the cylinder => C value
PartitionDescriptor->StartingCylinderMsb = (UCHAR) startC;
PartitionDescriptor->EndingCylinderMsb = (UCHAR) endC;
// 8 bits of head value => H value
PartitionDescriptor->StartingTrack = (UCHAR) startH;
PartitionDescriptor->EndingTrack = (UCHAR) endH;
// bits 8-9 of cylinder and 6 bits of the sector => S value
PartitionDescriptor->StartingCylinderLsb = (UCHAR) (((startS + 1) & 0x3f)
| ((startC >> 2) & 0xc0));
PartitionDescriptor->EndingCylinderLsb = (UCHAR) (((endS + 1) & 0x3f)
| ((endC >> 2) & 0xc0));
}
#define BOOTABLE_PARTITION 0
#define PRIMARY_PARTITION 1
#define LOGICAL_PARTITION 2
#define FT_PARTITION 3
#define OTHER_PARTITION 4
#define GPT_PARTITION 5
NTSTATUS
HalpQueryPartitionType(
IN PUNICODE_STRING DeviceName,
IN PDRIVE_LAYOUT_INFORMATION DriveLayout,
OUT PULONG PartitionType
)
{
NTSTATUS status;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
KEVENT event;
PIRP irp;
PARTITION_INFORMATION_EX partInfo;
IO_STATUS_BLOCK ioStatus;
ULONG i;
status = IoGetDeviceObjectPointer(DeviceName,
FILE_READ_ATTRIBUTES,
&fileObject, &deviceObject);
if (!NT_SUCCESS(status)) {
return status;
}
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
ObDereferenceObject(fileObject);
if (deviceObject->Characteristics&FILE_REMOVABLE_MEDIA) {
ObDereferenceObject(deviceObject);
*PartitionType = LOGICAL_PARTITION;
return STATUS_SUCCESS;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
deviceObject, NULL, 0, &partInfo,
sizeof(partInfo), FALSE, &event,
&ioStatus);
if (!irp) {
ObDereferenceObject(deviceObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
ObDereferenceObject(deviceObject);
if (!NT_SUCCESS(status)) {
if (!DriveLayout) {
*PartitionType = LOGICAL_PARTITION;
return STATUS_SUCCESS;
}
return status;
}
if (partInfo.PartitionStyle != PARTITION_STYLE_MBR) {
if (partInfo.PartitionStyle != PARTITION_STYLE_GPT) {
*PartitionType = OTHER_PARTITION;
return STATUS_SUCCESS;
}
if (IsEqualGUID(&partInfo.Gpt.PartitionType,
&PARTITION_BASIC_DATA_GUID)) {
*PartitionType = GPT_PARTITION;
return STATUS_SUCCESS;
}
*PartitionType = OTHER_PARTITION;
return STATUS_SUCCESS;
}
if (!IsRecognizedPartition(partInfo.Mbr.PartitionType)) {
*PartitionType = OTHER_PARTITION;
return STATUS_SUCCESS;
}
if (partInfo.Mbr.PartitionType&0x80) {
*PartitionType = FT_PARTITION;
return STATUS_SUCCESS;
}
if (!DriveLayout) {
*PartitionType = LOGICAL_PARTITION;
return STATUS_SUCCESS;
}
for (i = 0; i < 4; i++) {
if (partInfo.StartingOffset.QuadPart ==
DriveLayout->PartitionEntry[i].StartingOffset.QuadPart) {
if (partInfo.Mbr.BootIndicator) {
*PartitionType = BOOTABLE_PARTITION;
} else {
*PartitionType = PRIMARY_PARTITION;
}
return STATUS_SUCCESS;
}
}
*PartitionType = LOGICAL_PARTITION;
return STATUS_SUCCESS;
}
NTSTATUS
HalpQueryDriveLayout(
IN PUNICODE_STRING DeviceName,
OUT PDRIVE_LAYOUT_INFORMATION* DriveLayout
)
{
NTSTATUS status;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
KEVENT event;
PIRP irp;
DISK_GEOMETRY geometry;
IO_STATUS_BLOCK ioStatus;
status = IoGetDeviceObjectPointer(DeviceName, FILE_READ_ATTRIBUTES,
&fileObject, &deviceObject);
if (!NT_SUCCESS(status)) {
return status;
}
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
ObDereferenceObject(fileObject);
if (deviceObject->Characteristics&FILE_REMOVABLE_MEDIA) {
ObDereferenceObject(deviceObject);
return STATUS_NO_MEDIA;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
deviceObject, NULL, 0, &geometry,
sizeof(geometry), FALSE, &event,
&ioStatus);
if (!irp) {
ObDereferenceObject(deviceObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
if (!NT_SUCCESS(status)) {
ObDereferenceObject(deviceObject);
return status;
}
status = IoReadPartitionTable(deviceObject, geometry.BytesPerSector,
FALSE, DriveLayout);
ObDereferenceObject(deviceObject);
return status;
}
NTSTATUS
HalpNextMountLetter(
IN PUNICODE_STRING DeviceName,
OUT PUCHAR DriveLetter
)
/*++
Routine Description:
This routine gives the device the next available drive letter.
Arguments:
DeviceName - Supplies the device name.
DriveLetter - Returns the drive letter assigned or 0.
Return Value:
NTSTATUS
--*/
{
UNICODE_STRING name;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
PMOUNTMGR_DRIVE_LETTER_TARGET input;
KEVENT event;
PIRP irp;
MOUNTMGR_DRIVE_LETTER_INFORMATION output;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
RtlInitUnicodeString(&name, MOUNTMGR_DEVICE_NAME);
status = IoGetDeviceObjectPointer(&name, FILE_READ_ATTRIBUTES, &fileObject,
&deviceObject);
if (!NT_SUCCESS(status)) {
return status;
}
input = ExAllocatePoolWithTag(PagedPool,
(sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) +
DeviceName->Length),
'btsF'
);
if (!input) {
ObDereferenceObject(fileObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
input->DeviceNameLength = DeviceName->Length;
RtlCopyMemory(input->DeviceName, DeviceName->Buffer, DeviceName->Length);
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER,
deviceObject, input,
sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) +
DeviceName->Length, &output,
sizeof(output), FALSE, &event,
&ioStatus);
if (!irp) {
ExFreePool(input);
ObDereferenceObject(fileObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
ExFreePool(input);
ObDereferenceObject(fileObject);
*DriveLetter = output.CurrentDriveLetter;
return status;
}
UCHAR
HalpNextDriveLetter(
IN PUNICODE_STRING DeviceName,
IN PSTRING NtDeviceName,
OUT PUCHAR NtSystemPath,
IN BOOLEAN UseHardLinksIfNecessary
)
/*++
Routine Description:
This routine gives the device the next available drive letter.
Arguments:
DeviceName - Supplies the device name.
NtDeviceName - Supplies the NT device name.
NtSystemPath - Supplies the NT system path.
Return Value:
The drive letter assigned or 0.
--*/
{
NTSTATUS status;
UCHAR firstDriveLetter, driveLetter;
WCHAR name[40];
UNICODE_STRING symName;
UNICODE_STRING unicodeString, floppyPrefix, cdromPrefix;
status = HalpNextMountLetter(DeviceName, &driveLetter);
if (NT_SUCCESS(status)) {
return driveLetter;
}
if (!NtDeviceName || !NtSystemPath) {
return 0xFF;
}
if (!UseHardLinksIfNecessary) {
return 0;
}
RtlInitUnicodeString(&floppyPrefix, L"\\Device\\Floppy");
RtlInitUnicodeString(&cdromPrefix, L"\\Device\\CdRom");
if (RtlPrefixUnicodeString(&floppyPrefix, DeviceName, TRUE)) {
firstDriveLetter = 'A';
} else if (RtlPrefixUnicodeString(&cdromPrefix, DeviceName, TRUE)) {
firstDriveLetter = 'D';
} else {
firstDriveLetter = 'C';
}
for (driveLetter = firstDriveLetter; driveLetter <= 'Z'; driveLetter++) {
status = HalpSetMountLetter(DeviceName, driveLetter);
if (NT_SUCCESS(status)) {
RtlAnsiStringToUnicodeString(&unicodeString, NtDeviceName, TRUE);
if (RtlEqualUnicodeString(&unicodeString, DeviceName, TRUE)) {
NtSystemPath[0] = driveLetter;
}
RtlFreeUnicodeString(&unicodeString);
return driveLetter;
}
}
for (driveLetter = firstDriveLetter; driveLetter <= 'Z'; driveLetter++) {
swprintf(name, L"\\DosDevices\\%c:", driveLetter);
RtlInitUnicodeString(&symName, name);
status = IoCreateSymbolicLink(&symName, DeviceName);
if (NT_SUCCESS(status)) {
RtlAnsiStringToUnicodeString(&unicodeString, NtDeviceName, TRUE);
if (RtlEqualUnicodeString(&unicodeString, DeviceName, TRUE)) {
NtSystemPath[0] = driveLetter;
}
RtlFreeUnicodeString(&unicodeString);
return driveLetter;
}
}
return 0;
}
VOID
HalpEnableAutomaticDriveLetterAssignment(
)
/*++
Routine Description:
This routine enables automatic drive letter assignment by the mount
point manager.
Arguments:
None.
Return Value:
None.
--*/
{
UNICODE_STRING name;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
KEVENT event;
PIRP irp;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
RtlInitUnicodeString(&name, MOUNTMGR_DEVICE_NAME);
status = IoGetDeviceObjectPointer(&name, FILE_READ_ATTRIBUTES, &fileObject,
&deviceObject);
if (!NT_SUCCESS(status)) {
return;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS,
deviceObject, NULL, 0, NULL, 0, FALSE,
&event, &ioStatus);
if (!irp) {
ObDereferenceObject(fileObject);
return;
}
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
ObDereferenceObject(fileObject);
}
NTSTATUS
HalpSetMountLetter(
IN PUNICODE_STRING DeviceName,
IN UCHAR DriveLetter
)
/*++
Routine Description:
This routine sets the drive letter for the given device.
Arguments:
DeviceName - Supplies the device name.
DriveLetter - Supplies the drive letter.
Return Value:
NTSTATUS
--*/
{
WCHAR dosBuffer[30];
UNICODE_STRING dosName;
ULONG createPointSize;
PMOUNTMGR_CREATE_POINT_INPUT createPoint;
UNICODE_STRING name;
NTSTATUS status;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
KEVENT event;
PIRP irp;
IO_STATUS_BLOCK ioStatus;
swprintf(dosBuffer, L"\\DosDevices\\%c:", DriveLetter);
RtlInitUnicodeString(&dosName, dosBuffer);
createPointSize = sizeof(MOUNTMGR_CREATE_POINT_INPUT) +
dosName.Length + DeviceName->Length;
createPoint = (PMOUNTMGR_CREATE_POINT_INPUT)
ExAllocatePoolWithTag(PagedPool, createPointSize, 'btsF');
if (!createPoint) {
return STATUS_INSUFFICIENT_RESOURCES;
}
createPoint->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
createPoint->SymbolicLinkNameLength = dosName.Length;
createPoint->DeviceNameOffset = createPoint->SymbolicLinkNameOffset +
createPoint->SymbolicLinkNameLength;
createPoint->DeviceNameLength = DeviceName->Length;
RtlCopyMemory((PCHAR) createPoint + createPoint->SymbolicLinkNameOffset,
dosName.Buffer, dosName.Length);
RtlCopyMemory((PCHAR) createPoint + createPoint->DeviceNameOffset,
DeviceName->Buffer, DeviceName->Length);
RtlInitUnicodeString(&name, MOUNTMGR_DEVICE_NAME);
status = IoGetDeviceObjectPointer(&name, FILE_READ_ATTRIBUTES, &fileObject,
&deviceObject);
if (!NT_SUCCESS(status)) {
ExFreePool(createPoint);
return status;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_CREATE_POINT,
deviceObject, createPoint,
createPointSize, NULL, 0, FALSE,
&event, &ioStatus);
if (!irp) {
ObDereferenceObject(fileObject);
ExFreePool(createPoint);
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
ObDereferenceObject(fileObject);
ExFreePool(createPoint);
return status;
}
BOOLEAN
HalpIsOldStyleFloppy(
IN PUNICODE_STRING DeviceName
)
/*++
Routine Description:
This routine determines whether or not the given device is an old style
floppy. That is, a floppy controlled by a traditional floppy controller.
These floppies have precedent in the drive letter ordering.
Arguments:
DeviceName - Supplies the device name.
Return Value:
FALSE - The given device is not an old style floppy.
TRUE - The given device is an old style floppy.
--*/
{
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
KEVENT event;
PIRP irp;
MOUNTDEV_NAME name;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
PAGED_CODE();
status = IoGetDeviceObjectPointer(DeviceName, FILE_READ_ATTRIBUTES,
&fileObject, &deviceObject);
if (!NT_SUCCESS(status)) {
return FALSE;
}
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
ObDereferenceObject(fileObject);
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
deviceObject, NULL, 0, &name,
sizeof(name), FALSE, &event,
&ioStatus);
if (!irp) {
ObDereferenceObject(deviceObject);
return FALSE;
}
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
ObDereferenceObject(deviceObject);
if (status == STATUS_BUFFER_OVERFLOW) {
return FALSE;
}
return TRUE;
}
PULONG
IopComputeHarddiskDerangements(
IN ULONG DiskCount
)
/*++
Routine Description:
This routine returns an array of hard disk numbers in the correct firmware
(BIOS) order. It does this by using the \ArcName\multi() names.
Arguments:
DiskCount - Supplies the number of disks in the system.
Return Value:
An array of hard disk numbers. The caller must free this list with
ExFreePool.
--*/
{
PULONG r;
ULONG i, j;
WCHAR deviceNameBuffer[50];
UNICODE_STRING deviceName;
NTSTATUS status;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
KEVENT event;
PIRP irp;
STORAGE_DEVICE_NUMBER number;
IO_STATUS_BLOCK ioStatus;
if (DiskCount == 0) {
return NULL;
}
r = ExAllocatePool(PagedPool|POOL_COLD_ALLOCATION, DiskCount*sizeof(ULONG));
if (!r) {
return NULL;
}
for (i = 0; i < DiskCount; i++) {
swprintf(deviceNameBuffer, L"\\ArcName\\multi(0)disk(0)rdisk(%d)", i);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
status = IoGetDeviceObjectPointer(&deviceName, FILE_READ_ATTRIBUTES,
&fileObject, &deviceObject);
if (!NT_SUCCESS(status)) {
r[i] = (ULONG) -1;
continue;
}
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
ObDereferenceObject(fileObject);
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
deviceObject, NULL, 0, &number,
sizeof(number), FALSE, &event,
&ioStatus);
if (!irp) {
ObDereferenceObject(deviceObject);
r[i] = (ULONG) -1;
continue;
}
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
ObDereferenceObject(deviceObject);
if (!NT_SUCCESS(status)) {
r[i] = (ULONG) -1;
continue;
}
r[i] = number.DeviceNumber;
}
for (i = 0; i < DiskCount; i++) {
for (j = 0; j < DiskCount; j++) {
if (r[j] == i) {
break;
}
}
if (j < DiskCount) {
continue;
}
for (j = 0; j < DiskCount; j++) {
if (r[j] == (ULONG) -1) {
r[j] = i;
break;
}
}
}
return r;
}
VOID
FASTCALL
IoAssignDriveLetters(
IN struct _LOADER_PARAMETER_BLOCK *LoaderBlock,
IN PSTRING NtDeviceName,
OUT PUCHAR NtSystemPath,
OUT PSTRING NtSystemPathString
)
/*++
Routine Description:
This routine assigns DOS drive letters to eligible disk partitions
and CDROM drives. It also maps the partition containing the NT
boot path to \SystemRoot. In NT, objects are built for all partition
types except 0 (unused) and 5 (extended). But drive letters are assigned
only to recognized partition types (1, 4, 6, 7, e).
Drive letter assignment is done in several stages:
1) For each CdRom:
Determine if sticky letters are assigned and reserve the letter.
2) For each disk:
Determine how many primary partitions and which is bootable.
Determine which partitions already have 'sticky letters'
and create their symbolic links.
Create a bit map for each disk that idicates which partitions
require default drive letter assignments.
3) For each disk:
Assign default drive letters for the bootable
primary partition or the first nonbootable primary partition.
4) For each disk:
Assign default drive letters for the partitions in
extended volumes.
5) For each disk:
Assign default drive letters for the remaining (ENHANCED)
primary partitions.
6) Assign A: and B: to the first two floppies in the system if they
exist. Then assign remaining floppies next available drive letters.
7) Assign drive letters to CdRoms (either sticky or default).
Arguments:
LoaderBlock - pointer to a loader parameter block.
NtDeviceName - pointer to the boot device name string used
to resolve NtSystemPath.
Return Value:
None.
--*/
{
PUCHAR ntName;
STRING ansiString;
UNICODE_STRING unicodeString;
PUCHAR ntPhysicalName;
STRING ansiPhysicalString;
UNICODE_STRING unicodePhysicalString;
NTSTATUS status;
OBJECT_ATTRIBUTES objectAttributes;
PCONFIGURATION_INFORMATION configurationInformation;
ULONG diskCount;
ULONG floppyCount;
HANDLE deviceHandle;
IO_STATUS_BLOCK ioStatusBlock;
ULONG diskNumber;
ULONG i, j, k;
UCHAR driveLetter;
WCHAR deviceNameBuffer[50];
UNICODE_STRING deviceName, floppyPrefix, cdromPrefix;
PDRIVE_LAYOUT_INFORMATION layout;
BOOLEAN bootable;
ULONG partitionType;
ULONG skip;
ULONG diskCountIncrement;
ULONG actualDiskCount = 0;
PULONG harddiskDerangementArray;
PAGED_CODE();
//
// Get the count of devices from the registry.
//
configurationInformation = IoGetConfigurationInformation();
diskCount = configurationInformation->DiskCount;
floppyCount = configurationInformation->FloppyCount;
//
// Allocate general NT name buffer.
//
ntName = ExAllocatePoolWithTag( NonPagedPool, 128, 'btsF');
ntPhysicalName = ExAllocatePoolWithTag( NonPagedPool, 64, 'btsF');
if (ntName == NULL || ntPhysicalName == NULL) {
KeBugCheck( ASSIGN_DRIVE_LETTERS_FAILED );
}
//
// If we're doing a remote boot, set NtSystemPath appropriately.
//
if (IoRemoteBootClient) {
PUCHAR p;
PUCHAR q;
//
// If this is a remote boot setup boot, NtBootPathName is of the
// form \<server>\<share>\setup\<install-directory>\<platform>.
// We want the root of the X: drive to be the root of the install
// directory.
//
// If this is a normal remote boot, NtBootPathName is of the form
// \<server>\<share>\images\<machine>\winnt. We want the root of
// the X: drive to be the root of the machine directory.
//
// Thus in either case, we need to remove all but the last element
// of the path.
//
// Find the beginning of the last element of the path (including
// the leading backslash).
//
p = strrchr( LoaderBlock->NtBootPathName, '\\' ); // find last separator
q = NULL;
if ( (p != NULL) && (*(p+1) == 0) ) {
//
// NtBootPathName ends with a backslash, so we need to back up
// to the previous backslash.
//
q = p;
*q = 0;
p = strrchr( LoaderBlock->NtBootPathName, '\\' ); // find last separator
*q = '\\';
}
if ( p == NULL ) {
KeBugCheck( ASSIGN_DRIVE_LETTERS_FAILED );
}
//
// Set NtSystemPath to X:\<last element of path>. Note that the symbolic
// link for X: is created in io\ioinit.c\IopInitializeBootDrivers.
//
// Note that we use X: for the textmode setup phase of a remote
// installation. But for a true remote boot, we use C:.
//
#if defined(REMOTE_BOOT)
if ((LoaderBlock->SetupLoaderBlock->Flags & (SETUPBLK_FLAGS_REMOTE_INSTALL |
SETUPBLK_FLAGS_SYSPREP_INSTALL)) == 0) {
NtSystemPath[0] = 'C';
} else
#endif
{
NtSystemPath[0] = 'X';
}
NtSystemPath[1] = ':';
strcpy(&NtSystemPath[2], p );
if ( q != NULL ) {
NtSystemPath[strlen(NtSystemPath)-1] = '\0'; // remove trailing backslash
}
RtlInitString(NtSystemPathString, NtSystemPath);
}
//
// For each disk ...
//
diskCountIncrement = 0;
for (diskNumber = 0; diskNumber < diskCount; diskNumber++) {
//
// Create ANSI name string for physical disk.
//
sprintf( ntName, DiskPartitionName, diskNumber, 0 );
//
// Convert to unicode string.
//
RtlInitAnsiString( &ansiString, ntName );
RtlAnsiStringToUnicodeString( &unicodeString, &ansiString, TRUE );
InitializeObjectAttributes( &objectAttributes,
&unicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
//
// Open device by name.
//
status = ZwOpenFile( &deviceHandle,
FILE_READ_DATA | SYNCHRONIZE,
&objectAttributes,
&ioStatusBlock,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT );
if (NT_SUCCESS( status )) {
//
// The device was successfully opened. Generate a DOS device name
// for the drive itself.
//
sprintf( ntPhysicalName, "\\DosDevices\\PhysicalDrive%d", diskNumber );
RtlInitAnsiString( &ansiPhysicalString, ntPhysicalName );
RtlAnsiStringToUnicodeString( &unicodePhysicalString, &ansiPhysicalString, TRUE );
IoCreateSymbolicLink( &unicodePhysicalString, &unicodeString );
RtlFreeUnicodeString( &unicodePhysicalString );
ZwClose(deviceHandle);
actualDiskCount = diskNumber + 1;
}
RtlFreeUnicodeString( &unicodeString );
if (!NT_SUCCESS( status )) {
#if DBG
DbgPrint( "IoAssignDriveLetters: Failed to open %s\n", ntName );
#endif // DBG
//
// This may be a sparse name space. Try going farther but
// not forever.
//
if (diskCountIncrement < 50) {
diskCountIncrement++;
diskCount++;
}
}
} // end for diskNumber ...
ExFreePool( ntName );
ExFreePool( ntPhysicalName );
diskCount -= diskCountIncrement;
if (actualDiskCount > diskCount) {
diskCount = actualDiskCount;
}
harddiskDerangementArray = IopComputeHarddiskDerangements(diskCount);
for (k = 0; k < diskCount; k++) {
if (harddiskDerangementArray) {
i = harddiskDerangementArray[k];
} else {
i = k;
}
swprintf(deviceNameBuffer, L"\\Device\\Harddisk%d\\Partition0", i);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
status = HalpQueryDriveLayout(&deviceName, &layout);
if (!NT_SUCCESS(status)) {
layout = NULL;
}
bootable = FALSE;
for (j = 1; ; j++) {
swprintf(deviceNameBuffer, L"\\Device\\Harddisk%d\\Partition%d",
i, j);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
status = HalpQueryPartitionType(&deviceName, layout,
&partitionType);
if (!NT_SUCCESS(status)) {
break;
}
if (partitionType != BOOTABLE_PARTITION &&
partitionType != GPT_PARTITION) {
continue;
}
bootable = TRUE;
HalpNextDriveLetter(&deviceName, NtDeviceName, NtSystemPath, FALSE);
if (partitionType == BOOTABLE_PARTITION) {
break;
}
}
if (bootable) {
if (layout) {
ExFreePool(layout);
}
continue;
}
for (j = 1; ; j++) {
swprintf(deviceNameBuffer, L"\\Device\\Harddisk%d\\Partition%d",
i, j);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
status = HalpQueryPartitionType(&deviceName, layout,
&partitionType);
if (!NT_SUCCESS(status)) {
break;
}
if (partitionType != PRIMARY_PARTITION) {
continue;
}
HalpNextDriveLetter(&deviceName, NtDeviceName, NtSystemPath, FALSE);
break;
}
if (layout) {
ExFreePool(layout);
}
}
for (k = 0; k < diskCount; k++) {
if (harddiskDerangementArray) {
i = harddiskDerangementArray[k];
} else {
i = k;
}
swprintf(deviceNameBuffer, L"\\Device\\Harddisk%d\\Partition0", i);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
status = HalpQueryDriveLayout(&deviceName, &layout);
if (!NT_SUCCESS(status)) {
layout = NULL;
}
for (j = 1; ; j++) {
swprintf(deviceNameBuffer, L"\\Device\\Harddisk%d\\Partition%d",
i, j);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
status = HalpQueryPartitionType(&deviceName, layout,
&partitionType);
if (!NT_SUCCESS(status)) {
break;
}
if (partitionType != LOGICAL_PARTITION) {
continue;
}
HalpNextDriveLetter(&deviceName, NtDeviceName, NtSystemPath, FALSE);
}
if (layout) {
ExFreePool(layout);
}
}
for (k = 0; k < diskCount; k++) {
if (harddiskDerangementArray) {
i = harddiskDerangementArray[k];
} else {
i = k;
}
swprintf(deviceNameBuffer, L"\\Device\\Harddisk%d\\Partition0", i);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
status = HalpQueryDriveLayout(&deviceName, &layout);
if (!NT_SUCCESS(status)) {
layout = NULL;
}
skip = 0;
for (j = 1; ; j++) {
swprintf(deviceNameBuffer, L"\\Device\\Harddisk%d\\Partition%d",
i, j);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
status = HalpQueryPartitionType(&deviceName, layout,
&partitionType);
if (!NT_SUCCESS(status)) {
break;
}
if (partitionType == BOOTABLE_PARTITION) {
skip = j;
} else if (partitionType == PRIMARY_PARTITION) {
if (!skip) {
skip = j;
}
}
}
for (j = 1; ; j++) {
if (j == skip) {
continue;
}
swprintf(deviceNameBuffer, L"\\Device\\Harddisk%d\\Partition%d",
i, j);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
status = HalpQueryPartitionType(&deviceName, layout,
&partitionType);
if (!NT_SUCCESS(status)) {
break;
}
if (partitionType != PRIMARY_PARTITION &&
partitionType != FT_PARTITION) {
continue;
}
HalpNextDriveLetter(&deviceName, NtDeviceName, NtSystemPath, FALSE);
}
if (layout) {
ExFreePool(layout);
}
}
if (harddiskDerangementArray) {
ExFreePool(harddiskDerangementArray);
}
for (i = 0; i < floppyCount; i++) {
swprintf(deviceNameBuffer, L"\\Device\\Floppy%d", i);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
if (!HalpIsOldStyleFloppy(&deviceName)) {
continue;
}
HalpNextDriveLetter(&deviceName, NtDeviceName, NtSystemPath, TRUE);
}
for (i = 0; i < floppyCount; i++) {
swprintf(deviceNameBuffer, L"\\Device\\Floppy%d", i);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
if (HalpIsOldStyleFloppy(&deviceName)) {
continue;
}
HalpNextDriveLetter(&deviceName, NtDeviceName, NtSystemPath, TRUE);
}
for (i = 0; i < configurationInformation->CdRomCount; i++) {
swprintf(deviceNameBuffer, L"\\Device\\CdRom%d", i);
RtlInitUnicodeString(&deviceName, deviceNameBuffer);
HalpNextDriveLetter(&deviceName, NtDeviceName, NtSystemPath, TRUE);
}
if (!IoRemoteBootClient) {
RtlAnsiStringToUnicodeString(&unicodeString, NtDeviceName, TRUE);
driveLetter = HalpNextDriveLetter(&unicodeString, NULL, NULL, TRUE);
if (driveLetter) {
if (driveLetter != 0xFF) {
NtSystemPath[0] = driveLetter;
}
} else {
RtlInitUnicodeString(&floppyPrefix, L"\\Device\\Floppy");
RtlInitUnicodeString(&cdromPrefix, L"\\Device\\CdRom");
if (RtlPrefixUnicodeString(&floppyPrefix, &unicodeString, TRUE)) {
driveLetter = 'A';
} else if (RtlPrefixUnicodeString(&cdromPrefix, &unicodeString, TRUE)) {
driveLetter = 'D';
} else {
driveLetter = 'C';
}
for (; driveLetter <= 'Z'; driveLetter++) {
status = HalpSetMountLetter(&unicodeString, driveLetter);
if (NT_SUCCESS(status)) {
NtSystemPath[0] = driveLetter;
}
}
}
RtlFreeUnicodeString(&unicodeString);
}
HalpEnableAutomaticDriveLetterAssignment();
} // end IoAssignDriveLetters()
NTSTATUS
FASTCALL
IoReadPartitionTable(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN BOOLEAN ReturnRecognizedPartitions,
OUT struct _DRIVE_LAYOUT_INFORMATION **PartitionBuffer
)
/*++
Routine Description:
This routine walks the disk reading the partition tables and creates
an entry in the partition list buffer for each partition.
The algorithm used by this routine is two-fold:
1) Read each partition table and for each valid, recognized
partition found, to build a descriptor in a partition list.
Extended partitions are located in order to find other
partition tables, but no descriptors are built for these.
The partition list is built in nonpaged pool that is allocated
by this routine. It is the caller's responsibility to free
this pool after it has gathered the appropriate information
from the list.
2) Read each partition table and for each and every entry, build
a descriptor in the partition list. Extended partitions are
located to find each partition table on the disk, and entries
are built for these as well. The partition list is build in
nonpaged pool that is allocated by this routine. It is the
caller's responsibility to free this pool after it has copied
the information back to its caller.
The first algorithm is used when the ReturnRecognizedPartitions flag
is set. This is used to determine how many partition device objects
the device driver is to create, and where each lives on the drive.
The second algorithm is used when the ReturnRecognizedPartitions flag
is clear. This is used to find all of the partition tables and their
entries for a utility such as fdisk, that would like to revamp where
the partitions live.
Arguments:
DeviceObject - Pointer to device object for this disk.
SectorSize - Sector size on the device.
ReturnRecognizedPartitions - A flag indicated whether only recognized
partition descriptors are to be returned, or whether all partition
entries are to be returned.
PartitionBuffer - Pointer to the pointer of the buffer in which the list
of partition will be stored.
Return Value:
The functional value is STATUS_SUCCESS if at least one sector table was
read.
Notes:
It is the responsibility of the caller to deallocate the partition list
buffer allocated by this routine.
--*/
{
ULONG partitionBufferSize = PARTITION_BUFFER_SIZE;
PDRIVE_LAYOUT_INFORMATION newPartitionBuffer = NULL;
LONG partitionTableCounter = -1;
DISK_GEOMETRY diskGeometry;
ULONGLONG endSector;
ULONGLONG maxSector;
ULONGLONG maxOffset;
LARGE_INTEGER partitionTableOffset;
LARGE_INTEGER volumeStartOffset;
LARGE_INTEGER tempInt;
BOOLEAN primaryPartitionTable;
LONG partitionNumber;
PUCHAR readBuffer = (PUCHAR) NULL;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PIRP irp;
PPARTITION_DESCRIPTOR partitionTableEntry;
CCHAR partitionEntry;
NTSTATUS status = STATUS_SUCCESS;
ULONG readSize;
PPARTITION_INFORMATION partitionInfo;
BOOLEAN foundEZHooker = FALSE;
BOOLEAN mbrSignatureFound = FALSE;
BOOLEAN emptyPartitionTable = TRUE;
PAGED_CODE();
//
// Create the buffer that will be passed back to the driver containing
// the list of partitions on the disk.
//
*PartitionBuffer = ExAllocatePoolWithTag( NonPagedPool,
partitionBufferSize,
'btsF' );
if (*PartitionBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Determine the size of a read operation to ensure that at least 512
// bytes are read. This will guarantee that enough data is read to
// include an entire partition table. Note that this code assumes that
// the actual sector size of the disk (if less than 512 bytes) is a
// multiple of 2, a fairly reasonable assumption.
//
if (SectorSize >= 512) {
readSize = SectorSize;
} else {
readSize = 512;
}
//
// Look to see if this is an EZDrive Disk. If it is then get the
// real parititon table at 1.
//
{
PVOID buff;
HalExamineMBR(
DeviceObject,
readSize,
(ULONG)0x55,
&buff
);
if (buff) {
foundEZHooker = TRUE;
ExFreePool(buff);
partitionTableOffset.QuadPart = 512;
} else {
partitionTableOffset.QuadPart = 0;
}
}
//
// Get the drive size so we can verify that the partition table is
// correct.
//
status = HalpGetFullGeometry(DeviceObject,
&diskGeometry,
&maxOffset);
if(!NT_SUCCESS(status)) {
ExFreePool(*PartitionBuffer);
*PartitionBuffer = NULL;
return status;
}
//
// Partition offsets need to fit on the disk or we're not going to
// expose them. Partition ends are generally very very sloppy so we
// need to allow some slop. Adding in a cylinders worth isn't enough
// so now we'll assume that all partitions end within 2x of the real end
// of the disk.
//
endSector = maxOffset;
maxSector = maxOffset * 2;
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: MaxOffset = %#I64x, maxSector = %#I64x\n",
maxOffset,
maxSector));
//
// Indicate that the primary partition table is being read and
// processed.
//
primaryPartitionTable = TRUE;
//
// The partitions in this volume have their start sector as 0.
//
volumeStartOffset.QuadPart = 0;
//
// Initialize the number of partitions in the list.
//
partitionNumber = -1;
//
// Allocate a buffer that will hold the reads.
//
readBuffer = ExAllocatePoolWithTag( NonPagedPoolCacheAligned,
PAGE_SIZE,
'btsF' );
if (readBuffer == NULL) {
ExFreePool( *PartitionBuffer );
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Read each partition table, create an object for the partition(s)
// it represents, and then if there is a link entry to another
// partition table, repeat.
//
do {
BOOLEAN tableIsValid;
ULONG containerPartitionCount;
tableIsValid = TRUE;
//
// Read record containing partition table.
//
// Create a notification event object to be used while waiting for
// the read request to complete.
//
KeInitializeEvent( &event, NotificationEvent, FALSE );
//
// Zero out the buffer we're reading into. In case we get back
// STATUS_NO_DATA_DETECTED we'll be prepared.
//
RtlZeroMemory(readBuffer, readSize);
irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
DeviceObject,
readBuffer,
readSize,
&partitionTableOffset,
&event,
&ioStatus );
if (!irp) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
} else {
PIO_STACK_LOCATION irpStack;
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
status = IoCallDriver( DeviceObject, irp );
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL);
status = ioStatus.Status;
}
//
// Special case - if we got a blank-check reading the sector then
// pretend it was just successful so we can deal with superfloppies
// where noone bothered to write anything to the non-filesystem sectors
//
if(status == STATUS_NO_DATA_DETECTED) {
status = STATUS_SUCCESS;
}
if (!NT_SUCCESS( status )) {
break;
}
//
// If EZDrive is hooking the MBR then we found the first partition table
// in sector 1 rather than 0. However that partition table is relative
// to sector zero. So, Even though we got it from one, reset the partition
// offset to 0.
//
if (foundEZHooker && (partitionTableOffset.QuadPart == 512)) {
partitionTableOffset.QuadPart = 0;
}
//
// Check for Boot Record signature.
//
if (((PUSHORT) readBuffer)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE) {
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_WARNING_LEVEL,
"FSTUB: (IoReadPartitionTable) No 0xaa55 found in partition table %d\n",
partitionTableCounter + 1));
break;
} else {
mbrSignatureFound = TRUE;
}
//
// Copy NTFT disk signature to buffer
//
if (partitionTableOffset.QuadPart == 0) {
(*PartitionBuffer)->Signature = ((PULONG) readBuffer)[PARTITION_TABLE_OFFSET/2-1];
}
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) readBuffer)[PARTITION_TABLE_OFFSET]);
//
// Keep count of partition tables in case we have an extended partition;
//
partitionTableCounter++;
//
// First create the objects corresponding to the entries in this
// table that are not link entries or are unused.
//
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: Partition Table %d:\n",
partitionTableCounter));
for (partitionEntry = 1, containerPartitionCount = 0;
partitionEntry <= NUM_PARTITION_TABLE_ENTRIES;
partitionEntry++, partitionTableEntry++) {
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"Partition Entry %d,%d: type %#x %s\n",
partitionTableCounter,
partitionEntry,
partitionTableEntry->PartitionType,
(partitionTableEntry->ActiveFlag) ? "Active" : ""));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"\tOffset %#08lx for %#08lx Sectors\n",
GET_STARTING_SECTOR(partitionTableEntry),
GET_PARTITION_LENGTH(partitionTableEntry)));
if (partitionTableEntry->PartitionType == 0xEE) {
FstubFixupEfiPartition (partitionTableEntry,
maxOffset);
}
//
// Do a quick pass over the entry to see if this table is valid.
// It's only fatal if the master partition table is invalid.
//
if((HalpIsValidPartitionEntry(partitionTableEntry,
maxOffset,
maxSector) == FALSE) &&
(partitionTableCounter == 0)) {
tableIsValid = FALSE;
break;
}
//
// Only one container partition is allowed per table - any more
// and it's invalid.
//
if(IsContainerPartition(partitionTableEntry->PartitionType)) {
containerPartitionCount++;
if(containerPartitionCount != 1) {
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_ERROR_LEVEL,
"FSTUB: Multiple container partitions found in "
"partition table %d\n - table is invalid\n",
partitionTableCounter));
tableIsValid = FALSE;
break;
}
}
if(emptyPartitionTable) {
if((GET_STARTING_SECTOR(partitionTableEntry) != 0) ||
(GET_PARTITION_LENGTH(partitionTableEntry) != 0)) {
//
// There's a valid, non-empty partition here. The table
// is not empty.
//
emptyPartitionTable = FALSE;
}
}
//
// If the partition entry is not used or not recognized, skip
// it. Note that this is only done if the caller wanted only
// recognized partition descriptors returned.
//
if (ReturnRecognizedPartitions) {
//
// Check if partition type is 0 (unused) or 5/f (extended).
// The definition of recognized partitions has broadened
// to include any partition type other than 0 or 5/f.
//
if ((partitionTableEntry->PartitionType == PARTITION_ENTRY_UNUSED) ||
IsContainerPartition(partitionTableEntry->PartitionType)) {
continue;
}
}
//
// Bump up to the next partition entry.
//
partitionNumber++;
if (((partitionNumber * sizeof( PARTITION_INFORMATION )) +
sizeof( DRIVE_LAYOUT_INFORMATION )) >
(ULONG) partitionBufferSize) {
//
// The partition list is too small to contain all of the
// entries, so create a buffer that is twice as large to
// store the partition list and copy the old buffer into
// the new one.
//
newPartitionBuffer = ExAllocatePoolWithTag( NonPagedPool,
partitionBufferSize << 1,
'btsF' );
if (newPartitionBuffer == NULL) {
--partitionNumber;
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
RtlCopyMemory( newPartitionBuffer,
*PartitionBuffer,
partitionBufferSize );
ExFreePool( *PartitionBuffer );
//
// Reassign the new buffer to the return parameter and
// reset the size of the buffer.
//
*PartitionBuffer = newPartitionBuffer;
partitionBufferSize <<= 1;
}
//
// Describe this partition table entry in the partition list
// entry being built for the driver. This includes writing
// the partition type, starting offset of the partition, and
// the length of the partition.
//
partitionInfo = &(*PartitionBuffer)->PartitionEntry[partitionNumber];
partitionInfo->PartitionType = partitionTableEntry->PartitionType;
partitionInfo->RewritePartition = FALSE;
if (partitionTableEntry->PartitionType != PARTITION_ENTRY_UNUSED) {
LONGLONG startOffset;
partitionInfo->BootIndicator =
partitionTableEntry->ActiveFlag & PARTITION_ACTIVE_FLAG ?
(BOOLEAN) TRUE : (BOOLEAN) FALSE;
if (IsContainerPartition(partitionTableEntry->PartitionType)) {
partitionInfo->RecognizedPartition = FALSE;
startOffset = volumeStartOffset.QuadPart;
} else {
partitionInfo->RecognizedPartition = TRUE;
startOffset = partitionTableOffset.QuadPart;
}
partitionInfo->StartingOffset.QuadPart = startOffset +
UInt32x32To64(GET_STARTING_SECTOR(partitionTableEntry),
SectorSize);
tempInt.QuadPart = (partitionInfo->StartingOffset.QuadPart -
startOffset) / SectorSize;
partitionInfo->HiddenSectors = tempInt.LowPart;
partitionInfo->PartitionLength.QuadPart =
UInt32x32To64(GET_PARTITION_LENGTH(partitionTableEntry),
SectorSize);
} else {
//
// Partitions that are not used do not describe any part
// of the disk. These types are recorded in the partition
// list buffer when the caller requested all of the entries
// be returned. Simply zero out the remaining fields in
// the entry.
//
partitionInfo->BootIndicator = FALSE;
partitionInfo->RecognizedPartition = FALSE;
partitionInfo->StartingOffset.QuadPart = 0;
partitionInfo->PartitionLength.QuadPart = 0;
partitionInfo->HiddenSectors = 0;
}
}
KdPrintEx((DPFLTR_FSTUB_ID, DPFLTR_TRACE_LEVEL, "\n"));
//
// If an error occurred, leave the routine now.
//
if (!NT_SUCCESS( status )) {
break;
}
if(tableIsValid == FALSE) {
//
// Invalidate this partition table and stop looking for new ones.
// we'll build the partition list based on the ones we found
// previously.
//
partitionTableCounter--;
break;
}
//
// Now check to see if there are any link entries in this table,
// and if so, set up the sector address of the next partition table.
// There can only be one link entry in each partition table, and it
// will point to the next table.
//
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) readBuffer)[PARTITION_TABLE_OFFSET]);
//
// Assume that the link entry is empty.
//
partitionTableOffset.QuadPart = 0;
for (partitionEntry = 1;
partitionEntry <= NUM_PARTITION_TABLE_ENTRIES;
partitionEntry++, partitionTableEntry++) {
if (IsContainerPartition(partitionTableEntry->PartitionType)) {
//
// Obtain the address of the next partition table on the
// disk. This is the number of hidden sectors added to
// the beginning of the extended partition (in the case of
// logical drives), since all logical drives are relative
// to the extended partition. The VolumeStartSector will
// be zero if this is the primary parition table.
//
partitionTableOffset.QuadPart = volumeStartOffset.QuadPart +
UInt32x32To64(GET_STARTING_SECTOR(partitionTableEntry),
SectorSize);
//
// Set the VolumeStartSector to be the begining of the
// second partition (extended partition) because all of
// the offsets to the partition tables of the logical drives
// are relative to this extended partition.
//
if (primaryPartitionTable) {
volumeStartOffset = partitionTableOffset;
}
//
// Update the maximum sector to be the end of the container
// partition.
//
maxSector = GET_PARTITION_LENGTH(partitionTableEntry);
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: MaxSector now = %#08lx\n",
maxSector));
//
// There is only ever one link entry per partition table,
// exit the loop once it has been found.
//
break;
}
}
//
// All the other partitions will be logical drives.
//
primaryPartitionTable = FALSE;
} while (partitionTableOffset.HighPart | partitionTableOffset.LowPart);
//
// Detect super-floppy media attempt #1.
// If the media is removable and has an 0xaa55 signature on it and
// is empty then check to see if we can recognize the BPB. If we recognize
// a jump-byte at the beginning of the media then it's a super floppy. If
// we don't then it's an unpartitioned disk.
//
if((diskGeometry.MediaType == RemovableMedia) &&
(partitionTableCounter == 0) &&
(mbrSignatureFound == TRUE) &&
(emptyPartitionTable == TRUE)) {
PBOOT_SECTOR_INFO bootSector = (PBOOT_SECTOR_INFO) readBuffer;
if((bootSector->JumpByte[0] == 0xeb) ||
(bootSector->JumpByte[0] == 0xe9)) {
//
// We've got a superfloppy of some sort.
//
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: Jump byte %#x found "
"along with empty partition table - disk is a "
"super floppy and has no valid MBR\n",
bootSector->JumpByte));
partitionTableCounter = -1;
}
}
//
// If the partition table count is still -1 then we didn't find any
// valid partition records. In this case we'll build a partition list
// that contiains one partition spanning the entire disk.
//
if(partitionTableCounter == -1) {
if((mbrSignatureFound == TRUE) ||
(diskGeometry.MediaType == RemovableMedia)) {
//
// Either we found a signature but the partition layout was
// invalid (for all disks) or we didn't find a signature but this
// is a removable disk. Either of these two cases makes a
// superfloppy.
//
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: Drive %#p has no valid MBR. "
"Make it into a super-floppy\n", DeviceObject));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: Drive has %#08lx sectors "
"and is %#016I64x bytes large\n",
endSector,
endSector * diskGeometry.BytesPerSector));
if (endSector > 0) {
partitionInfo = &(*PartitionBuffer)->PartitionEntry[0];
partitionInfo->RewritePartition = FALSE;
partitionInfo->RecognizedPartition = TRUE;
partitionInfo->PartitionType = PARTITION_FAT_16;
partitionInfo->BootIndicator = FALSE;
partitionInfo->HiddenSectors = 0;
partitionInfo->StartingOffset.QuadPart = 0;
partitionInfo->PartitionLength.QuadPart =
(endSector * diskGeometry.BytesPerSector);
(*PartitionBuffer)->Signature = 1;
partitionNumber = 0;
}
} else {
//
// We found no partitions. Make sure the partition count is -1
// so that we setup a zeroed-out partition table below.
//
partitionNumber = -1;
}
}
//
// Fill in the first field in the PartitionBuffer. This field indicates how
// many partition entries there are in the PartitionBuffer.
//
(*PartitionBuffer)->PartitionCount = ++partitionNumber;
if (!partitionNumber) {
//
// Zero out disk signature.
//
(*PartitionBuffer)->Signature = 0;
}
//
// Deallocate read buffer if it was allocated it.
//
if (readBuffer != NULL) {
ExFreePool( readBuffer );
}
if (!NT_SUCCESS(status)) {
ExFreePool(*PartitionBuffer);
*PartitionBuffer = NULL;
}
#if DBG
if (NT_SUCCESS(status)) {
FstubDbgPrintDriveLayout(*PartitionBuffer);
}
#endif
return status;
}
NTSTATUS
FASTCALL
IoSetPartitionInformation(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONG PartitionNumber,
IN ULONG PartitionType
)
/*++
Routine Description:
This routine is invoked when a disk device driver is asked to set the
partition type in a partition table entry via an I/O control code. This
control code is generally issued by the format utility just after it
has formatted the partition. The format utility performs the I/O control
function on the partition and the driver passes the address of the base
physical device object and the number of the partition associated with
the device object that the format utility has open. If this routine
returns success, then the disk driver should updates its notion of the
partition type for this partition in its device extension.
Arguments:
DeviceObject - Pointer to the base physical device object for the device
on which the partition type is to be set.
SectorSize - Supplies the size of a sector on the disk in bytes.
PartitionNumber - Specifies the partition number on the device whose
partition type is to be changed.
PartitionType - Specifies the new type for the partition.
Return Value:
The function value is the final status of the operation.
Notes:
This routine is synchronous. Therefore, it MUST be invoked by the disk
driver's dispatch routine, or by a disk driver's thread. Likewise, all
users, FSP threads, etc., must be prepared to enter a wait state when
issuing the I/O control code to set the partition type for the device.
Note also that this routine assumes that the partition number passed
in by the disk driver actually exists since the driver itself supplies
this parameter.
Finally, note that this routine may NOT be invoked at APC_LEVEL. It
must be invoked at PASSIVE_LEVEL. This is due to the fact that this
routine uses a kernel event object to synchronize I/O completion on the
device. The event cannot be set to the signaled state without queueing
the I/O system's special kernel APC routine for I/O completion and
executing it. (This rules is a bit esoteric since it only holds true
if the device driver returns something other than STATUS_PENDING, which
it will probably never do.)
--*/
{
#define GET_STARTING_SECTOR( p ) ( \
(ULONG) (p->StartingSectorLsb0) + \
(ULONG) (p->StartingSectorLsb1 << 8) + \
(ULONG) (p->StartingSectorMsb0 << 16) + \
(ULONG) (p->StartingSectorMsb1 << 24) )
PIRP irp;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
LARGE_INTEGER partitionTableOffset;
LARGE_INTEGER volumeStartOffset;
PUCHAR buffer = (PUCHAR) NULL;
ULONG transferSize;
ULONG partitionNumber;
ULONG partitionEntry;
PPARTITION_DESCRIPTOR partitionTableEntry;
BOOLEAN primaryPartitionTable;
BOOLEAN foundEZHooker = FALSE;
PAGED_CODE();
//
// Begin by determining the size of the buffer required to read and write
// the partition information to/from the disk. This is done to ensure
// that at least 512 bytes are read, thereby guaranteeing that enough data
// is read to include an entire partition table. Note that this code
// assumes that the actual sector size of the disk (if less than 512
// bytes) is a multiple of 2, a
// fairly reasonable assumption.
//
if (SectorSize >= 512) {
transferSize = SectorSize;
} else {
transferSize = 512;
}
//
// Look to see if this is an EZDrive Disk. If it is then get the
// real parititon table at 1.
//
{
PVOID buff;
HalExamineMBR(
DeviceObject,
transferSize,
(ULONG)0x55,
&buff
);
if (buff) {
foundEZHooker = TRUE;
ExFreePool(buff);
partitionTableOffset.QuadPart = 512;
} else {
partitionTableOffset.QuadPart = 0;
}
}
//
// The partitions in this primary partition have their start sector 0.
//
volumeStartOffset.QuadPart = 0;
//
// Indicate that the table being read and processed is the primary partition
// table.
//
primaryPartitionTable = TRUE;
//
// Initialize the number of partitions found thus far.
//
partitionNumber = 0;
//
// Allocate a buffer that will hold the read/write data.
//
buffer = ExAllocatePoolWithTag( NonPagedPoolCacheAligned, PAGE_SIZE, 'btsF');
if (buffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize a kernel event to use in synchronizing device requests
// with I/O completion.
//
KeInitializeEvent( &event, NotificationEvent, FALSE );
//
// Read each partition table scanning for the partition table entry that
// the caller wishes to modify.
//
do {
//
// Read the record containing the partition table.
//
(VOID) KeResetEvent( &event );
irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
DeviceObject,
buffer,
transferSize,
&partitionTableOffset,
&event,
&ioStatus );
if (!irp) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
} else {
PIO_STACK_LOCATION irpStack;
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
status = IoCallDriver( DeviceObject, irp );
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL );
status = ioStatus.Status;
}
if (!NT_SUCCESS( status )) {
break;
}
//
// If EZDrive is hooking the MBR then we found the first partition table
// in sector 1 rather than 0. However that partition table is relative
// to sector zero. So, Even though we got it from one, reset the partition
// offset to 0.
//
if (foundEZHooker && (partitionTableOffset.QuadPart == 512)) {
partitionTableOffset.QuadPart = 0;
}
//
// Check for a valid Boot Record signature in the partition table
// record.
//
if (((PUSHORT) buffer)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE) {
status = STATUS_BAD_MASTER_BOOT_RECORD;
break;
}
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) buffer)[PARTITION_TABLE_OFFSET]);
//
// Scan the partition entries in this partition table to determine if
// any of the entries are the desired entry. Each entry in each
// table must be scanned in the same order as in IoReadPartitionTable
// so that the partition table entry cooresponding to the driver's
// notion of the partition number can be located.
//
for (partitionEntry = 1;
partitionEntry <= NUM_PARTITION_TABLE_ENTRIES;
partitionEntry++, partitionTableEntry++) {
//
// If the partition entry is empty or for an extended, skip it.
//
if ((partitionTableEntry->PartitionType == PARTITION_ENTRY_UNUSED) ||
IsContainerPartition(partitionTableEntry->PartitionType)) {
continue;
}
//
// A valid partition entry that is recognized has been located.
// Bump the count and check to see if this entry is the desired
// entry.
//
partitionNumber++;
if (partitionNumber == PartitionNumber) {
//
// This is the desired partition that is to be changed. Simply
// overwrite the partition type and write the entire partition
// buffer back out to the disk.
//
partitionTableEntry->PartitionType = (UCHAR) PartitionType;
(VOID) KeResetEvent( &event );
irp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE,
DeviceObject,
buffer,
transferSize,
&partitionTableOffset,
&event,
&ioStatus );
if (!irp) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
} else {
PIO_STACK_LOCATION irpStack;
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
status = IoCallDriver( DeviceObject, irp );
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL );
status = ioStatus.Status;
}
break;
}
}
//
// If all of the entries in the current buffer were scanned and the
// desired entry was not found, then continue. Otherwise, leave the
// routine.
//
if (partitionEntry <= NUM_PARTITION_TABLE_ENTRIES) {
break;
}
//
// Now scan the current buffer to locate an extended partition entry
// in the table so that its partition information can be read. There
// can only be one extended partition entry in each partition table,
// and it will point to the next table.
//
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) buffer)[PARTITION_TABLE_OFFSET]);
for (partitionEntry = 1;
partitionEntry <= NUM_PARTITION_TABLE_ENTRIES;
partitionEntry++, partitionTableEntry++) {
if (IsContainerPartition(partitionTableEntry->PartitionType)) {
//
// Obtain the address of the next partition table on the disk.
// This is the number of hidden sectors added to the beginning
// of the extended partition (in the case of logical drives),
// since all logical drives are relative to the extended
// partition. The starting offset of the volume will be zero
// if this is the primary partition table.
//
partitionTableOffset.QuadPart = volumeStartOffset.QuadPart +
UInt32x32To64(GET_STARTING_SECTOR(partitionTableEntry),
SectorSize);
//
// Set the starting offset of the volume to be the beginning of
// the second partition (the extended partition) because all of
// the offsets to the partition tables of the logical drives
// are relative to this extended partition.
//
if (primaryPartitionTable) {
volumeStartOffset = partitionTableOffset;
}
break;
}
}
//
// Ensure that a partition entry was located that was an extended
// partition, otherwise the desired partition will never be found.
//
if (partitionEntry > NUM_PARTITION_TABLE_ENTRIES) {
status = STATUS_BAD_MASTER_BOOT_RECORD;
break;
}
//
// All the other partitions will be logical drives.
//
primaryPartitionTable = FALSE;
} while (partitionNumber < PartitionNumber);
//
// If a data buffer was successfully allocated, deallocate it now.
//
if (buffer != NULL) {
ExFreePool( buffer );
}
return status;
}
NTSTATUS
FASTCALL
IoWritePartitionTable(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONG SectorsPerTrack,
IN ULONG NumberOfHeads,
IN struct _DRIVE_LAYOUT_INFORMATION *PartitionBuffer
)
/*++
Routine Description:
This routine walks the disk writing the partition tables from
the entries in the partition list buffer for each partition.
Applications that create and delete partitions should issue a
IoReadPartitionTable call with the 'return recognized partitions'
boolean set to false to get a full description of the system.
Then the drive layout structure can be modified by the application to
reflect the new configuration of the disk and then is written back
to the disk using this routine.
Arguments:
DeviceObject - Pointer to device object for this disk.
SectorSize - Sector size on the device.
SectorsPerTrack - Track size on the device.
NumberOfHeads - Same as tracks per cylinder.
PartitionBuffer - Pointer drive layout buffer.
Return Value:
The functional value is STATUS_SUCCESS if all writes are completed
without error.
--*/
{
//
// This macro has the effect of Bit = log2(Data)
//
#define WHICH_BIT(Data, Bit) { \
for (Bit = 0; Bit < 32; Bit++) { \
if ((Data >> Bit) == 1) { \
break; \
} \
} \
}
ULONG writeSize;
PUSHORT writeBuffer = NULL;
PPTE partitionEntry;
PPARTITION_TABLE partitionTable;
CCHAR shiftCount;
LARGE_INTEGER partitionTableOffset;
LARGE_INTEGER nextRecordOffset;
ULONG partitionTableCount;
ULONG partitionEntryCount;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PIRP irp;
BOOLEAN rewritePartition = FALSE;
NTSTATUS status = STATUS_SUCCESS;
LARGE_INTEGER tempInt;
BOOLEAN foundEZHooker = FALSE;
ULONG conventionalCylinders;
LONGLONG diskSize;
BOOLEAN isSuperFloppy = FALSE;
//
// Cast to a structure that is easier to use.
//
PDISK_LAYOUT diskLayout = (PDISK_LAYOUT) PartitionBuffer;
//
// Ensure that no one is calling this function illegally.
//
PAGED_CODE();
FstubDbgPrintDriveLayout ( PartitionBuffer );
//
// Determine the size of a write operation to ensure that at least 512
// bytes are written. This will guarantee that enough data is written to
// include an entire partition table. Note that this code assumes that
// the actual sector size of the disk (if less than 512 bytes) is a
// multiple of 2, a fairly reasonable assumption.
//
if (SectorSize >= 512) {
writeSize = SectorSize;
} else {
writeSize = 512;
}
xHalGetPartialGeometry( DeviceObject,
&conventionalCylinders,
&diskSize );
//
// Look to see if this is an EZDrive Disk. If it is then get the
// real partititon table at 1.
//
{
PVOID buff;
HalExamineMBR(
DeviceObject,
writeSize,
(ULONG)0x55,
&buff
);
if (buff) {
foundEZHooker = TRUE;
ExFreePool(buff);
partitionTableOffset.QuadPart = 512;
} else {
partitionTableOffset.QuadPart = 0;
}
}
//
// Initialize starting variables.
//
nextRecordOffset.QuadPart = 0;
//
// Calculate shift count for converting between byte and sector.
//
WHICH_BIT( SectorSize, shiftCount );
//
// Check to see if this device is partitioned (or is being partitioned)
// as a floppy. Floppys have a single partititon with hidden sector count
// and partition offset equal to zero. If the disk is being partitioned
// like this then we need to be sure not to write an MBR signature or
// an NTFT signature to the media.
//
// NOTE: this is only to catch ourself when someone tries to write the
// existing partition table back to disk. Any changes to the table will
// result in a real MBR being written out.
//
if(PartitionBuffer->PartitionCount == 1) {
PPARTITION_INFORMATION partitionEntry = PartitionBuffer->PartitionEntry;
if((partitionEntry->StartingOffset.QuadPart == 0) &&
(partitionEntry->HiddenSectors == 0)) {
isSuperFloppy = TRUE;
//
// This would indeed appear to be an attempt to format a floppy.
// Make sure the other parameters match the defaut values we
// provide in ReadParititonTable. If they don't then fail
// the write operation.
//
if((partitionEntry->PartitionNumber != 0) ||
(partitionEntry->PartitionType != PARTITION_FAT_16) ||
(partitionEntry->BootIndicator == TRUE)) {
return STATUS_INVALID_PARAMETER;
}
if(partitionEntry->RewritePartition == TRUE) {
rewritePartition = TRUE;
}
foundEZHooker = FALSE;
}
}
//
// Convert partition count to partition table or boot sector count.
//
diskLayout->TableCount =
(PartitionBuffer->PartitionCount +
NUM_PARTITION_TABLE_ENTRIES - 1) /
NUM_PARTITION_TABLE_ENTRIES;
//
// Allocate a buffer for the sector writes.
//
writeBuffer = ExAllocatePoolWithTag( NonPagedPoolCacheAligned, PAGE_SIZE, 'btsF');
if (writeBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Point to the partition table entries in write buffer.
//
partitionEntry = (PPTE) &writeBuffer[PARTITION_TABLE_OFFSET];
for (partitionTableCount = 0;
partitionTableCount < diskLayout->TableCount;
partitionTableCount++) {
UCHAR partitionType;
//
// the first partition table is in the mbr (physical sector 0).
// other partition tables are in ebr's within the extended partition.
//
BOOLEAN mbr = (BOOLEAN) (!partitionTableCount);
LARGE_INTEGER extendedPartitionOffset;
//
// Read the boot record that's already there into the write buffer
// and save its boot code area if the signature is valid. This way
// we don't clobber any boot code that might be there already.
//
KeInitializeEvent( &event, NotificationEvent, FALSE );
irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
DeviceObject,
writeBuffer,
writeSize,
&partitionTableOffset,
&event,
&ioStatus );
if (!irp) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
} else {
PIO_STACK_LOCATION irpStack;
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
status = IoCallDriver( DeviceObject, irp );
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL);
status = ioStatus.Status;
}
if (!NT_SUCCESS( status )) {
break;
}
//
// If EZDrive is hooking the MBR then we found the first partition table
// in sector 1 rather than 0. However that partition table is relative
// to sector zero. So, Even though we got it from one, reset the partition
// offset to 0.
//
if (foundEZHooker && (partitionTableOffset.QuadPart == 512)) {
partitionTableOffset.QuadPart = 0;
}
if(isSuperFloppy == FALSE) {
//
// Write signature to last word of boot sector.
//
writeBuffer[BOOT_SIGNATURE_OFFSET] = BOOT_RECORD_SIGNATURE;
//
// Write NTFT disk signature if it changed and this is the MBR.
//
rewritePartition = FALSE;
if (partitionTableOffset.QuadPart == 0) {
if (((PULONG)writeBuffer)[PARTITION_TABLE_OFFSET/2-1] !=
PartitionBuffer->Signature) {
((PULONG) writeBuffer)[PARTITION_TABLE_OFFSET/2-1] =
PartitionBuffer->Signature;
rewritePartition = TRUE;
}
}
//
// Get pointer to first partition table.
//
partitionTable = &diskLayout->PartitionTable[partitionTableCount];
//
// Walk table to determine whether this boot record has changed
// and update partition table in write buffer in case it needs
// to be written out to disk.
//
for (partitionEntryCount = 0;
partitionEntryCount < NUM_PARTITION_TABLE_ENTRIES;
partitionEntryCount++) {
partitionType =
partitionTable->PartitionEntry[partitionEntryCount].PartitionType;
//
// If the rewrite ISN'T true then copy then just leave the data
// alone that is in the on-disk table.
//
if (partitionTable->PartitionEntry[partitionEntryCount].RewritePartition) {
//
// This boot record needs to be written back to disk.
//
rewritePartition = TRUE;
//
// Copy partition type from user buffer to write buffer.
//
partitionEntry[partitionEntryCount].PartitionType =
partitionTable->PartitionEntry[partitionEntryCount].PartitionType;
//
// Copy the partition active flag.
//
partitionEntry[partitionEntryCount].ActiveFlag =
partitionTable->PartitionEntry[partitionEntryCount].BootIndicator ?
(UCHAR) PARTITION_ACTIVE_FLAG : (UCHAR) 0;
if (partitionType != PARTITION_ENTRY_UNUSED) {
LARGE_INTEGER sectorOffset;
//
// Calculate partition offset.
// If in the mbr or the entry is not a link entry, partition offset
// is sectors past last boot record. Otherwise (not in the mbr and
// entry is a link entry), partition offset is sectors past start
// of extended partition.
//
if (mbr || !IsContainerPartition(partitionType)) {
tempInt.QuadPart = partitionTableOffset.QuadPart;
} else {
tempInt.QuadPart = extendedPartitionOffset.QuadPart;
}
sectorOffset.QuadPart =
partitionTable->PartitionEntry[partitionEntryCount].StartingOffset.QuadPart -
tempInt.QuadPart;
tempInt.QuadPart = sectorOffset.QuadPart >> shiftCount;
partitionEntry[partitionEntryCount].StartingSector = tempInt.LowPart;
//
// Calculate partition length.
//
tempInt.QuadPart = partitionTable->PartitionEntry[partitionEntryCount].PartitionLength.QuadPart >> shiftCount;
partitionEntry[partitionEntryCount].PartitionLength = tempInt.LowPart;
//
// Fill in CHS values
//
HalpCalculateChsValues(
&partitionTable->PartitionEntry[partitionEntryCount].StartingOffset,
&partitionTable->PartitionEntry[partitionEntryCount].PartitionLength,
shiftCount,
SectorsPerTrack,
NumberOfHeads,
conventionalCylinders,
(PPARTITION_DESCRIPTOR) &partitionEntry[partitionEntryCount]);
} else {
//
// Zero out partition entry fields in case an entry
// was deleted.
//
partitionEntry[partitionEntryCount].StartingSector = 0;
partitionEntry[partitionEntryCount].PartitionLength = 0;
partitionEntry[partitionEntryCount].StartingTrack = 0;
partitionEntry[partitionEntryCount].EndingTrack = 0;
partitionEntry[partitionEntryCount].StartingCylinder = 0;
partitionEntry[partitionEntryCount].EndingCylinder = 0;
}
}
if (IsContainerPartition(partitionType)) {
//
// Save next record offset.
//
nextRecordOffset =
partitionTable->PartitionEntry[partitionEntryCount].StartingOffset;
}
} // end for partitionEntryCount ...
}
if (rewritePartition == TRUE) {
rewritePartition = FALSE;
//
// Create a notification event object to be used while waiting for
// the write request to complete.
//
KeInitializeEvent( &event, NotificationEvent, FALSE );
if (foundEZHooker && (partitionTableOffset.QuadPart == 0)) {
partitionTableOffset.QuadPart = 512;
}
irp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE,
DeviceObject,
writeBuffer,
writeSize,
&partitionTableOffset,
&event,
&ioStatus );
if (!irp) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
} else {
PIO_STACK_LOCATION irpStack;
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
status = IoCallDriver( DeviceObject, irp );
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL);
status = ioStatus.Status;
}
if (!NT_SUCCESS( status )) {
break;
}
if (foundEZHooker && (partitionTableOffset.QuadPart == 512)) {
partitionTableOffset.QuadPart = 0;
}
} // end if (reWrite ...
//
// Update partitionTableOffset to next boot record offset
//
partitionTableOffset = nextRecordOffset;
if(mbr) {
extendedPartitionOffset = nextRecordOffset;
}
} // end for partitionTableCount ...
//
// Deallocate write buffer if it was allocated it.
//
if (writeBuffer != NULL) {
ExFreePool( writeBuffer );
}
return status;
}
VOID
FstubFixupEfiPartition(
IN PPARTITION_DESCRIPTOR Entry,
IN ULONGLONG MaxSector
)
/*++
Routine Description:
Protective GPT partition entries can have invalid sizes. The EFI
standard explicitly allows this. For these partitions, fixup
the length so it doesn't go past the end of the disk.
Arguments:
Entry - Supplies the partition entry to modify.
MaxSector - Supplies the maximum valid sector.
Return Value:
NTSTATUS code
--*/
{
ULONGLONG endingSector;
PPTE partitionEntry;
PAGED_CODE();
partitionEntry = (PPTE) Entry;
endingSector = partitionEntry->StartingSector;
endingSector += partitionEntry->PartitionLength;
if (endingSector > MaxSector) {
partitionEntry->PartitionLength =
(ULONG)(MaxSector - partitionEntry->StartingSector);
}
}
BOOLEAN
HalpIsValidPartitionEntry(
PPARTITION_DESCRIPTOR Entry,
ULONGLONG MaxOffset,
ULONGLONG MaxSector
)
{
ULONGLONG endingSector;
PAGED_CODE();
if(Entry->PartitionType == PARTITION_ENTRY_UNUSED) {
//
// Unused partition entries are always valid.
//
return TRUE;
}
//
// Container partition entries and normal partition entries are valid iff
// the partition they describe can possibly fit on the disk. We add
// the base sector, the sector offset of the partition and the partition
// length. If they exceed the sector count then this partition entry
// is considered invalid.
//
//
// Do this in two steps to avoid 32-bit truncation.
//
endingSector = GET_STARTING_SECTOR(Entry);
endingSector += GET_PARTITION_LENGTH(Entry);
if(endingSector > MaxSector) {
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: entry is invalid\n"));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: offset %#08lx\n",
GET_STARTING_SECTOR(Entry)));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: length %#08lx\n",
GET_PARTITION_LENGTH(Entry)));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: end %#I64x\n",
endingSector));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: max %#I64x\n",
MaxSector));
return FALSE;
} else if(GET_STARTING_SECTOR(Entry) > MaxOffset) {
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: entry is invalid\n"));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: offset %#08lx\n",
GET_STARTING_SECTOR(Entry)));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: length %#08lx\n",
GET_PARTITION_LENGTH(Entry)));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: end %#I64x\n",
endingSector));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: maxOffset %#I64x\n",
MaxOffset));
return FALSE;
}
return TRUE;
}
NTSTATUS
HalpGetFullGeometry(
IN PDEVICE_OBJECT DeviceObject,
IN PDISK_GEOMETRY Geometry,
OUT PULONGLONG RealSectorCount
)
/*++
Routine Description:
We need this routine to get the number of cylinders that the disk driver
thinks is on the drive. We will need this to calculate CHS values
when we fill in the partition table entries.
Arguments:
DeviceObject - The device object describing the entire drive.
Geometry - The geometry of the drive
RealSectorCount - the actual number of sectors reported by the drive (
this may be less than the size computed by the geometry)
Return Value:
None.
--*/
{
PIRP localIrp;
IO_STATUS_BLOCK iosb;
PKEVENT eventPtr;
NTSTATUS status;
PAGED_CODE();
eventPtr = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(KEVENT),
'btsF'
);
if (!eventPtr) {
return STATUS_INSUFFICIENT_RESOURCES;
}
KeInitializeEvent(
eventPtr,
NotificationEvent,
FALSE
);
localIrp = IoBuildDeviceIoControlRequest(
IOCTL_DISK_GET_DRIVE_GEOMETRY,
DeviceObject,
NULL,
0UL,
Geometry,
sizeof(DISK_GEOMETRY),
FALSE,
eventPtr,
&iosb
);
if (!localIrp) {
ExFreePool(eventPtr);
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Call the lower level driver, wait for the opertion
// to finish.
//
status = IoCallDriver(
DeviceObject,
localIrp
);
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject(
eventPtr,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL
);
status = iosb.Status;
}
KeClearEvent (eventPtr);
if(NT_SUCCESS(status)) {
PARTITION_INFORMATION partitionInfo;
localIrp = IoBuildDeviceIoControlRequest(
IOCTL_DISK_GET_PARTITION_INFO,
DeviceObject,
NULL,
0UL,
&partitionInfo,
sizeof(PARTITION_INFORMATION),
FALSE,
eventPtr,
&iosb
);
if (!localIrp) {
ExFreePool(eventPtr);
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Call the lower level driver, wait for the opertion
// to finish.
//
status = IoCallDriver(
DeviceObject,
localIrp
);
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject(
eventPtr,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL
);
status = iosb.Status;
}
if(NT_SUCCESS(status)) {
*RealSectorCount = (partitionInfo.PartitionLength.QuadPart /
Geometry->BytesPerSector);
}
}
ExFreePool(eventPtr);
return status;
}