mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3715 lines
105 KiB
3715 lines
105 KiB
|
|
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
hanfnc.c
|
|
|
|
Abstract:
|
|
|
|
default handlers for hal functions which don't get handlers
|
|
installed by the hal
|
|
|
|
Author:
|
|
|
|
Ken Reneris (kenr) 19-July-1994
|
|
|
|
Revision History:
|
|
G.Chrysanthakopoulos (georgioc)
|
|
Added support for removable disk with a BPB,instead of a partition table.
|
|
All changes in HalIoReadParitionTable. Started 01-June-1996
|
|
|
|
|
|
--*/
|
|
|
|
#include "ntos.h"
|
|
#include "zwapi.h"
|
|
#include "hal.h"
|
|
#include "ntdddisk.h"
|
|
#include "haldisp.h"
|
|
#include "ntddft.h"
|
|
#include "stdio.h"
|
|
|
|
//
|
|
// Strings definitions
|
|
//
|
|
|
|
static PUCHAR DiskPartitionName = "\\Device\\Harddisk%d\\Partition%d";
|
|
static PUCHAR CdRomDeviceName = "\\Device\\CdRom%d";
|
|
static PUCHAR RegistryKeyName = DISK_REGISTRY_KEY;
|
|
|
|
|
|
VOID
|
|
HalpCalculateChsValues(
|
|
IN PLARGE_INTEGER PartitionOffset,
|
|
IN PLARGE_INTEGER PartitionLength,
|
|
IN CCHAR ShiftCount,
|
|
IN ULONG SectorsPerTrack,
|
|
IN ULONG NumberOfTracks,
|
|
IN ULONG ConventionalCylinders,
|
|
OUT PPARTITION_DESCRIPTOR PartitionDescriptor
|
|
);
|
|
|
|
BOOLEAN
|
|
HalpCreateDosLink(
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
|
IN UCHAR DriveLetter,
|
|
IN PUCHAR DeviceName,
|
|
IN PSTRING BootDeviceName,
|
|
OUT PUCHAR NtSystemPath,
|
|
OUT PSTRING NtSystemPathString
|
|
);
|
|
|
|
NTSTATUS
|
|
HalpGetRegistryPartitionInformation(
|
|
IN ULONG DiskSignature,
|
|
IN LARGE_INTEGER PartitionOffset,
|
|
IN LARGE_INTEGER PartitionLength,
|
|
IN OUT DISK_PARTITION *PartitionConfiguration
|
|
);
|
|
|
|
UCHAR
|
|
HalpGetRegistryCdromInformation(
|
|
IN PUCHAR CdromName
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, HalpCalculateChsValues)
|
|
#pragma alloc_text(PAGE, HalpCreateDosLink)
|
|
#pragma alloc_text(PAGE, HalpGetRegistryPartitionInformation)
|
|
#pragma alloc_text(PAGE, HalpGetRegistryCdromInformation)
|
|
#pragma alloc_text(PAGE, xHalIoAssignDriveLetters)
|
|
#pragma alloc_text(PAGE, xHalIoReadPartitionTable)
|
|
#pragma alloc_text(PAGE, xHalIoSetPartitionInformation)
|
|
#pragma alloc_text(PAGE, xHalIoWritePartitionTable)
|
|
#endif
|
|
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
xHalExamineMBR(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG SectorSize,
|
|
IN ULONG MBRTypeIdentifier,
|
|
OUT PVOID *Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a master boot record type (MBR - the zero'th sector on the disk),
|
|
read the master boot record of a disk. If the MBR is found to be of that
|
|
type, allocate a structure whose layout is dependant upon that partition
|
|
type, fill with the appropriate values, and return a pointer to that buffer
|
|
in the output parameter.
|
|
|
|
The best example for a use of this routine is to support Ontrack
|
|
systems DiskManager software. Ontrack software lays down a special
|
|
partition describing the entire drive. The special partition type
|
|
(0x54) will be recognized and a couple of longwords of data will
|
|
be passed back in a buffer for a disk driver to act upon.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object describing the entire drive.
|
|
|
|
SectorSize - The minimum number of bytes that an IO operation can
|
|
fetch.
|
|
|
|
MBRIndentifier - A value that will be searched for in the
|
|
in the MBR. This routine will understand
|
|
the semantics implied by this value.
|
|
|
|
Buffer - Pointer to a buffer that returns data according to the
|
|
type of MBR searched for. If the MBR is not of the
|
|
type asked for, the buffer will not be allocated and this
|
|
pointer will be NULL. It is the responsibility of the
|
|
caller of HalExamineMBR to deallocate the buffer. The
|
|
caller should deallocate the memory ASAP.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
|
|
LARGE_INTEGER partitionTableOffset;
|
|
PUCHAR readBuffer = (PUCHAR) NULL;
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PIRP irp;
|
|
PPARTITION_DESCRIPTOR partitionTableEntry;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG readSize;
|
|
|
|
*Buffer = NULL;
|
|
//
|
|
// Determine the size of a read operation to ensure that at least 512
|
|
// bytes are read. This will guarantee that enough data is read to
|
|
// include an entire partition table. Note that this code assumes that
|
|
// the actual sector size of the disk (if less than 512 bytes) is a
|
|
// multiple of 2, a fairly reasonable assumption.
|
|
//
|
|
|
|
if (SectorSize >= 512) {
|
|
readSize = SectorSize;
|
|
} else {
|
|
readSize = 512;
|
|
}
|
|
|
|
//
|
|
// Start at sector 0 of the device.
|
|
//
|
|
|
|
partitionTableOffset = RtlConvertUlongToLargeInteger( 0 );
|
|
|
|
//
|
|
// Allocate a buffer that will hold the reads.
|
|
//
|
|
|
|
readBuffer = ExAllocatePool(
|
|
NonPagedPoolCacheAligned,
|
|
PAGE_SIZE>readSize?PAGE_SIZE:readSize
|
|
);
|
|
|
|
if (readBuffer == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Read record containing partition table.
|
|
//
|
|
// Create a notification event object to be used while waiting for
|
|
// the read request to complete.
|
|
//
|
|
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
|
|
|
irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
|
|
DeviceObject,
|
|
readBuffer,
|
|
readSize,
|
|
&partitionTableOffset,
|
|
&event,
|
|
&ioStatus );
|
|
|
|
status = IoCallDriver( DeviceObject, irp );
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
ExFreePool(readBuffer);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check for Boot Record signature.
|
|
//
|
|
|
|
if (((PUSHORT) readBuffer)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE) {
|
|
ExFreePool(readBuffer);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check for DM type partition.
|
|
//
|
|
|
|
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) readBuffer)[PARTITION_TABLE_OFFSET]);
|
|
|
|
if (partitionTableEntry->PartitionType != MBRTypeIdentifier) {
|
|
|
|
//
|
|
// The partition type isn't what the caller cares about.
|
|
//
|
|
ExFreePool(readBuffer);
|
|
|
|
} else {
|
|
|
|
if (partitionTableEntry->PartitionType == 0x54) {
|
|
|
|
//
|
|
// Rather than allocate a new piece of memory to return
|
|
// the data - just use the memory allocated for the buffer.
|
|
// We can assume the caller will delete this shortly.
|
|
//
|
|
|
|
((PULONG)readBuffer)[0] = 63;
|
|
*Buffer = readBuffer;
|
|
|
|
} else if (partitionTableEntry->PartitionType == 0x55) {
|
|
|
|
//
|
|
// EzDrive Parititon. Simply return the pointer to non-null
|
|
// There is no skewing here.
|
|
//
|
|
|
|
*Buffer = readBuffer;
|
|
|
|
} else {
|
|
|
|
ASSERT(partitionTableEntry->PartitionType == 0x55);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
xHalGetPartialGeometry(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PULONG ConventionalCylinders,
|
|
IN PLONGLONG DiskSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need this routine to get the number of cylinders that the disk driver
|
|
thinks is on the drive. We will need this to calculate CHS values
|
|
when we fill in the partition table entries.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object describing the entire drive.
|
|
|
|
ConventionalCylinders - Number of cylinders on the drive.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP localIrp;
|
|
PDISK_GEOMETRY diskGeometry;
|
|
PIO_STATUS_BLOCK iosb;
|
|
PKEVENT eventPtr;
|
|
NTSTATUS status;
|
|
|
|
*ConventionalCylinders = 0UL;
|
|
*DiskSize = 0UL;
|
|
|
|
diskGeometry = ExAllocatePool(
|
|
NonPagedPool,
|
|
sizeof(DISK_GEOMETRY)
|
|
);
|
|
|
|
if (!diskGeometry) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
iosb = ExAllocatePool(
|
|
NonPagedPool,
|
|
sizeof(IO_STATUS_BLOCK)
|
|
);
|
|
|
|
if (!iosb) {
|
|
|
|
ExFreePool(diskGeometry);
|
|
return;
|
|
|
|
}
|
|
|
|
eventPtr = ExAllocatePool(
|
|
NonPagedPool,
|
|
sizeof(KEVENT)
|
|
);
|
|
|
|
if (!eventPtr) {
|
|
|
|
ExFreePool(iosb);
|
|
ExFreePool(diskGeometry);
|
|
return;
|
|
|
|
}
|
|
|
|
KeInitializeEvent(
|
|
eventPtr,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
|
|
localIrp = IoBuildDeviceIoControlRequest(
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
DeviceObject,
|
|
NULL,
|
|
0UL,
|
|
diskGeometry,
|
|
sizeof(DISK_GEOMETRY),
|
|
FALSE,
|
|
eventPtr,
|
|
iosb
|
|
);
|
|
|
|
if (!localIrp) {
|
|
|
|
ExFreePool(eventPtr);
|
|
ExFreePool(iosb);
|
|
ExFreePool(diskGeometry);
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Call the lower level driver, wait for the opertion
|
|
// to finish.
|
|
//
|
|
|
|
status = IoCallDriver(
|
|
DeviceObject,
|
|
localIrp
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject(
|
|
eventPtr,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL
|
|
);
|
|
status = iosb->Status;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// The operation completed successfully. Get the cylinder
|
|
// count of the drive.
|
|
//
|
|
|
|
*ConventionalCylinders = diskGeometry->Cylinders.LowPart;
|
|
|
|
//
|
|
// If the count is less than 1024 we can pass that back. Otherwise
|
|
// send back the 1024
|
|
//
|
|
|
|
if (diskGeometry->Cylinders.QuadPart >= (LONGLONG)1024) {
|
|
|
|
*ConventionalCylinders = 1024;
|
|
|
|
}
|
|
|
|
//
|
|
// Calculate disk size from gemotry information
|
|
//
|
|
|
|
*DiskSize = (ULONG) diskGeometry->Cylinders.QuadPart *
|
|
diskGeometry->TracksPerCylinder *
|
|
diskGeometry->SectorsPerTrack *
|
|
diskGeometry->BytesPerSector;
|
|
|
|
}
|
|
|
|
ExFreePool(eventPtr);
|
|
ExFreePool(iosb);
|
|
ExFreePool(diskGeometry);
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// Define macros local to this module.
|
|
//
|
|
|
|
//++
|
|
//
|
|
// VOID
|
|
// GetNextAvailableDriveLetter(
|
|
// IN ULONG BitMap,
|
|
// OUT PCHAR DriveLetter
|
|
// )
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This routine determines the next available drive letter to be used
|
|
// based on a bitmap of the mapped drives and returns the letter.
|
|
// NOTE: The drive letter returned must be in UPPERCASE.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// BitMap - Specifies the bitmap of the assigned drives.
|
|
//
|
|
// DriveLetter - Supplies a variable to receive the letter for the next
|
|
// available drive.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
//
|
|
//--
|
|
|
|
#define GetNextAvailableDriveLetter( BitMap, DriveLetter ) { \
|
|
ULONG bit; \
|
|
DriveLetter = 'C'; \
|
|
for (bit = 0; bit < 32; bit++) { \
|
|
if ((BitMap >> bit) & 1) { \
|
|
DriveLetter++; \
|
|
continue; \
|
|
} else { \
|
|
break; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
VOID
|
|
HalpCalculateChsValues(
|
|
IN PLARGE_INTEGER PartitionOffset,
|
|
IN PLARGE_INTEGER PartitionLength,
|
|
IN CCHAR ShiftCount,
|
|
IN ULONG SectorsPerTrack,
|
|
IN ULONG NumberOfTracks,
|
|
IN ULONG ConventionalCylinders,
|
|
OUT PPARTITION_DESCRIPTOR PartitionDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine the cylinder, head, and sector (CHS) values
|
|
that should be placed in a partition table entry, given the partition's
|
|
location on the disk and its size. The values calculated are packed into
|
|
int13 format -- the high two bits of the sector byte contain bits 8 and 9
|
|
of the 10 bit cylinder value, the low 6 bits of the sector byte contain
|
|
the 6 bit sector value; the cylinder byte contains the low 8 bits
|
|
of the cylinder value; and the head byte contains the 8-bit head value.
|
|
Both the start and end CHS values are calculated.
|
|
|
|
Arguments:
|
|
|
|
PartitionOffset - Byte offset of the partition, relative to the entire
|
|
physical disk.
|
|
|
|
PartitionLength - Size in bytes of the partition.
|
|
|
|
ShiftCount - Shift count to convert from byte counts to sector counts.
|
|
|
|
SectorsPerTrack - Number of sectors in a track on the media on which
|
|
the partition resides.
|
|
|
|
NumberOfTracks - Number of tracks in a cylinder on the media on which
|
|
the partition resides.
|
|
|
|
ConventionalCylinders - The "normalized" disk cylinders. We will never
|
|
set the cylinders greater than this.
|
|
|
|
PartitionDescriptor - Structure to be filled in with the start and
|
|
end CHS values. Other fields in the structure are not referenced
|
|
or modified.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Note:
|
|
|
|
The Cylinder and Head values are 0-based but the Sector value is 1-based.
|
|
|
|
If the start or end cylinder overflows 10 bits (ie, > 1023), CHS values
|
|
will be set to all 1's.
|
|
|
|
No checking is done on the SectorsPerTrack and NumberOfTrack values.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG startSector, sectorCount, endSector;
|
|
ULONG sectorsPerCylinder;
|
|
ULONG remainder;
|
|
ULONG startC, startH, startS, endC, endH, endS;
|
|
LARGE_INTEGER tempInt;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Calculate the number of sectors in a cylinder. This is the
|
|
// number of heads multiplied by the number of sectors per track.
|
|
//
|
|
|
|
sectorsPerCylinder = SectorsPerTrack * NumberOfTracks;
|
|
|
|
//
|
|
// Convert byte offset/count to sector offset/count.
|
|
//
|
|
|
|
tempInt.QuadPart = PartitionOffset->QuadPart >> ShiftCount;
|
|
startSector = tempInt.LowPart;
|
|
|
|
tempInt.QuadPart = PartitionLength->QuadPart >> ShiftCount;
|
|
sectorCount = tempInt.LowPart;
|
|
|
|
endSector = startSector + sectorCount - 1;
|
|
|
|
startC = startSector / sectorsPerCylinder;
|
|
endC = endSector / sectorsPerCylinder;
|
|
|
|
if (!ConventionalCylinders) {
|
|
|
|
ConventionalCylinders = 1024;
|
|
|
|
}
|
|
|
|
//
|
|
// Set these values so that win95 is happy.
|
|
//
|
|
|
|
if (startC >= ConventionalCylinders) {
|
|
|
|
startC = ConventionalCylinders - 1;
|
|
|
|
}
|
|
|
|
if (endC >= ConventionalCylinders) {
|
|
|
|
endC = ConventionalCylinders - 1;
|
|
|
|
}
|
|
|
|
//
|
|
// Calculate the starting track and sector.
|
|
//
|
|
|
|
remainder = startSector % sectorsPerCylinder;
|
|
startH = remainder / SectorsPerTrack;
|
|
startS = remainder % SectorsPerTrack;
|
|
|
|
//
|
|
// Calculate the ending track and sector.
|
|
//
|
|
|
|
remainder = endSector % sectorsPerCylinder;
|
|
endH = remainder / SectorsPerTrack;
|
|
endS = remainder % SectorsPerTrack;
|
|
|
|
//
|
|
// Pack the result into the caller's structure.
|
|
//
|
|
|
|
// low 8 bits of the cylinder => C value
|
|
|
|
PartitionDescriptor->StartingCylinderMsb = (UCHAR) startC;
|
|
PartitionDescriptor->EndingCylinderMsb = (UCHAR) endC;
|
|
|
|
// 8 bits of head value => H value
|
|
|
|
PartitionDescriptor->StartingTrack = (UCHAR) startH;
|
|
PartitionDescriptor->EndingTrack = (UCHAR) endH;
|
|
|
|
// bits 8-9 of cylinder and 6 bits of the sector => S value
|
|
|
|
PartitionDescriptor->StartingCylinderLsb = (UCHAR) (((startS + 1) & 0x3f)
|
|
| ((startC >> 2) & 0xc0));
|
|
|
|
PartitionDescriptor->EndingCylinderLsb = (UCHAR) (((endS + 1) & 0x3f)
|
|
| ((endC >> 2) & 0xc0));
|
|
}
|
|
|
|
BOOLEAN
|
|
HalpCreateDosLink(
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
|
IN UCHAR DriveLetter,
|
|
IN PUCHAR DeviceName,
|
|
IN PSTRING BootDeviceName,
|
|
OUT PUCHAR NtSystemPath,
|
|
OUT PSTRING NtSystemPathString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine links an NT device name path (\Devices ...) to
|
|
a DOS drive letter (\DosDevices\C:, for instance). It also
|
|
checks to see if this device name path is the same as the
|
|
path the loader passed in to assign the system path (SystemRoot).
|
|
|
|
Arguments:
|
|
|
|
LoaderBlock - Loader information passed in by boot loader. Contains
|
|
boot path.
|
|
|
|
DriveLetter - Drive letter to assign to this partition.
|
|
|
|
DeviceName - Device name path corresponding to partition.
|
|
|
|
BootDeviceName - NT device name path from loader.
|
|
|
|
NtSystemPath - Set to point to the name of the path string that was
|
|
booted from.
|
|
|
|
NtSystemPathString - String that describes the system path.
|
|
|
|
Return Value:
|
|
|
|
TRUE if link successful.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UCHAR driveName[16];
|
|
UNICODE_STRING unicodeLinkName;
|
|
ANSI_STRING linkName;
|
|
STRING linkTarget;
|
|
UNICODE_STRING unicodeLinkTarget;
|
|
BOOLEAN DoubleSpaceBoot;
|
|
|
|
#if DBG
|
|
UCHAR debugBuffer[256];
|
|
STRING debugString;
|
|
UNICODE_STRING debugMessage;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
sprintf( driveName, "\\DosDevices\\%c:", DriveLetter );
|
|
|
|
RtlInitAnsiString(&linkName, driveName);
|
|
|
|
status = RtlAnsiStringToUnicodeString(&unicodeLinkName,
|
|
&linkName,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
RtlInitAnsiString(&linkTarget, DeviceName);
|
|
|
|
//
|
|
// Check if this should be a double space assignment
|
|
//
|
|
|
|
if (!RtlEqualString(
|
|
&linkTarget,
|
|
BootDeviceName,
|
|
FALSE
|
|
)) {
|
|
|
|
status = RtlAnsiStringToUnicodeString(&unicodeLinkTarget,
|
|
&linkTarget,
|
|
TRUE);
|
|
DoubleSpaceBoot = FALSE;
|
|
} else {
|
|
|
|
status = RtlAnsiStringToUnicodeString(&unicodeLinkTarget,
|
|
BootDeviceName,
|
|
TRUE);
|
|
DoubleSpaceBoot = TRUE;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
RtlFreeUnicodeString(&unicodeLinkName);
|
|
return FALSE;
|
|
}
|
|
|
|
status = IoCreateSymbolicLink(&unicodeLinkName,
|
|
&unicodeLinkTarget);
|
|
|
|
RtlFreeUnicodeString(&unicodeLinkName);
|
|
RtlFreeUnicodeString(&unicodeLinkTarget);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
sprintf(debugBuffer,
|
|
"INIT: %c: => %s\n",
|
|
DriveLetter,
|
|
DeviceName);
|
|
|
|
RtlInitAnsiString(&debugString, debugBuffer);
|
|
|
|
if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&debugMessage,
|
|
&debugString,
|
|
TRUE))) {
|
|
|
|
//
|
|
// Print message to console.
|
|
//
|
|
|
|
if (ZwDisplayString(&debugMessage)) {
|
|
|
|
DbgPrint("HalpCreateDosLink: ZwDisplayString failed\n");
|
|
}
|
|
RtlFreeUnicodeString(&debugMessage);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Check if this partition is the one that holds the NT tree.
|
|
//
|
|
|
|
if (RtlEqualString(BootDeviceName, &linkTarget, TRUE) || DoubleSpaceBoot) {
|
|
|
|
NtSystemPath[0] = DriveLetter;
|
|
NtSystemPath[1] = ':';
|
|
|
|
strcpy(&NtSystemPath[2],
|
|
LoaderBlock->NtBootPathName);
|
|
|
|
NtSystemPath[strlen(NtSystemPath)-1] = '\0';
|
|
|
|
RtlInitString(NtSystemPathString, NtSystemPath);
|
|
|
|
#if DBG
|
|
|
|
sprintf(debugBuffer,
|
|
"INIT: NtSystemPath == %s\n",
|
|
NtSystemPath);
|
|
|
|
RtlInitAnsiString(&debugString, debugBuffer);
|
|
|
|
if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&debugMessage,
|
|
&debugString,
|
|
TRUE))) {
|
|
|
|
//
|
|
// Print message to console.
|
|
//
|
|
|
|
if (ZwDisplayString(&debugMessage)) {
|
|
|
|
DbgPrint("HalpCreateDosLink: ZwDisplayString failed\n");
|
|
}
|
|
RtlFreeUnicodeString(&debugMessage);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
HalpGetRegistryPartitionInformation(
|
|
IN ULONG DiskSignature,
|
|
IN LARGE_INTEGER PartitionOffset,
|
|
IN LARGE_INTEGER PartitionLength,
|
|
IN OUT DISK_PARTITION *PartitionConfiguration
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to open the configuration registry key for the
|
|
disk signature passed in. If successful, it uses the partition offset
|
|
and length to find the partition and returns the information in the
|
|
specified buffer.
|
|
|
|
Arguments:
|
|
|
|
DiskSignature - 32-bit timestamp uniquely identifying a disk.
|
|
|
|
PartitionOffset - byte offset from beginning of disk of start
|
|
of this partition.
|
|
|
|
PartitionLength - length of partition in bytes.
|
|
|
|
PartitionInformation - Pointer to buffer in which to return registry
|
|
information.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the lookup and search operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle;
|
|
STRING keyString;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING unicodeKeyName;
|
|
ULONG resultLength;
|
|
ULONG numberDisks;
|
|
ULONG i;
|
|
ULONG j;
|
|
STRING valueString;
|
|
UNICODE_STRING unicodeValueName;
|
|
PDISK_REGISTRY diskRegistry;
|
|
PDISK_DESCRIPTION disk;
|
|
PDISK_PARTITION partition;
|
|
PDISK_CONFIG_HEADER regHeader;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
ULONG requestedSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Open the registry key for the disk information.
|
|
//
|
|
|
|
RtlInitString( &keyString, RegistryKeyName );
|
|
|
|
RtlAnsiStringToUnicodeString( &unicodeKeyName, &keyString, TRUE );
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&unicodeKeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
(HANDLE) NULL,
|
|
(PSECURITY_DESCRIPTOR) NULL );
|
|
|
|
status = ZwOpenKey( &handle, KEY_READ, &objectAttributes );
|
|
|
|
RtlFreeUnicodeString( &unicodeKeyName );
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
|
|
//
|
|
// There is no registry key for disk information. Return the
|
|
// failure from the configuration registry.
|
|
//
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Get the disk registry value.
|
|
//
|
|
|
|
RtlInitString( &valueString, DISK_REGISTRY_VALUE );
|
|
|
|
RtlAnsiStringToUnicodeString( &unicodeValueName, &valueString, TRUE );
|
|
|
|
requestedSize = PAGE_SIZE;
|
|
|
|
while (1) {
|
|
|
|
keyValueInformation =
|
|
(PKEY_VALUE_FULL_INFORMATION) ExAllocatePool( NonPagedPool,
|
|
requestedSize );
|
|
|
|
status = ZwQueryValueKey( handle,
|
|
&unicodeValueName,
|
|
KeyValueFullInformation,
|
|
keyValueInformation,
|
|
requestedSize,
|
|
&resultLength );
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
|
|
//
|
|
// Get bigger buffer.
|
|
//
|
|
|
|
requestedSize += 256;
|
|
ExFreePool( keyValueInformation );
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlFreeUnicodeString( &unicodeValueName );
|
|
ZwClose( handle );
|
|
|
|
if (NT_SUCCESS( status )) {
|
|
|
|
//
|
|
// The disk registry information is constructed in the following
|
|
// manner:
|
|
//
|
|
// RegistryHeader
|
|
// DiskHeader
|
|
// DiskInformation
|
|
// PartitionInformation
|
|
// FtHeader
|
|
// FtComponentInformation
|
|
// FtMemberInformation
|
|
//
|
|
// There is one RegistryHeader, one DiskHeader, and one FtHeader.
|
|
// Inside the DiskHeader area there are as many DiskInformation
|
|
// sections as there are disks in the registry. Inside the
|
|
// DiskInformation there are as many PartitionInformation sections
|
|
// as paritition on the disk.
|
|
//
|
|
// The algorithm used is to search DiskInformation sections for
|
|
// a match on the Signature desired then search the PartitionInformation
|
|
// within the located disk for a match on starting offset and length.
|
|
// Since the DiskInformation sections are packed together, if the
|
|
// current DiskInformation is not the desired section, the next
|
|
// DiskInformation section can be located by taking the address of
|
|
// the current DiskInformation after the last PartitionInformation
|
|
// section it contains.
|
|
//
|
|
|
|
if (keyValueInformation->DataLength) {
|
|
regHeader = (PDISK_CONFIG_HEADER) ((PUCHAR)keyValueInformation + keyValueInformation->DataOffset);
|
|
} else {
|
|
return STATUS_RESOURCE_DATA_NOT_FOUND;
|
|
}
|
|
|
|
diskRegistry = (PDISK_REGISTRY) ((PUCHAR)regHeader + regHeader->DiskInformationOffset);
|
|
|
|
numberDisks = diskRegistry->NumberOfDisks;
|
|
|
|
disk = &diskRegistry->Disks[0];
|
|
|
|
//
|
|
// Search the disk descriptions for a signature that matches the
|
|
// one requested.
|
|
//
|
|
|
|
for (i = 0; i < numberDisks; i++) {
|
|
|
|
if (disk->Signature == DiskSignature) {
|
|
|
|
//
|
|
// Having found a matching disk description, search the
|
|
// partition descriptions for a match on starting offset
|
|
// and length.
|
|
//
|
|
|
|
for (j = 0; j < (ULONG)disk->NumberOfPartitions; j++) {
|
|
|
|
partition = &disk->Partitions[j];
|
|
|
|
if (partition->StartingOffset.QuadPart == PartitionOffset.QuadPart &&
|
|
partition->Length.QuadPart == PartitionLength.QuadPart ) {
|
|
|
|
//
|
|
// Copy to output buffer.
|
|
//
|
|
|
|
*PartitionConfiguration = *partition;
|
|
|
|
ExFreePool( keyValueInformation );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// The next disk description is after that last partition
|
|
// description.
|
|
//
|
|
|
|
disk = (PDISK_DESCRIPTION) &disk->Partitions[disk->NumberOfPartitions];
|
|
}
|
|
|
|
status = STATUS_RESOURCE_DATA_NOT_FOUND;
|
|
}
|
|
|
|
ExFreePool( keyValueInformation );
|
|
return status;
|
|
}
|
|
|
|
UCHAR
|
|
HalpGetRegistryCdromInformation(
|
|
IN PUCHAR CdromName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to open the configuration registry key containing
|
|
stick cdrom letter information and returns this information if present.
|
|
|
|
Arguments:
|
|
|
|
CdromName - The ASCII string for the device in question.
|
|
|
|
Return Value:
|
|
|
|
Zero if there is a problem or there is no stick letter assignment.
|
|
The drive letter if there is an assignment.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle;
|
|
STRING keyString;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING unicodeKeyName;
|
|
ULONG resultLength;
|
|
ULONG numberDisks;
|
|
ULONG i;
|
|
ULONG j;
|
|
STRING valueString;
|
|
UNICODE_STRING unicodeValueName;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
ULONG requestedSize;
|
|
UCHAR returnValue;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize the return value to zero for all error conditions.
|
|
//
|
|
|
|
returnValue = 0;
|
|
|
|
//
|
|
// Open the registry key for the cdrom information.
|
|
//
|
|
|
|
RtlInitString( &keyString, RegistryKeyName );
|
|
|
|
RtlAnsiStringToUnicodeString( &unicodeKeyName, &keyString, TRUE );
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&unicodeKeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
(HANDLE) NULL,
|
|
(PSECURITY_DESCRIPTOR) NULL );
|
|
|
|
status = ZwOpenKey( &handle, KEY_READ, &objectAttributes );
|
|
|
|
RtlFreeUnicodeString( &unicodeKeyName );
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
|
|
//
|
|
// There is no registry key for disk information. Return the
|
|
// failure from the configuration registry.
|
|
//
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
//
|
|
// Get the cdrom information
|
|
//
|
|
|
|
RtlInitString( &valueString, CdromName );
|
|
|
|
RtlAnsiStringToUnicodeString( &unicodeValueName, &valueString, TRUE );
|
|
|
|
requestedSize = PAGE_SIZE;
|
|
|
|
while (1) {
|
|
|
|
keyValueInformation =
|
|
(PKEY_VALUE_FULL_INFORMATION) ExAllocatePool( NonPagedPool,
|
|
requestedSize );
|
|
|
|
status = ZwQueryValueKey( handle,
|
|
&unicodeValueName,
|
|
KeyValueFullInformation,
|
|
keyValueInformation,
|
|
requestedSize,
|
|
&resultLength );
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
|
|
//
|
|
// Get bigger buffer.
|
|
//
|
|
|
|
requestedSize += 256;
|
|
ExFreePool( keyValueInformation );
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlFreeUnicodeString( &unicodeValueName );
|
|
ZwClose( handle );
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Check for an entry without any information.
|
|
|
|
if (keyValueInformation->DataLength) {
|
|
|
|
//
|
|
// There is a drive letter present. Pick it up to be returned.
|
|
//
|
|
|
|
returnValue = (UCHAR) (*(PUCHAR)((PUCHAR)keyValueInformation + keyValueInformation->DataOffset));
|
|
}
|
|
|
|
}
|
|
|
|
ExFreePool( keyValueInformation );
|
|
return returnValue;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
xHalIoAssignDriveLetters(
|
|
IN struct _LOADER_PARAMETER_BLOCK *LoaderBlock,
|
|
IN PSTRING NtDeviceName,
|
|
OUT PUCHAR NtSystemPath,
|
|
OUT PSTRING NtSystemPathString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine assigns DOS drive letters to eligible disk partitions
|
|
and CDROM drives. It also maps the partition containing the NT
|
|
boot path to \SystemRoot. In NT, objects are built for all partition
|
|
types except 0 (unused) and 5 (extended). But drive letters are assigned
|
|
only to recognized partition types (1, 4, 6, 7, e).
|
|
|
|
Drive letter assignment is done in several stages:
|
|
|
|
1) For each CdRom:
|
|
Determine if sticky letters are assigned and reserve the letter.
|
|
|
|
2) For each disk:
|
|
Determine how many primary partitions and which is bootable.
|
|
Determine which partitions already have 'sticky letters'
|
|
and create their symbolic links.
|
|
Create a bit map for each disk that idicates which partitions
|
|
require default drive letter assignments.
|
|
|
|
3) For each disk:
|
|
Assign default drive letters for the bootable
|
|
primary partition or the first nonbootable primary partition.
|
|
|
|
4) For each disk:
|
|
Assign default drive letters for the partitions in
|
|
extended volumes.
|
|
|
|
5) For each disk:
|
|
Assign default drive letters for the remaining (ENHANCED)
|
|
primary partitions.
|
|
|
|
6) Assign A: and B: to the first two floppies in the system if they
|
|
exist. Then assign remaining floppies next available drive letters.
|
|
|
|
7) Assign drive letters to CdRoms (either sticky or default).
|
|
|
|
Arguments:
|
|
|
|
LoaderBlock - pointer to a loader parameter block.
|
|
|
|
NtDeviceName - pointer to the boot device name string used
|
|
to resolve NtSystemPath.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
typedef struct _IO_DRIVE_LAYOUT {
|
|
UCHAR PartitionCount;
|
|
UCHAR PrimaryPartitions;
|
|
ULONG NeedsDriveLetter;
|
|
UCHAR BootablePrimary;
|
|
} IO_DRIVE_LAYOUT, *PIO_DRIVE_LAYOUT;
|
|
|
|
PUCHAR ntName;
|
|
STRING ansiString;
|
|
UNICODE_STRING unicodeString;
|
|
PUCHAR ntPhysicalName;
|
|
STRING ansiPhysicalString;
|
|
UNICODE_STRING unicodePhysicalString;
|
|
PVOID buffer;
|
|
ULONG bufferSize;
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
PCONFIGURATION_INFORMATION configurationInformation;
|
|
ULONG diskCount;
|
|
ULONG floppyCount;
|
|
ULONG cdromCount;
|
|
HANDLE deviceHandle;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
PDRIVE_LAYOUT_INFORMATION partitionInformation;
|
|
PPARTITION_INFORMATION partitionEntry;
|
|
ULONG partitionNumber;
|
|
PIO_DRIVE_LAYOUT driveLayout = NULL;
|
|
UCHAR nextDriveLetter = 'C';
|
|
UCHAR stickyDriveLetter;
|
|
ULONG driveLetterMap;
|
|
ULONG diskNumber;
|
|
ULONG floppyNumber;
|
|
ULONG cdromNumber;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get the count of devices from the registry.
|
|
//
|
|
|
|
configurationInformation = IoGetConfigurationInformation();
|
|
|
|
diskCount = configurationInformation->DiskCount;
|
|
cdromCount = configurationInformation->CdRomCount;
|
|
floppyCount = configurationInformation->FloppyCount;
|
|
|
|
//
|
|
// Allocate drive layout buffer if there are fixed disks in the system.
|
|
//
|
|
|
|
if (diskCount) {
|
|
|
|
driveLayout =
|
|
ExAllocatePool( NonPagedPool, sizeof(IO_DRIVE_LAYOUT) * diskCount);
|
|
|
|
if (driveLayout == NULL) {
|
|
|
|
KeBugCheck( ASSIGN_DRIVE_LETTERS_FAILED );
|
|
}
|
|
//
|
|
// Initialize drive layout structure.
|
|
//
|
|
|
|
RtlZeroMemory( driveLayout, sizeof(IO_DRIVE_LAYOUT) * diskCount);
|
|
}
|
|
|
|
//
|
|
// Allocate general NT name buffer.
|
|
//
|
|
|
|
ntName = ExAllocatePool( NonPagedPool, 64 );
|
|
|
|
ntPhysicalName = ExAllocatePool( NonPagedPool, 64 );
|
|
|
|
if (ntName == NULL || ntPhysicalName == NULL) {
|
|
|
|
KeBugCheck( ASSIGN_DRIVE_LETTERS_FAILED );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Initialize the drive letter map so all drive letters are available
|
|
//
|
|
|
|
driveLetterMap = 0;
|
|
|
|
//
|
|
// Reserve the drive letters for "sticky" CdRom drives.
|
|
//
|
|
|
|
for (cdromNumber = 0; cdromNumber < cdromCount; cdromNumber++) {
|
|
|
|
//
|
|
// Construct the Registry path to look for CdRom drive letter
|
|
// assignments.
|
|
//
|
|
|
|
sprintf( ntName, CdRomDeviceName, cdromNumber );
|
|
|
|
//
|
|
// Determine if there is an assigned device letter for this Cdrom.
|
|
//
|
|
|
|
stickyDriveLetter = HalpGetRegistryCdromInformation( ntName );
|
|
|
|
if (stickyDriveLetter) {
|
|
|
|
|
|
//
|
|
// Mark the drive letter in use in the letter map. This will
|
|
// avoid the problem of somebody adding a new disk to the system
|
|
// and the new disk partitions would default to the sticky letter
|
|
// for the Cdrom. It does not fix the problem where there is
|
|
// disk information for the new disk and that information also
|
|
// allocates the same sticky drive letter. Note, if no letter,
|
|
// don't try to put in the map.
|
|
//
|
|
|
|
if (stickyDriveLetter != '%') {
|
|
|
|
driveLetterMap |= 1 << (stickyDriveLetter - 'C');
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// For each disk ...
|
|
//
|
|
|
|
for (diskNumber = 0; diskNumber < diskCount; diskNumber++) {
|
|
|
|
//
|
|
// This var is used to count the number of times we've tried
|
|
// to read the partition information for a particular disk. We
|
|
// will retry X times on a device not ready.
|
|
//
|
|
ULONG retryTimes = 0;
|
|
|
|
//
|
|
// Create ANSI name string for physical disk.
|
|
//
|
|
|
|
sprintf( ntName, DiskPartitionName, diskNumber, 0 );
|
|
|
|
//
|
|
// Convert to unicode string.
|
|
//
|
|
|
|
RtlInitAnsiString( &ansiString, ntName );
|
|
|
|
RtlAnsiStringToUnicodeString( &unicodeString, &ansiString, TRUE );
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
|
|
//
|
|
// Open device by name.
|
|
//
|
|
|
|
status = ZwOpenFile( &deviceHandle,
|
|
FILE_READ_DATA | SYNCHRONIZE,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
FILE_SHARE_READ,
|
|
FILE_SYNCHRONOUS_IO_NONALERT );
|
|
|
|
if (NT_SUCCESS( status )) {
|
|
|
|
//
|
|
// The device was successfully opened. Generate a DOS device name
|
|
// for the drive itself.
|
|
//
|
|
|
|
sprintf( ntPhysicalName, "\\DosDevices\\PhysicalDrive%d", diskNumber );
|
|
|
|
RtlInitAnsiString( &ansiPhysicalString, ntPhysicalName );
|
|
|
|
RtlAnsiStringToUnicodeString( &unicodePhysicalString, &ansiPhysicalString, TRUE );
|
|
|
|
IoCreateSymbolicLink( &unicodePhysicalString, &unicodeString );
|
|
|
|
RtlFreeUnicodeString( &unicodePhysicalString );
|
|
}
|
|
|
|
RtlFreeUnicodeString( &unicodeString );
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
|
|
#if DBG
|
|
DbgPrint( "IoAssignDriveLetters: Failed to open %s\n", ntName );
|
|
#endif // DBG
|
|
|
|
//
|
|
// Assume no more disks.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate 1k buffer to read partition information.
|
|
//
|
|
|
|
bufferSize = 1024;
|
|
|
|
retry:
|
|
|
|
buffer = ExAllocatePool( NonPagedPool, bufferSize );
|
|
|
|
if (!buffer) {
|
|
|
|
//
|
|
// Skip this disk.
|
|
//
|
|
|
|
ZwClose( deviceHandle );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Determine if this is a removable disk by issuing a
|
|
// query volume information file call.
|
|
//
|
|
|
|
status = ZwQueryVolumeInformationFile( deviceHandle,
|
|
&ioStatusBlock,
|
|
buffer,
|
|
sizeof(FILE_FS_DEVICE_INFORMATION),
|
|
FileFsDeviceInformation );
|
|
|
|
//
|
|
// If this call fails, then skip the device.
|
|
//
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Skip this disk.
|
|
//
|
|
|
|
ZwClose( deviceHandle );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Determine if this is a removable partition.
|
|
//
|
|
|
|
if (((PFILE_FS_DEVICE_INFORMATION) buffer)->Characteristics &
|
|
FILE_REMOVABLE_MEDIA) {
|
|
|
|
//
|
|
// Indicate there is one partition and it needs a
|
|
// drive letter.
|
|
//
|
|
|
|
driveLayout[diskNumber].PartitionCount++;
|
|
driveLayout[diskNumber].NeedsDriveLetter = 1;
|
|
|
|
//
|
|
// Continue on to the next disk.
|
|
//
|
|
|
|
ZwClose( deviceHandle );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Issue device control to get partition information.
|
|
//
|
|
|
|
status = ZwDeviceIoControlFile( deviceHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_DISK_GET_DRIVE_LAYOUT,
|
|
NULL,
|
|
0,
|
|
buffer,
|
|
bufferSize );
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
|
|
ExFreePool( buffer );
|
|
|
|
//
|
|
// Check if buffer too small.
|
|
//
|
|
|
|
if (status == STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
//
|
|
// Double buffer size.
|
|
//
|
|
|
|
bufferSize = bufferSize << 1;
|
|
|
|
//
|
|
// Try again with larger buffer.
|
|
//
|
|
|
|
goto retry;
|
|
|
|
} else if (status == STATUS_DEVICE_NOT_READY) {
|
|
|
|
LARGE_INTEGER delayTime;
|
|
|
|
if (retryTimes < 4) {
|
|
|
|
delayTime.QuadPart = (LONGLONG)-1 * 500 * 1000 * 10;
|
|
KeDelayExecutionThread(
|
|
KernelMode,
|
|
FALSE,
|
|
&delayTime
|
|
);
|
|
|
|
retryTimes++;
|
|
goto retry;
|
|
|
|
} else {
|
|
|
|
ZwClose( deviceHandle );
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Skip this disk.
|
|
//
|
|
|
|
ZwClose( deviceHandle );
|
|
continue;
|
|
}
|
|
|
|
} else {
|
|
|
|
ZwClose( deviceHandle );
|
|
}
|
|
|
|
//
|
|
// Get pointer to partition information.
|
|
//
|
|
|
|
partitionInformation = (PDRIVE_LAYOUT_INFORMATION) buffer;
|
|
|
|
//
|
|
// For each partition on this disk ...
|
|
//
|
|
|
|
for (partitionNumber = 0;
|
|
partitionNumber < partitionInformation->PartitionCount;
|
|
partitionNumber++) {
|
|
|
|
//
|
|
// Get pointer to partition entry.
|
|
//
|
|
|
|
partitionEntry =
|
|
&partitionInformation->PartitionEntry[partitionNumber];
|
|
|
|
//
|
|
// Check if partition entry describes a partition that
|
|
// requires a drive letter assignment.
|
|
//
|
|
|
|
if (IsRecognizedPartition( partitionEntry->PartitionType )) {
|
|
|
|
//
|
|
// Check for NTFT disk signature.
|
|
//
|
|
|
|
if (partitionInformation->Signature) {
|
|
|
|
DISK_PARTITION partitionConfiguration;
|
|
|
|
//
|
|
// Check if partition has a 'sticky' drive assignment.
|
|
//
|
|
|
|
status = HalpGetRegistryPartitionInformation(
|
|
partitionInformation->Signature,
|
|
partitionEntry->StartingOffset,
|
|
partitionEntry->PartitionLength,
|
|
&partitionConfiguration );
|
|
|
|
if (NT_SUCCESS( status )) {
|
|
|
|
//
|
|
// Check if this is the NTFT member that should
|
|
// receive the drive letter assignment.
|
|
//
|
|
|
|
if (partitionConfiguration.AssignDriveLetter) {
|
|
|
|
//
|
|
// Assign drive letter from registry.
|
|
//
|
|
|
|
stickyDriveLetter =
|
|
partitionConfiguration.DriveLetter;
|
|
|
|
} else {
|
|
|
|
stickyDriveLetter = 0xff;
|
|
}
|
|
|
|
} else {
|
|
stickyDriveLetter = 0;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// No signature - no configuration registry information.
|
|
//
|
|
|
|
stickyDriveLetter = 0;
|
|
}
|
|
|
|
if (stickyDriveLetter) {
|
|
|
|
UCHAR deviceNameBuffer[64];
|
|
|
|
if (stickyDriveLetter != 0xff) {
|
|
|
|
//
|
|
// Create symbolic link to drive letter.
|
|
//
|
|
|
|
sprintf( deviceNameBuffer,
|
|
DiskPartitionName,
|
|
diskNumber,
|
|
driveLayout[diskNumber].PartitionCount + 1 );
|
|
|
|
if (!HalpCreateDosLink( LoaderBlock,
|
|
stickyDriveLetter,
|
|
deviceNameBuffer,
|
|
NtDeviceName,
|
|
NtSystemPath,
|
|
NtSystemPathString )) {
|
|
|
|
//
|
|
// Check if this drive letter already taken.
|
|
//
|
|
|
|
if (driveLetterMap & (1 << (stickyDriveLetter - 'C'))) {
|
|
|
|
//
|
|
// *TMP* - Somehow indicate this configuration
|
|
// conflict to the user.
|
|
//
|
|
#if DBG
|
|
DbgPrint("IoAssignDriveLetter: Drive letter assignment conflict in registry\n");
|
|
#endif // DBG
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Set corresponding bit in drive letter map to
|
|
// indicate this letter is taken.
|
|
//
|
|
|
|
driveLetterMap |= 1 << (stickyDriveLetter - 'C');
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Check if this partition is part of an NTFT volume.
|
|
//
|
|
|
|
if (partitionEntry->PartitionType & PARTITION_NTFT) {
|
|
|
|
//
|
|
// Increment count of partitions.
|
|
//
|
|
|
|
driveLayout[diskNumber].PartitionCount++;
|
|
|
|
//
|
|
// These partitions do not require drive letter
|
|
// assignments.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Set corresponding bit in drive layout structure to
|
|
// identify a partition that needs a drive letter
|
|
// assignment.
|
|
|
|
driveLayout[diskNumber].NeedsDriveLetter |=
|
|
(1 << (driveLayout[diskNumber].PartitionCount));
|
|
|
|
} // end if (stickyDriveLetter ...)
|
|
|
|
//
|
|
// Check if partition is primary.
|
|
//
|
|
|
|
if (partitionNumber < 4) {
|
|
|
|
//
|
|
// Determine if this primary partition is bootable
|
|
// or if this is the first recognized primary.
|
|
//
|
|
|
|
if (partitionEntry->BootIndicator) {
|
|
|
|
driveLayout[diskNumber].BootablePrimary =
|
|
(UCHAR) (driveLayout[diskNumber].PartitionCount);
|
|
}
|
|
}
|
|
|
|
} // end if (IsRecognizedPartition ...
|
|
|
|
//
|
|
// Check if partition type is extended or unused.
|
|
//
|
|
|
|
if ((partitionEntry->PartitionType != PARTITION_ENTRY_UNUSED) &&
|
|
!IsContainerPartition(partitionEntry->PartitionType)) {
|
|
|
|
//
|
|
// Increment count of partitions.
|
|
//
|
|
|
|
driveLayout[diskNumber].PartitionCount++;
|
|
|
|
if (partitionNumber < 4) {
|
|
|
|
//
|
|
// Increment count of primary partitions.
|
|
//
|
|
|
|
driveLayout[diskNumber].PrimaryPartitions++;
|
|
}
|
|
}
|
|
|
|
} // end for partitionNumber = ...
|
|
|
|
//
|
|
// Free partition information buffer.
|
|
//
|
|
|
|
ExFreePool( buffer );
|
|
|
|
} // end for diskNumber ...
|
|
|
|
//
|
|
// For each disk ...
|
|
//
|
|
|
|
for (diskNumber=0; diskNumber<diskCount; diskNumber++) {
|
|
|
|
//
|
|
// If there are primary partitions then assign the next
|
|
// available drive letter to the one with the boot indicator
|
|
// set. If none of the primaries are bootable then assign
|
|
// the drive letter to the first primary.
|
|
//
|
|
|
|
if (driveLayout[diskNumber].PrimaryPartitions) {
|
|
|
|
//
|
|
// Check if the primary partition marked bootable
|
|
// needs a drive letter.
|
|
//
|
|
|
|
if ((driveLayout[diskNumber].NeedsDriveLetter &
|
|
(1 << driveLayout[diskNumber].BootablePrimary))) {
|
|
|
|
//
|
|
// Create symbolic link to drive letter.
|
|
//
|
|
|
|
sprintf( ntName,
|
|
DiskPartitionName,
|
|
diskNumber,
|
|
driveLayout[diskNumber].BootablePrimary + 1);
|
|
|
|
GetNextAvailableDriveLetter(driveLetterMap, nextDriveLetter);
|
|
|
|
if (nextDriveLetter > 'Z') {
|
|
|
|
continue;
|
|
|
|
}
|
|
if (!HalpCreateDosLink( LoaderBlock,
|
|
nextDriveLetter,
|
|
ntName,
|
|
NtDeviceName,
|
|
NtSystemPath,
|
|
NtSystemPathString )) {
|
|
|
|
//
|
|
// Check if this drive letter already taken.
|
|
//
|
|
|
|
if (driveLetterMap & (1 << (nextDriveLetter - 'C'))) {
|
|
|
|
//
|
|
// *TMP* - Somehow indicate this configuration conflict
|
|
// to the user.
|
|
//
|
|
#if DBG
|
|
DbgPrint("IoAssignDriveLetter: Drive letter assignment conflict in registry\n");
|
|
#endif // DBG
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Clear bit indicating this partition needs driver letter.
|
|
//
|
|
|
|
driveLayout[diskNumber].NeedsDriveLetter &=
|
|
~(1 << driveLayout[diskNumber].BootablePrimary);
|
|
|
|
//
|
|
// Set corresponding bit in drive letter map to
|
|
// indicate this letter is taken.
|
|
//
|
|
|
|
driveLetterMap |= 1 << (nextDriveLetter - 'C');
|
|
}
|
|
}
|
|
|
|
} // end if ((driveLayout[diskNumber].PrimaryPartitions)
|
|
|
|
} // end for diskNumber ...
|
|
|
|
//
|
|
// For each disk ...
|
|
//
|
|
|
|
for (diskNumber = 0; diskNumber < diskCount; diskNumber++) {
|
|
|
|
//
|
|
// Assign drive letters to partitions in extended
|
|
// volumes that don't already have them.
|
|
//
|
|
|
|
for (partitionNumber = driveLayout[diskNumber].PrimaryPartitions;
|
|
partitionNumber < (ULONG)driveLayout[diskNumber].PartitionCount;
|
|
partitionNumber++) {
|
|
|
|
//
|
|
// Check if this partition requires a drive letter assignment.
|
|
//
|
|
|
|
if (driveLayout[diskNumber].NeedsDriveLetter &
|
|
(1 << partitionNumber)) {
|
|
|
|
//
|
|
// Create symbolic link to drive letter.
|
|
//
|
|
|
|
sprintf( ntName,
|
|
DiskPartitionName,
|
|
diskNumber,
|
|
partitionNumber + 1);
|
|
|
|
GetNextAvailableDriveLetter( driveLetterMap, nextDriveLetter );
|
|
|
|
if (nextDriveLetter > 'Z') {
|
|
|
|
continue;
|
|
|
|
}
|
|
if (!HalpCreateDosLink( LoaderBlock,
|
|
nextDriveLetter,
|
|
ntName,
|
|
NtDeviceName,
|
|
NtSystemPath,
|
|
NtSystemPathString )) {
|
|
|
|
//
|
|
// Check if this drive letter already taken.
|
|
//
|
|
|
|
if (driveLetterMap & (1 << (nextDriveLetter - 'C'))) {
|
|
|
|
//
|
|
// *TMP* - Somehow indicate this configuration conflict
|
|
// to the user.
|
|
//
|
|
|
|
#if DBG
|
|
DbgPrint( "IoAssignDriveLetters: %c: already taken\n",
|
|
nextDriveLetter );
|
|
#endif // DBG
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Set corresponding bit in drive letter map to
|
|
// indicate this letter is taken.
|
|
//
|
|
|
|
driveLetterMap |= 1 << (nextDriveLetter - 'C');
|
|
}
|
|
}
|
|
|
|
} // end for partitionNumber ...
|
|
|
|
} // end for diskNumber ...
|
|
|
|
//
|
|
// For each disk ...
|
|
//
|
|
|
|
for (diskNumber=0; diskNumber<diskCount; diskNumber++) {
|
|
|
|
//
|
|
// Assign drive letters to remaining partitions. These are nonbootable
|
|
// primaries (ENHANCED).
|
|
//
|
|
|
|
for (partitionNumber = 0;
|
|
partitionNumber < (ULONG)driveLayout[diskNumber].PrimaryPartitions;
|
|
partitionNumber++) {
|
|
|
|
//
|
|
// Check if this partition requires a drive letter assignment.
|
|
//
|
|
|
|
if (driveLayout[diskNumber].NeedsDriveLetter &
|
|
(1 << partitionNumber)) {
|
|
|
|
//
|
|
// Create symbolic link to drive letter.
|
|
//
|
|
|
|
sprintf( ntName,
|
|
DiskPartitionName,
|
|
diskNumber,
|
|
partitionNumber + 1 );
|
|
|
|
GetNextAvailableDriveLetter( driveLetterMap, nextDriveLetter );
|
|
|
|
if (nextDriveLetter > 'Z') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!HalpCreateDosLink( LoaderBlock,
|
|
nextDriveLetter,
|
|
ntName,
|
|
NtDeviceName,
|
|
NtSystemPath,
|
|
NtSystemPathString )) {
|
|
|
|
//
|
|
// Check if this drive letter already taken.
|
|
//
|
|
|
|
if (driveLetterMap & (1 << (nextDriveLetter - 'C'))) {
|
|
|
|
//
|
|
// *TMP* - Somehow indicate this configuration conflict
|
|
// to the user.
|
|
//
|
|
#if DBG
|
|
DbgPrint( "IoAssignDriveLetters: %c: already taken\n",
|
|
nextDriveLetter );
|
|
#endif // DBG
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Set corresponding bit in drive letter map to
|
|
// indicate this letter is taken.
|
|
//
|
|
|
|
driveLetterMap |= 1 << (nextDriveLetter - 'C');
|
|
}
|
|
}
|
|
|
|
} // end for partitionNumber ...
|
|
|
|
} // end for diskNumber ...
|
|
|
|
//
|
|
// For each floppy ...
|
|
//
|
|
|
|
for (floppyNumber = 0; floppyNumber < floppyCount; floppyNumber++) {
|
|
|
|
//
|
|
// Create ANSI device name string.
|
|
//
|
|
|
|
sprintf( ntName,
|
|
"\\Device\\Floppy%d",
|
|
floppyNumber );
|
|
|
|
//
|
|
// Check if this is one of the first two floppies in the system.
|
|
//
|
|
|
|
if (floppyNumber == 0) {
|
|
|
|
//
|
|
// The first floppy in the system is assigned drive letter A:.
|
|
//
|
|
|
|
if (!HalpCreateDosLink( LoaderBlock,
|
|
'A',
|
|
ntName,
|
|
NtDeviceName,
|
|
NtSystemPath,
|
|
NtSystemPathString )) {
|
|
|
|
#if DBG
|
|
DbgPrint( "IoAssignDriveLetters: Drive letter 'A' already taken\n",
|
|
nextDriveLetter );
|
|
#endif // DBG
|
|
|
|
}
|
|
|
|
} else if (floppyNumber == 1) {
|
|
|
|
//
|
|
// The secod floppy in the system is assigned drive letter B:.
|
|
//
|
|
|
|
if (!HalpCreateDosLink( LoaderBlock,
|
|
'B',
|
|
ntName,
|
|
NtDeviceName,
|
|
NtSystemPath,
|
|
NtSystemPathString )) {
|
|
|
|
#if DBG
|
|
DbgPrint( "IoAssignDriveLetters: Drive letter 'B' already taken\n",
|
|
nextDriveLetter );
|
|
#endif // DBG
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
GetNextAvailableDriveLetter( driveLetterMap, nextDriveLetter );
|
|
|
|
if (nextDriveLetter > 'Z') {
|
|
|
|
continue;
|
|
|
|
}
|
|
//
|
|
// Create symbolic link to drive letter.
|
|
//
|
|
|
|
if (!HalpCreateDosLink( LoaderBlock,
|
|
nextDriveLetter,
|
|
ntName,
|
|
NtDeviceName,
|
|
NtSystemPath,
|
|
NtSystemPathString )) {
|
|
|
|
//
|
|
// Check if this drive letter already taken.
|
|
//
|
|
|
|
if (driveLetterMap & (1 << (nextDriveLetter - 'C:'))) {
|
|
|
|
//
|
|
// Somehow indicate this configuration conflict
|
|
// to the user.
|
|
//
|
|
#if DBG
|
|
DbgPrint( "IoAssignDriveLetters: %c: already taken\n",
|
|
nextDriveLetter );
|
|
#endif // DBG
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Set corresponding bit in drive letter map to
|
|
// indicate this letter is taken.
|
|
//
|
|
|
|
driveLetterMap |= 1 << (nextDriveLetter - 'C');
|
|
}
|
|
}
|
|
|
|
} // end for floppyNumber ...
|
|
|
|
//
|
|
// For each cdrom ... Count was obtained before looking for disks.
|
|
//
|
|
|
|
for (cdromNumber = 0; cdromNumber < cdromCount; cdromNumber++) {
|
|
|
|
//
|
|
// Create ANSI device name string.
|
|
//
|
|
|
|
sprintf( ntName, CdRomDeviceName, cdromNumber );
|
|
|
|
//
|
|
// Determine if there is an assigned device letter for this Cdrom.
|
|
//
|
|
|
|
nextDriveLetter = HalpGetRegistryCdromInformation( ntName );
|
|
|
|
if (!nextDriveLetter) {
|
|
|
|
//
|
|
// There is no sticky drive letter for the drive. Allocate
|
|
// the next one available.
|
|
//
|
|
|
|
GetNextAvailableDriveLetter( driveLetterMap, nextDriveLetter );
|
|
}
|
|
|
|
//
|
|
// If there is NOT supposed to be a drive letter assigned, simply go
|
|
// on to the next device.
|
|
//
|
|
|
|
if (nextDriveLetter == '%') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (nextDriveLetter > 'Z') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// Create symbolic link to drive letter.
|
|
//
|
|
|
|
while (!HalpCreateDosLink( LoaderBlock,
|
|
nextDriveLetter,
|
|
ntName,
|
|
NtDeviceName,
|
|
NtSystemPath,
|
|
NtSystemPathString )) {
|
|
|
|
//
|
|
// Somehow this letter is already taken. Try the next
|
|
// available letter based on the letter map.
|
|
//
|
|
|
|
if (driveLetterMap & (1 << (nextDriveLetter - 'C:'))) {
|
|
|
|
//
|
|
// Somehow indicate this configuration conflict
|
|
// to the user.
|
|
//
|
|
#if DBG
|
|
DbgPrint( "IoAssignDriveLetters: %c: already taken\n",
|
|
nextDriveLetter );
|
|
#endif // DBG
|
|
|
|
}
|
|
|
|
//
|
|
// Insure that it is marked in use by the map before getting
|
|
// a new letter.
|
|
//
|
|
|
|
driveLetterMap |= 1 << (nextDriveLetter - 'C');
|
|
GetNextAvailableDriveLetter( driveLetterMap, nextDriveLetter );
|
|
if (nextDriveLetter > 'Z') {
|
|
|
|
//
|
|
// No more letters just get out of here.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
} // end for cdromNumber ...
|
|
|
|
//
|
|
// Free drive layout buffer and NT name buffer.
|
|
//
|
|
|
|
if (diskCount) {
|
|
ExFreePool( driveLayout );
|
|
}
|
|
ExFreePool( ntName );
|
|
ExFreePool( ntPhysicalName );
|
|
|
|
} // end IoAssignDriveLetters()
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
xHalIoReadPartitionTable(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG SectorSize,
|
|
IN BOOLEAN ReturnRecognizedPartitions,
|
|
OUT struct _DRIVE_LAYOUT_INFORMATION **PartitionBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine walks the disk reading the partition tables and creates
|
|
an entry in the partition list buffer for each partition.
|
|
|
|
The algorithm used by this routine is two-fold:
|
|
|
|
1) Read each partition table and for each valid, recognized
|
|
partition found, to build a descriptor in a partition list.
|
|
Extended partitions are located in order to find other
|
|
partition tables, but no descriptors are built for these.
|
|
The partition list is built in nonpaged pool that is allocated
|
|
by this routine. It is the caller's responsibility to free
|
|
this pool after it has gathered the appropriate information
|
|
from the list.
|
|
|
|
2) Read each partition table and for each and every entry, build
|
|
a descriptor in the partition list. Extended partitions are
|
|
located to find each partition table on the disk, and entries
|
|
are built for these as well. The partition list is build in
|
|
nonpaged pool that is allocated by this routine. It is the
|
|
caller's responsibility to free this pool after it has copied
|
|
the information back to its caller.
|
|
|
|
The first algorithm is used when the ReturnRecognizedPartitions flag
|
|
is set. This is used to determine how many partition device objects
|
|
the device driver is to create, and where each lives on the drive.
|
|
|
|
The second algorithm is used when the ReturnRecognizedPartitions flag
|
|
is clear. This is used to find all of the partition tables and their
|
|
entries for a utility such as fdisk, that would like to revamp where
|
|
the partitions live.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to device object for this disk.
|
|
|
|
SectorSize - Sector size on the device.
|
|
|
|
ReturnRecognizedPartitions - A flag indicated whether only recognized
|
|
partition descriptors are to be returned, or whether all partition
|
|
entries are to be returned.
|
|
|
|
PartitionBuffer - Pointer to the pointer of the buffer in which the list
|
|
of partition will be stored.
|
|
|
|
Return Value:
|
|
|
|
The functional value is STATUS_SUCCESS if at least one sector table was
|
|
read.
|
|
|
|
Notes:
|
|
|
|
It is the responsibility of the caller to deallocate the partition list
|
|
buffer allocated by this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// We need this structure in case we encounter a disk with a BPB instead of
|
|
// an MBR. In that case we will still return a valid partition table entry
|
|
// effectively simulating a partition, using the data from the BPB.
|
|
//
|
|
typedef struct _BOOT_SECTOR_INFO {
|
|
UCHAR JumpByte[3];
|
|
UCHAR OemData[8];
|
|
UCHAR BytesPerSector[2];
|
|
UCHAR SectorsPerCluster[1];
|
|
UCHAR NumberOfReservedSectors[2];
|
|
UCHAR NumberOfFatTables[1];
|
|
UCHAR NumberOfDirectoryEntries[2];
|
|
UCHAR SmallNumberOfSectors[2];
|
|
UCHAR MediaByte[1];
|
|
UCHAR NumberOfFatSectors[2];
|
|
UCHAR SectorsPerTrack[2];
|
|
UCHAR NumberOfHeads[2];
|
|
UCHAR NumberOfHiddenSectors[2];
|
|
UCHAR Ignore4[2];
|
|
UCHAR LargeNumberOfSectors[3];
|
|
} BOOT_SECTOR_INFO, *PBOOT_SECTOR_INFO;
|
|
|
|
|
|
|
|
#define GET_STARTING_SECTOR( p ) ( \
|
|
(ULONG) (p->StartingSectorLsb0) + \
|
|
(ULONG) (p->StartingSectorLsb1 << 8) + \
|
|
(ULONG) (p->StartingSectorMsb0 << 16) + \
|
|
(ULONG) (p->StartingSectorMsb1 << 24) )
|
|
|
|
#define GET_PARTITION_LENGTH( p ) ( \
|
|
(ULONG) (p->PartitionLengthLsb0) + \
|
|
(ULONG) (p->PartitionLengthLsb1 << 8) + \
|
|
(ULONG) (p->PartitionLengthMsb0 << 16) + \
|
|
(ULONG) (p->PartitionLengthMsb1 << 24) )
|
|
|
|
ULONG partitionBufferSize = PARTITION_BUFFER_SIZE;
|
|
PDRIVE_LAYOUT_INFORMATION newPartitionBuffer = NULL;
|
|
|
|
//
|
|
// Super floppy detection variables
|
|
//
|
|
UCHAR partitionTableCounter = 0;
|
|
LONGLONG partitionLength = 0;
|
|
LONGLONG partitionStartingOffset = 0;
|
|
PBOOT_SECTOR_INFO bootSector;
|
|
UCHAR bpbJumpByte;
|
|
ULONG bpbNumberOfSectors;
|
|
ULONG bpbBytesPerSector;
|
|
ULONG bpbNumberOfHiddenSectors;
|
|
|
|
|
|
|
|
LARGE_INTEGER partitionTableOffset;
|
|
LARGE_INTEGER volumeStartOffset;
|
|
LARGE_INTEGER tempInt;
|
|
BOOLEAN primaryPartitionTable;
|
|
LONG partitionNumber;
|
|
PUCHAR readBuffer = (PUCHAR) NULL;
|
|
KEVENT event;
|
|
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PIRP irp;
|
|
PPARTITION_DESCRIPTOR partitionTableEntry;
|
|
CCHAR partitionEntry;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG readSize;
|
|
LONGLONG diskSize;
|
|
ULONG conventionalCylinders;
|
|
PPARTITION_INFORMATION partitionInfo;
|
|
BOOLEAN foundEZHooker = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Create the buffer that will be passed back to the driver containing
|
|
// the list of partitions on the disk.
|
|
//
|
|
|
|
*PartitionBuffer = ExAllocatePool( NonPagedPool,
|
|
partitionBufferSize );
|
|
|
|
if (*PartitionBuffer == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Determine the size of a read operation to ensure that at least 512
|
|
// bytes are read. This will guarantee that enough data is read to
|
|
// include an entire partition table. Note that this code assumes that
|
|
// the actual sector size of the disk (if less than 512 bytes) is a
|
|
// multiple of 2, a fairly reasonable assumption.
|
|
//
|
|
|
|
if (SectorSize >= 512) {
|
|
readSize = SectorSize;
|
|
} else {
|
|
readSize = 512;
|
|
}
|
|
|
|
//
|
|
// Look to see if this is an EZDrive Disk. If it is then get the
|
|
// real parititon table at 1.
|
|
//
|
|
|
|
{
|
|
|
|
PVOID buff;
|
|
|
|
HalExamineMBR(
|
|
DeviceObject,
|
|
readSize,
|
|
(ULONG)0x55,
|
|
&buff
|
|
);
|
|
|
|
if (buff) {
|
|
|
|
foundEZHooker = TRUE;
|
|
ExFreePool(buff);
|
|
partitionTableOffset.QuadPart = 512;
|
|
|
|
} else {
|
|
|
|
partitionTableOffset.QuadPart = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Indicate that the primary partition table is being read and
|
|
// processed.
|
|
//
|
|
|
|
primaryPartitionTable = TRUE;
|
|
|
|
//
|
|
// The partitions in this volume have their start sector as 0.
|
|
//
|
|
|
|
volumeStartOffset.QuadPart = 0;
|
|
|
|
//
|
|
// Initialize the number of partitions in the list.
|
|
//
|
|
|
|
partitionNumber = -1;
|
|
|
|
//
|
|
// Allocate a buffer that will hold the reads.
|
|
//
|
|
|
|
readBuffer = ExAllocatePool( NonPagedPoolCacheAligned, PAGE_SIZE );
|
|
|
|
if (readBuffer == NULL) {
|
|
ExFreePool( *PartitionBuffer );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Read each partition table, create an object for the partition(s)
|
|
// it represents, and then if there is a link entry to another
|
|
// partition table, repeat.
|
|
//
|
|
|
|
do {
|
|
|
|
//
|
|
// Read record containing partition table.
|
|
//
|
|
// Create a notification event object to be used while waiting for
|
|
// the read request to complete.
|
|
//
|
|
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
|
|
|
irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
|
|
DeviceObject,
|
|
readBuffer,
|
|
readSize,
|
|
&partitionTableOffset,
|
|
&event,
|
|
&ioStatus );
|
|
|
|
status = IoCallDriver( DeviceObject, irp );
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If EZDrive is hooking the MBR then we found the first partition table
|
|
// in sector 1 rather than 0. However that partition table is relative
|
|
// to sector zero. So, Even though we got it from one, reset the partition
|
|
// offset to 0.
|
|
//
|
|
|
|
if (foundEZHooker && (partitionTableOffset.QuadPart == 512)) {
|
|
|
|
partitionTableOffset.QuadPart = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Check for Boot Record signature.
|
|
//
|
|
|
|
if (((PUSHORT) readBuffer)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE) {
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Copy NTFT disk signature to buffer
|
|
//
|
|
|
|
if (partitionTableOffset.QuadPart == 0) {
|
|
(*PartitionBuffer)->Signature = ((PULONG) readBuffer)[PARTITION_TABLE_OFFSET/2-1];
|
|
}
|
|
|
|
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) readBuffer)[PARTITION_TABLE_OFFSET]);
|
|
|
|
//
|
|
// the partitionInfo could be wrong, if there is
|
|
// a BPB instead of an MBR. To make sure we can check if the first partitionTable
|
|
// entry has data that make sense. If not we can use BPB info to fix them
|
|
// This is valid only for the first partition table, so in case of extenede partitions
|
|
// we wont check.
|
|
//
|
|
|
|
if (partitionTableCounter == 0) {
|
|
bootSector = (PBOOT_SECTOR_INFO) &(((PUSHORT) readBuffer)[0]);
|
|
|
|
//
|
|
// If disk geometry information returns zero, we have to use the BPB at
|
|
// the 0th sector to get size information.
|
|
//
|
|
|
|
bpbJumpByte = bootSector->JumpByte[0];
|
|
bpbNumberOfSectors = (bootSector->LargeNumberOfSectors[2] * 0x10000) +
|
|
(bootSector->LargeNumberOfSectors[1] *0x100) +
|
|
bootSector->LargeNumberOfSectors[0];
|
|
|
|
bpbBytesPerSector = (bootSector->BytesPerSector[1] * 0x100) +
|
|
bootSector->BytesPerSector[0];
|
|
|
|
bpbNumberOfHiddenSectors = (bootSector->NumberOfHiddenSectors[1] * 0x100) +
|
|
bootSector->NumberOfHiddenSectors[0];
|
|
|
|
}
|
|
|
|
//
|
|
// Keep count of partition tables in case we have an extended partition;
|
|
//
|
|
|
|
partitionTableCounter++;
|
|
|
|
|
|
|
|
|
|
//
|
|
// First create the objects corresponding to the entries in this
|
|
// table that are not link entries or are unused.
|
|
//
|
|
|
|
for (partitionEntry = 1;
|
|
partitionEntry <= NUM_PARTITION_TABLE_ENTRIES;
|
|
partitionEntry++, partitionTableEntry++) {
|
|
|
|
//
|
|
// If the partition entry is not used or not recognized, skip
|
|
// it. Note that this is only done if the caller wanted only
|
|
// recognized partition descriptors returned.
|
|
//
|
|
|
|
if (ReturnRecognizedPartitions) {
|
|
|
|
//
|
|
// Check if partition type is 0 (unused) or 5/f (extended).
|
|
// The definition of recognized partitions has broadened
|
|
// to include any partition type other than 0 or 5/f.
|
|
//
|
|
|
|
if ((partitionTableEntry->PartitionType == PARTITION_ENTRY_UNUSED) ||
|
|
IsContainerPartition(partitionTableEntry->PartitionType)) {
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Bump up to the next partition entry.
|
|
//
|
|
|
|
partitionNumber++;
|
|
|
|
if (((partitionNumber * sizeof( PARTITION_INFORMATION )) + sizeof( DRIVE_LAYOUT_INFORMATION )) > (ULONG) partitionBufferSize) {
|
|
|
|
//
|
|
// The partition list is too small to contain all of the
|
|
// entries, so create a buffer that is twice as large to
|
|
// store the partition list and copy the old buffer into
|
|
// the new one.
|
|
//
|
|
|
|
newPartitionBuffer = ExAllocatePool( NonPagedPool,
|
|
partitionBufferSize << 1 );
|
|
|
|
if (newPartitionBuffer == NULL) {
|
|
--partitionNumber;
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
RtlMoveMemory( newPartitionBuffer,
|
|
*PartitionBuffer,
|
|
partitionBufferSize );
|
|
|
|
ExFreePool( *PartitionBuffer );
|
|
|
|
//
|
|
// Reassign the new buffer to the return parameter and
|
|
// reset the size of the buffer.
|
|
//
|
|
|
|
*PartitionBuffer = newPartitionBuffer;
|
|
partitionBufferSize <<= 1;
|
|
}
|
|
|
|
//
|
|
// Describe this partition table entry in the partition list
|
|
// entry being built for the driver. This includes writing
|
|
// the partition type, starting offset of the partition, and
|
|
// the length of the partition.
|
|
//
|
|
|
|
partitionInfo = &(*PartitionBuffer)->PartitionEntry[partitionNumber];
|
|
|
|
partitionInfo->PartitionType = partitionTableEntry->PartitionType;
|
|
|
|
partitionInfo->RewritePartition = FALSE;
|
|
|
|
|
|
|
|
if (partitionTableEntry->PartitionType != PARTITION_ENTRY_UNUSED) {
|
|
LONGLONG startOffset;
|
|
|
|
partitionInfo->BootIndicator =
|
|
partitionTableEntry->ActiveFlag & PARTITION_ACTIVE_FLAG ?
|
|
(BOOLEAN) TRUE : (BOOLEAN) FALSE;
|
|
|
|
if (IsContainerPartition(partitionTableEntry->PartitionType)) {
|
|
partitionInfo->RecognizedPartition = FALSE;
|
|
startOffset = volumeStartOffset.QuadPart;
|
|
} else {
|
|
partitionInfo->RecognizedPartition = TRUE;
|
|
startOffset = partitionTableOffset.QuadPart;
|
|
}
|
|
|
|
partitionInfo->StartingOffset.QuadPart = startOffset +
|
|
UInt32x32To64(GET_STARTING_SECTOR(partitionTableEntry),
|
|
SectorSize);
|
|
tempInt.QuadPart = (partitionInfo->StartingOffset.QuadPart -
|
|
startOffset) / SectorSize;
|
|
partitionInfo->HiddenSectors = tempInt.LowPart;
|
|
|
|
partitionInfo->PartitionLength.QuadPart =
|
|
UInt32x32To64(GET_PARTITION_LENGTH(partitionTableEntry),
|
|
SectorSize);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Partitions that are not used do not describe any part
|
|
// of the disk. These types are recorded in the partition
|
|
// list buffer when the caller requested all of the entries
|
|
// be returned. Simply zero out the remaining fields in
|
|
// the entry.
|
|
//
|
|
|
|
partitionInfo->BootIndicator = FALSE;
|
|
partitionInfo->RecognizedPartition = FALSE;
|
|
partitionInfo->StartingOffset.QuadPart = 0;
|
|
partitionInfo->PartitionLength.QuadPart = 0;
|
|
partitionInfo->HiddenSectors = 0;
|
|
}
|
|
|
|
//
|
|
// Save relevant information to check later if this is a super floppy disk
|
|
//
|
|
|
|
if (partitionTableCounter == 1 && partitionNumber == 0) {
|
|
partitionLength = (ULONG) partitionInfo->PartitionLength.QuadPart;
|
|
partitionStartingOffset = (ULONG) partitionInfo->StartingOffset.QuadPart;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// If an error occurred, leave the routine now.
|
|
//
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now check to see if there are any link entries in this table,
|
|
// and if so, set up the sector address of the next partition table.
|
|
// There can only be one link entry in each partition table, and it
|
|
// will point to the next table.
|
|
//
|
|
|
|
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) readBuffer)[PARTITION_TABLE_OFFSET]);
|
|
|
|
//
|
|
// Assume that the link entry is empty.
|
|
//
|
|
|
|
partitionTableOffset.QuadPart = 0;
|
|
|
|
for (partitionEntry = 1;
|
|
partitionEntry <= NUM_PARTITION_TABLE_ENTRIES;
|
|
partitionEntry++, partitionTableEntry++) {
|
|
|
|
if (IsContainerPartition(partitionTableEntry->PartitionType)) {
|
|
|
|
//
|
|
// Obtain the address of the next partition table on the
|
|
// disk. This is the number of hidden sectors added to
|
|
// the beginning of the extended partition (in the case of
|
|
// logical drives), since all logical drives are relative
|
|
// to the extended partition. The VolumeStartSector will
|
|
// be zero if this is the primary parition table.
|
|
//
|
|
|
|
partitionTableOffset.QuadPart = volumeStartOffset.QuadPart +
|
|
UInt32x32To64(GET_STARTING_SECTOR(partitionTableEntry),
|
|
SectorSize);
|
|
|
|
//
|
|
// Set the VolumeStartSector to be the begining of the
|
|
// second partition (extended partition) because all of
|
|
// the offsets to the partition tables of the logical drives
|
|
// are relative to this extended partition.
|
|
//
|
|
|
|
if (primaryPartitionTable) {
|
|
volumeStartOffset = partitionTableOffset;
|
|
}
|
|
|
|
//
|
|
// There is only ever one link entry per partition table,
|
|
// exit the loop once it has been found.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// All the other partitions will be logical drives.
|
|
//
|
|
|
|
primaryPartitionTable = FALSE;
|
|
|
|
|
|
} while (partitionTableOffset.HighPart | partitionTableOffset.LowPart);
|
|
|
|
//
|
|
// Fill in the first field in the PartitionBuffer. This field indicates how
|
|
// many partition entries there are in the PartitionBuffer.
|
|
//
|
|
|
|
(*PartitionBuffer)->PartitionCount = ++partitionNumber;
|
|
|
|
if (!partitionNumber) {
|
|
|
|
//
|
|
// Zero out disk signature.
|
|
//
|
|
|
|
(*PartitionBuffer)->Signature = 0;
|
|
}
|
|
|
|
//
|
|
// Following is the super-floppy support. Super-floppies are removable media
|
|
// such as zip disks, which are expected to have an MBR. However some media have only
|
|
// a BPB, just like a floppy. In that case we need to detect the absence of the MBR,
|
|
// and instead emulate one single partition the size of the entire disk. The BPB information
|
|
// is used to properly fill the partition List fields (size, hidden sectors, etc).
|
|
// If a BPB is found, this function will return a partition table table with one
|
|
// entry (primary partition) and a partition count of 1.
|
|
//
|
|
|
|
|
|
|
|
if (bpbJumpByte==0xEB || bpbJumpByte==0xE9 ){
|
|
|
|
|
|
xHalGetPartialGeometry( DeviceObject,
|
|
&conventionalCylinders,
|
|
&diskSize );
|
|
|
|
|
|
if (diskSize == 0){
|
|
|
|
diskSize = bpbNumberOfSectors * bpbBytesPerSector;
|
|
}
|
|
|
|
if (diskSize > 0) {
|
|
|
|
//
|
|
// We check if the partition length, retrieved from the MBR, is less
|
|
// to the disk size we got from disk geometry. We saw some cases were
|
|
// format had created a larger than the disk partition length. this has
|
|
// not been reporduced but for compatibility reasons, we allow up to 20MB
|
|
// larger partitionLength...
|
|
//
|
|
|
|
if (partitionStartingOffset > diskSize || partitionLength > (diskSize + 0x1400000)) {
|
|
|
|
partitionInfo = &(*PartitionBuffer)->PartitionEntry[0];
|
|
|
|
partitionInfo->RewritePartition = FALSE;
|
|
partitionInfo->RecognizedPartition = TRUE;
|
|
partitionInfo->PartitionType = PARTITION_FAT_16;
|
|
partitionInfo->BootIndicator = FALSE;
|
|
|
|
partitionInfo->HiddenSectors = 0;
|
|
|
|
partitionInfo->StartingOffset.QuadPart = 0;
|
|
|
|
partitionInfo->PartitionLength.QuadPart = diskSize -
|
|
partitionInfo->StartingOffset.QuadPart;
|
|
|
|
(*PartitionBuffer)->PartitionCount = 1;
|
|
(*PartitionBuffer)->Signature = 1;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Deallocate read buffer if it was allocated it.
|
|
//
|
|
if (readBuffer != NULL) {
|
|
ExFreePool( readBuffer );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
xHalIoSetPartitionInformation(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG SectorSize,
|
|
IN ULONG PartitionNumber,
|
|
IN ULONG PartitionType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when a disk device driver is asked to set the
|
|
partition type in a partition table entry via an I/O control code. This
|
|
control code is generally issued by the format utility just after it
|
|
has formatted the partition. The format utility performs the I/O control
|
|
function on the partition and the driver passes the address of the base
|
|
physical device object and the number of the partition associated with
|
|
the device object that the format utility has open. If this routine
|
|
returns success, then the disk driver should updates its notion of the
|
|
partition type for this partition in its device extension.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the base physical device object for the device
|
|
on which the partition type is to be set.
|
|
|
|
SectorSize - Supplies the size of a sector on the disk in bytes.
|
|
|
|
PartitionNumber - Specifies the partition number on the device whose
|
|
partition type is to be changed.
|
|
|
|
PartitionType - Specifies the new type for the partition.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the operation.
|
|
|
|
Notes:
|
|
|
|
This routine is synchronous. Therefore, it MUST be invoked by the disk
|
|
driver's dispatch routine, or by a disk driver's thread. Likewise, all
|
|
users, FSP threads, etc., must be prepared to enter a wait state when
|
|
issuing the I/O control code to set the partition type for the device.
|
|
|
|
Note also that this routine assumes that the partition number passed
|
|
in by the disk driver actually exists since the driver itself supplies
|
|
this parameter.
|
|
|
|
Finally, note that this routine may NOT be invoked at APC_LEVEL. It
|
|
must be invoked at PASSIVE_LEVEL. This is due to the fact that this
|
|
routine uses a kernel event object to synchronize I/O completion on the
|
|
device. The event cannot be set to the signaled state without queueing
|
|
the I/O system's special kernel APC routine for I/O completion and
|
|
executing it. (This rules is a bit esoteric since it only holds true
|
|
if the device driver returns something other than STATUS_PENDING, which
|
|
it will probably never do.)
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#define GET_STARTING_SECTOR( p ) ( \
|
|
(ULONG) (p->StartingSectorLsb0) + \
|
|
(ULONG) (p->StartingSectorLsb1 << 8) + \
|
|
(ULONG) (p->StartingSectorMsb0 << 16) + \
|
|
(ULONG) (p->StartingSectorMsb1 << 24) )
|
|
|
|
PIRP irp;
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
LARGE_INTEGER partitionTableOffset;
|
|
LARGE_INTEGER volumeStartOffset;
|
|
PUCHAR buffer = (PUCHAR) NULL;
|
|
ULONG transferSize;
|
|
ULONG partitionNumber;
|
|
ULONG partitionEntry;
|
|
PPARTITION_DESCRIPTOR partitionTableEntry;
|
|
BOOLEAN primaryPartitionTable;
|
|
BOOLEAN foundEZHooker = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Begin by determining the size of the buffer required to read and write
|
|
// the partition information to/from the disk. This is done to ensure
|
|
// that at least 512 bytes are read, thereby guaranteeing that enough data
|
|
// is read to include an entire partition table. Note that this code
|
|
// assumes that the actual sector size of the disk (if less than 512
|
|
// bytes) is a multiple of 2, a
|
|
// fairly reasonable assumption.
|
|
//
|
|
|
|
if (SectorSize >= 512) {
|
|
transferSize = SectorSize;
|
|
} else {
|
|
transferSize = 512;
|
|
}
|
|
|
|
|
|
//
|
|
// Look to see if this is an EZDrive Disk. If it is then get the
|
|
// real parititon table at 1.
|
|
//
|
|
|
|
{
|
|
|
|
PVOID buff;
|
|
|
|
HalExamineMBR(
|
|
DeviceObject,
|
|
transferSize,
|
|
(ULONG)0x55,
|
|
&buff
|
|
);
|
|
|
|
if (buff) {
|
|
|
|
foundEZHooker = TRUE;
|
|
ExFreePool(buff);
|
|
partitionTableOffset.QuadPart = 512;
|
|
|
|
} else {
|
|
|
|
partitionTableOffset.QuadPart = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// The partitions in this primary partition have their start sector 0.
|
|
//
|
|
|
|
volumeStartOffset.QuadPart = 0;
|
|
|
|
//
|
|
// Indicate that the table being read and processed is the primary partition
|
|
// table.
|
|
//
|
|
|
|
primaryPartitionTable = TRUE;
|
|
|
|
//
|
|
// Initialize the number of partitions found thus far.
|
|
//
|
|
|
|
partitionNumber = 0;
|
|
|
|
//
|
|
// Allocate a buffer that will hold the read/write data.
|
|
//
|
|
|
|
buffer = ExAllocatePool( NonPagedPoolCacheAligned, PAGE_SIZE );
|
|
if (buffer == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Initialize a kernel event to use in synchronizing device requests
|
|
// with I/O completion.
|
|
//
|
|
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
|
|
|
//
|
|
// Read each partition table scanning for the partition table entry that
|
|
// the caller wishes to modify.
|
|
//
|
|
|
|
do {
|
|
|
|
//
|
|
// Read the record containing the partition table.
|
|
//
|
|
|
|
(VOID) KeResetEvent( &event );
|
|
|
|
irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
|
|
DeviceObject,
|
|
buffer,
|
|
transferSize,
|
|
&partitionTableOffset,
|
|
&event,
|
|
&ioStatus );
|
|
|
|
status = IoCallDriver( DeviceObject, irp );
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL );
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If EZDrive is hooking the MBR then we found the first partition table
|
|
// in sector 1 rather than 0. However that partition table is relative
|
|
// to sector zero. So, Even though we got it from one, reset the partition
|
|
// offset to 0.
|
|
//
|
|
|
|
if (foundEZHooker && (partitionTableOffset.QuadPart == 512)) {
|
|
|
|
partitionTableOffset.QuadPart = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Check for a valid Boot Record signature in the partition table
|
|
// record.
|
|
//
|
|
|
|
if (((PUSHORT) buffer)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE) {
|
|
status = STATUS_BAD_MASTER_BOOT_RECORD;
|
|
break;
|
|
}
|
|
|
|
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) buffer)[PARTITION_TABLE_OFFSET]);
|
|
|
|
//
|
|
// Scan the partition entries in this partition table to determine if
|
|
// any of the entries are the desired entry. Each entry in each
|
|
// table must be scanned in the same order as in IoReadPartitionTable
|
|
// so that the partition table entry cooresponding to the driver's
|
|
// notion of the partition number can be located.
|
|
//
|
|
|
|
for (partitionEntry = 1;
|
|
partitionEntry <= NUM_PARTITION_TABLE_ENTRIES;
|
|
partitionEntry++, partitionTableEntry++) {
|
|
|
|
|
|
//
|
|
// If the partition entry is empty or for an extended, skip it.
|
|
//
|
|
|
|
if ((partitionTableEntry->PartitionType == PARTITION_ENTRY_UNUSED) ||
|
|
IsContainerPartition(partitionTableEntry->PartitionType)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// A valid partition entry that is recognized has been located.
|
|
// Bump the count and check to see if this entry is the desired
|
|
// entry.
|
|
//
|
|
|
|
partitionNumber++;
|
|
|
|
if (partitionNumber == PartitionNumber) {
|
|
|
|
//
|
|
// This is the desired partition that is to be changed. Simply
|
|
// overwrite the partition type and write the entire partition
|
|
// buffer back out to the disk.
|
|
//
|
|
|
|
partitionTableEntry->PartitionType = (UCHAR) PartitionType;
|
|
|
|
(VOID) KeResetEvent( &event );
|
|
|
|
irp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE,
|
|
DeviceObject,
|
|
buffer,
|
|
transferSize,
|
|
&partitionTableOffset,
|
|
&event,
|
|
&ioStatus );
|
|
|
|
status = IoCallDriver( DeviceObject, irp );
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL );
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If all of the entries in the current buffer were scanned and the
|
|
// desired entry was not found, then continue. Otherwise, leave the
|
|
// routine.
|
|
//
|
|
|
|
if (partitionEntry <= NUM_PARTITION_TABLE_ENTRIES) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now scan the current buffer to locate an extended partition entry
|
|
// in the table so that its partition information can be read. There
|
|
// can only be one extended partition entry in each partition table,
|
|
// and it will point to the next table.
|
|
//
|
|
|
|
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) buffer)[PARTITION_TABLE_OFFSET]);
|
|
|
|
for (partitionEntry = 1;
|
|
partitionEntry <= NUM_PARTITION_TABLE_ENTRIES;
|
|
partitionEntry++, partitionTableEntry++) {
|
|
|
|
if (IsContainerPartition(partitionTableEntry->PartitionType)) {
|
|
|
|
//
|
|
// Obtain the address of the next partition table on the disk.
|
|
// This is the number of hidden sectors added to the beginning
|
|
// of the extended partition (in the case of logical drives),
|
|
// since all logical drives are relative to the extended
|
|
// partition. The starting offset of the volume will be zero
|
|
// if this is the primary partition table.
|
|
//
|
|
|
|
partitionTableOffset.QuadPart = volumeStartOffset.QuadPart +
|
|
UInt32x32To64(GET_STARTING_SECTOR(partitionTableEntry),
|
|
SectorSize);
|
|
|
|
//
|
|
// Set the starting offset of the volume to be the beginning of
|
|
// the second partition (the extended partition) because all of
|
|
// the offsets to the partition tables of the logical drives
|
|
// are relative to this extended partition.
|
|
//
|
|
|
|
if (primaryPartitionTable) {
|
|
volumeStartOffset = partitionTableOffset;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ensure that a partition entry was located that was an extended
|
|
// partition, otherwise the desired partition will never be found.
|
|
//
|
|
|
|
if (partitionEntry > NUM_PARTITION_TABLE_ENTRIES) {
|
|
status = STATUS_BAD_MASTER_BOOT_RECORD;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// All the other partitions will be logical drives.
|
|
//
|
|
|
|
primaryPartitionTable = FALSE;
|
|
|
|
} while (partitionNumber < PartitionNumber);
|
|
|
|
//
|
|
// If a data buffer was successfully allocated, deallocate it now.
|
|
//
|
|
|
|
if (buffer != NULL) {
|
|
ExFreePool( buffer );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
xHalIoWritePartitionTable(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG SectorSize,
|
|
IN ULONG SectorsPerTrack,
|
|
IN ULONG NumberOfHeads,
|
|
IN struct _DRIVE_LAYOUT_INFORMATION *PartitionBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine walks the disk writing the partition tables from
|
|
the entries in the partition list buffer for each partition.
|
|
|
|
Applications that create and delete partitions should issue a
|
|
IoReadPartitionTable call with the 'return recognized partitions'
|
|
boolean set to false to get a full description of the system.
|
|
|
|
Then the drive layout structure can be modified by the application to
|
|
reflect the new configuration of the disk and then is written back
|
|
to the disk using this routine.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to device object for this disk.
|
|
|
|
SectorSize - Sector size on the device.
|
|
|
|
SectorsPerTrack - Track size on the device.
|
|
|
|
NumberOfHeads - Same as tracks per cylinder.
|
|
|
|
PartitionBuffer - Pointer drive layout buffer.
|
|
|
|
Return Value:
|
|
|
|
The functional value is STATUS_SUCCESS if all writes are completed
|
|
without error.
|
|
|
|
--*/
|
|
|
|
{
|
|
typedef struct _PARTITION_TABLE {
|
|
PARTITION_INFORMATION PartitionEntry[4];
|
|
} PARTITION_TABLE, *PPARTITION_TABLE;
|
|
|
|
typedef struct _DISK_LAYOUT {
|
|
ULONG TableCount;
|
|
ULONG Signature;
|
|
PARTITION_TABLE PartitionTable[1];
|
|
} DISK_LAYOUT, *PDISK_LAYOUT;
|
|
|
|
typedef struct _PTE {
|
|
UCHAR ActiveFlag; // Bootable or not
|
|
UCHAR StartingTrack; // Not used
|
|
USHORT StartingCylinder; // Not used
|
|
UCHAR PartitionType; // 12 bit FAT, 16 bit FAT etc.
|
|
UCHAR EndingTrack; // Not used
|
|
USHORT EndingCylinder; // Not used
|
|
ULONG StartingSector; // Hidden sectors
|
|
ULONG PartitionLength; // Sectors in this partition
|
|
} PTE;
|
|
typedef PTE UNALIGNED *PPTE;
|
|
|
|
//
|
|
// This macro has the effect of Bit = log2(Data)
|
|
//
|
|
|
|
#define WHICH_BIT(Data, Bit) { \
|
|
for (Bit = 0; Bit < 32; Bit++) { \
|
|
if ((Data >> Bit) == 1) { \
|
|
break; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
ULONG writeSize;
|
|
PUSHORT writeBuffer = NULL;
|
|
PPTE partitionEntry;
|
|
PPARTITION_TABLE partitionTable;
|
|
CCHAR shiftCount;
|
|
LARGE_INTEGER partitionTableOffset;
|
|
LARGE_INTEGER nextRecordOffset;
|
|
ULONG partitionTableCount;
|
|
ULONG partitionEntryCount;
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PIRP irp;
|
|
BOOLEAN rewritePartition;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
LARGE_INTEGER tempInt;
|
|
BOOLEAN foundEZHooker = FALSE;
|
|
ULONG conventionalCylinders;
|
|
LONGLONG diskSize;
|
|
|
|
//
|
|
// Cast to a structure that is easier to use.
|
|
//
|
|
|
|
PDISK_LAYOUT diskLayout = (PDISK_LAYOUT) PartitionBuffer;
|
|
|
|
//
|
|
// Ensure that no one is calling this function illegally.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Determine the size of a write operation to ensure that at least 512
|
|
// bytes are written. This will guarantee that enough data is written to
|
|
// include an entire partition table. Note that this code assumes that
|
|
// the actual sector size of the disk (if less than 512 bytes) is a
|
|
// multiple of 2, a fairly reasonable assumption.
|
|
//
|
|
|
|
if (SectorSize >= 512) {
|
|
writeSize = SectorSize;
|
|
} else {
|
|
writeSize = 512;
|
|
}
|
|
|
|
xHalGetPartialGeometry( DeviceObject,
|
|
&conventionalCylinders,
|
|
&diskSize );
|
|
|
|
//
|
|
// Look to see if this is an EZDrive Disk. If it is then get the
|
|
// real partititon table at 1.
|
|
//
|
|
|
|
{
|
|
|
|
PVOID buff;
|
|
|
|
HalExamineMBR(
|
|
DeviceObject,
|
|
writeSize,
|
|
(ULONG)0x55,
|
|
&buff
|
|
);
|
|
|
|
if (buff) {
|
|
|
|
foundEZHooker = TRUE;
|
|
ExFreePool(buff);
|
|
partitionTableOffset.QuadPart = 512;
|
|
|
|
} else {
|
|
|
|
partitionTableOffset.QuadPart = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize starting variables.
|
|
//
|
|
|
|
nextRecordOffset.QuadPart = 0;
|
|
|
|
//
|
|
// Calculate shift count for converting between byte and sector.
|
|
//
|
|
|
|
WHICH_BIT( SectorSize, shiftCount );
|
|
|
|
//
|
|
// Convert partition count to partition table or boot sector count.
|
|
//
|
|
|
|
diskLayout->TableCount =
|
|
(PartitionBuffer->PartitionCount +
|
|
NUM_PARTITION_TABLE_ENTRIES - 1) /
|
|
NUM_PARTITION_TABLE_ENTRIES;
|
|
|
|
//
|
|
// Allocate a buffer for the sector writes.
|
|
//
|
|
|
|
writeBuffer = ExAllocatePool( NonPagedPoolCacheAligned, PAGE_SIZE );
|
|
|
|
if (writeBuffer == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Point to the partition table entries in write buffer.
|
|
//
|
|
|
|
partitionEntry = (PPTE) &writeBuffer[PARTITION_TABLE_OFFSET];
|
|
|
|
for (partitionTableCount = 0;
|
|
partitionTableCount < diskLayout->TableCount;
|
|
partitionTableCount++) {
|
|
|
|
UCHAR partitionType;
|
|
|
|
//
|
|
// the first partition table is in the mbr (physical sector 0).
|
|
// other partition tables are in ebr's within the extended partition.
|
|
//
|
|
|
|
BOOLEAN mbr = (BOOLEAN) (!partitionTableCount);
|
|
LARGE_INTEGER extendedPartitionOffset;
|
|
|
|
//
|
|
// Read the boot record that's already there into the write buffer
|
|
// and save its boot code area if the signature is valid. This way
|
|
// we don't clobber any boot code that might be there already.
|
|
//
|
|
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
|
|
|
irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
|
|
DeviceObject,
|
|
writeBuffer,
|
|
writeSize,
|
|
&partitionTableOffset,
|
|
&event,
|
|
&ioStatus );
|
|
|
|
status = IoCallDriver( DeviceObject, irp );
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If EZDrive is hooking the MBR then we found the first partition table
|
|
// in sector 1 rather than 0. However that partition table is relative
|
|
// to sector zero. So, Even though we got it from one, reset the partition
|
|
// offset to 0.
|
|
//
|
|
|
|
if (foundEZHooker && (partitionTableOffset.QuadPart == 512)) {
|
|
|
|
partitionTableOffset.QuadPart = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Write signature to last word of boot sector.
|
|
//
|
|
|
|
writeBuffer[BOOT_SIGNATURE_OFFSET] = BOOT_RECORD_SIGNATURE;
|
|
|
|
//
|
|
// Write NTFT disk signature if it changed and this is the MBR.
|
|
//
|
|
|
|
rewritePartition = FALSE;
|
|
if (partitionTableOffset.QuadPart == 0) {
|
|
|
|
if (((PULONG)writeBuffer)[PARTITION_TABLE_OFFSET/2-1] !=
|
|
PartitionBuffer->Signature) {
|
|
|
|
((PULONG) writeBuffer)[PARTITION_TABLE_OFFSET/2-1] =
|
|
PartitionBuffer->Signature;
|
|
rewritePartition = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get pointer to first partition table.
|
|
//
|
|
|
|
partitionTable = &diskLayout->PartitionTable[partitionTableCount];
|
|
|
|
//
|
|
// Walk table to determine whether this boot record has changed
|
|
// and update partition table in write buffer in case it needs
|
|
// to be written out to disk.
|
|
//
|
|
|
|
for (partitionEntryCount = 0;
|
|
partitionEntryCount < NUM_PARTITION_TABLE_ENTRIES;
|
|
partitionEntryCount++) {
|
|
|
|
partitionType =
|
|
partitionTable->PartitionEntry[partitionEntryCount].PartitionType;
|
|
|
|
//
|
|
// If the rewrite ISN'T true then copy then just leave the data
|
|
// alone that is in the on-disk table.
|
|
//
|
|
|
|
if (partitionTable->PartitionEntry[partitionEntryCount].RewritePartition) {
|
|
|
|
//
|
|
// This boot record needs to be written back to disk.
|
|
//
|
|
|
|
rewritePartition = TRUE;
|
|
|
|
//
|
|
// Copy partition type from user buffer to write buffer.
|
|
//
|
|
|
|
partitionEntry[partitionEntryCount].PartitionType =
|
|
partitionTable->PartitionEntry[partitionEntryCount].PartitionType;
|
|
|
|
//
|
|
// Copy the partition active flag.
|
|
//
|
|
|
|
partitionEntry[partitionEntryCount].ActiveFlag =
|
|
partitionTable->PartitionEntry[partitionEntryCount].BootIndicator ?
|
|
(UCHAR) PARTITION_ACTIVE_FLAG : (UCHAR) 0;
|
|
|
|
if (partitionType != PARTITION_ENTRY_UNUSED) {
|
|
|
|
LARGE_INTEGER sectorOffset;
|
|
|
|
//
|
|
// Calculate partition offset.
|
|
// If in the mbr or the entry is not a link entry, partition offset
|
|
// is sectors past last boot record. Otherwise (not in the mbr and
|
|
// entry is a link entry), partition offset is sectors past start
|
|
// of extended partition.
|
|
//
|
|
|
|
if (mbr || !IsContainerPartition(partitionType)) {
|
|
tempInt.QuadPart = partitionTableOffset.QuadPart;
|
|
} else {
|
|
tempInt.QuadPart = extendedPartitionOffset.QuadPart;
|
|
}
|
|
|
|
sectorOffset.QuadPart =
|
|
partitionTable->PartitionEntry[partitionEntryCount].StartingOffset.QuadPart -
|
|
tempInt.QuadPart;
|
|
|
|
tempInt.QuadPart = sectorOffset.QuadPart >> shiftCount;
|
|
partitionEntry[partitionEntryCount].StartingSector = tempInt.LowPart;
|
|
|
|
//
|
|
// Calculate partition length.
|
|
//
|
|
|
|
tempInt.QuadPart = partitionTable->PartitionEntry[partitionEntryCount].PartitionLength.QuadPart >> shiftCount;
|
|
partitionEntry[partitionEntryCount].PartitionLength = tempInt.LowPart;
|
|
|
|
//
|
|
// Fill in CHS values
|
|
//
|
|
|
|
HalpCalculateChsValues(
|
|
&partitionTable->PartitionEntry[partitionEntryCount].StartingOffset,
|
|
&partitionTable->PartitionEntry[partitionEntryCount].PartitionLength,
|
|
shiftCount,
|
|
SectorsPerTrack,
|
|
NumberOfHeads,
|
|
conventionalCylinders,
|
|
(PPARTITION_DESCRIPTOR) &partitionEntry[partitionEntryCount]);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Zero out partition entry fields in case an entry
|
|
// was deleted.
|
|
//
|
|
|
|
partitionEntry[partitionEntryCount].StartingSector = 0;
|
|
partitionEntry[partitionEntryCount].PartitionLength = 0;
|
|
partitionEntry[partitionEntryCount].StartingTrack = 0;
|
|
partitionEntry[partitionEntryCount].EndingTrack = 0;
|
|
partitionEntry[partitionEntryCount].StartingCylinder = 0;
|
|
partitionEntry[partitionEntryCount].EndingCylinder = 0;
|
|
}
|
|
|
|
}
|
|
|
|
if (IsContainerPartition(partitionType)) {
|
|
|
|
//
|
|
// Save next record offset.
|
|
//
|
|
|
|
nextRecordOffset =
|
|
partitionTable->PartitionEntry[partitionEntryCount].StartingOffset;
|
|
}
|
|
|
|
} // end for partitionEntryCount ...
|
|
|
|
if (rewritePartition == TRUE) {
|
|
|
|
rewritePartition = FALSE;
|
|
//
|
|
// Create a notification event object to be used while waiting for
|
|
// the write request to complete.
|
|
//
|
|
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
|
|
|
if (foundEZHooker && (partitionTableOffset.QuadPart == 0)) {
|
|
|
|
partitionTableOffset.QuadPart = 512;
|
|
|
|
}
|
|
irp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE,
|
|
DeviceObject,
|
|
writeBuffer,
|
|
writeSize,
|
|
&partitionTableOffset,
|
|
&event,
|
|
&ioStatus );
|
|
|
|
status = IoCallDriver( DeviceObject, irp );
|
|
|
|
if (status == STATUS_PENDING) {
|
|
(VOID) KeWaitForSingleObject( &event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER) NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS( status )) {
|
|
break;
|
|
}
|
|
|
|
|
|
if (foundEZHooker && (partitionTableOffset.QuadPart == 512)) {
|
|
|
|
partitionTableOffset.QuadPart = 0;
|
|
|
|
}
|
|
|
|
} // end if (reWrite ...
|
|
|
|
//
|
|
// Update partitionTableOffset to next boot record offset
|
|
//
|
|
|
|
partitionTableOffset = nextRecordOffset;
|
|
if(mbr) {
|
|
extendedPartitionOffset = nextRecordOffset;
|
|
}
|
|
|
|
} // end for partitionTableCount ...
|
|
|
|
//
|
|
// Deallocate write buffer if it was allocated it.
|
|
//
|
|
|
|
if (writeBuffer != NULL) {
|
|
ExFreePool( writeBuffer );
|
|
}
|
|
|
|
return status;
|
|
}
|