From 997c0a0f2809c57985ef67cb9439baf812dcd996 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Fri, 18 Jan 2013 01:39:24 +0000 Subject: [PATCH] [gpt] add support for GPT partitioning and EFI boot * Contains the bulk of the code required to address #11 * Tested with Windows 8 Pro x64 (en-gb_windows_8_x64_dvd_915412.iso) and Arch Linux (archlinux-2013.01.04-dual.iso) against an UEFI 2.1 BIOS * Also fixes missing status report beta versions check --- src/format.c | 61 ++++++++---- src/iso.c | 5 + src/net.c | 3 +- src/resource.h | 2 +- src/rufus.c | 262 +++++++++++++++++++++++++++++++++---------------- src/rufus.h | 14 ++- src/rufus.rc | 12 +-- src/stdio.c | 12 +++ 8 files changed, 257 insertions(+), 114 deletions(-) diff --git a/src/format.c b/src/format.c index eed80e4b..943567cc 100644 --- a/src/format.c +++ b/src/format.c @@ -2,7 +2,7 @@ * Rufus: The Reliable USB Formatting Utility * Formatting function calls * Copyright (c) 2007-2009 Tom Thornhill/Ridgecrop - * Copyright (c) 2011-2012 Pete Batard + * Copyright (c) 2011-2013 Pete Batard * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -743,20 +743,29 @@ static BOOL AnalyzePBR(HANDLE hLogicalVolume) return TRUE; } -static BOOL ClearMBR(HANDLE hPhysicalDrive) +static BOOL ClearMBRGPT(HANDLE hPhysicalDrive, LONGLONG DiskSize, DWORD SectorSize) { BOOL r = FALSE; - // Clearing the first 64K seems to help with reluctant access to large drive - size_t SectorSize = 65536; + uint64_t i, last_sector = DiskSize/SectorSize; unsigned char* pBuf = (unsigned char*) calloc(SectorSize, 1); - PrintStatus(0, TRUE, "Clearing MBR..."); + PrintStatus(0, TRUE, "Clearing MBR & GPT structures..."); if (pBuf == NULL) { FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_ENOUGH_MEMORY; goto out; } - if ((IS_ERROR(FormatStatus)) || (write_sectors(hPhysicalDrive, SectorSize, 0, 1, pBuf) != SectorSize)) { - goto out; + // http://en.wikipedia.org/wiki/GUID_Partition_Table tells us we should clear 34 sectors at the + // beginning and 33 at the end. We bump these values to MAX_SECTORS_TO_CLEAR each end to help + // with reluctant access to large drive. + for (i=0; i FS_FAT32) ) { + uprintf("Spock gone crazy error!\n"); + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_INSTALL_FAILURE; + goto out; + } + } else if ((dt == DT_WINME) || (dt == DT_FREEDOS) || ((dt == DT_ISO) && (fs == FS_NTFS))) { // We still have a lock, which we need to modify the volume boot record // => no need to reacquire the lock... hLogicalVolume = GetDriveHandle(num, drive_name, TRUE, FALSE); @@ -1292,7 +1311,7 @@ DWORD WINAPI FormatThread(LPVOID param) goto out; } } - if (IS_WINPE(iso_report.winpe)) { + if ( (pt == PT_MBR) && (IS_WINPE(iso_report.winpe)) ) { // Apply WinPe fixup if (!SetupWinPE(drive_name[0])) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH); diff --git a/src/iso.c b/src/iso.c index 0d5e43e7..4737cf44 100644 --- a/src/iso.c +++ b/src/iso.c @@ -57,6 +57,7 @@ int64_t iso_blocking_status = -1; static const char* psz_extract_dir; static const char* bootmgr_name = "bootmgr"; static const char* ldlinux_name = "ldlinux.sys"; +static const char* efi_dirname = "/efi/boot"; static const char* isolinux_name[] = { "isolinux.cfg", "syslinux.cfg", "extlinux.conf"}; static const char* pe_dirname[] = { "/i386", "/minint" }; static const char* pe_file[] = { "ntdetect.com", "setupldr.bin", "txtsetup.sif" }; @@ -126,6 +127,10 @@ static __inline BOOL check_iso_props(const char* psz_dirname, BOOL* is_syslinux_ if ((*psz_dirname == 0) && (safe_stricmp(psz_basename, bootmgr_name) == 0)) iso_report.has_bootmgr = TRUE; + // Check for the EFI boot directory + if (safe_stricmp(psz_dirname, efi_dirname) == 0) + iso_report.has_efi = TRUE; + // Check for PE (XP) specific files in "/i386" or "/minint" for (i=0; iDiskSize.QuadPart; memcpy(&SelectedDrive.Geometry, &DiskGeometry->Geometry, sizeof(DISK_GEOMETRY)); - uprintf("Cylinders: %lld, TracksPerCylinder: %d, SectorsPerTrack: %d, BytesPerSector: %d\n", - DiskGeometry->Geometry.Cylinders, DiskGeometry->Geometry.TracksPerCylinder, - DiskGeometry->Geometry.SectorsPerTrack, DiskGeometry->Geometry.BytesPerSector); + uprintf("Sector Size: %d bytes\n", DiskGeometry->Geometry.BytesPerSector); + uprintf("Cylinders: %lld, TracksPerCylinder: %d, SectorsPerTrack: %d\n", + DiskGeometry->Geometry.Cylinders, DiskGeometry->Geometry.TracksPerCylinder, DiskGeometry->Geometry.SectorsPerTrack); r = DeviceIoControl(hDrive, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, layout, sizeof(layout), &size, NULL ); @@ -389,28 +389,46 @@ static BOOL GetDriveInfo(void) } else { switch (DriveLayout->PartitionStyle) { case PARTITION_STYLE_MBR: - for (tmp_pos=0, i=0; (iPartitionCount)&&(tmp_pos>=0); i++) { + SelectedDrive.PartitionType = PT_MBR; + for (i=0; iPartitionCount; i++) { if (DriveLayout->PartitionEntry[i].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) { nb_partitions++; - uprintf("Partition %d:\n", i+1); - part_type = DriveLayout->PartitionEntry[i].Mbr.PartitionType; - uprintf(" Type: %s (0x%02x)\r\n Size: %s\r\n Boot: %s\r\n Recognized: %s\r\n Hidden Sectors: %d\n", - GetPartitionType(part_type), part_type, size_to_hr(DriveLayout->PartitionEntry[i].PartitionLength), - DriveLayout->PartitionEntry[i].Mbr.BootIndicator?"Yes":"No", - DriveLayout->PartitionEntry[i].Mbr.RecognizedPartition?"Yes":"No", - DriveLayout->PartitionEntry[i].Mbr.HiddenSectors); - tmp_pos = _snprintf(&tmp[tmp_pos], sizeof(tmp)-tmp_pos, "Partition %d: %s (%s)\n", - i+1, GetPartitionType(part_type), size_to_hr(DriveLayout->PartitionEntry[i].PartitionLength)); } } uprintf("Partition type: MBR, NB Partitions: %d\n", nb_partitions); - tmp[sizeof(tmp)-1] = 0; - CreateTooltip(hPartition, tmp, -1); + uprintf("Disk ID: 0x%08X\n", DriveLayout->Mbr.Signature); + for (i=0; iPartitionCount; i++) { + if (DriveLayout->PartitionEntry[i].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) { + uprintf("Partition %d:\n", DriveLayout->PartitionEntry[i].PartitionNumber); + part_type = DriveLayout->PartitionEntry[i].Mbr.PartitionType; + uprintf(" Type: %s (0x%02x)\r\n Size: %s (%lld bytes)\r\n Start Sector: %d, Boot: %s, Recognized: %s\n", + GetPartitionType(part_type), part_type, size_to_hr(DriveLayout->PartitionEntry[i].PartitionLength), + DriveLayout->PartitionEntry[i].PartitionLength, DriveLayout->PartitionEntry[i].Mbr.HiddenSectors, + DriveLayout->PartitionEntry[i].Mbr.BootIndicator?"Yes":"No", + DriveLayout->PartitionEntry[i].Mbr.RecognizedPartition?"Yes":"No"); + } + } break; case PARTITION_STYLE_GPT: + SelectedDrive.PartitionType = PT_GPT; uprintf("Partition type: GPT, NB Partitions: %d\n", DriveLayout->PartitionCount); + uprintf("Disk GUID: %s\n", GuidToString(&DriveLayout->Gpt.DiskId)); + uprintf("Max parts: %d, Start Offset: %lld, Usable = %lld bytes\n", + DriveLayout->Gpt.MaxPartitionCount, DriveLayout->Gpt.StartingUsableOffset.QuadPart, DriveLayout->Gpt.UsableLength.QuadPart); + for (i=0; iPartitionCount; i++) { + nb_partitions++; + tmp[0] = 0; + wchar_to_utf8_no_alloc(DriveLayout->PartitionEntry[i].Gpt.Name, tmp, sizeof(tmp)); + uprintf("Partition %d:\r\n Type: %s\r\n Name: '%s'\n", DriveLayout->PartitionEntry[i].PartitionNumber, + GuidToString(&DriveLayout->PartitionEntry[i].Gpt.PartitionType), tmp); + uprintf(" ID: %s\r\n Size: %s (%lld bytes)\r\n Start Sector: %lld, Attributes: 0x%016llX\n", + GuidToString(&DriveLayout->PartitionEntry[i].Gpt.PartitionId), size_to_hr(DriveLayout->PartitionEntry[i].PartitionLength), + DriveLayout->PartitionEntry[i].PartitionLength, DriveLayout->PartitionEntry[i].StartingOffset.QuadPart / DiskGeometry->Geometry.BytesPerSector, + DriveLayout->PartitionEntry[i].Gpt.Attributes); + } break; default: + SelectedDrive.PartitionType = PT_MBR; uprintf("Partition type: RAW\n"); break; } @@ -472,8 +490,8 @@ static void SetFSFromISO(void) fs_mask |= 1<= ARRAYSIZE(suffix)) + uprintf("Could not populate partition scheme data\n"); + IGNORE_RETVAL(ComboBox_SetCurSel(hPartitionScheme, SelectedDrive.PartitionType)); CreateTooltip(hDeviceList, DriveID.Table[ComboIndex], -1); // Set a proposed label according to the size (eg: "256MB", "8GB") @@ -604,68 +627,131 @@ typedef struct _DRIVE_LAYOUT_INFORMATION_EX4 { /* * Create a partition table */ +// See http://technet.microsoft.com/en-us/library/cc739412.aspx for some background info +#if !defined(PARTITION_BASIC_DATA_GUID) +const GUID PARTITION_BASIC_DATA_GUID = { 0xebd0a0a2, 0xb9e5, 0x4433, {0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7} }; +#endif BOOL CreatePartition(HANDLE hDrive) { + CREATE_DISK CreateDisk = {PARTITION_STYLE_RAW, {0}}; DRIVE_LAYOUT_INFORMATION_EX4 DriveLayoutEx = {0}; BOOL r; DWORD size; - LONGLONG sector_size; + LONGLONG size_in_sectors; + int pt = (int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme)); - PrintStatus(0, TRUE, "Partitioning..."); - DriveLayoutEx.PartitionStyle = PARTITION_STYLE_MBR; - DriveLayoutEx.PartitionCount = 4; // Must be multiple of 4 for MBR - DriveLayoutEx.Type.Mbr.Signature = GetTickCount(); - DriveLayoutEx.PartitionEntry[0].PartitionStyle = PARTITION_STYLE_MBR; - // TODO: CHS fixup (32 sectors/track) through a cheat mode, if requested - // NB: disk geometry is computed by BIOS & co. by finding a match between LBA and CHS value of first partition - // ms-sys's write_partition_number_of_heads() and write_partition_start_sector_number() can be used if needed + PrintStatus(0, TRUE, "Partitioning (%s)...", PartitionSchemeName[pt]); - DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart = - SelectedDrive.Geometry.BytesPerSector * SelectedDrive.Geometry.SectorsPerTrack; - sector_size = (SelectedDrive.DiskSize - DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart) / SelectedDrive.Geometry.BytesPerSector; - // Align on sector boundary if the extra part option is checked - if (IsChecked(IDC_EXTRA_PARTITION)) { - sector_size = ((sector_size / SelectedDrive.Geometry.SectorsPerTrack)-1) * SelectedDrive.Geometry.SectorsPerTrack; - if (sector_size <= 0) return FALSE; + if ((pt == PT_GPT) || (!IsChecked(IDC_EXTRA_PARTITION))) { + // Go with the MS 1 MB wastage at the beginning... + DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart = 1024*1024; + } else { + // Align on Cylinder + DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart = + SelectedDrive.Geometry.BytesPerSector * SelectedDrive.Geometry.SectorsPerTrack; } - DriveLayoutEx.PartitionEntry[0].PartitionLength.QuadPart = sector_size * SelectedDrive.Geometry.BytesPerSector; - DriveLayoutEx.PartitionEntry[0].PartitionNumber = 1; - DriveLayoutEx.PartitionEntry[0].RewritePartition = TRUE; - DriveLayoutEx.PartitionEntry[0].Mbr.HiddenSectors = SelectedDrive.Geometry.SectorsPerTrack; - switch (ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem))) { - case FS_FAT16: - DriveLayoutEx.PartitionEntry[0].Mbr.PartitionType = 0x0e; // FAT16 LBA + size_in_sectors = (SelectedDrive.DiskSize - DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart) / SelectedDrive.Geometry.BytesPerSector; + + switch (pt) { + case PT_MBR: + CreateDisk.PartitionStyle = PARTITION_STYLE_MBR; + CreateDisk.Mbr.Signature = GetTickCount(); + + DriveLayoutEx.PartitionStyle = PARTITION_STYLE_MBR; + DriveLayoutEx.PartitionCount = 4; // Must be multiple of 4 for MBR + DriveLayoutEx.Type.Mbr.Signature = CreateDisk.Mbr.Signature; + DriveLayoutEx.PartitionEntry[0].PartitionStyle = PARTITION_STYLE_MBR; + // TODO: CHS fixup (32 sectors/track) through a cheat mode, if requested + // NB: disk geometry is computed by BIOS & co. by finding a match between LBA and CHS value of first partition + // ms-sys's write_partition_number_of_heads() and write_partition_start_sector_number() can be used if needed + + // Align on sector boundary if the extra part option is checked + if (IsChecked(IDC_EXTRA_PARTITION)) { + size_in_sectors = ((size_in_sectors / SelectedDrive.Geometry.SectorsPerTrack)-1) * SelectedDrive.Geometry.SectorsPerTrack; + if (size_in_sectors <= 0) + return FALSE; + } break; - case FS_NTFS: - case FS_EXFAT: - DriveLayoutEx.PartitionEntry[0].Mbr.PartitionType = 0x07; // NTFS - break; - case FS_FAT32: - DriveLayoutEx.PartitionEntry[0].Mbr.PartitionType = 0x0c; // FAT32 LBA + case PT_GPT: + CreateDisk.PartitionStyle = PARTITION_STYLE_GPT; + IGNORE_RETVAL(CoCreateGuid(&CreateDisk.Gpt.DiskId)); + CreateDisk.Gpt.MaxPartitionCount = MAX_GPT_PARTITIONS; + + DriveLayoutEx.PartitionStyle = PARTITION_STYLE_GPT; + DriveLayoutEx.PartitionCount = 1; + // At the very least, a GPT disk has atv least 34 reserved (512 bytes) blocks at the beginning + // and 33 at the end. + DriveLayoutEx.Type.Gpt.StartingUsableOffset.QuadPart = 34*512; + DriveLayoutEx.Type.Gpt.UsableLength.QuadPart = SelectedDrive.DiskSize - (34+33)*512; + DriveLayoutEx.Type.Gpt.MaxPartitionCount = MAX_GPT_PARTITIONS; + DriveLayoutEx.Type.Gpt.DiskId = CreateDisk.Gpt.DiskId; + DriveLayoutEx.PartitionEntry[0].PartitionStyle = PARTITION_STYLE_GPT; + + size_in_sectors -= 33; // Need 33 sectors at the end for secondary GPT break; default: - uprintf("Unsupported file system\n"); + break; + } + + DriveLayoutEx.PartitionEntry[0].PartitionLength.QuadPart = size_in_sectors * SelectedDrive.Geometry.BytesPerSector; + DriveLayoutEx.PartitionEntry[0].PartitionNumber = 1; + DriveLayoutEx.PartitionEntry[0].RewritePartition = TRUE; + + switch (pt) { + case PT_MBR: + DriveLayoutEx.PartitionEntry[0].Mbr.HiddenSectors = SelectedDrive.Geometry.SectorsPerTrack; + switch (ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem))) { + case FS_FAT16: + DriveLayoutEx.PartitionEntry[0].Mbr.PartitionType = 0x0e; // FAT16 LBA + break; + case FS_NTFS: + case FS_EXFAT: + DriveLayoutEx.PartitionEntry[0].Mbr.PartitionType = 0x07; // NTFS + break; + case FS_FAT32: + DriveLayoutEx.PartitionEntry[0].Mbr.PartitionType = 0x0c; // FAT32 LBA + break; + default: + uprintf("Unsupported file system\n"); + return FALSE; + } + // Create an extra partition on request - can improve BIOS detection as HDD for older BIOSes + if (IsChecked(IDC_EXTRA_PARTITION)) { + DriveLayoutEx.PartitionEntry[1].PartitionStyle = PARTITION_STYLE_MBR; + // Should end on a sector boundary + DriveLayoutEx.PartitionEntry[1].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart + + DriveLayoutEx.PartitionEntry[0].PartitionLength.QuadPart; + DriveLayoutEx.PartitionEntry[1].PartitionLength.QuadPart = SelectedDrive.Geometry.SectorsPerTrack*SelectedDrive.Geometry.BytesPerSector; + DriveLayoutEx.PartitionEntry[1].PartitionNumber = 2; + DriveLayoutEx.PartitionEntry[1].RewritePartition = TRUE; + DriveLayoutEx.PartitionEntry[1].Mbr.HiddenSectors = SelectedDrive.Geometry.SectorsPerTrack*SelectedDrive.Geometry.BytesPerSector; + DriveLayoutEx.PartitionEntry[1].Mbr.PartitionType = DriveLayoutEx.PartitionEntry[0].Mbr.PartitionType + 0x10; // Hidden whatever + } + // For the remaining partitions, PartitionStyle & PartitionType have already + // been zeroed => already set to MBR/unused + break; + case PT_GPT: + DriveLayoutEx.PartitionEntry[0].Gpt.PartitionType = PARTITION_BASIC_DATA_GUID; + wcscpy(DriveLayoutEx.PartitionEntry[0].Gpt.Name, L"Microsoft Basic Data"); + IGNORE_RETVAL(CoCreateGuid(&DriveLayoutEx.PartitionEntry[0].Gpt.PartitionId)); + break; + default: + break; + } + + // If you don't call IOCTL_DISK_CREATE_DISK, the next call will fail + size = sizeof(CreateDisk); + r = DeviceIoControl(hDrive, IOCTL_DISK_CREATE_DISK, + (BYTE*)&CreateDisk, size, NULL, 0, &size, NULL ); + if (!r) { + uprintf("IOCTL_DISK_CREATE_DISK failed: %s\n", WindowsErrorString()); + safe_closehandle(hDrive); return FALSE; } - // Create an extra partition on request - can improve BIOS detection as HDD for older BIOSes - if (IsChecked(IDC_EXTRA_PARTITION)) { - DriveLayoutEx.PartitionEntry[1].PartitionStyle = PARTITION_STYLE_MBR; - // Should end on a sector boundary - DriveLayoutEx.PartitionEntry[1].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart + - DriveLayoutEx.PartitionEntry[0].PartitionLength.QuadPart; - DriveLayoutEx.PartitionEntry[1].PartitionLength.QuadPart = SelectedDrive.Geometry.SectorsPerTrack*SelectedDrive.Geometry.BytesPerSector; - DriveLayoutEx.PartitionEntry[1].PartitionNumber = 2; - DriveLayoutEx.PartitionEntry[1].RewritePartition = TRUE; - DriveLayoutEx.PartitionEntry[1].Mbr.HiddenSectors = SelectedDrive.Geometry.SectorsPerTrack*SelectedDrive.Geometry.BytesPerSector; - DriveLayoutEx.PartitionEntry[1].Mbr.PartitionType = DriveLayoutEx.PartitionEntry[0].Mbr.PartitionType + 0x10; // Hidden whatever - } - - // For the remaining partitions, PartitionStyle & PartitionType have already - // been zeroed => set to MBR/unused - - r = DeviceIoControl(hDrive, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, - (BYTE*)&DriveLayoutEx, sizeof(DriveLayoutEx), NULL, 0, &size, NULL ); + size = sizeof(DriveLayoutEx) - ((pt == PT_GPT)?(3*sizeof(PARTITION_INFORMATION_EX)):0); + r = DeviceIoControl(hDrive, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, + (BYTE*)&DriveLayoutEx, size, NULL, 0, &size, NULL ); if (!r) { uprintf("IOCTL_DISK_SET_DRIVE_LAYOUT_EX failed: %s\n", WindowsErrorString()); safe_closehandle(hDrive); @@ -1072,7 +1158,7 @@ static void EnableControls(BOOL bEnable) int fs; EnableWindow(GetDlgItem(hMainDialog, IDC_DEVICE), bEnable); - EnableWindow(GetDlgItem(hMainDialog, IDC_PARTITION), bEnable); + EnableWindow(GetDlgItem(hMainDialog, IDC_PARTITION_SCHEME), bEnable); EnableWindow(GetDlgItem(hMainDialog, IDC_FILESYSTEM), bEnable); EnableWindow(GetDlgItem(hMainDialog, IDC_CLUSTERSIZE), bEnable); EnableWindow(GetDlgItem(hMainDialog, IDC_LABEL), bEnable); @@ -1268,8 +1354,8 @@ DWORD WINAPI ISOScanThread(LPVOID param) safe_free(iso_path); goto out; } - uprintf("ISO label: '%s'\r\n Size: %lld bytes\r\n Has a >4GB file: %s\r\n Uses Bootmgr: %s\r\n Uses WinPE: %s%s\r\n Uses isolinux: %s\n", - iso_report.label, iso_report.projected_size, iso_report.has_4GB_file?"Yes":"No", iso_report.has_bootmgr?"Yes":"No", + uprintf("ISO label: '%s'\r\n Size: %lld bytes\r\n Has a >4GB file: %s\r\n Uses EFI: %s\r\n Uses Bootmgr: %s\r\n Uses WinPE: %s%s\r\n Uses isolinux: %s\n", + iso_report.label, iso_report.projected_size, iso_report.has_4GB_file?"Yes":"No", iso_report.has_efi?"Yes":"No", iso_report.has_bootmgr?"Yes":"No", IS_WINPE(iso_report.winpe)?"Yes":"No", (iso_report.uses_minint)?" (with /minint)":"", iso_report.has_isolinux?"Yes":"No"); if (iso_report.has_isolinux) { for (i=0; i LARGE_FAT32_SIZE)) { @@ -1722,7 +1810,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA } break; } - if (fs == FS_EXFAT) { + if ((fs == FS_EXFAT) || ((pt == PT_GPT) && (fs == FS_NTFS))) { if (IsWindowEnabled(hDOS)) { // unlikely to be supported by BIOSes => don't bother IGNORE_RETVAL(ComboBox_SetCurSel(hDOSType, 0)); @@ -1734,13 +1822,13 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA break; } IGNORE_RETVAL(ComboBox_ResetContent(hDOSType)); - if ((fs == FS_FAT16) || (fs == FS_FAT32)) { + if ((pt == PT_MBR) && ((fs == FS_FAT16) || (fs == FS_FAT32))) { IGNORE_RETVAL(ComboBox_SetItemData(hDOSType, ComboBox_AddStringU(hDOSType, "MS-DOS"), DT_WINME)); IGNORE_RETVAL(ComboBox_SetItemData(hDOSType, ComboBox_AddStringU(hDOSType, "FreeDOS"), DT_FREEDOS)); } IGNORE_RETVAL(ComboBox_SetItemData(hDOSType, ComboBox_AddStringU(hDOSType, "ISO Image"), DT_ISO)); // If needed (advanced mode) also append a Syslinux option - if ( ((fs == FS_FAT16) || (fs == FS_FAT32)) && (advanced_mode) ) + if ( (pt == PT_MBR) && (((fs == FS_FAT16) || (fs == FS_FAT32)) && (advanced_mode)) ) IGNORE_RETVAL(ComboBox_SetItemData(hDOSType, ComboBox_AddStringU(hDOSType, "SysLinux"), DT_SYSLINUX)); if ( ((!advanced_mode) && (selection_default == DT_SYSLINUX)) ) { selection_default = DT_FREEDOS; @@ -1833,7 +1921,13 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA break; } fs = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); - if ((fs == FS_NTFS) && (!iso_report.has_bootmgr) && (!IS_WINPE(iso_report.winpe))) { + pt = (int)ComboBox_GetItemData(hPartitionScheme, ComboBox_GetCurSel(hPartitionScheme)); + if ((pt == PT_GPT) && ((!iso_report.has_efi) || ((fs > FS_FAT32)))) { + MessageBoxA(hMainDialog, "When using GPT, only EFI bootable ISOs are supported. " + "Please select an EFI bootable ISO or change the Partition Scheme to MBR.", "Unsupported GPT ISO...", MB_OK|MB_ICONERROR); + break; + } + else if ((fs == FS_NTFS) && (!iso_report.has_bootmgr) && (!IS_WINPE(iso_report.winpe))) { if (iso_report.has_isolinux) { MessageBoxA(hMainDialog, "Only FAT32 is supported for this type of ISO. " "Please revert the filesystem back from NTFS to FAT32.", "Unsupported filesystem...", MB_OK|MB_ICONERROR); @@ -1842,7 +1936,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA "images can currently be used with NTFS.", "Unsupported ISO...", MB_OK|MB_ICONERROR); } break; - } else if (((fs == FS_FAT16)||(fs == FS_FAT32)) && (!iso_report.has_isolinux)) { + } else if (((fs == FS_FAT16)||(fs == FS_FAT32)) && ((!iso_report.has_isolinux) && (pt != PT_GPT))) { MessageBoxA(hMainDialog, "Only 'isolinux' based ISO " "images can currently be used with FAT.", "Unsupported ISO...", MB_OK|MB_ICONERROR); break; diff --git a/src/rufus.h b/src/rufus.h index e4cbca9b..ab463bc5 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -42,6 +42,9 @@ #define MAX_TOOLTIPS 32 #define MAX_PROGRESS (0xFFFF-1) // leave room for 1 more for insta-progress workaround #define MAX_LOG_SIZE 0x7FFFFFFE +#define MAX_GUID_STRING_LENGTH 40 +#define MAX_GPT_PARTITIONS 128 +#define MAX_SECTORS_TO_CLEAR 128 // nb sectors to zap when clearing the MBR/GPT (must be >34) #define PROPOSEDLABEL_TOLERANCE 0.10 #define FS_DEFAULT FS_FAT32 #define LARGE_FAT32_SIZE (32*1073741824LL) // Size at which we need to use fat32format @@ -147,6 +150,12 @@ enum dos_type { DT_MAX }; +enum part_type { + PT_MBR = 0, + PT_GPT, + PT_MAX +}; + /* Current drive info */ typedef struct { DWORD DeviceNumber; @@ -154,6 +163,7 @@ typedef struct { DISK_GEOMETRY Geometry; DWORD FirstSector; char proposed_label[16]; + int PartitionType; int FSType; struct { ULONG Allowed; @@ -180,6 +190,7 @@ typedef struct { uint8_t winpe; BOOL has_4GB_file; BOOL has_bootmgr; + BOOL has_efi; BOOL has_isolinux; BOOL has_autorun; BOOL has_old_c32[NB_OLD_C32]; @@ -221,7 +232,7 @@ enum WindowsVersion { */ extern HINSTANCE hMainInstance; extern HWND hMainDialog, hLogDlg, hStatus, hDeviceList, hCapacity; -extern HWND hFileSystem, hClusterSize, hLabel, hDOSType, hNBPasses, hLog; +extern HWND hPartitionScheme, hFileSystem, hClusterSize, hLabel, hDOSType, hNBPasses, hLog; extern HWND hISOProgressDlg, hISOProgressBar, hISOFileName, hDiskID; extern float fScale; extern char szFolderPath[MAX_PATH], app_dir[MAX_PATH]; @@ -246,6 +257,7 @@ extern void DumpBufferHex(void *buf, size_t size); extern void PrintStatus(unsigned int duration, BOOL debug, const char *format, ...); extern void UpdateProgress(int op, float percent); extern const char* StrError(DWORD error_code); +extern char* GuidToString(const GUID* guid); extern void CenterDialog(HWND hDlg); extern void CreateStatusBar(void); extern void SetTitleBarIcon(HWND hDlg); diff --git a/src/rufus.rc b/src/rufus.rc index 934625e8..3a878dd5 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -30,7 +30,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 206, 316 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW -CAPTION "Rufus v1.3.1.222" +CAPTION "Rufus v1.3.1.223" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "Start",IDC_START,94,278,50,14 @@ -39,7 +39,7 @@ BEGIN CONTROL "Device",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,9,6,22,8 COMBOBOX IDC_FILESYSTEM,8,75,190,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "File system",IDC_STATIC,9,64,51,10 - COMBOBOX IDC_PARTITION,8,46,190,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_PARTITION_SCHEME,8,46,190,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "Partition Scheme",IDC_STATIC,9,35,75,8 COMBOBOX IDC_CLUSTERSIZE,8,104,190,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "Cluster size",IDC_STATIC,9,93,105,10 @@ -274,8 +274,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,1,222 - PRODUCTVERSION 1,3,1,222 + FILEVERSION 1,3,1,223 + PRODUCTVERSION 1,3,1,223 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -292,13 +292,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "1.3.1.222" + VALUE "FileVersion", "1.3.1.223" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "(c) 2011-2012 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "1.3.1.222" + VALUE "ProductVersion", "1.3.1.223" END END BLOCK "VarFileInfo" diff --git a/src/stdio.c b/src/stdio.c index 717dfc82..07db5fef 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -177,6 +177,18 @@ void PrintStatus(unsigned int duration, BOOL debug, const char *format, ...) } } +char* GuidToString(const GUID* guid) +{ + static char guid_string[MAX_GUID_STRING_LENGTH]; + + if (guid == NULL) return NULL; + sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + (unsigned int)guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + return guid_string; +} + const char* StrError(DWORD error_code) { if ( (!IS_ERROR(error_code)) || (SCODE_CODE(error_code) == ERROR_SUCCESS)) {