/*++ Copyright (c) 2014 Minoca Corp. All Rights Reserved Module Name: part.c Abstract: This module implements partition support for the setup app on Minoca OS. Author: Evan Green 10-Apr-2014 Environment: User --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include #include "../setup.h" #include // // ---------------------------------------------------------------- Definitions // // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // // // -------------------------------------------------------------------- Globals // UUID SetupPartitionDeviceInformationUuid = PARTITION_DEVICE_INFORMATION_UUID; // // ------------------------------------------------------------------ Functions // INT SetupOsEnumerateDevices ( PSETUP_PARTITION_DESCRIPTION *DeviceArray, PULONG DeviceCount ) /*++ Routine Description: This routine enumerates all the disks and partitions on the system. Arguments: DeviceArray - Supplies a pointer where an array of partition structures will be returned on success. DeviceCount - Supplies a pointer where the number of elements in the partition array will be returned on success. Return Value: 0 on success. Returns an error code on failure. --*/ { UINTN AllocationSize; UINTN ArrayIndex; IO_BOOT_INFORMATION BootInformation; UINTN BootInformationSize; INT CompareResult; UINTN DataSize; SETUP_DESTINATION_TYPE DestinationType; PSETUP_PARTITION_DESCRIPTION Device; PSETUP_PARTITION_DESCRIPTION Devices; ULONG ResultCount; UINTN ResultIndex; PDEVICE_INFORMATION_RESULT Results; KSTATUS Status; ArrayIndex = 0; Devices = NULL; ResultCount = 0; Results = NULL; // // Get the boot information. // BootInformationSize = sizeof(IO_BOOT_INFORMATION); Status = OsGetSetSystemInformation(SystemInformationIo, IoInformationBoot, &BootInformation, &BootInformationSize, FALSE); if (!KSUCCESS(Status)) { goto OsEnumerateDevicesEnd; } // // Enumerate all the devices that support getting partition device // information. // Status = OsLocateDeviceInformation(&SetupPartitionDeviceInformationUuid, NULL, NULL, &ResultCount); if (Status != STATUS_BUFFER_TOO_SMALL) { goto OsEnumerateDevicesEnd; } if (ResultCount == 0) { Status = STATUS_SUCCESS; goto OsEnumerateDevicesEnd; } AllocationSize = sizeof(DEVICE_INFORMATION_RESULT) * ResultCount; Results = malloc(AllocationSize); if (Results == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto OsEnumerateDevicesEnd; } memset(Results, 0, AllocationSize); Status = OsLocateDeviceInformation(&SetupPartitionDeviceInformationUuid, NULL, Results, &ResultCount); if (!KSUCCESS(Status)) { goto OsEnumerateDevicesEnd; } if (ResultCount == 0) { Status = STATUS_SUCCESS; goto OsEnumerateDevicesEnd; } // // Allocate the real array. // AllocationSize = sizeof(SETUP_PARTITION_DESCRIPTION) * ResultCount; Devices = malloc(AllocationSize); if (Devices == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto OsEnumerateDevicesEnd; } memset(Devices, 0, AllocationSize); // // Loop through the results setting up the structure elements. // for (ResultIndex = 0; ResultIndex < ResultCount; ResultIndex += 1) { Device = &(Devices[ArrayIndex]); // // Get the partition information. // DataSize = sizeof(PARTITION_DEVICE_INFORMATION); Status = OsGetSetDeviceInformation(Results[ResultIndex].DeviceId, &SetupPartitionDeviceInformationUuid, &(Device->Partition), &DataSize, FALSE); // // If that worked, create the destination structure. // if (KSUCCESS(Status)) { if ((Device->Partition.Flags & PARTITION_FLAG_RAW_DISK) != 0) { DestinationType = SetupDestinationDisk; CompareResult = memcmp( &(Device->Partition.DiskId), &(BootInformation.SystemDiskIdentifier), sizeof(BootInformation.SystemDiskIdentifier)); if (CompareResult == 0) { Device->Flags |= SETUP_DEVICE_FLAG_SYSTEM; } } else { DestinationType = SetupDestinationPartition; CompareResult = memcmp( &(Device->Partition.PartitionId), &(BootInformation.SystemPartitionIdentifier), sizeof(BootInformation.SystemPartitionIdentifier)); if (CompareResult == 0) { Device->Flags |= SETUP_DEVICE_FLAG_SYSTEM; } } Device->Destination = SetupCreateDestination( DestinationType, NULL, Results[ResultIndex].DeviceId); // // If that worked, advance the array index. // if (Device->Destination != NULL) { ArrayIndex += 1; } } } Status = STATUS_SUCCESS; OsEnumerateDevicesEnd: if (Results != NULL) { free(Results); } if (!KSUCCESS(Status)) { if (Devices != NULL) { SetupDestroyDeviceDescriptions(Devices, ArrayIndex); Devices = NULL; } return ClConvertKstatusToErrorNumber(Status); } *DeviceArray = Devices; *DeviceCount = ArrayIndex; return 0; } INT SetupOsGetPartitionInformation ( PSETUP_DESTINATION Destination, PPARTITION_DEVICE_INFORMATION Information ) /*++ Routine Description: This routine returns the partition information for the given destination. Arguments: Destination - Supplies a pointer to the partition to query. Information - Supplies a pointer where the information will be returned on success. Return Value: 0 on success. Returns an error code on failure. --*/ { UINTN Size; KSTATUS Status; if (Destination->Path != NULL) { return EINVAL; } Size = sizeof(PARTITION_DEVICE_INFORMATION); Status = OsGetSetDeviceInformation(Destination->DeviceId, &SetupPartitionDeviceInformationUuid, Information, &Size, FALSE); if (!KSUCCESS(Status)) { fprintf(stderr, "Failed to get partition information: %d\n", Status); return ClConvertKstatusToErrorNumber(Status); } return 0; } PVOID SetupOsOpenBootVolume ( PSETUP_CONTEXT Context ) /*++ Routine Description: This routine opens the boot volume on the current machine. Arguments: Context - Supplies a pointer to the application context. Return Value: Returns the open handle to the boot volume on success. NULL on failure. --*/ { PSETUP_PARTITION_DESCRIPTION BootPartition; PVOID BootVolume; INT Compare; ULONG Index; BOOL MultipleNonSystem; PSETUP_PARTITION_DESCRIPTION Partition; ULONG PartitionCount; PARTITION_DEVICE_INFORMATION PartitionInformation; PSETUP_PARTITION_DESCRIPTION Partitions; INT Result; PSETUP_PARTITION_DESCRIPTION SecondBest; PSETUP_PARTITION_DESCRIPTION SystemDisk; BootVolume = NULL; Partitions = NULL; Result = SetupOsEnumerateDevices(&Partitions, &PartitionCount); if (Result != 0) { fprintf(stderr, "Failed to enumerate partitions.\n"); goto OsOpenBootVolumeEnd; } // // First find the system disk, or at least try to. // SystemDisk = NULL; for (Index = 0; Index < PartitionCount; Index += 1) { Partition = &(Partitions[Index]); if ((Partition->Destination->Type == SetupDestinationDisk) && ((Partition->Flags & SETUP_DEVICE_FLAG_SYSTEM) != 0)) { SystemDisk = Partition; break; } } // // Loop across all partitions and disks looking for the EFI system // partition. // MultipleNonSystem = FALSE; BootPartition = NULL; SecondBest = NULL; for (Index = 0; Index < PartitionCount; Index += 1) { Partition = &(Partitions[Index]); if ((Partition->Destination->Type == SetupDestinationPartition) && (((Partition->Partition.Flags & PARTITION_FLAG_BOOT) != 0) || (Partition->Partition.PartitionType == PartitionTypeEfiSystem))) { Compare = -1; if (SystemDisk != NULL) { Compare = memcmp(Partition->Partition.DiskId, SystemDisk->Partition.DiskId, DISK_IDENTIFIER_SIZE); } // // If it is on the system disk, try to assign it directly to // the winner, and fail if it's already set. // if (Compare == 0) { if (BootPartition == NULL) { BootPartition = Partition; } else { Result = ENODEV; printf("Error: Setup found multiple boot " "partitions.\n"); goto OsOpenBootVolumeEnd; } // // If it's not on the boot device, track it as the second best // and remember if there are multiple of these. // } else { if (SecondBest == NULL) { SecondBest = Partition; } else { MultipleNonSystem = TRUE; } } } } if ((BootPartition == NULL) && (SecondBest != NULL)) { if (MultipleNonSystem != FALSE) { fprintf(stderr, "Error: Found more than one boot partition on the system " "disk.\n"); Result = ENODEV; goto OsOpenBootVolumeEnd; } BootPartition = SecondBest; } if (BootPartition == NULL) { fprintf(stderr, "Failed to find boot partition.\n"); goto OsOpenBootVolumeEnd; } assert(Context->Disk == NULL); memset(&PartitionInformation, 0, sizeof(PartitionInformation)); Context->Disk = SetupPartitionOpen(Context, BootPartition->Destination, &PartitionInformation); if (Context->Disk == NULL) { fprintf(stderr, "Failed to open boot partition.\n"); goto OsOpenBootVolumeEnd; } // // If the disk identifier has not yet been set, set it now. This assumes // that if installing to a directory, the directory resides on the same // disk as the boot partition. // Compare = memcmp(PartitionInformation.DiskId, &SetupZeroDiskIdentifier, DISK_IDENTIFIER_SIZE); if (Compare == 0) { memcpy(&(Context->PartitionContext.DiskIdentifier), PartitionInformation.DiskId, DISK_IDENTIFIER_SIZE); } Context->CurrentPartitionOffset = 0; Context->CurrentPartitionSize = BootPartition->Partition.LastBlock + 1 - BootPartition->Partition.FirstBlock; // // Always open the boot volume in compatibility mode, since firmware and // others may be looking into it. // BootVolume = SetupVolumeOpen(Context, BootPartition->Destination, SetupVolumeFormatNever, TRUE); OsOpenBootVolumeEnd: if (Partitions != NULL) { SetupDestroyDeviceDescriptions(Partitions, PartitionCount); } return BootVolume; } // // --------------------------------------------------------- Internal Functions //