/*++ Copyright (c) 2000 Microsoft Corporation Module Name: sppart3.c Abstract: Partitioning module for disks in textmode setup. Contains functions that initialize the in memory data structures, representing the extents on the disk. Author: Matt Holle (matth) 1-December-1999 Revision History: Vijay Jayaseelan (vijayj) 2-April-2000 - Clean up - Added lookup and prompting for system partition on ARC systems - Added on disk ordinals for MBR disks --*/ #include "spprecmp.h" #pragma hdrstop #include #include #include #include #include "sppart3.h" #define MAX_NTPATH_LENGTH (2048) #define SUGGESTED_SYSTEMPARTIION_SIZE_MB (100) #define SUGGESTED_INSTALL_PARTITION_SIZE_MB (4*1024) extern BOOLEAN ConsoleRunning; extern BOOLEAN ForceConsole; // // Debugging Macros // //#define PERF_STATS 1 //#define TESTING_SYSTEM_PARTITION 1 NTSTATUS SpPtnInitializeDiskDrive( IN ULONG DiskId ) /*++ Routine Description: Initializes the in memory disk region structures for the given disk number. Arguments: DiskId : Disk ID Return Value: STATUS_SUCCESS if successful otherwise appropriate error code --*/ { NTSTATUS Status; #ifdef PERF_STATS LARGE_INTEGER StartTime, EndTime; ULONGLONG Diff; KeQuerySystemTime(&StartTime); #endif // // Send the event // SendSetupProgressEvent(PartitioningEvent, ScanDiskEvent, &DiskId); // // It would have been better to just have a pointer to a list // of PDISK_REGIONs off of HARD_DISK, but for some reason, someone // long ago decided to also maintain a list of PARTITIONED_DISK, which // *does* contain a list of PDISK_REGIONs. I'm not sure of the use // of maintaining both HARD_DISKs and PARTITIONED_DISKs, but that's // the way the data structures are set, so we'll use those. // // But it doesn't end there. Rather than assuming that HardDisk[i] // is describing the same disk as PartitionedDisks[i], we'll // keep a pointer out of PartitionedDisks[i] that points to the // corresponding HardDisk entry. Oh well... // PartitionedDisks[DiskId].HardDisk = &HardDisks[DiskId]; // // Initialize structures that are based on the partition tables. // Status = SpPtnInitializeDiskAreas(DiskId); // // Now we need to fill in additional Region structures // to represent empty space on the disk. For example, assume // we have 2 partitions on the disk, but there's empty space // in between: // partition1: 0 - 200 sectors // // partition2: 500 - 1000 sectors // // I need to create another Region structure to represent // this empty space (ensuring that it's marked as unpartitioned. // This will allow me to present a nice UI to the user when it's // time to ask for input. // // // Sort the Partitions based on their location on the disk. // if( NT_SUCCESS(Status) ) { Status = SpPtnSortDiskAreas(DiskId); // // Create place holders for all empty spaces on the disk. // if( NT_SUCCESS(Status) ) { Status = SpPtnFillDiskFreeSpaceAreas(DiskId); if (NT_SUCCESS(Status)) { // // Mark logical drive's and its container, if any. // Status = SpPtnMarkLogicalDrives(DiskId); } } } #ifdef PERF_STATS KeQuerySystemTime(&EndTime); Diff = EndTime.QuadPart - StartTime.QuadPart; Diff /= 1000000; KdPrint(("SETUP:SpPtInitializeDiskDrive(%d) took %I64d Secs\n", DiskId, Diff)); #endif SpPtDumpDiskDriveInformation(TRUE); return Status; } NTSTATUS SpPtnInitializeDiskDrives( VOID ) /*++ Routine Description: Initializes all the disk drive's in memory data structure representing the disk regions (extents) Arguments: None Return Value: STATUS_SUCCESS if successful other wise appropriate error code. --*/ { ULONG disk; NTSTATUS Status = STATUS_SUCCESS; NTSTATUS ErrStatus = STATUS_SUCCESS; // // If there are no hard disks, bail now. // if(!HardDiskCount) { #if defined(REMOTE_BOOT) // // If this is a diskless remote boot setup, it's OK for there to be // no hard disks. Otherwise, this is a fatal error. // if (!RemoteBootSetup || RemoteInstallSetup) #endif // defined(REMOTE_BOOT) { SpDisplayScreen(SP_SCRN_NO_HARD_DRIVES,3,HEADER_HEIGHT+1); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE,SP_STAT_F3_EQUALS_EXIT,0); SpInputDrain(); while(SpInputGetKeypress() != KEY_F3); SpDone(0,FALSE,TRUE); } return STATUS_SUCCESS; } CLEAR_CLIENT_SCREEN(); // // Initialize all the RAW disks to platform specific // default disk styles // for(disk=0, Status = STATUS_SUCCESS; (disk < HardDiskCount); disk++) { if (SPPT_IS_RAW_DISK(disk) && SPPT_IS_BLANK_DISK(disk)) { PHARD_DISK HardDisk = SPPT_GET_HARDDISK(disk); PARTITION_STYLE Style = SPPT_DEFAULT_PARTITION_STYLE; // // Removable Media are always MBR (so don't bother) // if (HardDisk->Characteristics & FILE_REMOVABLE_MEDIA) { continue; } Status = SpPtnInitializeDiskStyle(disk, Style, NULL); if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP:SpPtnInitializeDiskStyle(%d) failed with" " %lx \n", disk, Status)); } } } // // Allocate an array for the partitioned disk descriptors. // PartitionedDisks = SpMemAlloc(HardDiskCount * sizeof(PARTITIONED_DISK)); if(!PartitionedDisks) { return(STATUS_NO_MEMORY); } RtlZeroMemory( PartitionedDisks, HardDiskCount * sizeof(PARTITIONED_DISK) ); // // Unlock the floppy if we booted off the ls-120 media // SpPtnUnlockDevice(L"\\device\\floppy0"); // // Collect information about each partition. // for(disk=0, Status = ErrStatus = STATUS_SUCCESS; (disk < HardDiskCount); disk++) { // // Initialize the region structure for the given // disk // Status = SpPtnInitializeDiskDrive(disk); // // TBD - In remote boot case the disk needs to have // a valid signature. I am assuming that setupldr // would have stamped the signature when booting off // the harddisk // // // Save of the last error // if (!NT_SUCCESS(Status)) ErrStatus = Status; } #if defined(_IA64_) // // Go and figure out the ESP partitions and // initialize the MSR partitions on valid GPT // disks // if (SpIsArc() && !SpDrEnabled()) { if (!ValidArcSystemPartition) { // // Create a system partition // Status = SpPtnCreateESP(TRUE); } // // Initialize the GPT disks, to have MSR // partition // Status = SpPtnInitializeGPTDisks(); } #endif return ErrStatus; } NTSTATUS SpPtnInitializeDiskAreas( IN ULONG DiskNumber ) /*++ Routine Description: Examine the disk for partitioning information and fill in our partition descriptors. We'll ask the volume manager for a list of partitions and fill in our descriptors from the information he provided us. Arguments: DiskNumber - supplies the disk number of the disk whose partitions we want to inspect for determining their types. Return Value: NTSTATUS. If all goes well, we should be returing STATUS_SUCCESS. --*/ { NTSTATUS Status = STATUS_SUCCESS; PDRIVE_LAYOUT_INFORMATION_EX DriveLayoutEx; WCHAR DevicePath[(sizeof(DISK_DEVICE_NAME_BASE)+sizeof(L"000"))/sizeof(WCHAR)]; HANDLE Handle; IO_STATUS_BLOCK IoStatusBlock; PDISK_REGION pDiskRegion = NULL; PFILE_FS_ATTRIBUTE_INFORMATION pFSInfo = NULL; PFILE_FS_SIZE_INFORMATION pSizeInfo = NULL; PFILE_FS_VOLUME_INFORMATION pLabelInfo = NULL; PWCHAR MyTempBuffer = NULL; ULONG DriveLayoutSize, i, r; PWSTR LocalSourceFiles[1] = { LocalSourceDirectory }; PHARD_DISK Disk = NULL; PPARTITIONED_DISK PartDisk = NULL; ULONGLONG *NewPartitions = NULL; ULONG NewPartitionCount; Disk = SPPT_GET_HARDDISK(DiskNumber); PartDisk = SPPT_GET_PARTITIONED_DISK(DiskNumber); // // Give the user some indication of what we're doing. // SpDisplayStatusText( SP_STAT_EXAMINING_DISK_N, DEFAULT_STATUS_ATTRIBUTE, Disk->Description); // // If we are updating the local source region disk then // make sure we invalidate LocalSourceRegion // if (LocalSourceRegion && (LocalSourceRegion->DiskNumber == DiskNumber)) { LocalSourceRegion = NULL; } // // Save off the new partitions created // NewPartitionCount = SpPtnCountPartitionsByFSType(DiskNumber, FilesystemNewlyCreated); if (NewPartitionCount) { PDISK_REGION NewRegion = SPPT_GET_PRIMARY_DISK_REGION(DiskNumber); ULONG Index; NewPartitions = (PULONGLONG) SpMemAlloc(sizeof(ULONGLONG) * NewPartitionCount); if (!NewPartitions) { return STATUS_NO_MEMORY; } RtlZeroMemory(NewPartitions, sizeof(ULONGLONG) * NewPartitionCount); Index = 0; while (NewRegion && (Index < NewPartitionCount)) { if (SPPT_IS_REGION_PARTITIONED(NewRegion) && !SPPT_IS_REGION_MARKED_DELETE(NewRegion) && (NewRegion->Filesystem == FilesystemNewlyCreated)) { NewPartitions[Index] = NewRegion->StartSector; Index++; } NewRegion = NewRegion->Next; } } // // Free the old regions we allocated, if there are any // SpPtnFreeDiskRegions(DiskNumber); // // ============================ // // Open a handle to this hard disk // // ============================ // // // Create a device path (NT style!) that will describe this disk. This // will be of the form: \Device\Harddisk0 // swprintf( DevicePath, L"\\Device\\Harddisk%u", DiskNumber ); // // Open partition 0 on this disk.. // Status = SpOpenPartition0( DevicePath, &Handle, FALSE ); if(!NT_SUCCESS(Status)) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: unable to open partition0 on device %ws (%lx)\n", DevicePath, Status )); if (NewPartitions) { SpMemFree(NewPartitions); } return( Status ); } // // ============================ // // Load the drive layout information. // // ============================ // // // Get the disk's layout information. We aren't // sure how big of a buffer we need, so brute-force it. // DriveLayoutSize = 0; DriveLayoutEx = NULL; Status = STATUS_BUFFER_TOO_SMALL; while ((Status == STATUS_BUFFER_TOO_SMALL) || (Status == STATUS_BUFFER_OVERFLOW)) { if (DriveLayoutEx) SpMemFree(DriveLayoutEx); DriveLayoutSize += 1024; DriveLayoutEx = SpMemAlloc( DriveLayoutSize ); if(!DriveLayoutEx) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: SpMemAlloc failed!\n" )); if (NewPartitions) { SpMemFree(NewPartitions); } return (STATUS_NO_MEMORY); } RtlZeroMemory( DriveLayoutEx, DriveLayoutSize ); // // Attempt to get the disk's partition information. // Status = ZwDeviceIoControlFile( Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, DriveLayoutEx, DriveLayoutSize ); } if(!NT_SUCCESS(Status)) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: unable to query IOCTL_DISK_GET_DRIVE_LAYOUT_EX on device %ws (%lx)\n", DevicePath, Status )); if (NewPartitions) { SpMemFree(NewPartitions); } if (DriveLayoutEx) SpMemFree(DriveLayoutEx); if (Handle != INVALID_HANDLE_VALUE) ZwClose(Handle); return ( Status ); } // // What kind of disk is this? // switch (DriveLayoutEx->PartitionStyle) { case PARTITION_STYLE_GPT: Disk->FormatType = DISK_FORMAT_TYPE_GPT; break; case PARTITION_STYLE_MBR: Disk->FormatType = DISK_FORMAT_TYPE_PCAT; Disk->Signature = DriveLayoutEx->Mbr.Signature; #if defined(_IA64_) // // Make sure that this is not a raw disk // which is being faked as MBR disk // if (SpPtnIsRawDiskDriveLayout(DriveLayoutEx)) { Disk->FormatType = DISK_FORMAT_TYPE_RAW; SPPT_SET_DISK_BLANK(DiskNumber, TRUE); } #endif break; case PARTITION_STYLE_RAW: Disk->FormatType = DISK_FORMAT_TYPE_RAW; SPPT_SET_DISK_BLANK(DiskNumber, TRUE); break; default: Disk->FormatType = DISK_FORMAT_TYPE_UNKNOWN; break; } SpAppendDiskTag(Disk); SpPtDumpDriveLayoutInformation(DevicePath, DriveLayoutEx); // // Don't need this guy anymore. // ZwClose( Handle ); // // might need this while committing // Disk->DriveLayout = *DriveLayoutEx; Status = STATUS_SUCCESS; // // ============================ // // Initialize partiton descriptors. // // ============================ // if(DriveLayoutEx->PartitionCount) { BOOLEAN SysPartFound = FALSE; ULONG PartitionedSpaceCount = 1; // // Initialize an area entry for each partition // on the disk. // for( i = 0, pDiskRegion = NULL; i < DriveLayoutEx->PartitionCount; i++ ) { ULONG Count = 0; ULONG TypeNameId = SP_TEXT_UNKNOWN; LARGE_INTEGER DelayTime; BOOLEAN AssignDriveLetter = TRUE; PPARTITION_INFORMATION_EX PartInfo = DriveLayoutEx->PartitionEntry + i; // // IOCTL_DISK_GET_DRIVE_LAYOUT_EX may return us a list of entries that // are not used, so ignore these partitions. // if (// if its partition 0, which indicates whole disk (SPPT_IS_GPT_DISK(DiskNumber) && (PartInfo->PartitionNumber == 0)) || (PartInfo->PartitionLength.QuadPart == 0) || // if MBR entry not used or length is zero ((DriveLayoutEx->PartitionStyle == PARTITION_STYLE_MBR) && (PartInfo->Mbr.PartitionType == PARTITION_ENTRY_UNUSED) && (PartInfo->PartitionLength.QuadPart == 0)) || // if unknown/unused GPT partition ((DriveLayoutEx->PartitionStyle == PARTITION_STYLE_GPT) && (!memcmp(&PartInfo->Gpt.PartitionType, &PARTITION_ENTRY_UNUSED_GUID, sizeof(GUID))))){ continue; } // // Allocate space for the next region. // if(pDiskRegion) { pDiskRegion->Next = SpMemAlloc( sizeof(DISK_REGION) ); pDiskRegion = pDiskRegion->Next; } else { // // First region allocation for harddisk so initialize // the region list head for the hardisk // ASSERT(PartDisk->PrimaryDiskRegions == NULL); pDiskRegion = SpMemAlloc(sizeof(DISK_REGION)); PartDisk->PrimaryDiskRegions = pDiskRegion; SPPT_SET_DISK_BLANK(DiskNumber, FALSE); } if(!pDiskRegion) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: SpMemAlloc failed!\n" )); Status = STATUS_NO_MEMORY; break; } RtlZeroMemory(pDiskRegion, sizeof(DISK_REGION)); // // Start filling out our Region descriptor... // // // DiskNumber // pDiskRegion->DiskNumber = DiskNumber; // // Partition information // pDiskRegion->PartInfo = *PartInfo; // // StartSector // pDiskRegion->StartSector = PartInfo->StartingOffset.QuadPart / Disk->Geometry.BytesPerSector; // // SectorCount // pDiskRegion->SectorCount = PartInfo->PartitionLength.QuadPart / Disk->Geometry.BytesPerSector; // // PartitionNumber // pDiskRegion->PartitionNumber = PartInfo->PartitionNumber; if (SPPT_IS_MBR_DISK(DiskNumber) && (PartInfo->PartitionNumber == 0)) { if (IsContainerPartition(PartInfo->Mbr.PartitionType)) { SPPT_SET_REGION_EPT(pDiskRegion, EPTContainerPartition); } // // nothing after this is really needed for container partition // continue; } else { // // PartitionedSpace // SPPT_SET_REGION_PARTITIONED(pDiskRegion, TRUE); } if (SPPT_IS_REGION_PARTITIONED(pDiskRegion)) { pDiskRegion->TablePosition = PartitionedSpaceCount++; } // // Partition Number should be valid // ASSERT(pDiskRegion->PartitionNumber); // // IsSystemPartition (is it active) // if( DriveLayoutEx->PartitionStyle == PARTITION_STYLE_MBR ) { // // On IA64 systems don't use active MBR partitions as system // partitions // if (!SpIsArc()) { // // He's an MBR disk, so we can rely on the BootIndicator field. // pDiskRegion->IsSystemPartition = PartInfo->Mbr.BootIndicator; } // // Don't assign drive letters to OEM partitions // if (IsOEMPartition(SPPT_GET_PARTITION_TYPE(pDiskRegion))) { AssignDriveLetter = FALSE; } } else { // // He's not MBR, look at his PartitionType (which is a GUID). // pDiskRegion->IsSystemPartition = FALSE; if( !memcmp(&PartInfo->Gpt.PartitionType, &PARTITION_SYSTEM_GUID, sizeof(GUID)) ) { pDiskRegion->IsSystemPartition = TRUE; AssignDriveLetter = FALSE; } } if (SPPT_IS_REGION_SYSTEMPARTITION(pDiskRegion)) { SysPartFound = TRUE; } // // FtPartition // if( DriveLayoutEx->PartitionStyle == PARTITION_STYLE_MBR ) { // // He's an MBR disk, so we can rely on the PartitionType field. // pDiskRegion->FtPartition = IsFTPartition(PartInfo->Mbr.PartitionType); } else { // // He's not MBR. Assume he's GPT and look at his PartitionType (which is a GUID). // pDiskRegion->FtPartition = FALSE; } // // DynamicVolume // DynamicVolumeSuitableForOS // if( DriveLayoutEx->PartitionStyle == PARTITION_STYLE_MBR ) { // // He's an MBR disk, so we can rely on the PartitionType field. // pDiskRegion->DynamicVolume = (PartInfo->Mbr.PartitionType == PARTITION_LDM); } else { // // He's not MBR. Assume he's GPT and look at his PartitionType (which is a GUID). // pDiskRegion->DynamicVolume = FALSE; if( !memcmp(&PartInfo->Gpt.PartitionType, &PARTITION_LDM_DATA_GUID, sizeof(GUID)) ) { // // The GUIDs match. pDiskRegion->DynamicVolume = TRUE; } } if (pDiskRegion->DynamicVolume) { TypeNameId = SP_TEXT_PARTITION_NAME_DYNVOL; } // // if MSFT reserved partition (we need to keep track of it but // not process it) // if((DriveLayoutEx->PartitionStyle == PARTITION_STYLE_GPT) && (IsEqualGUID(&(PartInfo->Gpt.PartitionType), &PARTITION_MSFT_RESERVED_GUID) || IsEqualGUID(&(PartInfo->Gpt.PartitionType), &PARTITION_LDM_METADATA_GUID))) { pDiskRegion->IsReserved = TRUE; AssignDriveLetter = FALSE; // // Get the type name from the resources. // SpFormatMessage(pDiskRegion->TypeName, sizeof(pDiskRegion->TypeName), SP_TEXT_PARTNAME_RESERVED); continue; } // // Assume we can't install on this dynamic volume. // pDiskRegion->DynamicVolumeSuitableForOS = FALSE; // // For the following entries, we need an Open handle to this partition. // // // If the partition just got created, we may have to wait for // a few seconds before its actually available // // Note : We wait for 20 secs at the max for each partition // for (Count = 0; (Count < 10); Count++) { // // Open the handle to the required partition // Status = SpOpenPartition( DevicePath, PartInfo->PartitionNumber, &Handle, FALSE ); if((Status == STATUS_NO_SUCH_DEVICE) || (Status == STATUS_OBJECT_NAME_NOT_FOUND)) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: unable to open partition%d on device %ws (%lx)\n", PartInfo->PartitionNumber, DevicePath, Status )); DelayTime.HighPart = -1; // relative time DelayTime.LowPart = (ULONG)(-20000000); // 2 secs in 100ns interval KeDelayExecutionThread(KernelMode, FALSE, &DelayTime); } else { break; } } // // Need the partition handle to continue // if (!NT_SUCCESS(Status)) { // // ignore the error while trying to open dynamic disks // if (SPPT_IS_REGION_DYNAMIC_VOLUME(pDiskRegion)) { Status = STATUS_SUCCESS; } // // Get the type name from the resources. // SpFormatMessage(pDiskRegion->TypeName, sizeof(pDiskRegion->TypeName), TypeNameId); continue; } // // Check, if installtion can be done on the dynamic volume // if( pDiskRegion->DynamicVolume ) { // // Call disk manager to tell me if it's okay to // install on this dynamic volume. If I get back // anything but STATUS_SUCCESS, then assume we // can't install here. // Status = ZwDeviceIoControlFile( Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_VOLUME_IS_PARTITION, NULL, 0, NULL, 0 ); if( NT_SUCCESS(Status) ){ pDiskRegion->DynamicVolumeSuitableForOS = TRUE; } } // // Filesystem // pFSInfo = SpMemAlloc( sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + (MAX_PATH*2) ); if( !pFSInfo ) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: SpMemAlloc failed!\n" )); ZwClose( Handle ); Status = STATUS_NO_MEMORY; break; } RtlZeroMemory( pFSInfo, sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + (MAX_PATH*2) ); Status = ZwQueryVolumeInformationFile( Handle, &IoStatusBlock, pFSInfo, sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + (MAX_PATH*2), FileFsAttributeInformation ); if (!NT_SUCCESS(Status)) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: Failed to retrieve partition attribute information (%lx)\n", Status )); } else { if (!wcscmp(pFSInfo->FileSystemName, L"NTFS")) { pDiskRegion->Filesystem = FilesystemNtfs; TypeNameId = SP_TEXT_FS_NAME_3; } else if (!wcscmp(pFSInfo->FileSystemName, L"FAT")) { pDiskRegion->Filesystem = FilesystemFat; TypeNameId = SP_TEXT_FS_NAME_2; } else if (!wcscmp(pFSInfo->FileSystemName, L"FAT32")) { pDiskRegion->Filesystem = FilesystemFat32; TypeNameId = SP_TEXT_FS_NAME_4; } else if (TypeNameId == SP_TEXT_UNKNOWN){ ULONG Index; pDiskRegion->Filesystem = FilesystemUnknown; // // Make sure it was not already created new partition // for (Index = 0; Index < NewPartitionCount; Index++) { if (pDiskRegion->StartSector == NewPartitions[Index]) { pDiskRegion->Filesystem = FilesystemNewlyCreated; TypeNameId = SP_TEXT_FS_NAME_1; break; } } } } // // if we cannot determine the partition type, then try // to use the known name from partition id. // if ((TypeNameId == SP_TEXT_UNKNOWN) && SPPT_IS_MBR_DISK(DiskNumber)) { ULONG PartitionType = SPPT_GET_PARTITION_TYPE(pDiskRegion); if (PartitionType < 256) { UCHAR NameId = PartitionNameIds[SPPT_GET_PARTITION_TYPE(pDiskRegion)]; if (NameId != 0xFF) { TypeNameId = SP_TEXT_PARTITION_NAME_BASE + NameId; } } } // // Get the type name from the resources. // SpFormatMessage(pDiskRegion->TypeName, sizeof(pDiskRegion->TypeName), TypeNameId); SpMemFree( pFSInfo ); // // FreeSpaceKB and BytesPerCluster (only if we know what FS it is) // if ((pDiskRegion->Filesystem != FilesystemUnknown) && (pDiskRegion->Filesystem != FilesystemNewlyCreated)) { // // Delete \pagefile.sys if it's there. This makes disk free space // calculations a little easier. // MyTempBuffer = (PWCHAR)SpMemAlloc( MAX_NTPATH_LENGTH ); if( !MyTempBuffer ) { // // No memory... // KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: SpMemAlloc failed!\n" )); Status = STATUS_NO_MEMORY; break; } SpNtNameFromRegion( pDiskRegion, MyTempBuffer, MAX_NTPATH_LENGTH, PrimaryArcPath ); SpConcatenatePaths( MyTempBuffer, L"" ); SpDeleteFile( MyTempBuffer, L"pagefile.sys", NULL ); SpMemFree( MyTempBuffer ); MyTempBuffer = NULL; pSizeInfo = SpMemAlloc( sizeof(FILE_FS_SIZE_INFORMATION) + (MAX_PATH*2) ); if( !pSizeInfo ) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: SpMemAlloc failed!\n" )); ZwClose( Handle ); Status = STATUS_NO_MEMORY; break; } RtlZeroMemory( pSizeInfo, sizeof(FILE_FS_SIZE_INFORMATION) + (MAX_PATH*2) ); Status = ZwQueryVolumeInformationFile( Handle, &IoStatusBlock, pSizeInfo, sizeof(FILE_FS_SIZE_INFORMATION) + (MAX_PATH*2), FileFsSizeInformation ); // // Waiting for another 2 secs for the volume to appear // if (Status == STATUS_NO_SUCH_DEVICE) { // // Wait for 2 seconds // DelayTime.HighPart = -1; // relative time DelayTime.LowPart = (ULONG)(-20000000); // 2 secs in 100ns interval KeDelayExecutionThread(KernelMode, FALSE, &DelayTime); RtlZeroMemory( pSizeInfo, sizeof(FILE_FS_SIZE_INFORMATION) + (MAX_PATH*2) ); Status = ZwQueryVolumeInformationFile( Handle, &IoStatusBlock, pSizeInfo, sizeof(FILE_FS_SIZE_INFORMATION) + (MAX_PATH*2), FileFsSizeInformation ); } if (!NT_SUCCESS(Status)) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: Failed to retrieve disk(%d)partition(%d) sizing information (%lx)\n", DiskNumber, pDiskRegion->PartitionNumber, Status )); } else { LARGE_INTEGER FreeBytes; FreeBytes = RtlExtendedIntegerMultiply( pSizeInfo->AvailableAllocationUnits, pSizeInfo->SectorsPerAllocationUnit * pSizeInfo->BytesPerSector ); pDiskRegion->FreeSpaceKB = RtlExtendedLargeIntegerDivide( FreeBytes, 1024, &r ).LowPart; if(r >= 512) { pDiskRegion->FreeSpaceKB++; } // // Sigh... Legacy stuff. SpPtDeterminePartitionGood() will want this // field so that he knows what the free-space+space_from_local_source is. // pDiskRegion->AdjustedFreeSpaceKB = pDiskRegion->FreeSpaceKB; pDiskRegion->BytesPerCluster = pSizeInfo->SectorsPerAllocationUnit * pSizeInfo->BytesPerSector; } SpMemFree( pSizeInfo ); // // VolumeLabel // pLabelInfo = SpMemAlloc( sizeof(FILE_FS_VOLUME_INFORMATION) + (MAX_PATH*2) ); if( !pFSInfo ) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: SpMemAlloc failed!\n" )); ZwClose( Handle ); Status = STATUS_NO_MEMORY; break; } RtlZeroMemory( pLabelInfo, sizeof(FILE_FS_VOLUME_INFORMATION) + (MAX_PATH*2) ); Status = ZwQueryVolumeInformationFile( Handle, &IoStatusBlock, pLabelInfo, sizeof(FILE_FS_VOLUME_INFORMATION) + (MAX_PATH*2), FileFsVolumeInformation ); if (!NT_SUCCESS(Status)) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: Failed to retrieve volume information (%lx)\n", Status)); } else { ULONG SaveCharCount; // // We'll only save away the first characters of // the volume label. // SaveCharCount = min( pLabelInfo->VolumeLabelLength + sizeof(WCHAR), sizeof(pDiskRegion->VolumeLabel) ) / sizeof(WCHAR); if(SaveCharCount) { SaveCharCount--; // allow for terminating NUL. } wcsncpy( pDiskRegion->VolumeLabel, pLabelInfo->VolumeLabel, SaveCharCount ); pDiskRegion->VolumeLabel[SaveCharCount] = 0; } SpMemFree( pLabelInfo ); } else { // // Free space is what ever the partition size is // pDiskRegion->FreeSpaceKB = (pDiskRegion->SectorCount * Disk->Geometry.BytesPerSector) / 1024; pDiskRegion->AdjustedFreeSpaceKB = pDiskRegion->FreeSpaceKB; } // // Assign the drive letter if required // if (AssignDriveLetter) { // // Retrieve nt pathname for this region. // MyTempBuffer = (PWCHAR)SpMemAlloc( MAX_NTPATH_LENGTH ); if( !MyTempBuffer ) { // // No memory... // KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: SpMemAlloc failed!\n" )); Status = STATUS_NO_MEMORY; break; } SpNtNameFromRegion( pDiskRegion, MyTempBuffer, MAX_NTPATH_LENGTH, PrimaryArcPath ); // // Assign the drive letter // pDiskRegion->DriveLetter = SpGetDriveLetter( MyTempBuffer, NULL ); KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtInitializeDiskAreas: Partition = %ls, DriveLetter = %wc: \n", MyTempBuffer, pDiskRegion->DriveLetter)); SpMemFree( MyTempBuffer ); MyTempBuffer = NULL; } // // See if this guy has the local source. // // MyTempBuffer = (PWCHAR)SpMemAlloc( MAX_NTPATH_LENGTH ); if( !MyTempBuffer ) { // // No memory... // KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtInitializeDiskAreas: SpMemAlloc failed!\n" )); Status = STATUS_NO_MEMORY; break; } SpNtNameFromRegion( pDiskRegion, MyTempBuffer, MAX_NTPATH_LENGTH, PrimaryArcPath ); SpConcatenatePaths( MyTempBuffer, L"" ); // // Don't need this guy anymore. // ZwClose( Handle ); if( WinntSetup && !WinntFromCd && !LocalSourceRegion && SpNFilesExist(MyTempBuffer, LocalSourceFiles, ELEMENT_COUNT(LocalSourceFiles), TRUE) ) { LocalSourceRegion = pDiskRegion; pDiskRegion->IsLocalSource = TRUE; KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: %ws is the local source partition.\n", MyTempBuffer)); } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: %ws is not the local source partition.\n", MyTempBuffer)); } SpMemFree( MyTempBuffer ); MyTempBuffer = NULL; Status = STATUS_SUCCESS; } // // Go ahead and locate the system partitions on this disk // if (SpIsArc()) { if (!SysPartFound) { SpPtnLocateDiskSystemPartitions(DiskNumber); } else { ValidArcSystemPartition = TRUE; } } } // // Update the boot entries to reflect the // new region pointers // SpUpdateRegionForBootEntries(); if (NewPartitions) { SpMemFree(NewPartitions); } SpMemFree( DriveLayoutEx ); return Status; } NTSTATUS SpPtnSortDiskAreas( IN ULONG DiskNumber ) /*++ Routine Description: Examine the partitions defined on this disk and sort them according to their location on the disk. Arguments: DiskNumber - supplies the disk number of the disk whose partitions we want to inspect. Return Value: NTSTATUS. If all goes well, we should be returing STATUS_SUCCESS. --*/ { NTSTATUS Status = STATUS_SUCCESS; PDISK_REGION pTempDiskRegion = NULL; PDISK_REGION pCurrentDiskRegion = NULL; PDISK_REGION pPreviousDiskRegion = NULL; // // Get a pointer to the list of regions. // pCurrentDiskRegion = PartitionedDisks[DiskNumber].PrimaryDiskRegions; if( !pCurrentDiskRegion ) { // // Odd. Either something is very wrong, or // this disk simply has no partitions, which is // certainly possible. Assume the best. // return STATUS_SUCCESS; } // // We got something. Go sort the list. There // can't be very many partitions, so just bubble-sort. // while( pCurrentDiskRegion->Next ) { // // There's another partition ahead of // us. See if we need to switch places. // if( pCurrentDiskRegion->StartSector > pCurrentDiskRegion->Next->StartSector ) { // // Yes, we need to swap these 2 entries. // Fixup the pointers. // if( pPreviousDiskRegion ) { // // 1. Set the previous disk region to point to // the region after us. // pPreviousDiskRegion->Next = pCurrentDiskRegion->Next; } else { // // We're at the very beginning of the linked // list. // // // 1. Set the disk's region pointer to point to // the region after us. // PartitionedDisks[DiskNumber].PrimaryDiskRegions = pCurrentDiskRegion->Next; } // // 2. Set our our next region's Next pointer to // come back to us. // pTempDiskRegion = pCurrentDiskRegion->Next->Next; pCurrentDiskRegion->Next->Next = pCurrentDiskRegion; // // 3. Set our own pointer to a couple of regions ahead. // pCurrentDiskRegion->Next = pTempDiskRegion; // // Now reset so we start the sort over again. // pCurrentDiskRegion = PartitionedDisks[DiskNumber].PrimaryDiskRegions; pPreviousDiskRegion = NULL; } else { // // No need to swap these two regions in our list. Increment // our pointers and continue. // pPreviousDiskRegion = pCurrentDiskRegion; pCurrentDiskRegion = pCurrentDiskRegion->Next; } } return Status; } NTSTATUS SpPtnInitRegionFromDisk( IN ULONG DiskNumber, OUT PDISK_REGION Region ) /*++ Routine Description: Given the disk id, creates a disk region representing the whole disk Arguments: DiskNumber : Disk Id Region : Region which gets initialized on return Return Value: STATUS_SUCCESS if successful, otherwise STATUS_INVALID_PARAMETER --*/ { NTSTATUS Status = STATUS_INVALID_PARAMETER; if (Region) { PHARD_DISK Disk = SPPT_GET_HARDDISK(DiskNumber); RtlZeroMemory(Region, sizeof(DISK_REGION)); // // Note : Most of the fields below don't need to be initialized // because of the memset above, but its done for sake of // clarity // Region->DiskNumber = DiskNumber; Region->StartSector = Disk->Geometry.SectorsPerTrack; Region->SectorCount = Disk->DiskSizeSectors - Region->StartSector; SPPT_SET_REGION_PARTITIONED(Region, FALSE); Region->PartitionNumber = 0; Region->MbrInfo = NULL; Region->TablePosition = 0; Region->IsSystemPartition = FALSE; Region->IsLocalSource = FALSE; Region->Filesystem = FilesystemUnknown; Region->FreeSpaceKB = Disk->DiskSizeMB * 1024; Region->BytesPerCluster = -1; Region->AdjustedFreeSpaceKB = Region->FreeSpaceKB; Region->DriveLetter = 0; Region->FtPartition = FALSE; Region->DynamicVolume = FALSE; Region->DynamicVolumeSuitableForOS = FALSE; Status = STATUS_SUCCESS; } return Status; } NTSTATUS SpPtnFillDiskFreeSpaceAreas( IN ULONG DiskNumber ) /*++ Routine Description: This function will go peruse all partitions on the disk. If there are any free regions on the disk, we'll create a region entry and mark it as unformatted. Arguments: DiskNumber - supplies the disk number of the disk whose partitions we want to inspect. Return Value: NTSTATUS. If all goes well, we should be returing STATUS_SUCCESS. --*/ { NTSTATUS Status = STATUS_SUCCESS; PDISK_REGION pTempDiskRegion; PDISK_REGION pCurrentDiskRegion = NULL; ULONGLONG NextStart; ULONGLONG NextSize; PDISK_REGION FirstContainer = NULL; // // Get a pointer to the list of regions. // pCurrentDiskRegion = PartitionedDisks[DiskNumber].PrimaryDiskRegions; if( !pCurrentDiskRegion ) { // // Odd. Either something is very wrong, or // this disk simply has no partitions, which is // certainly possible. Assume the best and // create one region entry that encompasses all // space on the disk, but is unpartitioned. // pCurrentDiskRegion = SpMemAlloc(sizeof(DISK_REGION)); if (pCurrentDiskRegion) { Status = SpPtnInitRegionFromDisk(DiskNumber, pCurrentDiskRegion); } else { Status = STATUS_NO_MEMORY; } if (NT_SUCCESS(Status)) { ASSERT(!PartitionedDisks[DiskNumber].PrimaryDiskRegions); PartitionedDisks[DiskNumber].PrimaryDiskRegions = pCurrentDiskRegion; SPPT_SET_DISK_BLANK(DiskNumber, TRUE); } return Status; } // // The regions are already sorted according to their relative // position on the disk, so before we go through them, let's // see if there's any empty space on the disk occurring *before* // the first partition. // if( pCurrentDiskRegion->StartSector > SPPT_DISK_TRACK_SIZE(DiskNumber) ) { // // Yep. Make a region descriptor for this guy (if he is more than // one cylinder in size) // NextStart = SPPT_DISK_TRACK_SIZE(DiskNumber); NextSize = pCurrentDiskRegion->StartSector - NextStart; // // The first partition can start at first track offset. So this need not always // be of minimum cylinder size // if (NextSize >= (SPPT_DISK_CYLINDER_SIZE(DiskNumber) - SPPT_DISK_TRACK_SIZE(DiskNumber))) { pTempDiskRegion = SpMemAlloc( sizeof(DISK_REGION) ); if(!pTempDiskRegion) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtFillFreeSpaceAreas: SpMemAlloc failed!\n" )); return STATUS_NO_MEMORY; } RtlZeroMemory( pTempDiskRegion, sizeof(DISK_REGION) ); pTempDiskRegion->DiskNumber = DiskNumber; pTempDiskRegion->StartSector = NextStart; pTempDiskRegion->SectorCount = NextSize; // // Put this region before the current region // pTempDiskRegion->Next = pCurrentDiskRegion; PartitionedDisks[DiskNumber].PrimaryDiskRegions = pTempDiskRegion; } } // // Now go through the regions, inserting regions to account for any // empty space between the partitions. // while( pCurrentDiskRegion ) { if( !pCurrentDiskRegion->Next ) { NextStart = 0; // // if this is container partition then all the space in this // container is free space // if (SPPT_IS_MBR_DISK(DiskNumber) && IsContainerPartition(SPPT_GET_PARTITION_TYPE(pCurrentDiskRegion))) { PDISK_REGION ExtFree = NULL; ASSERT(FirstContainer == NULL); // // We add one here because we should be able to differentiate the starting // free region inside the extended partition from the extended partition // itself. // NextStart = pCurrentDiskRegion->StartSector + 1; NextSize = pCurrentDiskRegion->SectorCount; if (NextSize >= SPPT_DISK_CYLINDER_SIZE(DiskNumber)) { PDISK_REGION ExtFree = SpMemAlloc(sizeof(DISK_REGION)); if (!ExtFree) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtFillFreeSpaceAreas: SpMemAlloc failed!\n" )); return STATUS_NO_MEMORY; } RtlZeroMemory(ExtFree, sizeof(DISK_REGION)); ExtFree->DiskNumber = DiskNumber; ExtFree->StartSector = NextStart; ExtFree->SectorCount = NextSize; pCurrentDiskRegion->Next = ExtFree; // // make the new region current region !!! // pCurrentDiskRegion = ExtFree; NextStart = NextStart + NextSize - 1; } else { // // Make sure that the free space after the extended // partition is accounted for // NextStart = 0; } } // // There's nothing behind of us. See if there's any // empty space back there that's unaccounted for. // if (!NextStart) { NextStart = pCurrentDiskRegion->StartSector + pCurrentDiskRegion->SectorCount; } if (PartitionedDisks[DiskNumber].HardDisk->DiskSizeSectors > NextStart) { NextSize = PartitionedDisks[DiskNumber].HardDisk->DiskSizeSectors - NextStart; } else { NextSize = 0; } // // For ASR, allow partition size on GPT disks to be >= 1 sector. // In all other cases, partition size must be >= 1 cylinder. // if ((NextSize >= SPPT_DISK_CYLINDER_SIZE(DiskNumber)) || (SpDrEnabled() && SPPT_IS_GPT_DISK(DiskNumber) && (NextSize >= 1)) ) { // // Yes there is. We need to make a region behind us that's // marked as unpartitioned. // if (FirstContainer) { // // there could be free space at the end of the // extended partition. Mark is separately from // the free space after the extended partition // ULONGLONG ExtEnd = FirstContainer->StartSector + FirstContainer->SectorCount; ULONGLONG ExtFreeStart = NextStart; ULONGLONG ExtFreeSize = (ExtEnd > ExtFreeStart) ? ExtEnd - ExtFreeStart : 0; KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP:SpPtnFillDiskFreeSpaces():EFS:%I64d,EFSize:%I64d,EE:%I64d\n", ExtFreeStart, ExtFreeSize, ExtEnd)); if (ExtFreeSize >= SPPT_DISK_CYLINDER_SIZE(DiskNumber)) { PDISK_REGION ExtFree = SpMemAlloc(sizeof(DISK_REGION)); if (!ExtFree) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtnFillFreeSpaceAreas: SpMemAlloc failed!\n" )); return STATUS_NO_MEMORY; } RtlZeroMemory(ExtFree, sizeof(DISK_REGION)); ExtFree->DiskNumber = DiskNumber; ExtFree->StartSector = ExtFreeStart; ExtFree->SectorCount = ExtFreeSize; pCurrentDiskRegion->Next = ExtFree; pCurrentDiskRegion = ExtFree; NextStart = ExtEnd; NextSize = 0; if (PartitionedDisks[DiskNumber].HardDisk->DiskSizeSectors > NextStart) { NextSize = PartitionedDisks[DiskNumber].HardDisk->DiskSizeSectors - NextStart; } } else { // // Get rid of any free space at the end which is lesser than a // cylinder partition inside the exteneded partition before // we try to see if there is adequate space at the end of extended // partition // NextStart += ExtFreeSize; NextSize -= ExtFreeSize; } } // // For ASR, allow partition size on GPT disks to be >= 1 sector. // In all other cases, partition size must be >= 1 cylinder. // if ((NextSize >= SPPT_DISK_CYLINDER_SIZE(DiskNumber)) || (SpDrEnabled() && SPPT_IS_GPT_DISK(DiskNumber) && (NextSize >= 1)) ) { pTempDiskRegion = SpMemAlloc( sizeof(DISK_REGION) ); if(!pTempDiskRegion) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtnFillFreeSpaceAreas: SpMemAlloc failed!\n" )); return(STATUS_NO_MEMORY); } RtlZeroMemory( pTempDiskRegion, sizeof(DISK_REGION) ); pTempDiskRegion->DiskNumber = DiskNumber; pTempDiskRegion->StartSector = NextStart; pTempDiskRegion->SectorCount = NextSize; pCurrentDiskRegion->Next = pTempDiskRegion; } } // // We just processed the last region. If there was any free space // behind that partition, we just accounted for it, in which case // we're done with this disk. If there wasn't any free space behind // that partition, then we're also done. // return( Status ); } else { // // There's another partition ahead of us. // See if there's free space between them. // NextStart = pCurrentDiskRegion->StartSector + pCurrentDiskRegion->SectorCount; if (pCurrentDiskRegion->Next->StartSector > NextStart) { NextSize = pCurrentDiskRegion->Next->StartSector - NextStart; // // Check to see if its a container partition // if (!FirstContainer && SPPT_IS_MBR_DISK(DiskNumber) && IsContainerPartition(SPPT_GET_PARTITION_TYPE(pCurrentDiskRegion))) { FirstContainer = pCurrentDiskRegion; NextStart = pCurrentDiskRegion->StartSector + 1; NextSize = pCurrentDiskRegion->Next->StartSector - NextStart; } if (FirstContainer) { ULONGLONG ExtEnd = FirstContainer->StartSector + FirstContainer->SectorCount; ULONGLONG FreeEnd = pCurrentDiskRegion->Next->StartSector; // // Split the free region into extended free and normal free region // if needed // if (!SPPT_IS_REGION_CONTAINED(FirstContainer, pCurrentDiskRegion->Next) && (ExtEnd < FreeEnd)) { PDISK_REGION ExtFree = NULL; NextSize = ExtEnd - NextStart; if (NextSize >= SPPT_DISK_CYLINDER_SIZE(DiskNumber)) { ExtFree = SpMemAlloc(sizeof(DISK_REGION)); if (!ExtFree) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtnFillFreeSpaceAreas: SpMemAlloc failed!\n" )); return STATUS_NO_MEMORY; } RtlZeroMemory(ExtFree, sizeof(DISK_REGION)); ExtFree->DiskNumber = DiskNumber; ExtFree->StartSector = NextStart; ExtFree->SectorCount = NextSize; // // insert the region after the current one // ExtFree->Next = pCurrentDiskRegion->Next; pCurrentDiskRegion->Next = ExtFree; // // make the new region current // pCurrentDiskRegion = ExtFree; } // // Fix the next free region start // NextStart += NextSize; if (FreeEnd > NextStart) { NextSize = FreeEnd - NextStart; } else { NextSize = 0; } } } } else { // // skip container partitions (expect for starting free space // inside the container partition) // NextSize = 0; if (SPPT_IS_MBR_DISK(DiskNumber) && IsContainerPartition(SPPT_GET_PARTITION_TYPE(pCurrentDiskRegion)) && (pCurrentDiskRegion->Next->StartSector > pCurrentDiskRegion->StartSector)) { if (!FirstContainer) { FirstContainer = pCurrentDiskRegion; KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP:SpPtnFillDiskFreeSpaces():%p is the first container\n", FirstContainer)); } // // We add one here because we should be able to differentiate the starting // free region inside the extended partition from the extended partition // itself. // NextStart = pCurrentDiskRegion->StartSector + 1; NextSize = pCurrentDiskRegion->Next->StartSector - NextStart + 1; } } // // For ASR, allow partition size on GPT disks to be >= 1 sector. // In all other cases, partition size must be >= 1 cylinder. // if ((NextSize >= SPPT_DISK_CYLINDER_SIZE(DiskNumber)) || (SpDrEnabled() && SPPT_IS_GPT_DISK(DiskNumber) && (NextSize >= 1)) ) { // // Yes, there's free space and we need to insert // a region here to represent it. Allocate a region // and initialize it as unpartitioned space. // pTempDiskRegion = SpMemAlloc( sizeof(DISK_REGION) ); if(!pTempDiskRegion) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtnFillFreeSpaceAreas: SpMemAlloc failed!\n" )); return(STATUS_NO_MEMORY); } RtlZeroMemory( pTempDiskRegion, sizeof(DISK_REGION) ); pTempDiskRegion->DiskNumber = DiskNumber; pTempDiskRegion->StartSector = NextStart; pTempDiskRegion->SectorCount = NextSize; pTempDiskRegion->Next = pCurrentDiskRegion->Next; pCurrentDiskRegion->Next = pTempDiskRegion; pCurrentDiskRegion = pTempDiskRegion; } } pCurrentDiskRegion = pCurrentDiskRegion->Next; } return Status; } #ifdef NOT_USED_CURRENTLY VOID SpDeleteDiskDriveLetters( VOID ) /*++ Routine Description: This routine will delete all drive letters assigned to disks and CD-ROM drives. The deletion will occur only if setup was started booting from the CD or boot floppies (in which case drive letter migration does not take place), and only if the non-removable dissks have no partitioned spaces. This ensures that on a clean install from the CD or boot floppies, the drive letters assigned to partitions on removable disks and CD-ROM drives will always be greater than the drive letters assigned to partitions on non-removable disks (unless the partitions on the removable disks were created before the ones in the removable disks, during textmode setup). Arguments: None. Return Value: None. --*/ { ULONG DiskNumber; PDISK_REGION pDiskRegion; PWCHAR MyTempBuffer = NULL; unsigned pass; BOOLEAN PartitionedSpaceFound = FALSE; if( WinntSetup ) { // // If setup started from winnt32.exe then do not delete the drive letters since we want to preserve them // return; } // // Setup started booting from a CD. // // Find out if the disks contain at least one partition that is not a container. // Note that we do not take into consideration partitions that are on removable media. // This is to avoid the situation in which a newly created partition on a non-removable disk ends up with // a drive letter that is greater than the one assigned to an existing partition on a removable disk. // for(DiskNumber = 0; !PartitionedSpaceFound && (DiskNumberGeometry.MediaType != RemovableMedia) { // // This disk isn't removable. Let's look at all the areas and see // if there's anything that's partitioned. // pDiskRegion = PartitionedDisks[DiskNumber].PrimaryDiskRegions; while( pDiskRegion ) { if(SPPT_IS_REGION_PARTITIONED(pDiskRegion)) { PartitionedSpaceFound = TRUE; } // // Now get the next region on this disk. // pDiskRegion = pDiskRegion->Next; } } } if( !PartitionedSpaceFound ) { // // There are no partitions on this machine. Delete all drive letters // so that the drive letters for each CD-ROM drive also get deleted. // // We'll do this by sending an IOCTL to the MountManager and ask him // to whack all his knowledge of drive letters. // NTSTATUS Status; OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING UnicodeString; HANDLE Handle; INIT_OBJA(&Obja,&UnicodeString,MOUNTMGR_DEVICE_NAME); Status = ZwOpenFile( &Handle, (ACCESS_MASK)(FILE_GENERIC_READ | FILE_GENERIC_WRITE), &Obja, &IoStatusBlock, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE ); if( NT_SUCCESS( Status ) ) { MOUNTMGR_MOUNT_POINT MountMgrMountPoint; MountMgrMountPoint.SymbolicLinkNameOffset = 0; MountMgrMountPoint.SymbolicLinkNameLength = 0; MountMgrMountPoint.UniqueIdOffset = 0; MountMgrMountPoint.UniqueIdLength = 0; MountMgrMountPoint.DeviceNameOffset = 0; MountMgrMountPoint.DeviceNameLength = 0; Status = ZwDeviceIoControlFile( Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_MOUNTMGR_DELETE_POINTS, &MountMgrMountPoint, sizeof( MOUNTMGR_MOUNT_POINT ), TemporaryBuffer, sizeof( TemporaryBuffer ) ); if( !NT_SUCCESS( Status ) ) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to delete drive letters. " "ZwDeviceIoControl( IOCTL_MOUNTMGR_DELETE_POINTS ) failed." "Status = %lx \n", Status)); } else { // // If the drive letters got deleted then reset the drive letters assigned to all partitions. // Note that we only really care about resetting the drive letters on the partitions on the // removable disks, since, if we got that far, there won't be any partition on the non-removable // disks // for(DiskNumber = 0; DiskNumberDriveLetter = 0; // // Now get the next region on this disk. // pDiskRegion = pDiskRegion->Next; } } } ZwClose( Handle ); } else { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to delete drive letters. " "ZwOpenFile( %ls ) failed. Status = %lx \n", MOUNTMGR_DEVICE_NAME, Status)); } } } NTSTATUS SpAssignDiskDriveLetters( VOID ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG DiskNumber; PDISK_REGION pDiskRegion; PWCHAR MyTempBuffer = NULL; unsigned pass; // // Before initializing the drive letters, delete them if necessary. // This is to get rid of the letters assigned to CD-ROM drives and removables, when the disks have no // partitioned space. // SpDeleteDiskDriveLetters(); // // Initialize all drive letters to nothing. // If it the region is a partitioned space, then assign a drive letter also. // for( DiskNumber=0; DiskNumberDriveLetter = 0; if(SPPT_IS_REGION_PARTITIONED(pDiskRegion)) { // // Retrieve nt pathname for this region. // MyTempBuffer = (PWCHAR)SpMemAlloc( MAX_NTPATH_LENGTH ); if( !MyTempBuffer ) { // // No memory... // KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPtAssignDriveLetters: SpMemAlloc failed!\n" )); return(STATUS_NO_MEMORY); } SpNtNameFromRegion( pDiskRegion, MyTempBuffer, MAX_NTPATH_LENGTH, PrimaryArcPath ); // // Assign the drive letter. // pDiskRegion->DriveLetter = SpGetDriveLetter( MyTempBuffer, NULL ); KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtAssignDriveLetters: Partition = %ls, DriveLetter = %wc: \n", MyTempBuffer, pDiskRegion->DriveLetter)); SpMemFree( MyTempBuffer ); MyTempBuffer = NULL; } // // Now get the next region on this disk. // pDiskRegion = pDiskRegion->Next; } } return( Status ); } #endif // NOT_USED_CURRENTLY // // ============================================================================ // ============================================================================ // // The following code provides support for disk/partition selection. // // ============================================================================ // ============================================================================ // #define MENU_LEFT_X 3 #define MENU_WIDTH (VideoVars.ScreenWidth-(2*MENU_LEFT_X)) #define MENU_INDENT 4 extern ULONG PartitionMnemonics[]; VOID SpPtnAutoCreatePartitions( IN PVOID SifHandle, IN PWSTR SetupSourceDevicePath, IN PWSTR DirectoryOnSetupSource ) /*++ Routine Description: If there are no partitions on any disks, create some. Arguments: SifHandle : Handle to txtsetup.sif SetupSourceDevicePath : Device from which setup was launced DirectoryOnSetupSource : Directory from where the kernel was loaded on Setup device Return Value: None. --*/ { PDISK_REGION p = NULL; PDISK_REGION Region = NULL; ULONG Index; BOOLEAN Found = FALSE; WCHAR RegionStr[128] = {0}; NTSTATUS FormatStatus; ULONG MyPartitionSizeMB = 0; NTSTATUS Status; KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - Checking for any existing partitions.\n" )); Found = FALSE; for(Index = 0; (Index < HardDiskCount) && (!Found); Index++) { Region = SPPT_GET_PRIMARY_DISK_REGION( Index ); while( (Region) && (!Found) ) { if( Region->PartitionedSpace && !SPPT_IS_REGION_RESERVED_PARTITION(Region)) { // // He's got something on the disk. // Found = TRUE; } Region = Region->Next; } } if( !Found ) { // // The disks are all empty. We need to go // create some partitions for the installation. // KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - No existing partitions were found.\n" )); if (SpIsArc()) { // // If we're on an ARC machine, go create a system // partition first. // KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - About to " "auto-generate a system partition.\n" )); #if defined(_IA64_) Status = SpPtnCreateESP(FALSE); if (!NT_SUCCESS(Status)) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - Could not " "autocreate ESP : %lx\n", Status)); return; } #endif } // // Now create a partition to install the operating system. // // To do this, we're going to take the following steps: // 1. go find some free space on a disk that's big enough. // 2. create a partitions that's half of this guy's free space, (make the // partition at least 4Gig). // KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - About to " "auto-generate an installation partition.\n" )); Found = FALSE; for(Index = 0; (Index < HardDiskCount) && (!Found); Index++) { Region = SPPT_GET_PRIMARY_DISK_REGION( Index ); while( (Region) && (!Found) ) { if( (!Region->PartitionedSpace) && (SPPT_REGION_FREESPACE_KB(Region)/1024 >= (SUGGESTED_INSTALL_PARTITION_SIZE_MB)) ) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - I found an area big enough for an installation.\n" )); MyPartitionSizeMB = max( (ULONG)(SPPT_REGION_FREESPACE_KB(Region)/(2*1024)), SUGGESTED_INSTALL_PARTITION_SIZE_MB ); if( SpPtnDoCreate( Region, &p, TRUE, MyPartitionSizeMB, NULL, FALSE ) ) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - I just created an installation partition.\n" )); // // Got it. // Found = TRUE; Region = p; // // Now format it. // swprintf( RegionStr, L"\\Harddisk%u\\Partition%u", Region->DiskNumber, Region->PartitionNumber ); // // Format the system region with NTFS file system // KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - I'm about to go format the installation partition.\n" )); FormatStatus = SpDoFormat( RegionStr, Region, FilesystemNtfs, TRUE, TRUE, FALSE, SifHandle, 0, // default cluster size SetupSourceDevicePath, DirectoryOnSetupSource ); KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - Format of an installation partition is complete.\n" )); } else { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - I failed to create an installation partition.\n" )); } } Region = Region->Next; } } } else { // let 'em know KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnAutoCreatePartitions - Existing partitions were found.\n" )); } } NTSTATUS SpPtnPrepareDisks( IN PVOID SifHandle, OUT PDISK_REGION *InstallArea, OUT PDISK_REGION *SystemPartitionArea, IN PWSTR SetupSourceDevicePath, IN PWSTR DirectoryOnSetupSource, IN BOOLEAN RemoteBootRepartition ) /*++ Routine Description: Shows the use the disk menu (with partitions) and locates the system and boot partition Arguments: SifHandle : Handle to txtsetup.sif InstallArea : Place holder for boot partition SystemPartitionArea : Place holder for system partition SetupSourceDevicePath : Device from which setup was launced DirectoryOnSetupSource : Directory from where the kernel was loaded on Setup device RemoteBootRePartition : Whether to repartition the disk for remote boot Return Value: Appropriate status code --*/ { NTSTATUS Status = STATUS_SUCCESS; WCHAR Buffer[256] = {0}; ULONG DiskNumber; PVOID Menu; ULONG MenuTopY; ULONG ValidKeys[3] = { ASCI_CR, KEY_F3, 0 }; ULONG ValidKeysCmdCons[2] = { ASCI_ESC, 0 }; ULONG Keypress; PDISK_REGION pDiskRegion; PDISK_REGION FirstDiskRegion,DefaultDiskRegion; BOOLEAN unattended = UnattendedOperation; BOOLEAN OldUnattendedOperation; BOOLEAN createdMenu; ULONG LastUsedDisk = -1; BOOLEAN Win9xPartition = FALSE; // // Do some special partitioning if there's nothing // on the disk and the user has asked us to do an express // installation. // if( (!CustomSetup) && (UnattendedOperation) && (HardDiskCount != 0) #if defined(REMOTE_BOOT) && (!RemoteBootSetup) && (!RemoteInstallSetup) #endif ) { // // See if we need to auto-generate some partitions for the // installation. // SpPtnAutoCreatePartitions( SifHandle, SetupSourceDevicePath, DirectoryOnSetupSource ); } if (SpIsArc()) { // // Select a system partition from among those defined in NV-RAM. // *SystemPartitionArea = SpPtnValidSystemPartitionArc(SifHandle, SetupSourceDevicePath, DirectoryOnSetupSource, FALSE); if (*SystemPartitionArea) { (*SystemPartitionArea)->IsSystemPartition = TRUE; } } // // If the user selected any accessibility option and wanted to choose partition, show the partition screen // if(AccessibleSetup && !AutoPartitionPicker) { unattended = FALSE; } // // Save the current unattended mode and put the temp one // OldUnattendedOperation = UnattendedOperation; UnattendedOperation = unattended; while(1) { createdMenu = FALSE; Keypress = 0; #if defined(REMOTE_BOOT) if (RemoteBootSetup && !RemoteInstallSetup && HardDiskCount == 0) { // // If there are no hard disks, allow diskless install // pDiskRegion = NULL; // // Run through the rest of the code as if the user had just // hit enter to select this partition. // Keypress = ASCI_CR; } else #endif // defined(REMOTE_BOOT) if (unattended && RemoteBootRepartition) { ULONG DiskNumber; ULONG DiskSpaceRequiredKB = 2 * 1024 * 1024; // 2 GB // // What's the space we required for installation // SpFetchDiskSpaceRequirements(SifHandle, 4 * 1024, &DiskSpaceRequiredKB, NULL); // // Prepare the disk for remote boot installation. This involves // converting disk 0 into as big a partition as possible. // if (*SystemPartitionArea != NULL) { DiskNumber = (*SystemPartitionArea)->DiskNumber; } else { #ifdef _X86_ DiskNumber = SpDetermineDisk0(); #elif _IA64_ DiskNumber = SpDetermineDisk0(); #else DiskNumber = 0; #endif } #ifdef _IA64_ Status = SpPtnRepartitionGPTDisk(DiskNumber, DiskSpaceRequiredKB, &pDiskRegion); #else Status = SpPtPartitionDiskForRemoteBoot(DiskNumber, &pDiskRegion); #endif if (NT_SUCCESS(Status)) { SpPtRegionDescription( &PartitionedDisks[pDiskRegion->DiskNumber], pDiskRegion, Buffer, sizeof(Buffer) ); // // Run through the rest of the code as if the user had just // hit enter to select this partition. // Keypress = ASCI_CR; } } if (Keypress == 0) { // // Display the text that goes above the menu on the partitioning screen. // SpDisplayScreen(ConsoleRunning ? SP_SCRN_PARTITION_CMDCONS:SP_SCRN_PARTITION, 3,CLIENT_TOP+1); // // Calculate menu placement. Leave one blank line // and one line for a frame. // MenuTopY = NextMessageTopLine + 2; // // Create a menu. // Menu = SpMnCreate( MENU_LEFT_X, MenuTopY, MENU_WIDTH, (VideoVars.ScreenHeight - MenuTopY - (SplangQueryMinimizeExtraSpacing() ? 1 : 2) - STATUS_HEIGHT) ); if(!Menu) { UnattendedOperation = OldUnattendedOperation; return(STATUS_NO_MEMORY); } createdMenu = TRUE; // // Build up a menu of partitions and free spaces. // FirstDiskRegion = NULL; for(DiskNumber=0; DiskNumberDynamicVolume || LocalSourceRegion->DynamicVolumeSuitableForOS)) { pDiskRegion = LocalSourceRegion; Keypress = ASCI_CR; } else { pDiskRegion = NULL; // // Unless we've been told not to, go look at each partition on each // disk and see if we can find anything suitable for an OS installation. // if( AutoPartitionPicker && !ConsoleRunning #if defined(REMOTE_BOOT) && (!RemoteBootSetup || RemoteInstallSetup) #endif // defined(REMOTE_BOOT) ) { PDISK_REGION pCurrentDiskRegion = NULL; ULONG RequiredKB = 0; ULONG SectorNo; KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: -------------------------------------------------------------\n" )); KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Looking for an install partition\n\n" )); for( DiskNumber=0; DiskNumber < HardDiskCount; DiskNumber++ ) { pCurrentDiskRegion = PartitionedDisks[DiskNumber].PrimaryDiskRegions; while( pCurrentDiskRegion ) { // // Fetch the amount of free space required on the windows nt drive. // SpFetchDiskSpaceRequirements( SifHandle, pCurrentDiskRegion->BytesPerCluster, &RequiredKB, NULL ); if( SpPtDeterminePartitionGood(pCurrentDiskRegion, RequiredKB, TRUE) ) { // // Got it. Remember the partition and pretend the user // hit the key. // KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Selected install partition = " "(DiskNumber:%d),(DriveLetter:%wc:),(%ws)\n", DiskNumber,pCurrentDiskRegion->DriveLetter, pCurrentDiskRegion->VolumeLabel)); pDiskRegion = pCurrentDiskRegion; Keypress = ASCI_CR; break; } pCurrentDiskRegion = pCurrentDiskRegion->Next; } } KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: -------------------------------------------------------------\n" )); } if( !pDiskRegion ) { // // We didn't find any suitable partitions, which means we'll be putting up a // menu very quickly. Initialize the partition to highlight in the // menu. // if (LastUsedDisk == -1) { DefaultDiskRegion = FirstDiskRegion; } else { // // Select the first region on the disk which the user last // operated on // PDISK_REGION ValidRegion = SPPT_GET_PRIMARY_DISK_REGION(LastUsedDisk); while (ValidRegion && SPPT_IS_REGION_CONTAINER_PARTITION(ValidRegion)) { ValidRegion = ValidRegion->Next; } if (!ValidRegion) ValidRegion = FirstDiskRegion; DefaultDiskRegion = ValidRegion; } // // Call the menu callback to initialize the status line. // SpPtMenuCallback( (ULONG_PTR)DefaultDiskRegion ); SpMnDisplay( Menu, (ULONG_PTR)DefaultDiskRegion, TRUE, ConsoleRunning ? ValidKeysCmdCons : ValidKeys, PartitionMnemonics, SpPtMenuCallback, &Keypress, (PULONG_PTR)(&pDiskRegion) ); } } } LastUsedDisk = pDiskRegion ? pDiskRegion->DiskNumber : -1; // // Now act on the user's selection. // if(Keypress & KEY_MNEMONIC) { Keypress &= ~KEY_MNEMONIC; } // // Disallow certain operations on partitions that contain local source // or are the system partition (in the x86 floppiless case). // switch(Keypress) { case MnemonicCreatePartition: case MnemonicMakeSystemPartition: case MnemonicDeletePartition: case MnemonicChangeDiskStyle: if( (pDiskRegion->IsLocalSource) || ((Keypress == MnemonicDeletePartition) && (SpPtnIsDeleteAllowedForRegion(pDiskRegion) == FALSE)) #ifdef _X86_ || (IsFloppylessBoot && pDiskRegion == (SpRegionFromArcName(ArcBootDevicePath, PartitionOrdinalOriginal, NULL))) #endif ) { // // Inform the user that we can't do this operation on this // partition. // ULONG MyValidKeys[] = { ASCI_CR }; SpDisplayScreen(SP_SCRN_CONFIRM_INABILITY,3,HEADER_HEIGHT+1); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, 0 ); SpInputDrain(); SpWaitValidKey(MyValidKeys,NULL,NULL); // // Now change the keypress so we'll fall through the next switch. // Keypress = MnemonicUnused; } } switch(Keypress) { case MnemonicCreatePartition: SpPtnDoCreate(pDiskRegion, NULL, FALSE, 0, 0, TRUE); break; case MnemonicMakeSystemPartition: { // // Make sure we don't have any other system partition // if (SPPT_IS_REGION_SYSTEMPARTITION(pDiskRegion)) { ValidArcSystemPartition = TRUE; } if (!ValidArcSystemPartition && pDiskRegion->PartitionedSpace && SpIsArc() && (pDiskRegion->Filesystem != FilesystemNtfs)) { if (NT_SUCCESS(SpPtnMakeRegionArcSysPart(pDiskRegion))) { PDISK_REGION SysPartRegion; // // Ok format the partition if required // SysPartRegion = SpPtnValidSystemPartitionArc(SifHandle, SetupSourceDevicePath, DirectoryOnSetupSource, FALSE); if (SysPartRegion) { ULONG SysPartDiskNumber = SysPartRegion->DiskNumber; BOOLEAN Changes = FALSE; if ((NT_SUCCESS(SpPtnCommitChanges(SysPartDiskNumber, &Changes))) && (NT_SUCCESS(SpPtnInitializeDiskDrive(SysPartDiskNumber)))) { // // create MSR partition if needed // SpPtnInitializeGPTDisk(SysPartDiskNumber); } } } else { ValidArcSystemPartition = FALSE; } } break; } case MnemonicDeletePartition: { BOOLEAN SysPartDeleted = FALSE; BOOLEAN DeletionResult; SysPartDeleted = SPPT_IS_REGION_SYSTEMPARTITION(pDiskRegion); DeletionResult = SpPtnDoDelete(pDiskRegion, SpMnGetText(Menu,(ULONG_PTR)pDiskRegion), TRUE); if (DeletionResult && SysPartDeleted && SpIsArc()) { // // Find out if there are any other // valid system partitions // SpPtnValidSystemPartitionArc(SifHandle, SetupSourceDevicePath, DirectoryOnSetupSource, FALSE); } break; } case MnemonicChangeDiskStyle: { // // Before changing style make sure, its allowed // on this platform for the selected disk // if (SpPtnIsDiskStyleChangeAllowed(pDiskRegion->DiskNumber)) { PARTITION_STYLE Style = SPPT_DEFAULT_PARTITION_STYLE; SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_PLEASE_WAIT, 0); // // flip the style // if (!SPPT_IS_RAW_DISK(pDiskRegion->DiskNumber)) { Style = SPPT_IS_GPT_DISK(pDiskRegion->DiskNumber) ? PARTITION_STYLE_MBR : PARTITION_STYLE_GPT; } Status = SpPtnInitializeDiskStyle(pDiskRegion->DiskNumber, Style, NULL); if (NT_SUCCESS(Status)) { Status = SpPtnInitializeDiskDrive(pDiskRegion->DiskNumber); #if defined(_IA64_) // // Go and figure out the ESP partitions and // initialize the MSR partitions on valid GPT // disks, if none present // if (Style == PARTITION_STYLE_GPT) { ULONG DiskNumber = pDiskRegion->DiskNumber; if (SpIsArc() && !ValidArcSystemPartition && !SpDrEnabled()) { // // Create a system partition // Status = SpPtnCreateESP(TRUE); } // // Initialize the GPT disks, to have MSR // partition // Status = SpPtnInitializeGPTDisk(DiskNumber); } #endif } } break; } case KEY_F3: SpConfirmExit(); break; case ASCI_ESC: if( ConsoleRunning ) { SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_PLEASE_WAIT, 0); SpPtDoCommitChanges(); } if (createdMenu) { SpMnDestroy(Menu); } UnattendedOperation = OldUnattendedOperation; return(STATUS_SUCCESS); case ASCI_CR: Win9xPartition = FALSE; if( SpPtDoPartitionSelection( &pDiskRegion, ((Buffer[0]) ? Buffer : SpMnGetText(Menu,(ULONG_PTR)pDiskRegion)), SifHandle, unattended, SetupSourceDevicePath, DirectoryOnSetupSource, RemoteBootRepartition, &Win9xPartition) ) { *InstallArea = pDiskRegion; #if defined(REMOTE_BOOT) // // Set the install region differently if this is a remote // boot -- in that case, the install region is always remote. // if (RemoteBootSetup && !RemoteInstallSetup) { *InstallArea = RemoteBootTargetRegion; } #endif // defined(REMOTE_BOOT) // // We need to figure out where the system partition is. // if (!SpIsArc()) { *SystemPartitionArea = SpPtnValidSystemPartition(); } else { // // Select a system partition from among those defined in NV-RAM. // We have to do this again because the user may have deleted the // system partition previously detected. // *SystemPartitionArea = SpPtnValidSystemPartitionArc(SifHandle, SetupSourceDevicePath, DirectoryOnSetupSource, FALSE); if (!(*SystemPartitionArea)) { SpPtnPromptForSysPart(SifHandle); break; // user pressed escape to mark the system partition } // // Disallow installation onto ESP / MSR // if (SPPT_IS_REGION_EFI_SYSTEM_PARTITION(*InstallArea) || SPPT_IS_REGION_MSFT_RESERVED(*InstallArea)) { ULONG ValidKeys[] = { ASCI_CR, 0 }; SpDisplayScreen(SP_ESP_INSTALL_PARTITION_SAME, 3, HEADER_HEIGHT+1); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, 0); // // Wait for user input // SpInputDrain(); SpWaitValidKey(ValidKeys, NULL, NULL); break; } // // Disallow non GPT ESPs // if (SpIsArc() && !SPPT_IS_GPT_DISK((*SystemPartitionArea)->DiskNumber)) { ULONG ValidKeys[] = { ASCI_CR, 0 }; SpDisplayScreen(SP_NON_GPT_SYSTEM_PARTITION, 3, HEADER_HEIGHT+1); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, 0); // // Wait for user input // SpInputDrain(); SpWaitValidKey(ValidKeys, NULL, NULL); break; } } // // We're done here. // if (createdMenu) { SpMnDestroy(Menu); } #if defined(REMOTE_BOOT) ASSERT(*SystemPartitionArea || (RemoteBootSetup && !RemoteInstallSetup && (HardDiskCount == 0))); #else ASSERT(*SystemPartitionArea); ASSERT((*SystemPartitionArea)->Filesystem >= FilesystemFat); #endif // defined(REMOTE_BOOT) #ifdef _X86_ // // If we are installing on to the same partition as Win9x then // remove the boot entry for the old operating system // if (Win9xPartition) { DiscardOldSystemLine = TRUE; } #endif UnattendedOperation = OldUnattendedOperation; return(STATUS_SUCCESS); } else { // // Something happened when we tried to select the // partition. Make sure that autopartition-picker // doesn't invoke next time through our while loop. // AutoPartitionPicker = FALSE; } break; } if (createdMenu) { SpMnDestroy(Menu); } unattended = FALSE; } } BOOLEAN SpPtnGenerateDiskMenu( IN PVOID Menu, IN ULONG DiskNumber, OUT PDISK_REGION *FirstDiskRegion ) /*++ Routine Description: Examine the disk for partitioning information and fill in our menu. Arguments: DiskNumber - supplies the disk number of the disk whose partitions we want to inspect for determining their types. Return Value: TRUE Everything went okay. FALSE Something horrible happened. --*/ { WCHAR Buffer[128]; ULONG MessageId; PDISK_REGION Region = NULL; WCHAR DriveLetter[3]; WCHAR PartitionName[128]; ULONGLONG FreeSpaceMB; ULONGLONG AreaSizeMB; ULONGLONG AreaSizeBytes; ULONGLONG OneMB = 1024 * 1024; PHARD_DISK Disk = SPPT_GET_HARDDISK(DiskNumber); PPARTITIONED_DISK PartDisk = SPPT_GET_PARTITIONED_DISK(DiskNumber); // // Get a pointer to the list of regions. // Region = PartDisk->PrimaryDiskRegions; // // Add the disk name/description. // if(!SpMnAddItem(Menu, Disk->Description, MENU_LEFT_X, MENU_WIDTH, FALSE, 0)) { return(FALSE); } // // Only add a line between the disk name and partitions if we have space on // the screen. Not fatal if the space can't be added. // if(!SplangQueryMinimizeExtraSpacing()) { SpMnAddItem(Menu,L"",MENU_LEFT_X,MENU_WIDTH,FALSE,0); } // // If the disk is off-line, add a message indicating such. // if((Disk->Status == DiskOffLine) || !Region) { MessageId = SP_TEXT_DISK_OFF_LINE; if( Disk->Characteristics & FILE_REMOVABLE_MEDIA ) { // // This is removable media, then just tell the user there's // no media in the drive. // MessageId = SP_TEXT_HARD_DISK_NO_MEDIA; } SpFormatMessage( Buffer, sizeof(Buffer), MessageId ); return SpMnAddItem(Menu, Buffer, MENU_LEFT_X + MENU_INDENT, MENU_WIDTH - (2 * MENU_INDENT), FALSE, 0); } // // Now iterate through the areas on the disk and insert that info into the // menu. // while( Region ) { if (!SPPT_IS_REGION_CONTAINER_PARTITION(Region)) { // // remember the very first area that we examine. // if(*FirstDiskRegion == NULL) { *FirstDiskRegion = Region; } // // Figure out how big this disk area is and how much // free space we've got. // if (Region->AdjustedFreeSpaceKB != -1) { FreeSpaceMB = Region->AdjustedFreeSpaceKB / 1024; } else { FreeSpaceMB = 0; } AreaSizeBytes = Region->SectorCount * Disk->Geometry.BytesPerSector; AreaSizeMB = AreaSizeBytes / OneMB; if ((AreaSizeBytes % OneMB) > (OneMB / 2)) AreaSizeMB++; /* SpPtDumpDiskRegion(Region); KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Menu Item Details Free:%I64d,%I64d,%I64d\n", FreeSpaceMB, AreaSizeBytes, AreaSizeMB)); */ // // See if this guy's partitioned. // if(SPPT_IS_REGION_PARTITIONED(Region)){ // // Pickup the driveletter // if( Region->DriveLetter ) { DriveLetter[0] = Region->DriveLetter; } else { DriveLetter[0] = L'-'; } DriveLetter[1] = L':'; DriveLetter[2] = 0; // // Format the partition name // PartitionName[0] = 0; SpPtnGetPartitionName(Region, PartitionName, sizeof(PartitionName)/sizeof(PartitionName[0])); SpFormatMessage( Buffer, sizeof( Buffer ), SP_TEXT_REGION_DESCR_1, DriveLetter, SplangPadString(-35, PartitionName), (ULONG)AreaSizeMB, (ULONG)FreeSpaceMB ); } else { // // It's an unformatted area. Use a different message. // SpFormatMessage( Buffer, sizeof( Buffer ), SP_TEXT_REGION_DESCR_3, (ULONG)AreaSizeMB ); } // // Add the formatted information into the menu. // if(!SpMnAddItem(Menu, Buffer, MENU_LEFT_X + MENU_INDENT, MENU_WIDTH - (2 * MENU_INDENT), TRUE, (ULONG_PTR)Region)) { return(FALSE); } } Region = Region->Next; } return (SplangQueryMinimizeExtraSpacing() ? TRUE : SpMnAddItem(Menu,L"",MENU_LEFT_X,MENU_WIDTH,FALSE,0)); } PDISK_REGION SpPtnValidSystemPartition( VOID ) /*++ Routine Description: Determine whether there is a valid disk partition suitable for use as the system partition on an x86 machine (ie, C:). A primary, recognized (1/4/6/7 type) partition on disk 0 is suitable. If there is a partition that meets these criteria that is marked active, then it is the system partition, regardless of whether there are other partitions that also meet the criteria. Arguments: None. Return Value: Pointer to a disk region descriptor for a suitable system partition (C:) for an x86 machine. NULL if no such partition currently exists. --*/ { PDISK_REGION ActiveRegion , FirstRegion, CurrRegion; PHARD_DISK Disk = NULL; ULONG DiskNumber; DiskNumber = SpDetermineDisk0(); #if defined(REMOTE_BOOT) // // If this is a diskless remote boot setup, there is no drive 0. // if ( DiskNumber == (ULONG)-1 ) { return NULL; } #endif // defined(REMOTE_BOOT) if (!PartitionedDisks) { return NULL; } // // Look for the active partition on drive 0 // and for the first recognized primary partition on drive 0. // CurrRegion = SPPT_GET_PRIMARY_DISK_REGION(DiskNumber); FirstRegion = NULL; ActiveRegion = NULL; while (CurrRegion) { if (SPPT_IS_REGION_PRIMARY_PARTITION(CurrRegion)) { UCHAR PartitionType = SPPT_GET_PARTITION_TYPE(CurrRegion); if(!IsContainerPartition(PartitionType) && ((IsRecognizedPartition(PartitionType)) || (CurrRegion->DynamicVolume && CurrRegion->DynamicVolumeSuitableForOS) || ((RepairWinnt || WinntSetup || SpDrEnabled() ) && CurrRegion->FtPartition))) { if (!FirstRegion) FirstRegion = CurrRegion; if (!ActiveRegion && SPPT_IS_REGION_ACTIVE_PARTITION(CurrRegion)) { ActiveRegion = CurrRegion; break; } } } CurrRegion = CurrRegion->Next; } #ifdef TESTING_SYSTEM_PARTITION KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "%p Active, %p First\n", ActiveRegion, FirstRegion)); if (ActiveRegion) FirstRegion = ActiveRegion; ActiveRegion = NULL; #endif /* // // Don't do commit here as the multiple caller's are trying // to reuse the old region from the existing linked list // of regions for the disk after this // if (!ActiveRegion && FirstRegion) { BOOLEAN Changes = FALSE; ULONGLONG StartSector = FirstRegion->StartSector; SpPtnMakeRegionActive(FirstRegion); SPPT_MARK_REGION_AS_SYSTEMPARTITION(FirstRegion, TRUE); if (NT_SUCCESS(SpPtnCommitChanges(DiskNumber, &Changes)) && Changes) { SPPT_MARK_REGION_AS_ACTIVE(FirstRegion, TRUE); KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP:SpPtnValidSystempartition():succeeded in marking\n")); } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP:SpPtnValidSystempartition():Could not mark the first " "partition on primary disk as active\n")); } } */ // // If there is an active, recognized region, use it as the // system partition. Otherwise, use the first primary // we encountered as the system partition. If there is // no recognized primary, then there is no valid system partition. // return (ActiveRegion ? ActiveRegion : FirstRegion); } #if 0 ULONG SpDetermineDisk0( VOID ) /*++ Routine Description: Determine the real disk 0, which may not be the same as \device\harddisk0. Consider the case where we have 2 scsi adapters and the NT drivers load in an order such that the one with the BIOS gets loaded *second* -- meaning that the system partition is actually on disk 1, not disk 0. Arguments: None. Return Value: NT disk ordinal suitable for use in generating nt device paths of the form \device\harddiskx. --*/ { ULONG DiskNumber = SpArcDevicePathToDiskNumber(L"multi(0)disk(0)rdisk(0)"); #if defined(REMOTE_BOOT) // // If this is a diskless remote boot setup, there is no drive 0. // if ( RemoteBootSetup && (DiskNumber == (ULONG)-1) && (HardDiskCount == 0) ) { return DiskNumber; } #endif // defined(REMOTE_BOOT) return((DiskNumber == (ULONG)(-1)) ? 0 : DiskNumber); } #endif BOOL SpPtnIsSystemPartitionRecognizable( VOID ) /*++ Routine Description: Determine whether the active partition is suitable for use as the system partition on an x86 machine (ie, C:). A primary, recognized (1/4/6/7 type) partition on disk 0 is suitable. Arguments: None. Return Value: TRUE - We found a suitable partition FALSE - We didn't find a suitable partition --*/ { ULONG DiskNumber; PDISK_REGION Region = NULL; // // Any partitions on NEC98 are primary and active. So don't need to check on NEC98. // if( IsNEC_98 ) { return TRUE; } DiskNumber = SpDetermineDisk0(); // // Look for the active partition on drive 0 // and for the first recognized primary partition on drive 0. // Region = SPPT_GET_PRIMARY_DISK_REGION(DiskNumber); if (SPPT_IS_GPT_DISK(DiskNumber)) { // // On GPT we just need a valid formatted partition // while (Region) { if (SPPT_IS_REGION_PARTITIONED(Region) && SPPT_IS_RECOGNIZED_FILESYSTEM(Region->Filesystem)) { break; } Region = Region->Next; } } else { // // On MBR we need a valid active formatted partition // while (Region) { if (SPPT_IS_REGION_ACTIVE_PARTITION(Region) && SPPT_IS_RECOGNIZED_FILESYSTEM(Region->Filesystem)) { break; } Region = Region->Next; } } return (Region) ? TRUE : FALSE; } BOOLEAN SpPtnValidSystemPartitionArcRegion( IN PVOID SifHandle, IN PDISK_REGION Region ) { BOOLEAN Valid = FALSE; if (SPPT_IS_REGION_SYSTEMPARTITION(Region) && (Region->FreeSpaceKB != -1) && (Region->Filesystem == FilesystemFat)) { ULONG TotalSizeOfFilesOnOsWinnt = 0; ULONG RequiredSpaceKB = 0; // // On non-x86 platformrs, specially alpha machines that in general // have small system partitions (~3 MB), we should compute the size // of the files on \os\winnt (currently, osloader.exe and hall.dll), // and consider this size as available disk space. We can do this // since these files will be overwritten by the new ones. // This fixes the problem that we see on Alpha, when the system // partition is too full. // SpFindSizeOfFilesInOsWinnt( SifHandle, Region, &TotalSizeOfFilesOnOsWinnt ); // // Transform the size into KB // TotalSizeOfFilesOnOsWinnt /= 1024; // // Determine the amount of free space required on a system partition. // SpFetchDiskSpaceRequirements( SifHandle, Region->BytesPerCluster, NULL, &RequiredSpaceKB ); if ((Region->FreeSpaceKB + TotalSizeOfFilesOnOsWinnt) >= RequiredSpaceKB) { Valid = TRUE; } } return Valid; } PDISK_REGION SpPtnValidSystemPartitionArc( IN PVOID SifHandle, IN PWSTR SetupSourceDevicePath, IN PWSTR DirectoryOnSetupSource, IN BOOLEAN SysPartNeeded ) /*++ Routine Description: Determine whether there is a valid disk partition suitable for use as the system partition on an ARC machine. A partition is suitable if it is marked as a system partition in nvram, has the required free space and is formatted with the FAT filesystem. Arguments: SifHandle - supplies handle to loaded setup information file. Return Value: Pointer to a disk region descriptor for a suitable system partition. Does not return if no such partition exists. --*/ { ULONG RequiredSpaceKB = 0; PDISK_REGION Region = NULL; PPARTITIONED_DISK PartDisk; ULONG Index; // // Go through all the regions. The one that's maked system partition // or is valid system partition is used for further validation. // for(Index = 0; (Index < HardDiskCount) && (!Region); Index++) { PartDisk = SPPT_GET_PARTITIONED_DISK(Index); Region = SPPT_GET_PRIMARY_DISK_REGION(Index); while (Region) { if (SPPT_IS_REGION_PARTITIONED(Region) && SPPT_IS_REGION_SYSTEMPARTITION(Region)) { break; // found the required region } Region = Region->Next; } } // // If the region is there and not formatted format it as FAT // file system // if (Region && (Region->Filesystem < FilesystemFat)) { WCHAR DriveLetterString[4] = {0}; DriveLetterString[0] = Region->DriveLetter; if (!UnattendedOperation) { ULONG ValidKeys[] = { KEY_F3, 0 }; ULONG Mnemonics[] = { MnemonicFormat, 0 }; ULONG KeyPressed; ULONG EscKey = SysPartNeeded ? KEY_F3 : ASCI_ESC; ValidKeys[0] = SysPartNeeded ? KEY_F3 : ASCI_ESC; SpStartScreen(SysPartNeeded ? SP_SCRN_C_UNKNOWN_1 : SP_SCRN_C_UNKNOWN, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, DriveLetterString ); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F_EQUALS_FORMAT, SysPartNeeded ? SP_STAT_F3_EQUALS_EXIT : SP_STAT_ESC_EQUALS_CANCEL, 0); SpInputDrain(); KeyPressed = SpWaitValidKey(ValidKeys, NULL, Mnemonics); if (KeyPressed == EscKey) { Region = NULL; } } if (Region) { WCHAR RegionStr[128]; NTSTATUS FormatStatus; swprintf( RegionStr, L"\\Harddisk%u\\Partition%u", Region->DiskNumber, Region->PartitionNumber ); // // Format the system region with Fat file system // FormatStatus = SpDoFormat(RegionStr, Region, FilesystemFat, TRUE, TRUE, FALSE, SifHandle, 0, // default cluster size SetupSourceDevicePath, DirectoryOnSetupSource); if (!NT_SUCCESS(FormatStatus)) { SpStartScreen(SP_SCRN_SYSPART_FORMAT_ERROR, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, DriveLetterString ); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, 0); SpInputDrain(); while(SpInputGetKeypress() != KEY_F3) ; SpDone(0, FALSE, TRUE); } // // Since we have formatted system partition, make sure // it has adequate space to hold the startup files // if(!SpPtnValidSystemPartitionArcRegion(SifHandle, Region)) Region = NULL; } } if (!Region && SysPartNeeded) { // // Make sure we don't look bad. // if( RequiredSpaceKB == 0 ) { SpFetchDiskSpaceRequirements( SifHandle, (32 * 1024), NULL, &RequiredSpaceKB ); } // // No valid system partition. // SpStartScreen( SP_SCRN_NO_SYSPARTS, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, RequiredSpaceKB ); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, 0); SpInputDrain(); // // wait for F3 // while (SpInputGetKeypress() != KEY_F3) ; SpDone(0, FALSE, TRUE); } ValidArcSystemPartition = (Region != NULL); return Region; } NTSTATUS SpPtnMarkLogicalDrives( IN ULONG DiskId ) /*++ Routine Description: Walks through the region linked list and marks the container partition and the logical drives. Also marks the free space inside container partition as contained space Arguments: DiskId : Disk to process Return Value: STATUS_SUCCESS if successful, otherwise approprite error code --*/ { NTSTATUS Status = STATUS_SUCCESS; if (SPPT_IS_MBR_DISK(DiskId)) { PDISK_REGION Region = SPPT_GET_PRIMARY_DISK_REGION(DiskId); PDISK_REGION FirstContainer = NULL; PDISK_REGION PrevContainer = NULL; while (Region) { if (SPPT_IS_REGION_CONTAINER_PARTITION(Region)) { if (!FirstContainer) { FirstContainer = Region; Region->Container = NULL; } else { Region->Container = FirstContainer; } PrevContainer = Region; } else { if (PrevContainer) { if (SPPT_IS_REGION_CONTAINED(PrevContainer, Region)) { Region->Container = PrevContainer; if (SPPT_IS_REGION_PARTITIONED(Region)) SPPT_SET_REGION_EPT(Region, EPTLogicalDrive); } else { if (SPPT_IS_REGION_CONTAINED(FirstContainer, Region)) Region->Container = FirstContainer; } } } Region = Region->Next; } } return Status; } ULONG SpPtnGetOrdinal( IN PDISK_REGION Region, IN PartitionOrdinalType OrdinalType ) /*++ Routine Description: Gets the Ordinal for the specified region of the specified type. Arguments: Region - Region whose ordinal has to be found OrdinalType - Type of ordinal for the region Return Value: -1 if invalid request, otherwise appropriate ordinal number for the region. --*/ { ULONG Ordinal = -1; if (Region && Region->PartitionNumber && SPPT_IS_REGION_PARTITIONED(Region)) { switch (OrdinalType) { case PartitionOrdinalOnDisk: if (SPPT_IS_MBR_DISK(Region->DiskNumber) && !SPPT_IS_REGION_CONTAINER_PARTITION(Region)) { Ordinal = Region->TablePosition; } else if (SPPT_IS_GPT_DISK(Region->DiskNumber)) { Ordinal = Region->TablePosition; } // // Ordinal zero is not valid // if (Ordinal == 0) { Ordinal = -1; } break; default: Ordinal = Region->PartitionNumber; break; } } if( Ordinal == -1 ) { // // This is really bad. We're about to // fall over. Atleast try... // ASSERT(FALSE); Ordinal = 1; KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPtnGetOrdinal: We didn't get an ordinal! Force it.\n" )); } return Ordinal; } VOID SpPtnGetSectorLayoutInformation( IN PDISK_REGION Region, OUT PULONGLONG HiddenSectors, OUT PULONGLONG VolumeSectorCount ) /*++ Routine Description: Gets the hidden sector and sector count for the formatted partitions (volumes) Arguments: Region - The region for which the sector layout information is needed HiddenSectors - Place holder to return the # of hidden sectors for the region VolumeSectorCount- Place holder to return the # of valid sectors for the volume Return Value: None --*/ { ULONGLONG Hidden = 0; if (Region) { if (ARGUMENT_PRESENT(HiddenSectors)) { if (Region->PartInfo.PartitionStyle == PARTITION_STYLE_MBR) Hidden = Region->PartInfo.Mbr.HiddenSectors; else Hidden = 0; *HiddenSectors = Hidden; } if (ARGUMENT_PRESENT(VolumeSectorCount)) { *VolumeSectorCount = Region->SectorCount - Hidden; } } } NTSTATUS SpPtnUnlockDevice( IN PWSTR DeviceName ) /*++ Routine Description: Attempts to unlock the media for the given device name (NT device pathname) Arguments: DeviceName : The device for which the media needs to be unlocked Return Value: STATUS_SUCCESS if successful, otherwise appropriate error code --*/ { NTSTATUS Status = STATUS_INVALID_PARAMETER; if (DeviceName) { IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UnicodeString; HANDLE Handle; PREVENT_MEDIA_REMOVAL PMRemoval; INIT_OBJA(&ObjectAttributes, &UnicodeString, DeviceName); // // Open the device // Status = ZwCreateFile( &Handle, FILE_GENERIC_WRITE, &ObjectAttributes, &IoStatusBlock, NULL, // allocation size FILE_ATTRIBUTE_NORMAL, FILE_SHARE_VALID_FLAGS, // full sharing FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, // no EAs 0 ); if( NT_SUCCESS(Status) ) { // // Allow media removal // PMRemoval.PreventMediaRemoval = FALSE; Status = ZwDeviceIoControlFile( Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_STORAGE_MEDIA_REMOVAL, &PMRemoval, sizeof(PMRemoval), NULL, 0 ); ZwClose(Handle); if( !NT_SUCCESS(Status) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "Setup: SpPtnUnlockDevice(%ws) - " "Failed to tell the floppy to release its media.\n", DeviceName)); } } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "Setup: SpPtnUnlockDevice(%ws) - Failed to open the device.\n", DeviceName)); } } return Status; } VOID SpPtnAssignOrdinals( IN ULONG DiskNumber ) /*++ Routine Description: Assigns the on disk ordinal for the partitions for the requested disk. This on disk ordinal is used in the boot.ini (or NVRAM) ARC names to identify the boot and system partition devices. Arguments: DiskNumber : Disk Index for the disk which needs to be assigned on disk ordinal for its partitions Return Value: None. --*/ { if ((DiskNumber < HardDiskCount)) { PDISK_REGION Region = SPPT_GET_PRIMARY_DISK_REGION(DiskNumber); ULONG OnDiskOrdinal = 1; if (SPPT_IS_MBR_DISK(DiskNumber)) { // // assign the ordinals to the primary partitions first // while (Region) { if (SPPT_IS_REGION_PRIMARY_PARTITION(Region)) { Region->TablePosition = OnDiskOrdinal++; } Region = Region->Next; } Region = SPPT_GET_PRIMARY_DISK_REGION(DiskNumber); // // assign the ordinals to the logical drives next // while (Region) { if (SPPT_IS_REGION_LOGICAL_DRIVE(Region)) { Region->TablePosition = OnDiskOrdinal++; } Region = Region->Next; } } else { // // assign ordinals to the valid partition entries // while (Region) { if (SPPT_IS_REGION_PARTITIONED(Region)) { Region->TablePosition = OnDiskOrdinal++; } Region = Region->Next; } } } } VOID SpPtnLocateSystemPartitions( VOID ) /*++ Routine Description: Locates and marks the system partition, by looking into all the partitioned space on all the disks. For non ARC machines, locates and marks the system partition only on the primary disk Arguments: None Return Value: None --*/ { ULONG DiskNumber; if (SpIsArc()) { for (DiskNumber = 0; DiskNumber < HardDiskCount; DiskNumber++) { SpPtnLocateDiskSystemPartitions(DiskNumber); } } else { DiskNumber = SpDetermineDisk0(); if (DiskNumber != -1) SpPtnLocateDiskSystemPartitions(DiskNumber); } } VOID SpPtnLocateDiskSystemPartitions( IN ULONG DiskNumber ) /*++ Routine Description: Locates and marks the system partition for the requested disk (if none exists) For non ARC machine, only operates on primary disk Arguments: DiskNumber : Disk index, for which system partition needs to be located and marked. Return Value: None. --*/ { PDISK_REGION Region = NULL; if(!SpIsArc()) { // // Note: On X86 we currently don't allow system partitions to reside // on GPT disks // if (SPPT_IS_MBR_DISK(DiskNumber) && (DiskNumber == SpDetermineDisk0())) { // // On x86 machines, we will mark any primary partitions on drive 0 // as system partition, since such a partition is potentially bootable. // Region = SPPT_GET_PRIMARY_DISK_REGION(DiskNumber); while (Region && !SPPT_IS_REGION_SYSTEMPARTITION(Region)) { Region = Region->Next; } if (!Region) { Region = SPPT_GET_PRIMARY_DISK_REGION(DiskNumber); while (Region) { if (SPPT_IS_REGION_PRIMARY_PARTITION(Region)) { SPPT_MARK_REGION_AS_SYSTEMPARTITION(Region, TRUE); SPPT_SET_REGION_DIRTY(Region, TRUE); break; } Region = Region->Next; } } } } else { PSP_BOOT_ENTRY BootEntry; // // Don't look for system partitions on MBR disks // on IA64 // if (!SPPT_IS_GPT_DISK(DiskNumber)) { return; } // // On ARC machines, system partitions are specifically enumerated // in the NVRAM boot environment. // Region = SPPT_GET_PRIMARY_DISK_REGION(DiskNumber); while (Region) { // // Skip if not a partition or extended partition. // if(SPPT_IS_REGION_PARTITIONED(Region)) { // // Get the nt pathname for this region. // SpNtNameFromRegion( Region, TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalOriginal ); // // Determine if it is a system partition. // for(BootEntry = SpBootEntries; BootEntry != NULL; BootEntry = BootEntry->Next) { if(!IS_BOOT_ENTRY_DELETED(BootEntry) && IS_BOOT_ENTRY_WINDOWS(BootEntry) && (BootEntry->LoaderPartitionNtName != 0) && !_wcsicmp(BootEntry->LoaderPartitionNtName,TemporaryBuffer)) { if (!SPPT_IS_REGION_SYSTEMPARTITION(Region)) { SPPT_MARK_REGION_AS_SYSTEMPARTITION(Region, TRUE); SPPT_SET_REGION_DIRTY(Region, TRUE); ValidArcSystemPartition = TRUE; } break; } } } Region = Region->Next; } } KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP:SpPtnLocateDiskSystemPartitions(%d):%p\n", DiskNumber, Region)); if (Region) SpPtDumpDiskRegion(Region); } BOOLEAN SpPtnIsDiskStyleChangeAllowed( IN ULONG DiskNumber ) /*++ Routine Description: Finds out whether disk style change is allowed for the given disk. On AXP machines disk style change is not allowed. On X-86 machines currently disk style change is disabled for primary disks. Arguments: DiskNumber : Disk, whose style needs to be changed. Return Value: TRUE if disk style change is allowed, otherwise FALSE --*/ { BOOLEAN Result = FALSE; if (DiskNumber < HardDiskCount) { #if defined(_X86_) // // On non ARC x86 machines, the disk should be a clean // non-removable secondary disk // // Don't allow MBR to GPT disk conversion on X86 // Result = (!SPPT_IS_REMOVABLE_DISK(DiskNumber) && SPPT_IS_BLANK_DISK(DiskNumber) && !SpIsArc() && SPPT_IS_GPT_DISK(DiskNumber)); #elif defined (_IA64_) // // Don't allow conversion from GPT to MBR on IA-64 // Result = !SPPT_IS_REMOVABLE_DISK(DiskNumber) && SPPT_IS_BLANK_DISK(DiskNumber) && SPPT_IS_MBR_DISK(DiskNumber); #endif } return Result; } VOID SpPtnPromptForSysPart( IN PVOID SifHandle ) /*++ Routine Description: Prompts the user about the absence of system partition while installating to another valid non-system partition. Allows the user to quit setup or continue (generally go back to the partitioning engine) Arguments: SifHandle : Handle to txtsetup.sif (to do space calculation) Return Value: None --*/ { ULONG RequiredSpaceKB = 0; ULONG KeyPressed = 0; SpFetchDiskSpaceRequirements( SifHandle, (32 * 1024), NULL, &RequiredSpaceKB ); // // No valid system partition. // SpStartScreen( SP_SCRN_MARK_SYSPART, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, RequiredSpaceKB ); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ESC_EQUALS_CANCEL, SP_STAT_F3_EQUALS_EXIT, 0); SpInputDrain(); // // wait for F3 or ESC key // while ((KeyPressed != KEY_F3) && (KeyPressed != ASCI_ESC)) { KeyPressed = SpInputGetKeypress(); } if (KeyPressed == KEY_F3) { SpDone(0, FALSE, TRUE); } } BOOLEAN SpPtnIsDeleteAllowedForRegion( IN PDISK_REGION Region ) /*++ Routine Description: Given a region this function tries to find out if the region can be deleted. Arguments: Region : Pointer to region which is to be checked for deletion Return Value: TRUE if the given region can be deleted otherwise FALSE --*/ { BOOLEAN Result = FALSE; if (Region && SPPT_IS_REGION_PARTITIONED(Region)) { PDISK_REGION BootRegion = SpRegionFromNtName(NtBootDevicePath, PartitionOrdinalCurrent); ULONG DiskNumber = Region->DiskNumber; if (SPPT_IS_REGION_DYNAMIC_VOLUME(Region)) { // // Don't delete the dynamic volume if its on // the same disk as local source or system partition // if (!(LocalSourceRegion && (LocalSourceRegion->DiskNumber == DiskNumber)) && !(BootRegion && (BootRegion->DiskNumber == DiskNumber))) { Result = TRUE; } } else { Result = ((BootRegion != Region) && (LocalSourceRegion != Region)); } } return Result; } BOOLEAN SpPtnIsRawDiskDriveLayout( IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout ) /*++ Routine Description: Given a drive layout tests whether the given drive layout could be for a raw disk NOTE : If all the partition entries are empty entries or if there are no partition entries then we assume the disk to be RAW disk. Arguments: DriveLayout : Drive layout information that needs to be tested Return Value: TRUE if the given disk is RAW otherwise FALSE --*/ { BOOLEAN Result = TRUE; if (DriveLayout && DriveLayout->PartitionCount && (DriveLayout->PartitionStyle != PARTITION_STYLE_RAW)) { ULONG Index; for (Index=0; Index < DriveLayout->PartitionCount; Index++) { PPARTITION_INFORMATION_EX PartInfo = DriveLayout->PartitionEntry + Index; // // Partition is invalid partition if // - starting offset is 0 and // - length is 0 and // - partition number is 0 // if ((PartInfo->StartingOffset.QuadPart) || (PartInfo->PartitionLength.QuadPart) || (PartInfo->PartitionNumber)) { Result = FALSE; break; // found an valid partition entry } } } return Result; } BOOLEAN SpPtnIsDynamicDisk( IN ULONG DiskIndex ) /*++ Routine Description: Determines whether the given disk is dynamic i.e. it has atleast a single dynamic volume Arguments: DiskIndex - Zero based index of the disk to test Return Value: TRUE, if the disk has a dynamic volume otherwise FALSE --*/ { BOOLEAN Result = FALSE; if ((DiskIndex < HardDiskCount) && !SPPT_IS_REMOVABLE_DISK(DiskIndex)) { PDISK_REGION Region = SPPT_GET_PRIMARY_DISK_REGION(DiskIndex); while (Region && !SPPT_IS_REGION_DYNAMIC_VOLUME(Region)) { Region = Region->Next; } if (Region) { Result = TRUE; } } return Result; } // // Callback context structure for finding the Guid volume name // for the specified NT partition name // typedef struct _NT_TO_GUID_VOLUME_NAME { WCHAR NtName[MAX_PATH]; WCHAR GuidVolumeName[MAX_PATH]; } NT_TO_GUID_VOLUME_NAME, *PNT_TO_GUID_VOLUME_NAME; static BOOLEAN SppPtnCompareGuidNameForPartition( IN PVOID Context, IN PMOUNTMGR_MOUNT_POINTS MountPoints, IN PMOUNTMGR_MOUNT_POINT MountPoint ) /*++ Routine Description: Callback routine for searching the appropriate GUID volume name for the specified NT partition. Arguments: Context : PNT_TO_GUID_VOLUME_NAME pointer disguised as PVOID MountPoints : The MountPoints which were received from mountmgr. NOTE : The only reason this is here is because somebody created MOUNT_POINT structure abstraction contained inside MOUNT_POINTS which has some fields (like SymbolicNameOffset) which are relative to the MOUNT_POINTS. MountPoint : The current mountpoint (as part of MountPoints) Return Value: TRUE if we found a match and want to terminate the iteration else FALSE. --*/ { BOOLEAN Result = FALSE; if (Context && MountPoint && MountPoint->SymbolicLinkNameLength) { WCHAR CanonicalName[MAX_PATH]; PWSTR GuidName = NULL; UNICODE_STRING String; PNT_TO_GUID_VOLUME_NAME Map = (PNT_TO_GUID_VOLUME_NAME)Context; GuidName = SpMemAlloc(MountPoint->SymbolicLinkNameLength + 2); if (GuidName) { // // Copy over the symbolic name and null terminate it // RtlCopyMemory(GuidName, ((PCHAR)MountPoints) + MountPoint->SymbolicLinkNameOffset, MountPoint->SymbolicLinkNameLength); GuidName[MountPoint->SymbolicLinkNameLength/sizeof(WCHAR)] = UNICODE_NULL; RtlInitUnicodeString(&String, GuidName); // // We are only bothered about volume names & // resolve the actual object name // if (MOUNTMGR_IS_VOLUME_NAME(&String) && NT_SUCCESS(SpQueryCanonicalName(GuidName, -1, CanonicalName, sizeof(CanonicalName)))) { // // Do the names compare correctly // Result = (_wcsicmp(CanonicalName, Map->NtName) == 0); if (Result) { // // Copy the name to the result // RtlZeroMemory(Map->GuidVolumeName, sizeof(Map->GuidVolumeName)); wcsncpy(Map->GuidVolumeName, GuidName, sizeof(Map->GuidVolumeName)/sizeof(WCHAR) - 1); Map->GuidVolumeName[sizeof(Map->GuidVolumeName)/sizeof(WCHAR) - 1] = UNICODE_NULL; } } SpMemFree(GuidName); } } return Result; } NTSTATUS SpPtnGetGuidNameForPartition( IN PWSTR NtPartitionName, IN OUT PWSTR VolumeName ) /*++ Routine Description: Gets the GUID volume name (in \\??\Volume{a-b-c-d} format) for the given NT partition name (in \Device\harddiskX\PartitionY format). Arguments: NtPartitionName : NT partition name VolumeName : Place holder buffer for receiving the GUID volume name. Should be atlease MAX_PATH in length. Return Value: Approriate NTSTATUS code. --*/ { NTSTATUS Status = STATUS_INVALID_PARAMETER; if (NtPartitionName && VolumeName) { NT_TO_GUID_VOLUME_NAME Context = {0}; // // Resolve the NT name to actual object name // Status = SpQueryCanonicalName(NtPartitionName, -1, Context.NtName, sizeof(Context.NtName)); if (NT_SUCCESS(Status)) { // // Iterate through mountpoints and try to // get the GUID volume name for the NT name // Status = SpIterateMountMgrMountPoints(&Context, SppPtnCompareGuidNameForPartition); if (NT_SUCCESS(Status)) { if (Context.GuidVolumeName[0]) { // // Copy over the result // wcscpy(VolumeName, Context.GuidVolumeName); } else { Status = STATUS_OBJECT_NAME_NOT_FOUND; } } } } return Status; }