mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 20:32:36 +00:00
29615feeb6
The buffer is now PUCHAR or PVOID everywhere
2759 lines
92 KiB
C
2759 lines
92 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 (IsFTPartition(PartitionInfo.Mbr.PartitionType))
|
|
{
|
|
*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,
|
|
OUT PDISK_GEOMETRY_EX Geometry)
|
|
{
|
|
PIRP Irp;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PKEVENT Event;
|
|
NTSTATUS Status;
|
|
|
|
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_EX,
|
|
DeviceObject,
|
|
NULL,
|
|
0UL,
|
|
Geometry,
|
|
sizeof(DISK_GEOMETRY_EX),
|
|
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;
|
|
}
|
|
|
|
/* 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(512, SectorSize);
|
|
|
|
/* Allocate the buffer */
|
|
Buffer = ExAllocatePoolWithTag(NonPagedPool,
|
|
max(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 (*(PUINT16)&Buffer[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE)
|
|
{
|
|
/* Failed */
|
|
ExFreePoolWithTag(Buffer, TAG_FILE_SYSTEM);
|
|
return;
|
|
}
|
|
|
|
/* Get the partition entry */
|
|
PartitionDescriptor = (PPARTITION_DESCRIPTOR)&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 for OnTrack Disk Manager 6.0 / EZ-Drive partitions */
|
|
|
|
if (PartitionDescriptor->PartitionType == PARTITION_DM)
|
|
{
|
|
/* Return our buffer, but at sector 63 */
|
|
*(PULONG)Buffer = 63;
|
|
*MbrBuffer = Buffer;
|
|
}
|
|
else if (PartitionDescriptor->PartitionType == PARTITION_EZDRIVE)
|
|
{
|
|
/* EZ-Drive, return the buffer directly */
|
|
*MbrBuffer = Buffer;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise crash on debug builds */
|
|
ASSERT(PartitionDescriptor->PartitionType == PARTITION_EZDRIVE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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_EX DiskGeometryEx;
|
|
LONGLONG EndSector, MaxSector, StartOffset;
|
|
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;
|
|
|
|
PAGED_CODE();
|
|
|
|
VolumeOffset.QuadPart = Offset.QuadPart = 0;
|
|
|
|
/* 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, PARTITION_EZDRIVE, &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, &DiskGeometryEx);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePoolWithTag(*PartitionBuffer, TAG_FILE_SYSTEM);
|
|
*PartitionBuffer = NULL;
|
|
return Status;
|
|
}
|
|
|
|
/* Get the end and maximum sector */
|
|
EndSector = DiskGeometryEx.DiskSize.QuadPart / DiskGeometryEx.Geometry.BytesPerSector;
|
|
MaxSector = EndSector << 1;
|
|
DPRINT("FSTUB: DiskSize = %#I64x, MaxSector = %#I64x\n",
|
|
DiskGeometryEx.DiskSize, MaxSector);
|
|
|
|
/* Allocate our buffer */
|
|
Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, 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 (*(PUINT16)&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 = *(PUINT32)&Buffer[DISK_SIGNATURE_OFFSET];
|
|
}
|
|
|
|
/* Get the partition descriptor array */
|
|
PartitionDescriptor = (PPARTITION_DESCRIPTOR)&Buffer[PARTITION_TABLE_OFFSET];
|
|
|
|
/* Start looping partitions */
|
|
j++;
|
|
DPRINT("FSTUB: Partition Table %d:\n", j);
|
|
for (Entry = 1, k = 0; Entry <= NUM_PARTITION_TABLE_ENTRIES; 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, DiskGeometryEx.DiskSize.QuadPart);
|
|
}
|
|
|
|
/* Make sure that the partition is valid, unless it's the first */
|
|
if (!(HalpIsValidPartitionEntry(PartitionDescriptor,
|
|
DiskGeometryEx.DiskSize.QuadPart,
|
|
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, undo 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 */
|
|
/* FIXME: REACTOS HACK -- Needed for xHalIoAssignDriveLetters() */
|
|
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;
|
|
|
|
/* FIXME: REACTOS HACK -- Needed for xHalIoAssignDriveLetters() */
|
|
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)&Buffer[PARTITION_TABLE_OFFSET];
|
|
for (Entry = 1; Entry <= NUM_PARTITION_TABLE_ENTRIES; 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 ((DiskGeometryEx.Geometry.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) || (DiskGeometryEx.Geometry.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, DiskGeometryEx.DiskSize);
|
|
|
|
/* 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 = DiskGeometryEx.DiskSize;
|
|
|
|
/* FIXME: REACTOS HACK -- Needed for xHalIoAssignDriveLetters() */
|
|
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;
|
|
|
|
PAGED_CODE();
|
|
|
|
VolumeOffset.QuadPart = Offset.QuadPart = 0;
|
|
|
|
/* Normalize the buffer size */
|
|
BufferSize = max(512, SectorSize);
|
|
|
|
/* Check for EZ-Drive */
|
|
HalExamineMBR(DeviceObject, BufferSize, PARTITION_EZDRIVE, &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(NonPagedPoolCacheAligned, 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 (*(PUINT16)&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)&Buffer[PARTITION_TABLE_OFFSET];
|
|
for (Entry = 1; Entry <= NUM_PARTITION_TABLE_ENTRIES; 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)&Buffer[PARTITION_TABLE_OFFSET];
|
|
for (Entry = 1; Entry <= NUM_PARTITION_TABLE_ENTRIES; 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;
|
|
PUCHAR 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;
|
|
|
|
PAGED_CODE();
|
|
|
|
ExtendedOffset.QuadPart = NextOffset.QuadPart = Offset.QuadPart = 0;
|
|
|
|
/* Normalize the buffer size */
|
|
BufferSize = max(512, SectorSize);
|
|
|
|
/* Get the partial drive geometry */
|
|
xHalGetPartialGeometry(DeviceObject, &ConventionalCylinders, &DiskSize);
|
|
|
|
/* Check for EZ-Drive */
|
|
HalExamineMBR(DeviceObject, BufferSize, PARTITION_EZDRIVE, &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 + NUM_PARTITION_TABLE_ENTRIES - 1) / NUM_PARTITION_TABLE_ENTRIES;
|
|
|
|
/* Allocate our partition buffer */
|
|
Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, 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 */
|
|
*(PUINT16)&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 (*(PUINT32)&Buffer[DISK_SIGNATURE_OFFSET] != PartitionBuffer->Signature)
|
|
{
|
|
/* Then write the signature and now we need a rewrite */
|
|
*(PUINT32)&Buffer[DISK_SIGNATURE_OFFSET] = PartitionBuffer->Signature;
|
|
DoRewrite = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Loop the partition table entries */
|
|
PartitionTable = &DiskLayout->PartitionTable[i];
|
|
for (j = 0; j < NUM_PARTITION_TABLE_ENTRIES; 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 */
|