/*++ 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 '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 '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; }