summaryrefslogtreecommitdiffstats
path: root/private/ntos/fstub
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/fstub
downloadNT4.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')
-rw-r--r--private/ntos/fstub/dirs24
-rw-r--r--private/ntos/fstub/drivesup.c3715
-rw-r--r--private/ntos/fstub/haldisp.h138
-rw-r--r--private/ntos/fstub/halfnc.c174
-rw-r--r--private/ntos/fstub/mp/makefile6
-rw-r--r--private/ntos/fstub/mp/sources29
-rw-r--r--private/ntos/fstub/sources.inc43
-rw-r--r--private/ntos/fstub/up/makefile6
-rw-r--r--private/ntos/fstub/up/sources27
9 files changed, 4162 insertions, 0 deletions
diff --git a/private/ntos/fstub/dirs b/private/ntos/fstub/dirs
new file mode 100644
index 000000000..a2a38f0fd
--- /dev/null
+++ b/private/ntos/fstub/dirs
@@ -0,0 +1,24 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=up
+
+OPTIONAL_DIRS=mp
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;
+}
diff --git a/private/ntos/fstub/haldisp.h b/private/ntos/fstub/haldisp.h
new file mode 100644
index 000000000..a1c5ed5ea
--- /dev/null
+++ b/private/ntos/fstub/haldisp.h
@@ -0,0 +1,138 @@
+/*++ BUILD Version: 0001
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ haldisp.h
+
+Abstract:
+
+ This module contains the private structure definitions and APIs used by
+ the NT haldisp
+
+Author:
+
+
+Revision History:
+
+
+--*/
+
+
+//
+// Function prototypes
+//
+
+NTSTATUS
+xHalQuerySystemInformation(
+ IN HAL_QUERY_INFORMATION_CLASS InformationClass,
+ IN ULONG BufferSize,
+ OUT PVOID Buffer,
+ OUT PULONG ReturnedLength
+ );
+
+NTSTATUS
+xHalSetSystemInformation(
+ IN HAL_SET_INFORMATION_CLASS InformationClass,
+ IN ULONG BufferSize,
+ OUT PVOID Buffer
+ );
+
+NTSTATUS
+xHalQueryBusSlots(
+ IN PBUS_HANDLER BusHandler,
+ IN ULONG BufferSize,
+ OUT PULONG SlotNumbers,
+ OUT PULONG ReturnedLength
+ );
+
+NTSTATUS
+xHalDeviceControl(
+ IN PDEVICE_HANDLER_OBJECT DeviceHandler,
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ULONG ControlCode,
+ IN PVOID Buffer OPTIONAL,
+ IN OUT PULONG BufferLength OPTIONAL,
+ IN PVOID Context,
+ IN PDEVICE_CONTROL_COMPLETION CompletionRoutine
+ );
+
+NTSTATUS
+xHalRegisterBusHandler(
+ IN INTERFACE_TYPE InterfaceType,
+ IN BUS_DATA_TYPE ConfigurationSpace,
+ IN ULONG BusNumber,
+ IN INTERFACE_TYPE ParentBusType,
+ IN ULONG ParentBusNumber,
+ IN ULONG SizeofBusExtensionData,
+ IN PINSTALL_BUS_HANDLER InstallBusHandlers,
+ OUT PBUS_HANDLER *BusHandler
+ );
+
+NTSTATUS
+xHalSuspendHibernateSystem (
+ IN PTIME_FIELDS ResumeTime OPTIONAL,
+ IN PHIBERNATE_CALLBACK SystemCallback OPTIONAL
+ );
+
+
+PBUS_HANDLER
+FASTCALL
+xHalHandlerForBus (
+ IN INTERFACE_TYPE InterfaceType,
+ IN ULONG BusNumber
+ );
+
+VOID
+FASTCALL
+xHalExamineMBR(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ULONG SectorSize,
+ IN ULONG MBRTypeIdentifier,
+ OUT PVOID *Buffer
+ );
+
+VOID
+FASTCALL
+xHalIoAssignDriveLetters(
+ IN struct _LOADER_PARAMETER_BLOCK *LoaderBlock,
+ IN PSTRING NtDeviceName,
+ OUT PUCHAR NtSystemPath,
+ OUT PSTRING NtSystemPathString
+ );
+
+NTSTATUS
+FASTCALL
+xHalIoReadPartitionTable(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ULONG SectorSize,
+ IN BOOLEAN ReturnRecognizedPartitions,
+ OUT struct _DRIVE_LAYOUT_INFORMATION **PartitionBuffer
+ );
+
+NTSTATUS
+FASTCALL
+xHalIoSetPartitionInformation(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ULONG SectorSize,
+ IN ULONG PartitionNumber,
+ IN ULONG PartitionType
+ );
+
+NTSTATUS
+FASTCALL
+xHalIoWritePartitionTable(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ULONG SectorSize,
+ IN ULONG SectorsPerTrack,
+ IN ULONG NumberOfHeads,
+ IN struct _DRIVE_LAYOUT_INFORMATION *PartitionBuffer
+ );
+
+
+VOID
+FASTCALL
+xHalReferenceHandler (
+ IN PBUS_HANDLER Handler
+ );
diff --git a/private/ntos/fstub/halfnc.c b/private/ntos/fstub/halfnc.c
new file mode 100644
index 000000000..f83c0be9c
--- /dev/null
+++ b/private/ntos/fstub/halfnc.c
@@ -0,0 +1,174 @@
+/*++
+
+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:
+
+--*/
+
+#include "ntos.h"
+#include "haldisp.h"
+
+HAL_DISPATCH HalDispatchTable = {
+ HAL_DISPATCH_VERSION,
+ xHalQuerySystemInformation,
+ xHalSetSystemInformation,
+ xHalQueryBusSlots,
+ xHalDeviceControl,
+ xHalExamineMBR,
+ xHalIoAssignDriveLetters,
+ xHalIoReadPartitionTable,
+ xHalIoSetPartitionInformation,
+ xHalIoWritePartitionTable,
+ xHalHandlerForBus, // HalReferenceHandlerByBus
+ xHalReferenceHandler, // HalReferenceBusHandler
+ xHalReferenceHandler // HalDereferenceBusHandler
+ };
+
+HAL_PRIVATE_DISPATCH HalPrivateDispatchTable = {
+ HAL_PRIVATE_DISPATCH_VERSION,
+ xHalHandlerForBus,
+ xHalHandlerForBus,
+ NULL, // reserved
+ xHalRegisterBusHandler,
+ NULL, // HalHibernateProcessor
+ xHalSuspendHibernateSystem,
+ NULL // HalpSuspendHibernateSystem;
+ };
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, xHalQuerySystemInformation)
+#pragma alloc_text(PAGE, xHalSetSystemInformation)
+#pragma alloc_text(PAGE, xHalQueryBusSlots)
+#pragma alloc_text(PAGE, xHalRegisterBusHandler)
+#endif
+
+
+//
+// Global dispatch table for HAL apis
+//
+
+
+//
+// Stub handlers for hals which don't provide the above functions
+//
+
+NTSTATUS
+xHalQuerySystemInformation(
+ IN HAL_QUERY_INFORMATION_CLASS InformationClass,
+ IN ULONG BufferSize,
+ OUT PVOID Buffer,
+ OUT PULONG ReturnedLength
+ )
+{
+ PAGED_CODE ();
+ return STATUS_INVALID_LEVEL;
+}
+
+NTSTATUS
+xHalSetSystemInformation(
+ IN HAL_SET_INFORMATION_CLASS InformationClass,
+ IN ULONG BufferSize,
+ OUT PVOID Buffer
+ )
+{
+ PAGED_CODE ();
+ return STATUS_INVALID_LEVEL;
+}
+
+NTSTATUS
+xHalQueryBusSlots(
+ IN PBUS_HANDLER BusHandler,
+ IN ULONG BufferSize,
+ OUT PULONG SlotNumbers,
+ OUT PULONG ReturnedLength
+ )
+{
+ PAGED_CODE ();
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+NTSTATUS
+xHalDeviceControl(
+ IN PDEVICE_HANDLER_OBJECT DeviceHandler,
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ULONG ControlCode,
+ IN PVOID Buffer OPTIONAL,
+ IN OUT PULONG BufferLength OPTIONAL,
+ IN PVOID CompletionContext,
+ IN PDEVICE_CONTROL_COMPLETION CompletionRoutine
+ )
+{
+ DEVICE_CONTROL_CONTEXT Context;
+
+ if (CompletionRoutine) {
+ Context.Status = STATUS_NOT_SUPPORTED;
+ Context.DeviceHandler = DeviceHandler;
+ Context.ControlCode = ControlCode;
+ Context.Buffer = Buffer;
+ Context.BufferLength = BufferLength;
+
+ CompletionRoutine (&Context);
+ }
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+NTSTATUS
+xHalRegisterBusHandler(
+ IN INTERFACE_TYPE InterfaceType,
+ IN BUS_DATA_TYPE ConfigurationSpace,
+ IN ULONG BusNumber,
+ IN INTERFACE_TYPE ParentBusType,
+ IN ULONG ParentBusNumber,
+ IN ULONG SizeofBusExtensionData,
+ IN PINSTALL_BUS_HANDLER InstallBusHandler,
+ OUT PBUS_HANDLER *BusHandler
+ )
+{
+ PAGED_CODE ();
+ return STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS
+xHalSuspendHibernateSystem (
+ IN PTIME_FIELDS ResumeTime OPTIONAL,
+ IN PHIBERNATE_CALLBACK SystemCallback OPTIONAL
+ )
+{
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+
+PBUS_HANDLER
+FASTCALL
+xHalHandlerForBus (
+ IN INTERFACE_TYPE InterfaceType,
+ IN ULONG BusNumber
+ )
+{
+ return NULL;
+}
+
+VOID
+FASTCALL
+xHalReferenceHandler (
+ IN PBUS_HANDLER Handler
+ )
+{
+}
diff --git a/private/ntos/fstub/mp/makefile b/private/ntos/fstub/mp/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/fstub/mp/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/fstub/mp/sources b/private/ntos/fstub/mp/sources
new file mode 100644
index 000000000..dbeb18d62
--- /dev/null
+++ b/private/ntos/fstub/mp/sources
@@ -0,0 +1,29 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+NT_UP=0
+
+TARGETPATH=..\..\mpobj
+
+!include ..\sources.inc
diff --git a/private/ntos/fstub/sources.inc b/private/ntos/fstub/sources.inc
new file mode 100644
index 000000000..6d767234a
--- /dev/null
+++ b/private/ntos/fstub/sources.inc
@@ -0,0 +1,43 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=fstub
+
+TARGETNAME=fstub
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\inc
+MIPS_OPTIONS=-nodwalign
+GPSIZE=32
+
+C_DEFINES=$(C_DEFINES) -D_NTSYSTEM_
+
+SOURCES=..\halfnc.c \
+ ..\drivesup.c
+
+NTTEST=
+OPTIONAL_NTTEST=tio
+
+SOURCES_USED=..\sources.inc
diff --git a/private/ntos/fstub/up/makefile b/private/ntos/fstub/up/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/fstub/up/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/fstub/up/sources b/private/ntos/fstub/up/sources
new file mode 100644
index 000000000..6dca9c583
--- /dev/null
+++ b/private/ntos/fstub/up/sources
@@ -0,0 +1,27 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+TARGETPATH=..\..\obj
+
+!include ..\sources.inc