diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/fstub/drivesup.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/fstub/drivesup.c')
-rw-r--r-- | private/ntos/fstub/drivesup.c | 3715 |
1 files changed, 3715 insertions, 0 deletions
diff --git a/private/ntos/fstub/drivesup.c b/private/ntos/fstub/drivesup.c new file mode 100644 index 000000000..57b1b76a8 --- /dev/null +++ b/private/ntos/fstub/drivesup.c @@ -0,0 +1,3715 @@ + +/*++ + +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; +} |