reactos/ntoskrnl/fstub/disksup.c
Pierre Schweitzer 5ab1cfc553
[NTOSKRNL] Rewrite IoAssignDriveLetters to make NT5 compliant
The major change with this rewrite is the support for the mount
manager. Fstub will now assume that most of the devices are PnP
and that they are already registered to the mount manager.
It will thus ask the mount manager to assign the drive letter.
Fstub will keep assigning drive letters non mission critical devices
such as CDs, floppies and other removable devices.

See MountMgr:QueryPoints API test that will now return mount points :-).
2019-10-21 18:28:40 +02:00

2804 lines
94 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/fstub/disksup.c
* PURPOSE: I/O HAL Routines for Disk Access
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Eric Kohl
* Casper S. Hornstrup (chorns@users.sourceforge.net)
* Pierre Schweitzer
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#include <internal/hal.h>
const WCHAR DiskMountString[] = L"\\DosDevices\\%C:";
#define AUTO_DRIVE MAXULONG
#define PARTITION_MAGIC 0xaa55
#define EFI_PMBR_OSTYPE_EFI 0xEE
#include <pshpack1.h>
typedef struct _REG_DISK_MOUNT_INFO
{
ULONG Signature;
LARGE_INTEGER StartingOffset;
} REG_DISK_MOUNT_INFO, *PREG_DISK_MOUNT_INFO;
#include <poppack.h>
typedef enum _DISK_MANAGER
{
NoDiskManager,
OntrackDiskManager,
EZ_Drive
} DISK_MANAGER;
typedef enum _PARTITION_TYPE
{
BootablePartition,
PrimaryPartition,
LogicalPartition,
FtPartition,
UnknownPartition,
DataPartition
} PARTITION_TYPE, *PPARTITION_TYPE;
NTSTATUS
FASTCALL
HalpQueryDriveLayout(IN PUNICODE_STRING DeviceName,
OUT PDRIVE_LAYOUT_INFORMATION *LayoutInfo)
{
IO_STATUS_BLOCK StatusBlock;
PDEVICE_OBJECT DeviceObject = NULL;
PFILE_OBJECT FileObject;
KEVENT Event;
PIRP Irp;
NTSTATUS Status;
ULONG BufferSize;
PDRIVE_LAYOUT_INFORMATION Buffer;
PAGED_CODE();
/* Get device pointers */
Status = IoGetDeviceObjectPointer(DeviceName,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Get attached device object */
DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
ObDereferenceObject(FileObject);
/* Do not handle removable media */
if (BooleanFlagOn(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
{
ObDereferenceObject(DeviceObject);
return STATUS_NO_MEDIA;
}
/* We'll loop until our buffer is big enough */
Buffer = NULL;
BufferSize = 0x1000;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
do
{
/* If we already had a buffer, it means it's not big
* enough, so free and multiply size by two
*/
if (Buffer != NULL)
{
ExFreePoolWithTag(Buffer, TAG_FSTUB);
BufferSize *= 2;
}
/* Allocate buffer for output buffer */
Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, TAG_FSTUB);
if (Buffer == NULL)
{
Status = STATUS_NO_MEMORY;
break;
}
/* Build the IRP to query drive layout */
Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_LAYOUT,
DeviceObject,
NULL,
0,
Buffer,
BufferSize,
FALSE,
&Event,
&StatusBlock);
if (Irp == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
/* Call the driver and wait if appropriate */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = StatusBlock.Status;
}
/* If buffer is too small, keep looping */
} while (Status == STATUS_BUFFER_TOO_SMALL);
/* We're done with the device */
ObDereferenceObject(DeviceObject);
/* If querying worked, then return the buffer to the caller */
if (NT_SUCCESS(Status))
{
ASSERT(Buffer != NULL);
*LayoutInfo = Buffer;
}
/* Else, release the buffer if still allocated and fail */
else
{
if (Buffer != NULL)
{
ExFreePoolWithTag(Buffer, TAG_FSTUB);
}
}
return Status;
}
NTSTATUS
HalpQueryPartitionType(IN PUNICODE_STRING DeviceName,
IN PDRIVE_LAYOUT_INFORMATION LayoutInfo,
OUT PPARTITION_TYPE PartitionType)
{
USHORT i;
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
IO_STATUS_BLOCK IoStatusBlock;
PARTITION_INFORMATION_EX PartitionInfo;
PAGED_CODE();
/* Get device pointers */
Status = IoGetDeviceObjectPointer(DeviceName,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Get attached device object */
DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
ObDereferenceObject(FileObject);
/* Assume logical partition for removable devices */
if (BooleanFlagOn(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
{
ObDereferenceObject(DeviceObject);
*PartitionType = LogicalPartition;
return STATUS_SUCCESS;
}
/* For the others, query partition info */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
DeviceObject,
NULL,
0,
&PartitionInfo,
sizeof(PartitionInfo),
FALSE,
&Event,
&IoStatusBlock);
if (Irp == NULL)
{
ObDereferenceObject(DeviceObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
/* We're done with the device */
ObDereferenceObject(DeviceObject);
/* If we failed querying partition info, try to return something
* if caller didn't provide a precise layout, assume logical
* partition and fake success. Otherwise, just fail.
*/
if (!NT_SUCCESS(Status))
{
if (LayoutInfo == NULL)
{
*PartitionType = LogicalPartition;
return STATUS_SUCCESS;
}
return Status;
}
/* First, handle non MBR style (easy cases) */
if (PartitionInfo.PartitionStyle != PARTITION_STYLE_MBR)
{
/* If not GPT, we don't know what it is */
if (PartitionInfo.PartitionStyle != PARTITION_STYLE_GPT)
{
*PartitionType = UnknownPartition;
return STATUS_SUCCESS;
}
/* Check whether that's data partition */
if (RtlCompareMemory(&PartitionInfo.Gpt.PartitionType,
&PARTITION_BASIC_DATA_GUID,
sizeof(GUID)) == sizeof(GUID))
{
*PartitionType = DataPartition;
return STATUS_SUCCESS;
}
/* Otherwise, we don't know */
*PartitionType = UnknownPartition;
return STATUS_SUCCESS;
}
/* If we don't recognize partition type, return unknown */
if (!IsRecognizedPartition(PartitionInfo.Mbr.PartitionType))
{
*PartitionType = UnknownPartition;
return STATUS_SUCCESS;
}
/* Check if that's a FT volume */
if (PartitionInfo.Mbr.PartitionType & PARTITION_NTFT)
{
*PartitionType = FtPartition;
return STATUS_SUCCESS;
}
/* If the caller didn't provide the complete layout, just return */
if (LayoutInfo == NULL)
{
*PartitionType = LogicalPartition;
return STATUS_SUCCESS;
}
/* Now, evaluate the partition to the 4 in the input layout */
for (i = 0; i < 4; ++i)
{
/* If we find a partition matching */
if (LayoutInfo->PartitionEntry[i].StartingOffset.QuadPart == PartitionInfo.StartingOffset.QuadPart)
{
/* Return boot if boot flag is set */
if (PartitionInfo.Mbr.BootIndicator)
{
*PartitionType = BootablePartition;
}
/* Primary otherwise */
else
{
*PartitionType = PrimaryPartition;
}
return STATUS_SUCCESS;
}
}
/* Otherwise, assume logical */
*PartitionType = LogicalPartition;
return STATUS_SUCCESS;
}
PULONG
IopComputeHarddiskDerangements(IN ULONG DiskCount)
{
PIRP Irp;
KEVENT Event;
ULONG i, j, k;
PULONG Devices;
NTSTATUS Status;
WCHAR Buffer[100];
UNICODE_STRING ArcName;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
IO_STATUS_BLOCK IoStatusBlock;
STORAGE_DEVICE_NUMBER DeviceNumber;
/* No disks, nothing to do */
if (DiskCount == 0)
{
return NULL;
}
/* Allocate a buffer big enough to hold all the disks */
Devices = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
sizeof(ULONG) * DiskCount,
TAG_FSTUB);
if (Devices == NULL)
{
return NULL;
}
/* Now, we'll query all the disks */
for (i = 0; i < DiskCount; ++i)
{
/* Using their ARC name */
swprintf(Buffer, L"\\ArcName\\multi(0)disk(0)rdisk(%d)", i);
RtlInitUnicodeString(&ArcName, Buffer);
/* Get the attached DeviceObject */
if (NT_SUCCESS(IoGetDeviceObjectPointer(&ArcName, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject)))
{
DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
ObDereferenceObject(FileObject);
/* And query it for device number */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
DeviceObject,
NULL,
0,
&DeviceNumber,
sizeof(DeviceNumber),
FALSE,
&Event,
&IoStatusBlock);
if (Irp != NULL)
{
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
ObDereferenceObject(DeviceObject);
/* In case of a success remember device number */
if (NT_SUCCESS(Status))
{
Devices[i] = DeviceNumber.DeviceNumber;
/* Move on, not to fall into our default case */
continue;
}
}
else
{
ObDereferenceObject(DeviceObject);
}
/* Default case, for failures, set -1 */
Devices[i] = -1;
}
}
/* Now, we'll check all device numbers */
for (i = 0; i < DiskCount; ++i)
{
/* First of all, check if we're at the right place */
for (j = 0; j < DiskCount; ++j)
{
if (Devices[j] == i)
{
break;
}
}
/* If not, perform the change */
if (j >= DiskCount)
{
k = 0;
while (Devices[k] != -1)
{
if (++k >= DiskCount)
{
break;
}
}
if (k < DiskCount)
{
Devices[k] = i;
}
}
}
/* Return our device derangement map */
return Devices;
}
NTSTATUS
HalpNextMountLetter(IN PUNICODE_STRING DeviceName,
OUT PUCHAR DriveLetter)
{
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
UNICODE_STRING MountMgr;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
IO_STATUS_BLOCK IoStatusBlock;
PMOUNTMGR_DRIVE_LETTER_TARGET Target;
MOUNTMGR_DRIVE_LETTER_INFORMATION LetterInfo;
/* To get next mount letter, we need the MountMgr */
RtlInitUnicodeString(&MountMgr, L"\\Device\\MountPointManager");
Status = IoGetDeviceObjectPointer(&MountMgr,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Allocate our input buffer */
Target = ExAllocatePoolWithTag(PagedPool,
DeviceName->Length + FIELD_OFFSET(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName),
TAG_FSTUB);
if (Target == NULL)
{
ObDereferenceObject(FileObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* And fill it with the device hat needs a drive letter */
Target->DeviceNameLength = DeviceName->Length;
RtlCopyMemory(&Target->DeviceName[0], DeviceName->Buffer, DeviceName->Length);
/* Call the mount manager */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER,
DeviceObject,
Target,
DeviceName->Length + FIELD_OFFSET(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName),
&LetterInfo,
sizeof(LetterInfo),
FALSE,
&Event,
&IoStatusBlock);
if (Irp == NULL)
{
ExFreePoolWithTag(Target, TAG_FSTUB);
ObDereferenceObject(FileObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
ExFreePoolWithTag(Target, TAG_FSTUB);
ObDereferenceObject(FileObject);
DPRINT("Done: %d %c\n", LetterInfo.DriveLetterWasAssigned,
LetterInfo.CurrentDriveLetter);
/* Return the drive letter the MountMgr potentially assigned */
*DriveLetter = LetterInfo.CurrentDriveLetter;
/* Also return the success */
return Status;
}
NTSTATUS
HalpSetMountLetter(IN PUNICODE_STRING DeviceName,
UCHAR DriveLetter)
{
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
WCHAR Buffer[30];
ULONG InputBufferLength;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING DosDevice, MountMgr;
PMOUNTMGR_CREATE_POINT_INPUT InputBuffer;
/* Setup the DosDevice name */
swprintf(Buffer, L"\\DosDevices\\%c:", DriveLetter);
RtlInitUnicodeString(&DosDevice, Buffer);
/* Allocate the input buffer for the MountMgr */
InputBufferLength = DosDevice.Length + DeviceName->Length + sizeof(MOUNTMGR_CREATE_POINT_INPUT);
InputBuffer = ExAllocatePoolWithTag(PagedPool, InputBufferLength, TAG_FSTUB);
if (InputBuffer == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Fill the input buffer */
InputBuffer->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
InputBuffer->SymbolicLinkNameLength = DosDevice.Length;
InputBuffer->DeviceNameOffset = DosDevice.Length + sizeof(MOUNTMGR_CREATE_POINT_INPUT);
InputBuffer->DeviceNameLength = DeviceName->Length;
RtlCopyMemory(&InputBuffer[1], DosDevice.Buffer, DosDevice.Length);
RtlCopyMemory((PVOID)((ULONG_PTR)InputBuffer + InputBuffer->DeviceNameOffset),
DeviceName->Buffer,
DeviceName->Length);
/* Get the MountMgr device pointer, to send the IOCTL */
RtlInitUnicodeString(&MountMgr, L"\\Device\\MountPointManager");
Status = IoGetDeviceObjectPointer(&MountMgr,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
return Status;
}
/* Call the MountMgr */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_CREATE_POINT,
DeviceObject,
InputBuffer,
InputBufferLength,
NULL,
0,
FALSE,
&Event,
&IoStatusBlock);
if (Irp == NULL)
{
ObDereferenceObject(FileObject);
ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
ObDereferenceObject(FileObject);
ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
/* Return the MountMgr status */
return Status;
}
UCHAR
HalpNextDriveLetter(IN PUNICODE_STRING DeviceName,
IN PSTRING NtDeviceName,
OUT PUCHAR NtSystemPath,
BOOLEAN IsRemovable)
{
UCHAR i;
WCHAR Buffer[40];
UCHAR DriveLetter;
UNICODE_STRING FloppyString, CdString, NtDeviceNameU, DosDevice;
/* Quick path, ask directly the mount manager to assign the next
* free drive letter
*/
if (NT_SUCCESS(HalpNextMountLetter(DeviceName, &DriveLetter)))
{
return DriveLetter;
}
/* We'll allow MountMgr to fail only for non vital path */
if (NtDeviceName == NULL || NtSystemPath == NULL)
{
return -1;
}
/* And for removable devices */
if (!IsRemovable)
{
return 0;
}
/* Removable might be floppy or cdrom */
RtlInitUnicodeString(&FloppyString, L"\\Device\\Floppy");
RtlInitUnicodeString(&CdString, L"\\Device\\CdRom");
/* If floppy, start at A */
if (RtlPrefixUnicodeString(&FloppyString, DeviceName, TRUE))
{
DriveLetter = 'A';
}
/* If CD start C */
else if (RtlPrefixUnicodeString(&CdString, DeviceName, TRUE))
{
DriveLetter = 'D';
}
/* For the rest start at C */
else
{
DriveLetter = 'C';
}
/* Now, try to assign a drive letter manually with the MountMgr */
for (i = DriveLetter; i <= 'Z'; ++i)
{
if (NT_SUCCESS(HalpSetMountLetter(DeviceName, i)))
{
/* If it worked, if we were managing system path, update manually */
if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&NtDeviceNameU, NtDeviceName, TRUE)))
{
if (RtlEqualUnicodeString(&NtDeviceNameU, DeviceName, TRUE))
{
*NtSystemPath = i;
}
RtlFreeUnicodeString(&NtDeviceNameU);
}
return i;
}
}
/* Last fall back, we're not on a PnP device... */
for (i = DriveLetter; i <= 'Z'; ++i)
{
/* We'll link manually, without MountMgr knowing anything about the device */
swprintf(Buffer, L"\\DosDevices\\%c:", i);
RtlInitUnicodeString(&DosDevice, Buffer);
/* If linking worked, then the letter was free ;-) */
if (NT_SUCCESS(IoCreateSymbolicLink(&DosDevice, DeviceName)))
{
/* If it worked, if we were managing system path, update manually */
if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&NtDeviceNameU, NtDeviceName, TRUE)))
{
if (RtlEqualUnicodeString(&NtDeviceNameU, DeviceName, TRUE))
{
*NtSystemPath = i;
}
RtlFreeUnicodeString(&NtDeviceNameU);
}
return i;
}
}
/* We're done, nothing happened */
return 0;
}
BOOLEAN
HalpIsOldStyleFloppy(PUNICODE_STRING DeviceName)
{
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
MOUNTDEV_NAME DevName;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
IO_STATUS_BLOCK IoStatusBlock;
PAGED_CODE();
/* Get the attached device object to our device */
if (!NT_SUCCESS(IoGetDeviceObjectPointer(DeviceName,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject)))
{
return FALSE;
}
DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
ObDereferenceObject(FileObject);
/* Query its device name (ie, check floppy.sys implements MountMgr interface) */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
DeviceObject,
NULL,
0,
&DevName,
sizeof(DevName),
FALSE,
&Event,
&IoStatusBlock);
if (Irp == NULL)
{
ObDereferenceObject(DeviceObject);
return FALSE;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
/* If status is not STATUS_BUFFER_OVERFLOW, it means
* it's pre-mountmgr driver, aka "Old style".
*/
ObDereferenceObject(DeviceObject);
return (Status != STATUS_BUFFER_OVERFLOW);
}
NTSTATUS
HalpDeleteMountLetter(UCHAR DriveLetter)
{
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
WCHAR Buffer[30];
ULONG InputBufferLength;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
IO_STATUS_BLOCK IoStatusBlock;
PMOUNTMGR_MOUNT_POINT InputBuffer;
UNICODE_STRING DosDevice, MountMgr;
PMOUNTMGR_MOUNT_POINTS OutputBuffer;
/* Setup the device name of the letter to delete */
swprintf(Buffer, L"\\DosDevices\\%c:", DriveLetter);
RtlInitUnicodeString(&DosDevice, Buffer);
/* Allocate the input buffer for MountMgr */
InputBufferLength = DosDevice.Length + sizeof(MOUNTMGR_MOUNT_POINT);
InputBuffer = ExAllocatePoolWithTag(PagedPool, InputBufferLength, TAG_FSTUB);
if (InputBuffer == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Fill it in */
RtlZeroMemory(InputBuffer, InputBufferLength);
InputBuffer->SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
InputBuffer->SymbolicLinkNameLength = DosDevice.Length;
RtlCopyMemory(&InputBuffer[1], DosDevice.Buffer, DosDevice.Length);
/* Allocate big enough output buffer (we don't care about the output) */
OutputBuffer = ExAllocatePoolWithTag(PagedPool, 0x1000, TAG_FSTUB);
if (OutputBuffer == NULL)
{
ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Get the device pointer to the MountMgr */
RtlInitUnicodeString(&MountMgr, L"\\Device\\MountPointManager");
Status = IoGetDeviceObjectPointer(&MountMgr,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(OutputBuffer, TAG_FSTUB);
ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
return Status;
}
/* Call the mount manager to delete the drive letter */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_DELETE_POINTS,
DeviceObject,
InputBuffer,
InputBufferLength,
OutputBuffer,
0x1000,
FALSE,
&Event,
&IoStatusBlock);
if (Irp == NULL)
{
ObDereferenceObject(FileObject);
ExFreePoolWithTag(OutputBuffer, TAG_FSTUB);
ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
ObDereferenceObject(FileObject);
ExFreePoolWithTag(OutputBuffer, TAG_FSTUB);
ExFreePoolWithTag(InputBuffer, TAG_FSTUB);
return Status;
}
VOID
HalpEnableAutomaticDriveLetterAssignment(VOID)
{
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
UNICODE_STRING MountMgr;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
IO_STATUS_BLOCK IoStatusBlock;
/* Get the device pointer to the MountMgr */
RtlInitUnicodeString(&MountMgr, L"\\Device\\MountPointManager");
Status = IoGetDeviceObjectPointer(&MountMgr,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
return;
}
/* Just send an IOCTL to enable the feature */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS,
DeviceObject,
NULL,
0,
NULL,
0,
FALSE,
&Event,
&IoStatusBlock);
if (Irp == NULL)
{
return;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
ObDereferenceObject(FileObject);
return;
}
VOID
FASTCALL
xHalIoAssignDriveLetters(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
IN PSTRING NtDeviceName,
OUT PUCHAR NtSystemPath,
OUT PSTRING NtSystemPathString)
{
USHORT i;
PULONG Devices;
NTSTATUS Status;
WCHAR Buffer[50];
HANDLE FileHandle;
UCHAR DriveLetter;
BOOLEAN SystemFound;
IO_STATUS_BLOCK StatusBlock;
PARTITION_TYPE PartitionType;
ANSI_STRING StringA1, StringA2;
PSTR Buffer1, Buffer2, LoadOptions;
OBJECT_ATTRIBUTES ObjectAttributes;
PDRIVE_LAYOUT_INFORMATION LayoutInfo;
PCONFIGURATION_INFORMATION ConfigInfo;
UNICODE_STRING StringU1, StringU2, StringU3;
ULONG Increment, DiskCount, RealDiskCount, HarddiskCount, PartitionCount, SystemPartition;
PAGED_CODE();
/* Get our disk count */
ConfigInfo = IoGetConfigurationInformation();
DiskCount = ConfigInfo->DiskCount;
RealDiskCount = 0;
/* Allocate two generic string buffers we'll use and reuser later on */
Buffer1 = ExAllocatePoolWithTag(NonPagedPool, 128, TAG_FSTUB);
Buffer2 = ExAllocatePoolWithTag(NonPagedPool, 64, TAG_FSTUB);
if (Buffer1 == NULL || Buffer2 == NULL)
{
KeBugCheck(ASSIGN_DRIVE_LETTERS_FAILED);
}
/* In case of a remote boot, setup system path */
if (IoRemoteBootClient)
{
PSTR Last, Saved;
/* Find last \ */
Last = strrchr(LoaderBlock->NtBootPathName, '\\');
Saved = NULL;
/* Misformed name, fail */
if (Last == NULL)
{
KeBugCheck(ASSIGN_DRIVE_LETTERS_FAILED);
}
/* In case the name was terminated by a \... */
if (Last[1] == ANSI_NULL)
{
/* Erase it, save position and find the previous \ */
*Last = ANSI_NULL;
Saved = Last;
Last = strrchr(LoaderBlock->NtBootPathName, '\\');
*Saved = '\\';
}
/* Misformed name, fail */
if (Last == NULL)
{
KeBugCheck(ASSIGN_DRIVE_LETTERS_FAILED);
}
/* For a remote boot, assign X drive letter */
NtSystemPath[0] = 'X';
NtSystemPath[1] = ':';
/* And copy the end of the boot path */
strcpy((PSTR)&NtSystemPath[2], Last);
/* If we had to remove the trailing \, remove it here too */
if (Saved != NULL)
{
NtSystemPath[strlen((PSTR)NtSystemPath) - 1] = ANSI_NULL;
}
/* Setup output string */
RtlInitString(NtSystemPathString, (PSTR)NtSystemPath);
}
/* For each of our disks, create the physical device DOS device */
Increment = 0;
if (DiskCount != 0)
{
for (i = 0; i < DiskCount; ++i)
{
/* Setup the origin name */
sprintf(Buffer1, "\\Device\\Harddisk%d\\Partition%d", i, 0);
RtlInitAnsiString(&StringA1, Buffer1);
if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&StringU1, &StringA1, TRUE)))
{
/* We cannot fail */
KeBugCheck(ASSIGN_DRIVE_LETTERS_FAILED);
}
/* Open the device */
InitializeObjectAttributes(&ObjectAttributes,
&StringU1,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenFile(&FileHandle,
SYNCHRONIZE | FILE_READ_DATA,
&ObjectAttributes,
&StatusBlock,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT);
if (NT_SUCCESS(Status))
{
/* If we managed, create the link */
sprintf(Buffer2, "\\DosDevices\\PhysicalDrive%d", i);
RtlInitAnsiString(&StringA2, Buffer2);
Status = RtlAnsiStringToUnicodeString(&StringU2, &StringA2, TRUE);
if (NT_SUCCESS(Status))
{
IoCreateSymbolicLink(&StringU2, &StringU1);
RtlFreeUnicodeString(&StringU2);
}
ZwClose(FileHandle);
RealDiskCount = i + 1;
}
RtlFreeUnicodeString(&StringU1);
if (!NT_SUCCESS(Status))
{
if (Increment < 50)
{
++Increment;
++DiskCount;
}
}
}
}
/* We done for our buffers */
ExFreePoolWithTag(Buffer1, TAG_FSTUB);
ExFreePoolWithTag(Buffer2, TAG_FSTUB);
/* Upcase our load options, if any */
if (LoaderBlock->LoadOptions != NULL)
{
LoadOptions = _strupr(LoaderBlock->LoadOptions);
}
else
{
LoadOptions = NULL;
}
/* If we boot with /MININT (system hive as volatile) option, assign X letter to boot device */
if (LoadOptions != NULL &&
strstr(LoadOptions, "MININT") != 0 &&
NT_SUCCESS(RtlAnsiStringToUnicodeString(&StringU1, NtDeviceName, TRUE)))
{
if (NT_SUCCESS(HalpSetMountLetter(&StringU1, 'X')))
{
*NtSystemPath = 'X';
}
RtlFreeUnicodeString(&StringU1);
}
/* Compute our disks derangements */
DiskCount -= Increment;
if (RealDiskCount > DiskCount)
{
DiskCount = RealDiskCount;
}
Devices = IopComputeHarddiskDerangements(DiskCount);
/* Now, start browsing all our disks for assigning drive letters
* Here, we'll only handle boot partition and primary partitions
*/
HarddiskCount = 0;
for (i = 0; i < DiskCount; ++i)
{
/* Get device ID according to derangements map */
if (Devices != NULL)
{
HarddiskCount = Devices[i];
}
/* Query disk layout */
swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition0", HarddiskCount);
RtlInitUnicodeString(&StringU1, Buffer);
if (!NT_SUCCESS(HalpQueryDriveLayout(&StringU1, &LayoutInfo)))
{
LayoutInfo = NULL;
}
/* Assume we didn't find system */
SystemFound = FALSE;
swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, 1);
RtlInitUnicodeString(&StringU1, Buffer);
/* Query partition info for our disk */
if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
{
/* It failed, retry for all the partitions */
for (PartitionCount = 1; ; ++PartitionCount)
{
swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
RtlInitUnicodeString(&StringU1, Buffer);
if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
{
break;
}
/* We found a primary partition, assign a drive letter */
if (PartitionType == PrimaryPartition)
{
HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, 0);
break;
}
}
}
else
{
/* All right */
for (PartitionCount = 2; ; ++PartitionCount)
{
/* If our partition is bootable (MBR) or data (GPT), that's system partition */
if (PartitionType == BootablePartition || PartitionType == DataPartition)
{
SystemFound = TRUE;
/* Assign a drive letter and stop here if MBR */
HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, 0);
if (PartitionType == BootablePartition)
{
break;
}
}
/* Keep looping on all the partitions */
swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
RtlInitUnicodeString(&StringU1, Buffer);
if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
{
/* Mount every primary partition if we didn't find system */
if (!SystemFound)
{
for (PartitionCount = 1; ; ++PartitionCount)
{
swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
RtlInitUnicodeString(&StringU1, Buffer);
if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
{
break;
}
if (PartitionType == PrimaryPartition)
{
HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, 0);
break;
}
}
}
break;
}
}
}
/* Free layout, we'll reallocate it for next device */
if (LayoutInfo != NULL)
{
ExFreePoolWithTag(LayoutInfo, TAG_FSTUB);
}
HarddiskCount = i + 1;
}
/* Now, assign logical partitions */
for (i = 0; i < DiskCount; ++i)
{
/* Get device ID according to derangements map */
if (Devices != NULL)
{
HarddiskCount = Devices[i];
}
else
{
HarddiskCount = i;
}
/* Query device layout */
swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition0", HarddiskCount);
RtlInitUnicodeString(&StringU1, Buffer);
if (!NT_SUCCESS(HalpQueryDriveLayout(&StringU1, &LayoutInfo)))
{
LayoutInfo = NULL;
}
/* And assign drive letter to logical partitions */
for (PartitionCount = 1; ; ++PartitionCount)
{
swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
RtlInitUnicodeString(&StringU1, Buffer);
if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
{
break;
}
if (PartitionType == LogicalPartition)
{
HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, 0);
}
}
/* Free layout, we'll reallocate it for next device */
if (LayoutInfo != NULL)
{
ExFreePoolWithTag(LayoutInfo, 0);
}
}
/* Now, assign drive letters to everything else */
for (i = 0; i < DiskCount; ++i)
{
/* Get device ID according to derangements map */
if (Devices != NULL)
{
HarddiskCount = Devices[i];
}
else
{
HarddiskCount = i;
}
/* Query device layout */
swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition0", HarddiskCount);
RtlInitUnicodeString(&StringU1, Buffer);
if (!NT_SUCCESS(HalpQueryDriveLayout(&StringU1, &LayoutInfo)))
{
LayoutInfo = NULL;
}
/* Save system partition if any */
SystemPartition = 0;
for (PartitionCount = 1; ; ++PartitionCount)
{
swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
RtlInitUnicodeString(&StringU1, Buffer);
if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
{
break;
}
if ((PartitionType == BootablePartition || PartitionType == PrimaryPartition) && (SystemPartition == 0))
{
SystemPartition = PartitionCount;
}
}
/* And assign drive letter to anything but system partition */
for (PartitionCount = 1; ; ++PartitionCount)
{
if (PartitionCount != SystemPartition)
{
swprintf(Buffer, L"\\Device\\Harddisk%d\\Partition%d", HarddiskCount, PartitionCount);
RtlInitUnicodeString(&StringU1, Buffer);
if (!NT_SUCCESS(HalpQueryPartitionType(&StringU1, LayoutInfo, &PartitionType)))
{
if (LayoutInfo != NULL)
{
ExFreePoolWithTag(LayoutInfo, 0);
}
break;
}
if (PartitionType == PrimaryPartition || PartitionType == FtPartition)
{
HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, 0);
}
}
}
}
/* We're done with disks, if we have a device map, free it */
if (Devices != NULL)
{
ExFreePoolWithTag(Devices, TAG_FSTUB);
}
/* Now, assign drive letter to floppy drives */
for (i = 0; i < ConfigInfo->FloppyCount; ++i)
{
swprintf(Buffer, L"\\Device\\Floppy%d", i);
RtlInitUnicodeString(&StringU1, Buffer);
if (HalpIsOldStyleFloppy(&StringU1))
{
HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, TRUE);
}
}
/* And CD drives */
for (i = 0; i < ConfigInfo->CdRomCount; ++i)
{
swprintf(Buffer, L"\\Device\\CdRom%d", i);
RtlInitUnicodeString(&StringU1, Buffer);
HalpNextDriveLetter(&StringU1, NtDeviceName, NtSystemPath, TRUE);
}
/* If not remote boot, handle NtDeviceName */
if (!IoRemoteBootClient && NT_SUCCESS(RtlAnsiStringToUnicodeString(&StringU1, NtDeviceName, TRUE)))
{
/* Assign it a drive letter */
DriveLetter = HalpNextDriveLetter(&StringU1, NULL, NULL, TRUE);
if (DriveLetter != 0)
{
if (DriveLetter != 0xFF)
{
*NtSystemPath = DriveLetter;
}
}
/* If it fails through mount manager, retry manually */
else
{
RtlInitUnicodeString(&StringU2, L"\\Device\\Floppy");
RtlInitUnicodeString(&StringU3, L"\\Device\\CdRom");
if (RtlPrefixUnicodeString(&StringU2, &StringU1, TRUE))
{
DriveLetter = 'A';
}
else if (RtlPrefixUnicodeString(&StringU3, &StringU1, TRUE))
{
DriveLetter = 'D';
}
else
{
DriveLetter = 'C';
}
/* Try any drive letter */
while (HalpSetMountLetter(&StringU1, DriveLetter) != STATUS_SUCCESS)
{
++DriveLetter;
if (DriveLetter > 'Z')
{
break;
}
}
/* If we're beyond Z (ie, no slot left) */
if (DriveLetter > 'Z')
{
/* Delete Z, and reuse it for system */
HalpDeleteMountLetter('Z');
HalpSetMountLetter(&StringU1, 'Z');
*NtSystemPath = 'Z';
}
else
{
/* Return matching drive letter */
*NtSystemPath = DriveLetter;
}
}
RtlFreeUnicodeString(&StringU1);
}
/* Enable auto assignement for mountmgr */
HalpEnableAutomaticDriveLetterAssignment();
}
/* PRIVATE FUNCTIONS *********************************************************/
NTSTATUS
NTAPI
HalpGetFullGeometry(IN PDEVICE_OBJECT DeviceObject,
IN PDISK_GEOMETRY Geometry,
OUT PULONGLONG RealSectorCount)
{
PIRP Irp;
IO_STATUS_BLOCK IoStatusBlock;
PKEVENT Event;
NTSTATUS Status;
PARTITION_INFORMATION PartitionInfo;
PAGED_CODE();
/* Allocate a non-paged event */
Event = ExAllocatePoolWithTag(NonPagedPool,
sizeof(KEVENT),
TAG_FILE_SYSTEM);
if (!Event) return STATUS_INSUFFICIENT_RESOURCES;
/* Initialize it */
KeInitializeEvent(Event, NotificationEvent, FALSE);
/* Build the IRP */
Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
DeviceObject,
NULL,
0UL,
Geometry,
sizeof(DISK_GEOMETRY),
FALSE,
Event,
&IoStatusBlock);
if (!Irp)
{
/* Fail, free the event */
ExFreePoolWithTag(Event, TAG_FILE_SYSTEM);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Call the driver and check if it's pending */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait on the driver */
KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
/* Check if the driver returned success */
if(NT_SUCCESS(Status))
{
/* Build another IRP */
Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO,
DeviceObject,
NULL,
0UL,
&PartitionInfo,
sizeof(PARTITION_INFORMATION),
FALSE,
Event,
&IoStatusBlock);
if (!Irp)
{
/* Fail, free the event */
ExFreePoolWithTag(Event, TAG_FILE_SYSTEM);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Reset event */
KeClearEvent(Event);
/* Call the driver and check if it's pending */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait on the driver */
KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
/* Check if the driver returned success */
if(NT_SUCCESS(Status))
{
/* Get the number of sectors */
*RealSectorCount = (PartitionInfo.PartitionLength.QuadPart /
Geometry->BytesPerSector);
}
}
/* Free the event and return the Status */
ExFreePoolWithTag(Event, TAG_FILE_SYSTEM);
return Status;
}
BOOLEAN
NTAPI
HalpIsValidPartitionEntry(IN PPARTITION_DESCRIPTOR Entry,
IN ULONGLONG MaxOffset,
IN ULONGLONG MaxSector)
{
ULONGLONG EndingSector;
PAGED_CODE();
/* Unused partitions are considered valid */
if (Entry->PartitionType == PARTITION_ENTRY_UNUSED) return TRUE;
/* Get the last sector of the partition */
EndingSector = GET_STARTING_SECTOR(Entry) + GET_PARTITION_LENGTH(Entry);
/* Check if it's more then the maximum sector */
if (EndingSector > MaxSector)
{
/* Invalid partition */
DPRINT1("FSTUB: entry is invalid\n");
DPRINT1("FSTUB: offset %#08lx\n", GET_STARTING_SECTOR(Entry));
DPRINT1("FSTUB: length %#08lx\n", GET_PARTITION_LENGTH(Entry));
DPRINT1("FSTUB: end %#I64x\n", EndingSector);
DPRINT1("FSTUB: max %#I64x\n", MaxSector);
return FALSE;
}
else if(GET_STARTING_SECTOR(Entry) > MaxOffset)
{
/* Invalid partition */
DPRINT1("FSTUB: entry is invalid\n");
DPRINT1("FSTUB: offset %#08lx\n", GET_STARTING_SECTOR(Entry));
DPRINT1("FSTUB: length %#08lx\n", GET_PARTITION_LENGTH(Entry));
DPRINT1("FSTUB: end %#I64x\n", EndingSector);
DPRINT1("FSTUB: maxOffset %#I64x\n", MaxOffset);
return FALSE;
}
/* It's fine, return success */
return TRUE;
}
VOID
NTAPI
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)
{
LARGE_INTEGER FirstSector, SectorCount;
ULONG LastSector, Remainder, SectorsPerCylinder;
ULONG StartingCylinder, EndingCylinder;
ULONG StartingTrack, EndingTrack;
ULONG StartingSector, EndingSector;
PAGED_CODE();
/* Calculate the number of sectors for each cylinder */
SectorsPerCylinder = SectorsPerTrack * NumberOfTracks;
/* Calculate the first sector, and the sector count */
FirstSector.QuadPart = PartitionOffset->QuadPart >> ShiftCount;
SectorCount.QuadPart = PartitionLength->QuadPart >> ShiftCount;
/* Now calculate the last sector */
LastSector = FirstSector.LowPart + SectorCount.LowPart - 1;
/* Calculate the first and last cylinders */
StartingCylinder = FirstSector.LowPart / SectorsPerCylinder;
EndingCylinder = LastSector / SectorsPerCylinder;
/* Set the default number of cylinders */
if (!ConventionalCylinders) ConventionalCylinders = 1024;
/* Normalize the values */
if (StartingCylinder >= ConventionalCylinders)
{
/* Set the maximum to 1023 */
StartingCylinder = ConventionalCylinders - 1;
}
if (EndingCylinder >= ConventionalCylinders)
{
/* Set the maximum to 1023 */
EndingCylinder = ConventionalCylinders - 1;
}
/* Calculate the starting head and sector that still remain */
Remainder = FirstSector.LowPart % SectorsPerCylinder;
StartingTrack = Remainder / SectorsPerTrack;
StartingSector = Remainder % SectorsPerTrack;
/* Calculate the ending head and sector that still remain */
Remainder = LastSector % SectorsPerCylinder;
EndingTrack = Remainder / SectorsPerTrack;
EndingSector = Remainder % SectorsPerTrack;
/* Set cylinder data for the MSB */
PartitionDescriptor->StartingCylinderMsb = (UCHAR)StartingCylinder;
PartitionDescriptor->EndingCylinderMsb = (UCHAR)EndingCylinder;
/* Set the track data */
PartitionDescriptor->StartingTrack = (UCHAR)StartingTrack;
PartitionDescriptor->EndingTrack = (UCHAR)EndingTrack;
/* Update cylinder data for the LSB */
StartingCylinder = ((StartingSector + 1) & 0x3F) |
((StartingCylinder >> 2) & 0xC0);
EndingCylinder = ((EndingSector + 1) & 0x3F) |
((EndingCylinder >> 2) & 0xC0);
/* Set the cylinder data for the LSB */
PartitionDescriptor->StartingCylinderLsb = (UCHAR)StartingCylinder;
PartitionDescriptor->EndingCylinderLsb = (UCHAR)EndingCylinder;
}
VOID
FASTCALL
xHalGetPartialGeometry(IN PDEVICE_OBJECT DeviceObject,
IN PULONG ConventionalCylinders,
IN PLONGLONG DiskSize)
{
PDISK_GEOMETRY DiskGeometry = NULL;
PIO_STATUS_BLOCK IoStatusBlock = NULL;
PKEVENT Event = NULL;
PIRP Irp;
NTSTATUS Status;
/* Set defaults */
*ConventionalCylinders = 0;
*DiskSize = 0;
/* Allocate the structure in nonpaged pool */
DiskGeometry = ExAllocatePoolWithTag(NonPagedPool,
sizeof(DISK_GEOMETRY),
TAG_FILE_SYSTEM);
if (!DiskGeometry) goto Cleanup;
/* Allocate the status block in nonpaged pool */
IoStatusBlock = ExAllocatePoolWithTag(NonPagedPool,
sizeof(IO_STATUS_BLOCK),
TAG_FILE_SYSTEM);
if (!IoStatusBlock) goto Cleanup;
/* Allocate the event in nonpaged pool too */
Event = ExAllocatePoolWithTag(NonPagedPool,
sizeof(KEVENT),
TAG_FILE_SYSTEM);
if (!Event) goto Cleanup;
/* Initialize the event */
KeInitializeEvent(Event, NotificationEvent, FALSE);
/* Build the IRP */
Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
DeviceObject,
NULL,
0,
DiskGeometry,
sizeof(DISK_GEOMETRY),
FALSE,
Event,
IoStatusBlock);
if (!Irp) goto Cleanup;
/* Now call the driver */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait for it to complete */
KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock->Status;
}
/* Check driver status */
if (NT_SUCCESS(Status))
{
/* Return the cylinder count */
*ConventionalCylinders = DiskGeometry->Cylinders.LowPart;
/* Make sure it's not larger then 1024 */
if (DiskGeometry->Cylinders.LowPart >= 1024)
{
/* Otherwise, normalize the value */
*ConventionalCylinders = 1024;
}
/* Calculate the disk size */
*DiskSize = DiskGeometry->Cylinders.QuadPart *
DiskGeometry->TracksPerCylinder *
DiskGeometry->SectorsPerTrack *
DiskGeometry->BytesPerSector;
}
Cleanup:
/* Free all the pointers */
if (Event) ExFreePoolWithTag(Event, TAG_FILE_SYSTEM);
if (IoStatusBlock) ExFreePoolWithTag(IoStatusBlock, TAG_FILE_SYSTEM);
if (DiskGeometry) ExFreePoolWithTag(DiskGeometry, TAG_FILE_SYSTEM);
return;
}
VOID
FASTCALL
xHalExamineMBR(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONG MbrTypeIdentifier,
OUT PVOID *MbrBuffer)
{
LARGE_INTEGER Offset;
PUCHAR Buffer;
ULONG BufferSize;
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
PIRP Irp;
PPARTITION_DESCRIPTOR PartitionDescriptor;
NTSTATUS Status;
PIO_STACK_LOCATION IoStackLocation;
Offset.QuadPart = 0;
/* Assume failure */
*MbrBuffer = NULL;
/* Normalize the buffer size */
BufferSize = max(SectorSize, 512);
/* Allocate the buffer */
Buffer = ExAllocatePoolWithTag(NonPagedPool,
PAGE_SIZE > BufferSize ?
PAGE_SIZE : BufferSize,
TAG_FILE_SYSTEM);
if (!Buffer) return;
/* Initialize the Event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Build the IRP */
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
DeviceObject,
Buffer,
BufferSize,
&Offset,
&Event,
&IoStatusBlock);
if (!Irp)
{
/* Failed */
ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
return;
}
/* Make sure to override volume verification */
IoStackLocation = IoGetNextIrpStackLocation(Irp);
IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
/* Call the driver */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait for completion */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
/* Check driver Status */
if (NT_SUCCESS(Status))
{
/* Validate the MBR Signature */
if (((PUSHORT)Buffer)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
{
/* Failed */
ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
return;
}
/* Get the partition entry */
PartitionDescriptor = (PPARTITION_DESCRIPTOR)
&(((PUSHORT)Buffer)[PARTITION_TABLE_OFFSET]);
/* Make sure it's what the caller wanted */
if (PartitionDescriptor->PartitionType != MbrTypeIdentifier)
{
/* It's not, free our buffer */
ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
}
else
{
/* Check if this is a secondary entry */
if (PartitionDescriptor->PartitionType == 0x54)
{
/* Return our buffer, but at sector 63 */
*(PULONG)Buffer = 63;
*MbrBuffer = Buffer;
}
else if (PartitionDescriptor->PartitionType == 0x55)
{
/* EZ Drive, return the buffer directly */
*MbrBuffer = Buffer;
}
else
{
/* Otherwise crash on debug builds */
ASSERT(PartitionDescriptor->PartitionType == 0x55);
}
}
}
}
VOID
NTAPI
FstubFixupEfiPartition(IN PPARTITION_DESCRIPTOR PartitionDescriptor,
IN ULONGLONG MaxOffset)
{
ULONG PartitionMaxOffset, PartitionLength;
PAGED_CODE();
/* Compute partition length (according to MBR entry) */
PartitionMaxOffset = GET_STARTING_SECTOR(PartitionDescriptor) + GET_PARTITION_LENGTH(PartitionDescriptor);
/* In case the partition length goes beyond disk size... */
if (PartitionMaxOffset > MaxOffset)
{
/* Resize partition to its maximum real length */
PartitionLength = (ULONG)(PartitionMaxOffset - GET_STARTING_SECTOR(PartitionDescriptor));
SET_PARTITION_LENGTH(PartitionDescriptor, PartitionLength);
}
}
NTSTATUS
FASTCALL
xHalIoReadPartitionTable(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN BOOLEAN ReturnRecognizedPartitions,
IN OUT PDRIVE_LAYOUT_INFORMATION *PartitionBuffer)
{
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
PIRP Irp;
PPARTITION_DESCRIPTOR PartitionDescriptor;
CCHAR Entry;
NTSTATUS Status;
PPARTITION_INFORMATION PartitionInfo;
PUCHAR Buffer = NULL;
ULONG BufferSize = 2048, InputSize;
PDRIVE_LAYOUT_INFORMATION DriveLayoutInfo = NULL;
LONG j = -1, i = -1, k;
DISK_GEOMETRY DiskGeometry;
LONGLONG EndSector, MaxSector, StartOffset;
ULONGLONG MaxOffset;
LARGE_INTEGER Offset, VolumeOffset;
BOOLEAN IsPrimary = TRUE, IsEzDrive = FALSE, MbrFound = FALSE;
BOOLEAN IsValid, IsEmpty = TRUE;
PVOID MbrBuffer;
PIO_STACK_LOCATION IoStackLocation;
UCHAR PartitionType;
LARGE_INTEGER HiddenSectors64;
VolumeOffset.QuadPart = Offset.QuadPart = 0;
PAGED_CODE();
/* Allocate the buffer */
*PartitionBuffer = ExAllocatePoolWithTag(NonPagedPool,
BufferSize,
TAG_FILE_SYSTEM);
if (!(*PartitionBuffer)) return STATUS_INSUFFICIENT_RESOURCES;
/* Normalize the buffer size */
InputSize = max(512, SectorSize);
/* Check for EZ Drive */
HalExamineMBR(DeviceObject, InputSize, 0x55, &MbrBuffer);
if (MbrBuffer)
{
/* EZ Drive found, bias the offset */
IsEzDrive = TRUE;
ExFreePoolWithTag(MbrBuffer, TAG_FILE_SYSTEM);
Offset.QuadPart = 512;
}
/* Get drive geometry */
Status = HalpGetFullGeometry(DeviceObject, &DiskGeometry, &MaxOffset);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(*PartitionBuffer, TAG_FILE_SYSTEM);
*PartitionBuffer = NULL;
return Status;
}
/* Get the end and maximum sector */
EndSector = MaxOffset;
MaxSector = MaxOffset << 1;
DPRINT("FSTUB: MaxOffset = %#I64x, MaxSector = %#I64x\n",
MaxOffset, MaxSector);
/* Allocate our buffer */
Buffer = ExAllocatePoolWithTag(NonPagedPool, InputSize, TAG_FILE_SYSTEM);
if (!Buffer)
{
/* Fail, free the input buffer */
ExFreePoolWithTag(*PartitionBuffer, TAG_FILE_SYSTEM);
*PartitionBuffer = NULL;
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Start partition loop */
do
{
/* Assume the partition is valid */
IsValid = TRUE;
/* Initialize the event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Clear the buffer and build the IRP */
RtlZeroMemory(Buffer, InputSize);
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
DeviceObject,
Buffer,
InputSize,
&Offset,
&Event,
&IoStatusBlock);
if (!Irp)
{
/* Failed */
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
/* Make sure to disable volume verification */
IoStackLocation = IoGetNextIrpStackLocation(Irp);
IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
/* Call the driver */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait for completion */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
/* Normalize status code and check for failure */
if (Status == STATUS_NO_DATA_DETECTED) Status = STATUS_SUCCESS;
if (!NT_SUCCESS(Status)) break;
/* If we biased for EZ-Drive, unbias now */
if (IsEzDrive && (Offset.QuadPart == 512)) Offset.QuadPart = 0;
/* Make sure this is a valid MBR */
if (((PUSHORT)Buffer)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
{
/* It's not, fail */
DPRINT1("FSTUB: (IoReadPartitionTable) No 0xaa55 found in "
"partition table %d\n", j + 1);
break;
}
/* At this point we have a valid MBR */
MbrFound = TRUE;
/* Check if we weren't given an offset */
if (!Offset.QuadPart)
{
/* Then read the signature off the disk */
(*PartitionBuffer)->Signature = ((PULONG)Buffer)[PARTITION_TABLE_OFFSET / 2 - 1];
}
/* Get the partition descriptor array */
PartitionDescriptor = (PPARTITION_DESCRIPTOR)
&(((PUSHORT)Buffer)[PARTITION_TABLE_OFFSET]);
/* Start looping partitions */
j++;
DPRINT("FSTUB: Partition Table %d:\n", j);
for (Entry = 1, k = 0; Entry <= 4; Entry++, PartitionDescriptor++)
{
/* Get the partition type */
PartitionType = PartitionDescriptor->PartitionType;
/* Print debug messages */
DPRINT("Partition Entry %d,%d: type %#x %s\n",
j,
Entry,
PartitionType,
(PartitionDescriptor->ActiveFlag) ? "Active" : "");
DPRINT("\tOffset %#08lx for %#08lx Sectors\n",
GET_STARTING_SECTOR(PartitionDescriptor),
GET_PARTITION_LENGTH(PartitionDescriptor));
/* Check whether we're facing a protective MBR */
if (PartitionType == EFI_PMBR_OSTYPE_EFI)
{
/* Partition length might be bigger than disk size */
FstubFixupEfiPartition(PartitionDescriptor,
MaxOffset);
}
/* Make sure that the partition is valid, unless it's the first */
if (!(HalpIsValidPartitionEntry(PartitionDescriptor,
MaxOffset,
MaxSector)) && (j == 0))
{
/* It's invalid, so fail */
IsValid = FALSE;
break;
}
/* Check if it's a container */
if (IsContainerPartition(PartitionType))
{
/* Increase the count of containers */
if (++k != 1)
{
/* More then one table is invalid */
DPRINT1("FSTUB: Multiple container partitions found in "
"partition table %d\n - table is invalid\n",
j);
IsValid = FALSE;
break;
}
}
/* Check if the partition is supposedly empty */
if (IsEmpty)
{
/* But check if it actually has a start and/or length */
if ((GET_STARTING_SECTOR(PartitionDescriptor)) ||
(GET_PARTITION_LENGTH(PartitionDescriptor)))
{
/* So then it's not really empty */
IsEmpty = FALSE;
}
}
/* Check if the caller wanted only recognized partitions */
if (ReturnRecognizedPartitions)
{
/* Then check if this one is unused, or a container */
if ((PartitionType == PARTITION_ENTRY_UNUSED) ||
IsContainerPartition(PartitionType))
{
/* Skip it, since the caller doesn't want it */
continue;
}
}
/* Increase the structure count and check if they can fit */
if ((sizeof(DRIVE_LAYOUT_INFORMATION) +
(++i * sizeof(PARTITION_INFORMATION))) >
BufferSize)
{
/* Allocate a new buffer that's twice as big */
DriveLayoutInfo = ExAllocatePoolWithTag(NonPagedPool,
BufferSize << 1,
TAG_FILE_SYSTEM);
if (!DriveLayoutInfo)
{
/* Out of memory, unto this extra structure */
--i;
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
/* Copy the contents of the old buffer */
RtlMoveMemory(DriveLayoutInfo,
*PartitionBuffer,
BufferSize);
/* Free the old buffer and set this one as the new one */
ExFreePoolWithTag(*PartitionBuffer, TAG_FILE_SYSTEM);
*PartitionBuffer = DriveLayoutInfo;
/* Double the size */
BufferSize <<= 1;
}
/* Now get the current structure being filled and initialize it */
PartitionInfo = &(*PartitionBuffer)->PartitionEntry[i];
PartitionInfo->PartitionType = PartitionType;
PartitionInfo->RewritePartition = FALSE;
/* Check if we're dealing with a partition that's in use */
if (PartitionType != PARTITION_ENTRY_UNUSED)
{
/* Check if it's bootable */
PartitionInfo->BootIndicator = PartitionDescriptor->
ActiveFlag & 0x80 ?
TRUE : FALSE;
/* Check if its' a container */
if (IsContainerPartition(PartitionType))
{
/* Then don't recognize it and use the volume offset */
PartitionInfo->RecognizedPartition = FALSE;
StartOffset = VolumeOffset.QuadPart;
}
else
{
/* Then recognize it and use the partition offset */
PartitionInfo->RecognizedPartition = TRUE;
StartOffset = Offset.QuadPart;
}
/* Get the starting offset */
PartitionInfo->StartingOffset.QuadPart =
StartOffset +
UInt32x32To64(GET_STARTING_SECTOR(PartitionDescriptor),
SectorSize);
/* Calculate the number of hidden sectors */
HiddenSectors64.QuadPart = (PartitionInfo->
StartingOffset.QuadPart -
StartOffset) /
SectorSize;
PartitionInfo->HiddenSectors = HiddenSectors64.LowPart;
/* Get the partition length */
PartitionInfo->PartitionLength.QuadPart =
UInt32x32To64(GET_PARTITION_LENGTH(PartitionDescriptor),
SectorSize);
/* Get the partition number */
PartitionInfo->PartitionNumber = (!IsContainerPartition(PartitionType)) ? i + 1 : 0;
}
else
{
/* Otherwise, clear all the relevant fields */
PartitionInfo->BootIndicator = FALSE;
PartitionInfo->RecognizedPartition = FALSE;
PartitionInfo->StartingOffset.QuadPart = 0;
PartitionInfo->PartitionLength.QuadPart = 0;
PartitionInfo->HiddenSectors = 0;
PartitionInfo->PartitionNumber = 0;
}
}
/* Finish debug log, and check for failure */
DPRINT("\n");
if (!NT_SUCCESS(Status)) break;
/* Also check if we hit an invalid entry here */
if (!IsValid)
{
/* We did, so break out of the loop minus one entry */
j--;
break;
}
/* Reset the offset */
Offset.QuadPart = 0;
/* Go back to the descriptor array and loop it */
PartitionDescriptor = (PPARTITION_DESCRIPTOR)
&(((PUSHORT)Buffer)[PARTITION_TABLE_OFFSET]);
for (Entry = 1; Entry <= 4; Entry++, PartitionDescriptor++)
{
/* Check if this is a container partition, since we skipped them */
if (IsContainerPartition(PartitionDescriptor->PartitionType))
{
/* Get its offset */
Offset.QuadPart = VolumeOffset.QuadPart +
UInt32x32To64(
GET_STARTING_SECTOR(PartitionDescriptor),
SectorSize);
/* If this is a primary partition, this is the volume offset */
if (IsPrimary) VolumeOffset = Offset;
/* Also update the maximum sector */
MaxSector = GET_PARTITION_LENGTH(PartitionDescriptor);
DPRINT1("FSTUB: MaxSector now = %I64d\n", MaxSector);
break;
}
}
/* Loop the next partitions, which are not primary anymore */
IsPrimary = FALSE;
} while (Offset.HighPart | Offset.LowPart);
/* Check if this is a removable device that's probably a super-floppy */
if ((DiskGeometry.MediaType == RemovableMedia) &&
(j == 0) && (MbrFound) && (IsEmpty))
{
PBOOT_SECTOR_INFO BootSectorInfo = (PBOOT_SECTOR_INFO)Buffer;
/* Read the jump bytes to detect super-floppy */
if ((BootSectorInfo->JumpByte[0] == 0xeb) ||
(BootSectorInfo->JumpByte[0] == 0xe9))
{
/* Super floppes don't have typical MBRs, so skip them */
DPRINT1("FSTUB: Jump byte %#x found along with empty partition "
"table - disk is a super floppy and has no valid MBR\n",
BootSectorInfo->JumpByte);
j = -1;
}
}
/* Check if we're still at partition -1 */
if (j == -1)
{
/* The likely cause is the super floppy detection above */
if ((MbrFound) || (DiskGeometry.MediaType == RemovableMedia))
{
/* Print out debugging information */
DPRINT1("FSTUB: Drive %#p has no valid MBR. Make it into a "
"super-floppy\n",
DeviceObject);
DPRINT1("FSTUB: Drive has %I64d sectors and is %#016I64x "
"bytes large\n",
EndSector, EndSector * DiskGeometry.BytesPerSector);
/* We should at least have some sectors */
if (EndSector > 0)
{
/* Get the entry we'll use */
PartitionInfo = &(*PartitionBuffer)->PartitionEntry[0];
/* Fill it out with data for a super-floppy */
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);
/* FIXME: REACTOS HACK */
PartitionInfo->PartitionNumber = 0;
/* Set the signature and set the count back to 0 */
(*PartitionBuffer)->Signature = 1;
i = 0;
}
}
else
{
/* Otherwise, this isn't a super floppy, so set an invalid count */
i = -1;
}
}
/* Set the partition count */
(*PartitionBuffer)->PartitionCount = ++i;
/* If we have no count, delete the signature */
if (!i) (*PartitionBuffer)->Signature = 0;
/* Free the buffer and check for success */
if (Buffer) ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(*PartitionBuffer, TAG_FILE_SYSTEM);
*PartitionBuffer = NULL;
}
/* Return status */
return Status;
}
NTSTATUS
FASTCALL
xHalIoSetPartitionInformation(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONG PartitionNumber,
IN ULONG PartitionType)
{
PIRP Irp;
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS Status;
LARGE_INTEGER Offset, VolumeOffset;
PUCHAR Buffer = NULL;
ULONG BufferSize;
ULONG i = 0;
ULONG Entry;
PPARTITION_DESCRIPTOR PartitionDescriptor;
BOOLEAN IsPrimary = TRUE, IsEzDrive = FALSE;
PVOID MbrBuffer;
PIO_STACK_LOCATION IoStackLocation;
VolumeOffset.QuadPart = Offset.QuadPart = 0;
PAGED_CODE();
/* Normalize the buffer size */
BufferSize = max(512, SectorSize);
/* Check for EZ Drive */
HalExamineMBR(DeviceObject, BufferSize, 0x55, &MbrBuffer);
if (MbrBuffer)
{
/* EZ Drive found, bias the offset */
IsEzDrive = TRUE;
ExFreePoolWithTag(MbrBuffer, TAG_FILE_SYSTEM);
Offset.QuadPart = 512;
}
/* Allocate our partition buffer */
Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_FILE_SYSTEM);
if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
/* Initialize the event we'll use and loop partitions */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
do
{
/* Reset the event since we reuse it */
KeClearEvent(&Event);
/* Build the read IRP */
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
DeviceObject,
Buffer,
BufferSize,
&Offset,
&Event,
&IoStatusBlock);
if (!Irp)
{
/* Fail */
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
/* Make sure to disable volume verification */
IoStackLocation = IoGetNextIrpStackLocation(Irp);
IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
/* Call the driver */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait for completion */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
/* Check for failure */
if (!NT_SUCCESS(Status)) break;
/* If we biased for EZ-Drive, unbias now */
if (IsEzDrive && (Offset.QuadPart == 512)) Offset.QuadPart = 0;
/* Make sure this is a valid MBR */
if (((PUSHORT)Buffer)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
{
/* It's not, fail */
Status = STATUS_BAD_MASTER_BOOT_RECORD;
break;
}
/* Get the partition descriptors and loop them */
PartitionDescriptor = (PPARTITION_DESCRIPTOR)
&(((PUSHORT)Buffer)[PARTITION_TABLE_OFFSET]);
for (Entry = 1; Entry <= 4; Entry++, PartitionDescriptor++)
{
/* Check if it's unused or a container partition */
if ((PartitionDescriptor->PartitionType ==
PARTITION_ENTRY_UNUSED) ||
(IsContainerPartition(PartitionDescriptor->PartitionType)))
{
/* Go to the next one */
continue;
}
/* It's a valid partition, so increase the partition count */
if (++i == PartitionNumber)
{
/* We found a match, set the type */
PartitionDescriptor->PartitionType = (UCHAR)PartitionType;
/* Reset the reusable event */
KeClearEvent(&Event);
/* Build the write IRP */
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
DeviceObject,
Buffer,
BufferSize,
&Offset,
&Event,
&IoStatusBlock);
if (!Irp)
{
/* Fail */
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
/* Disable volume verification */
IoStackLocation = IoGetNextIrpStackLocation(Irp);
IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
/* Call the driver */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait for completion */
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
/* We're done, break out of the loop */
break;
}
}
/* If we looped all the partitions, break out */
if (Entry <= NUM_PARTITION_TABLE_ENTRIES) break;
/* Nothing found yet, get the partition array again */
PartitionDescriptor = (PPARTITION_DESCRIPTOR)
&(((PUSHORT)Buffer)[PARTITION_TABLE_OFFSET]);
for (Entry = 1; Entry <= 4; Entry++, PartitionDescriptor++)
{
/* Check if this was a container partition (we skipped these) */
if (IsContainerPartition(PartitionDescriptor->PartitionType))
{
/* Update the partition offset */
Offset.QuadPart = VolumeOffset.QuadPart +
GET_STARTING_SECTOR(PartitionDescriptor) *
SectorSize;
/* If this was the primary partition, update the volume too */
if (IsPrimary) VolumeOffset = Offset;
break;
}
}
/* Check if we already searched all the partitions */
if (Entry > NUM_PARTITION_TABLE_ENTRIES)
{
/* Then we failed to find a good MBR */
Status = STATUS_BAD_MASTER_BOOT_RECORD;
break;
}
/* Loop the next partitions, which are not primary anymore */
IsPrimary = FALSE;
} while (i < PartitionNumber);
/* Everything done, cleanup */
if (Buffer) ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
return Status;
}
NTSTATUS
FASTCALL
xHalIoWritePartitionTable(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONG SectorsPerTrack,
IN ULONG NumberOfHeads,
IN PDRIVE_LAYOUT_INFORMATION PartitionBuffer)
{
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
PIRP Irp;
NTSTATUS Status = STATUS_SUCCESS;
ULONG BufferSize;
PUSHORT Buffer;
PPTE Entry;
PPARTITION_TABLE PartitionTable;
LARGE_INTEGER Offset, NextOffset, ExtendedOffset, SectorOffset;
LARGE_INTEGER StartOffset, PartitionLength;
ULONG i, j;
CCHAR k;
BOOLEAN IsEzDrive = FALSE, IsSuperFloppy = FALSE, DoRewrite = FALSE, IsMbr;
ULONG ConventionalCylinders;
LONGLONG DiskSize;
PDISK_LAYOUT DiskLayout = (PDISK_LAYOUT)PartitionBuffer;
PVOID MbrBuffer;
UCHAR PartitionType;
PIO_STACK_LOCATION IoStackLocation;
PPARTITION_INFORMATION PartitionInfo = PartitionBuffer->PartitionEntry;
PPARTITION_INFORMATION TableEntry;
ExtendedOffset.QuadPart = NextOffset.QuadPart = Offset.QuadPart = 0;
PAGED_CODE();
/* Normalize the buffer size */
BufferSize = max(512, SectorSize);
/* Get the partial drive geometry */
xHalGetPartialGeometry(DeviceObject, &ConventionalCylinders, &DiskSize);
/* Check for EZ Drive */
HalExamineMBR(DeviceObject, BufferSize, 0x55, &MbrBuffer);
if (MbrBuffer)
{
/* EZ Drive found, bias the offset */
IsEzDrive = TRUE;
ExFreePoolWithTag(MbrBuffer, TAG_FILE_SYSTEM);
Offset.QuadPart = 512;
}
/* Get the number of bits to shift to multiply by the sector size */
for (k = 0; k < 32; k++) if ((SectorSize >> k) == 1) break;
/* Check if there's only one partition */
if (PartitionBuffer->PartitionCount == 1)
{
/* Check if it has no starting offset or hidden sectors */
if (!(PartitionInfo->StartingOffset.QuadPart) &&
!(PartitionInfo->HiddenSectors))
{
/* Then it's a super floppy */
IsSuperFloppy = TRUE;
/* Which also means it must be non-bootable FAT-16 */
if ((PartitionInfo->PartitionNumber) ||
(PartitionInfo->PartitionType != PARTITION_FAT_16) ||
(PartitionInfo->BootIndicator))
{
/* It's not, so we fail */
return STATUS_INVALID_PARAMETER;
}
/* Check if it needs a rewrite, and disable EZ drive for sure */
if (PartitionInfo->RewritePartition) DoRewrite = TRUE;
IsEzDrive = FALSE;
}
}
/* Count the number of partition tables */
DiskLayout->TableCount = (PartitionBuffer->PartitionCount + 4 - 1) / 4;
/* Allocate our partition buffer */
Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_FILE_SYSTEM);
if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
/* Loop the entries */
Entry = (PPTE)&Buffer[PARTITION_TABLE_OFFSET];
for (i = 0; i < DiskLayout->TableCount; i++)
{
/* Set if this is the MBR partition */
IsMbr= (BOOLEAN)!i;
/* Initialize th event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Build the read IRP */
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
DeviceObject,
Buffer,
BufferSize,
&Offset,
&Event,
&IoStatusBlock);
if (!Irp)
{
/* Fail */
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
/* Make sure to disable volume verification */
IoStackLocation = IoGetNextIrpStackLocation(Irp);
IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
/* Call the driver */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait for completion */
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
/* Check for failure */
if (!NT_SUCCESS(Status)) break;
/* If we biased for EZ-Drive, unbias now */
if (IsEzDrive && (Offset.QuadPart == 512)) Offset.QuadPart = 0;
/* Check if this is a normal disk */
if (!IsSuperFloppy)
{
/* Set the boot record signature */
Buffer[BOOT_SIGNATURE_OFFSET] = BOOT_RECORD_SIGNATURE;
/* By default, don't require a rewrite */
DoRewrite = FALSE;
/* Check if we don't have an offset */
if (!Offset.QuadPart)
{
/* Check if the signature doesn't match */
if (((PULONG)Buffer)[PARTITION_TABLE_OFFSET / 2 - 1] !=
PartitionBuffer->Signature)
{
/* Then write the signature and now we need a rewrite */
((PULONG)Buffer)[PARTITION_TABLE_OFFSET / 2 - 1] =
PartitionBuffer->Signature;
DoRewrite = TRUE;
}
}
/* Loop the partition table entries */
PartitionTable = &DiskLayout->PartitionTable[i];
for (j = 0; j < 4; j++)
{
/* Get the current entry and type */
TableEntry = &PartitionTable->PartitionEntry[j];
PartitionType = TableEntry->PartitionType;
/* Check if the entry needs a rewrite */
if (TableEntry->RewritePartition)
{
/* Then we need one too */
DoRewrite = TRUE;
/* Save the type and if it's a bootable partition */
Entry[j].PartitionType = TableEntry->PartitionType;
Entry[j].ActiveFlag = TableEntry->BootIndicator ? 0x80 : 0;
/* Make sure it's used */
if (PartitionType != PARTITION_ENTRY_UNUSED)
{
/* Make sure it's not a container (unless primary) */
if ((IsMbr) || !(IsContainerPartition(PartitionType)))
{
/* Use the partition offset */
StartOffset.QuadPart = Offset.QuadPart;
}
else
{
/* Use the extended logical partition offset */
StartOffset.QuadPart = ExtendedOffset.QuadPart;
}
/* Set the sector offset */
SectorOffset.QuadPart = TableEntry->
StartingOffset.QuadPart -
StartOffset.QuadPart;
/* Now calculate the starting sector */
StartOffset.QuadPart = SectorOffset.QuadPart >> k;
Entry[j].StartingSector = StartOffset.LowPart;
/* As well as the length */
PartitionLength.QuadPart = TableEntry->PartitionLength.
QuadPart >> k;
Entry[j].PartitionLength = PartitionLength.LowPart;
/* Calculate the CHS values */
HalpCalculateChsValues(&TableEntry->StartingOffset,
&TableEntry->PartitionLength,
k,
SectorsPerTrack,
NumberOfHeads,
ConventionalCylinders,
(PPARTITION_DESCRIPTOR)
&Entry[j]);
}
else
{
/* Otherwise set up an empty entry */
Entry[j].StartingSector = 0;
Entry[j].PartitionLength = 0;
Entry[j].StartingTrack = 0;
Entry[j].EndingTrack = 0;
Entry[j].StartingCylinder = 0;
Entry[j].EndingCylinder = 0;
}
}
/* Check if this is a container partition */
if (IsContainerPartition(PartitionType))
{
/* Then update the offset to use */
NextOffset = TableEntry->StartingOffset;
}
}
}
/* Check if we need to write back the buffer */
if (DoRewrite)
{
/* We don't need to do this again */
DoRewrite = FALSE;
/* Initialize the event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* If we unbiased for EZ-Drive, rebias now */
if ((IsEzDrive) && !(Offset.QuadPart)) Offset.QuadPart = 512;
/* Build the write IRP */
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
DeviceObject,
Buffer,
BufferSize,
&Offset,
&Event,
&IoStatusBlock);
if (!Irp)
{
/* Fail */
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
/* Make sure to disable volume verification */
IoStackLocation = IoGetNextIrpStackLocation(Irp);
IoStackLocation->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
/* Call the driver */
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
/* Wait for completion */
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
/* Check for failure */
if (!NT_SUCCESS(Status)) break;
/* If we biased for EZ-Drive, unbias now */
if (IsEzDrive && (Offset.QuadPart == 512)) Offset.QuadPart = 0;
}
/* Update the partition offset and set the extended offset if needed */
Offset = NextOffset;
if (IsMbr) ExtendedOffset = NextOffset;
}
/* If we had a buffer, free it, then return status */
if (Buffer) ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
return Status;
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @implemented
*/
VOID
FASTCALL
HalExamineMBR(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONG MbrTypeIdentifier,
OUT PVOID *MbrBuffer)
{
HALDISPATCH->HalExamineMBR(DeviceObject,
SectorSize,
MbrTypeIdentifier,
MbrBuffer);
}
/*
* @implemented
*/
NTSTATUS
FASTCALL
IoReadPartitionTable(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN BOOLEAN ReturnRecognizedPartitions,
IN OUT PDRIVE_LAYOUT_INFORMATION *PartitionBuffer)
{
return HALDISPATCH->HalIoReadPartitionTable(DeviceObject,
SectorSize,
ReturnRecognizedPartitions,
PartitionBuffer);
}
/*
* @implemented
*/
NTSTATUS
FASTCALL
IoSetPartitionInformation(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONG PartitionNumber,
IN ULONG PartitionType)
{
return HALDISPATCH->HalIoSetPartitionInformation(DeviceObject,
SectorSize,
PartitionNumber,
PartitionType);
}
/*
* @implemented
*/
NTSTATUS
FASTCALL
IoWritePartitionTable(IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN ULONG SectorsPerTrack,
IN ULONG NumberOfHeads,
IN PDRIVE_LAYOUT_INFORMATION PartitionBuffer)
{
return HALDISPATCH->HalIoWritePartitionTable(DeviceObject,
SectorSize,
SectorsPerTrack,
NumberOfHeads,
PartitionBuffer);
}
/*
* @implemented
*/
VOID
FASTCALL
IoAssignDriveLetters(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
IN PSTRING NtDeviceName,
OUT PUCHAR NtSystemPath,
OUT PSTRING NtSystemPathString)
{
HALDISPATCH->HalIoAssignDriveLetters(LoaderBlock,
NtDeviceName,
NtSystemPath,
NtSystemPathString);
}
/* EOF */