From 4ac182830d486e767316b12d1115c59b93aea521 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Tue, 9 Jul 2013 00:14:29 +0100 Subject: [PATCH] [core] add UDF filesystem support * Only supported on Vista or later * Also disable exFAT for XP (requires a KB => not worth it) * Also improve display of partition data for type 0x07 * Also fix and issue where exFAT/UDF would try to modify PBR * Also logically move and simplify some of the code * Closes #157 --- src/drive.c | 103 +++++++++++++++++++++++++++++++++++++++++++----- src/format.c | 76 ++++++----------------------------- src/rufus.c | 48 +++++++++++++--------- src/rufus.h | 3 ++ src/rufus.rc | 10 ++--- src/sys_types.h | 2 +- 6 files changed, 146 insertions(+), 96 deletions(-) diff --git a/src/drive.c b/src/drive.c index f673a408..90368da8 100644 --- a/src/drive.c +++ b/src/drive.c @@ -413,6 +413,14 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys char tmp[256]; DWORD i, nb_partitions = 0; + // Populate the filesystem data + FileSystemName[0] = 0; + volume_name = GetLogicalName(DriveIndex, TRUE, FALSE); + if ((volume_name == NULL) || (!GetVolumeInformationA(volume_name, NULL, 0, NULL, NULL, NULL, FileSystemName, FileSystemNameSize))) { + uprintf("No volume information for disk 0x%02x\n", DriveIndex); + } + safe_free(volume_name); + hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE); if (hPhysical == INVALID_HANDLE_VALUE) return FALSE; @@ -453,7 +461,8 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys 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, SizeToHumanReadable(DriveLayout->PartitionEntry[i].PartitionLength), + ((part_type==0x07)&&(FileSystemName[0]!=0))?FileSystemName:GetPartitionType(part_type), part_type, + SizeToHumanReadable(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"); @@ -487,17 +496,34 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys } safe_closehandle(hPhysical); - // Populate the filesystem data - volume_name = GetLogicalName(DriveIndex, TRUE, FALSE); - if ((volume_name == NULL) || (!GetVolumeInformationA(volume_name, NULL, 0, NULL, NULL, NULL, FileSystemName, FileSystemNameSize))) { - uprintf("No volume information for disk 0x%02x\n", DriveIndex); - FileSystemName[0] = 0; - } - safe_free(volume_name); - return TRUE; } +/* + * Flush file data + */ +static BOOL FlushDrive(char drive_letter) +{ + HANDLE hDrive = INVALID_HANDLE_VALUE; + BOOL r = FALSE; + char logical_drive[] = "\\\\.\\#:"; + + logical_drive[4] = drive_letter; + hDrive = CreateFileA(logical_drive, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0); + if (hDrive == INVALID_HANDLE_VALUE) { + uprintf("Failed to open %c: for flushing: %s\n", drive_letter, WindowsErrorString()); + goto out; + } + r = FlushFileBuffers(hDrive); + if (r == FALSE) + uprintf("Failed to flush %c: %s\n", drive_letter, WindowsErrorString()); + +out: + safe_closehandle(hDrive); + return r; +} + /* * Unmount of volume using the DISMOUNT_VOLUME ioctl */ @@ -512,6 +538,63 @@ BOOL UnmountVolume(HANDLE hDrive) return TRUE; } +/* + * Mount the volume identified by drive_guid to mountpoint drive_name + */ +BOOL MountVolume(char* drive_name, char *drive_guid) +{ + char mounted_guid[52]; // You need at least 51 characters on XP + + if (!SetVolumeMountPointA(drive_name, drive_guid)) { + // If the OS was faster than us at remounting the drive, this operation can fail + // with ERROR_DIR_NOT_EMPTY. If that's the case, just check that mountpoints match + if (GetLastError() == ERROR_DIR_NOT_EMPTY) { + if (!GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid))) { + uprintf("%s already mounted, but volume GUID could not be checked: %s\n", + drive_name, WindowsErrorString()); + return FALSE; + } + if (safe_strcmp(drive_guid, mounted_guid) != 0) { + uprintf("%s already mounted, but volume GUID doesn't match:\r\n expected %s, got %s\n", + drive_name, drive_guid, mounted_guid); + return FALSE; + } + uprintf("%s was already mounted as %s\n", drive_guid, drive_name); + } else { + return FALSE; + } + } + return TRUE; +} + +/* + * Issue a complete remount of the volume + */ +BOOL RemountVolume(char* drive_name) +{ + char drive_guid[51]; + + // UDF requires a sync/flush, and it's also a good idea for other FS's + FlushDrive(drive_name[0]); + if (GetVolumeNameForVolumeMountPointA(drive_name, drive_guid, sizeof(drive_guid))) { + if (DeleteVolumeMountPointA(drive_name)) { + Sleep(200); + if (MountVolume(drive_name, drive_guid)) { + uprintf("Successfully remounted %s on %s\n", &drive_guid[4], drive_name); + } else { + uprintf("Failed to remount %s on %s\n", &drive_guid[4], drive_name); + // This will leave the drive inaccessible and must be flagged as an error + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_REMOUNT_VOLUME); + return FALSE; + } + } else { + uprintf("Could not remount %s %s\n", drive_name, WindowsErrorString()); + // Try to continue regardless + } + } + return TRUE; +} + /* MinGW is unhappy about accessing partitions beside the first unless we redef */ typedef struct _DRIVE_LAYOUT_INFORMATION_EX4 { DWORD PartitionStyle; @@ -552,6 +635,7 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart = SelectedDrive.Geometry.BytesPerSector * SelectedDrive.Geometry.SectorsPerTrack; } + // TODO: should we try to align the following to the cluster size as well? size_in_sectors = (SelectedDrive.DiskSize - DriveLayoutEx.PartitionEntry[0].StartingOffset.QuadPart) / SelectedDrive.Geometry.BytesPerSector; switch (partition_style) { @@ -617,6 +701,7 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m break; case FS_NTFS: case FS_EXFAT: + case FS_UDF: DriveLayoutEx.PartitionEntry[0].Mbr.PartitionType = 0x07; // NTFS break; case FS_FAT32: diff --git a/src/format.c b/src/format.c index aee94141..c24a225d 100644 --- a/src/format.c +++ b/src/format.c @@ -51,8 +51,7 @@ DWORD FormatStatus; badblocks_report report; static float format_percent = 0.0f; static int task_number = 0; -/* Number of steps for each FS for FCC_STRUCTURE_PROGRESS */ -const int nb_steps[FS_MAX] = { 5, 5, 12, 10 }; +extern const int nb_steps[FS_MAX]; static int fs_index = 0; BOOL force_large_fat32 = FALSE; @@ -607,13 +606,13 @@ static BOOL FormatDrive(DWORD DriveIndex) { BOOL r = FALSE; PF_DECL(FormatEx); - char* VolumeName = NULL; - WCHAR* wVolumeName = NULL; char FSType[32], format_status[64]; + char *locale, *VolumeName = NULL; + WCHAR* wVolumeName = NULL; WCHAR wFSType[32]; WCHAR wLabel[64]; + ULONG ulClusterSize; size_t i; - char* locale; GetWindowTextA(hFileSystem, FSType, ARRAYSIZE(FSType)); safe_sprintf(format_status, ARRAYSIZE(format_status), "Formatting (%s)...", FSType); @@ -643,13 +642,19 @@ static BOOL FormatDrive(DWORD DriveIndex) GetWindowTextW(hLabel, wLabel, ARRAYSIZE(wLabel)); // Make sure the label is valid ToValidLabel(wLabel, (wFSType[0] == 'F') && (wFSType[1] == 'A') && (wFSType[2] == 'T')); - uprintf("Using cluster size: %d bytes\n", ComboBox_GetItemData(hClusterSize, ComboBox_GetCurSel(hClusterSize))); + ulClusterSize = (ULONG)ComboBox_GetItemData(hClusterSize, ComboBox_GetCurSel(hClusterSize)); + if (ulClusterSize < 0x200) { + // 0 is FormatEx's value for default, which we need to use for UDF + ulClusterSize = 0; + uprintf("Using default cluster size\n"); + } else { + uprintf("Using cluster size: %d bytes\n", ulClusterSize); + } format_percent = 0.0f; task_number = 0; fs_index = (int)ComboBox_GetItemData(hFileSystem, ComboBox_GetCurSel(hFileSystem)); pfFormatEx(wVolumeName, SelectedDrive.Geometry.MediaType, wFSType, wLabel, - IsChecked(IDC_QUICKFORMAT), (ULONG)ComboBox_GetItemData(hClusterSize, ComboBox_GetCurSel(hClusterSize)), - FormatExCallback); + IsChecked(IDC_QUICKFORMAT), ulClusterSize, FormatExCallback); if (!IS_ERROR(FormatStatus)) { uprintf("Format completed.\n"); r = TRUE; @@ -1092,61 +1097,6 @@ out: return r; } -/* - * Mount the volume identified by drive_guid to mountpoint drive_name - */ -static BOOL MountVolume(char* drive_name, char *drive_guid) -{ - char mounted_guid[52]; // You need at least 51 characters on XP - - if (!SetVolumeMountPointA(drive_name, drive_guid)) { - // If the OS was faster than us at remounting the drive, this operation can fail - // with ERROR_DIR_NOT_EMPTY. If that's the case, just check that mountpoints match - if (GetLastError() == ERROR_DIR_NOT_EMPTY) { - if (!GetVolumeNameForVolumeMountPointA(drive_name, mounted_guid, sizeof(mounted_guid))) { - uprintf("%s already mounted, but volume GUID could not be checked: %s\n", - drive_name, WindowsErrorString()); - return FALSE; - } - if (safe_strcmp(drive_guid, mounted_guid) != 0) { - uprintf("%s already mounted, but volume GUID doesn't match:\r\n expected %s, got %s\n", - drive_name, drive_guid, mounted_guid); - return FALSE; - } - uprintf("%s was already mounted as %s\n", drive_guid, drive_name); - } else { - return FALSE; - } - } - return TRUE; -} - -/* - * Issue a complete remount of the volume - */ -static BOOL RemountVolume(char* drive_name) -{ - char drive_guid[51]; - - if (GetVolumeNameForVolumeMountPointA(drive_name, drive_guid, sizeof(drive_guid))) { - if (DeleteVolumeMountPointA(drive_name)) { - Sleep(200); - if (MountVolume(drive_name, drive_guid)) { - uprintf("Successfully remounted %s on %s\n", &drive_guid[4], drive_name); - } else { - uprintf("Failed to remount %s on %s\n", &drive_guid[4], drive_name); - // This will leave the drive inaccessible and must be flagged as an error - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_REMOUNT_VOLUME); - return FALSE; - } - } else { - uprintf("Could not remount %s %s\n", drive_name, WindowsErrorString()); - // Try to continue regardless - } - } - return TRUE; -} - /* * Detect if a Windows Format prompt is active, by enumerating the * whole Windows tree and looking for the relevant popup diff --git a/src/rufus.c b/src/rufus.c index 3b2c7618..3d0d3da0 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -85,9 +85,12 @@ struct { UINT uAlign; } bi_iso = {0}, bi_up = {0}, bi_down = {0}; // BUTTON_IMAGELIST -static const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "exFAT" }; +const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "UDF", "exFAT" }; +// Number of steps for each FS for FCC_STRUCTURE_PROGRESS +const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10 }; // Don't ask me - just following the MS "standard" here -static const char* ClusterSizeLabel[] = { "512 bytes", "1024 bytes","2048 bytes","4096 bytes","8192 bytes", +// We hijack 256 as a "Default" for UDF, since we can't set clustersize there +static const char* ClusterSizeLabel[] = { "Default", "512 bytes", "1024 bytes","2048 bytes","4096 bytes","8192 bytes", "16 kilobytes", "32 kilobytes", "64 kilobytes", "128 kilobytes", "256 kilobytes", "512 kilobytes", "1024 kilobytes","2048 kilobytes","4096 kilobytes","8192 kilobytes","16 megabytes","32 megabytes" }; static const char* BiosTypeLabel[BT_MAX] = { "BIOS", "UEFI" }; @@ -241,8 +244,8 @@ static BOOL DefineClusterSizes(void) } } - // NTFS if (SelectedDrive.DiskSize < 256*TB) { + // NTFS SelectedDrive.ClusterSize[FS_NTFS].Allowed = 0x0001FE00; for (i=16; i<=256; i<<=1) { // 7 MB -> 256 TB if (SelectedDrive.DiskSize < i*TB) { @@ -250,17 +253,23 @@ static BOOL DefineClusterSizes(void) break; } } - } - // exFAT - if (SelectedDrive.DiskSize < 256*TB) { - SelectedDrive.ClusterSize[FS_EXFAT].Allowed = 0x03FFFE00; - if (SelectedDrive.DiskSize < 256*MB) // < 256 MB - SelectedDrive.ClusterSize[FS_EXFAT].Default = 4*1024; - else if (SelectedDrive.DiskSize < 32*GB) // < 32 GB - SelectedDrive.ClusterSize[FS_EXFAT].Default = 32*1024; - else - SelectedDrive.ClusterSize[FS_EXFAT].Default = 28*1024; + // exFAT (requires KB955704 installed on XP => don't bother) + if (nWindowsVersion > WINDOWS_XP) { + SelectedDrive.ClusterSize[FS_EXFAT].Allowed = 0x03FFFE00; + if (SelectedDrive.DiskSize < 256*MB) // < 256 MB + SelectedDrive.ClusterSize[FS_EXFAT].Default = 4*1024; + else if (SelectedDrive.DiskSize < 32*GB) // < 32 GB + SelectedDrive.ClusterSize[FS_EXFAT].Default = 32*1024; + else + SelectedDrive.ClusterSize[FS_EXFAT].Default = 28*1024; + } + + // UDF (only supported for Vista and later) + if (nWindowsVersion >= WINDOWS_VISTA) { + SelectedDrive.ClusterSize[FS_UDF].Allowed = 0x00000100; + SelectedDrive.ClusterSize[FS_UDF].Default = 1; + } } out: @@ -310,7 +319,7 @@ static BOOL SetClusterSizes(int FSType) return FALSE; } - for(i=0,j=0x200,k=0;j<0x10000000;i++,j<<=1) { + for(i=0,j=0x100,k=0;j<0x10000000;i++,j<<=1) { if (j & SelectedDrive.ClusterSize[FSType].Allowed) { safe_sprintf(szClustSize, sizeof(szClustSize), "%s", ClusterSizeLabel[i]); if (j == SelectedDrive.ClusterSize[FSType].Default) { @@ -1452,7 +1461,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA int nDeviceIndex, fs, bt, i, nWidth, nHeight; static DWORD DeviceNum = 0, LastRefresh = 0; char tmp[128], str[MAX_PATH]; - static UINT uDOSChecked = BST_CHECKED, uQFChecked; + static UINT uBootChecked = BST_CHECKED, uQFChecked; static BOOL first_log_display = TRUE, user_changed_label = FALSE; switch (message) { @@ -1646,13 +1655,16 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA } break; } - if (fs == FS_EXFAT) { + if ((fs == FS_EXFAT) || (fs == FS_UDF)) { if (IsWindowEnabled(hBoot)) { // unlikely to be supported by BIOSes => don't bother IGNORE_RETVAL(ComboBox_SetCurSel(hBootType, 0)); - uDOSChecked = IsDlgButtonChecked(hMainDialog, IDC_BOOT); + uBootChecked = IsDlgButtonChecked(hMainDialog, IDC_BOOT); CheckDlgButton(hDlg, IDC_BOOT, BST_UNCHECKED); EnableBootOptions(FALSE); + } else if (IsDlgButtonChecked(hMainDialog, IDC_BOOT)) { + uBootChecked = TRUE; + CheckDlgButton(hDlg, IDC_BOOT, BST_UNCHECKED); } SetMBRProps(); break; @@ -1685,7 +1697,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA EnableWindow(hBoot, TRUE); EnableWindow(hBootType, TRUE); EnableWindow(hSelectISO, TRUE); - CheckDlgButton(hDlg, IDC_BOOT, uDOSChecked); + CheckDlgButton(hDlg, IDC_BOOT, uBootChecked); } SetMBRProps(); break; diff --git a/src/rufus.h b/src/rufus.h index 213e4424..12bf7ee7 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -153,6 +153,7 @@ enum { FS_FAT16 = 0, FS_FAT32, FS_NTFS, + FS_UDF, FS_EXFAT, FS_MAX }; @@ -315,7 +316,9 @@ extern BOOL DeletePartitions(HANDLE hDrive); extern const char* GetPartitionType(BYTE Type); extern BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSystemNameSize); extern BOOL GetDriveLabel(DWORD DriveIndex, char* letter, char** label); +extern BOOL MountVolume(char* drive_name, char *drive_guid); extern BOOL UnmountVolume(HANDLE hDrive); +extern BOOL RemountVolume(char* drive_name); extern BOOL CreateProgress(void); extern BOOL SetAutorun(const char* path); extern char* FileDialog(BOOL save, char* path, char* filename, char* ext, char* ext_desc); diff --git a/src/rufus.rc b/src/rufus.rc index b7896f5b..6f86a95c 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -30,7 +30,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 206, 329 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW -CAPTION "Rufus v1.3.4.268" +CAPTION "Rufus v1.3.4.269" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "Start",IDC_START,94,291,50,14 @@ -278,8 +278,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,4,268 - PRODUCTVERSION 1,3,4,268 + FILEVERSION 1,3,4,269 + PRODUCTVERSION 1,3,4,269 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -296,13 +296,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "1.3.4.268" + VALUE "FileVersion", "1.3.4.269" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2013 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "1.3.4.268" + VALUE "ProductVersion", "1.3.4.269" END END BLOCK "VarFileInfo" diff --git a/src/sys_types.h b/src/sys_types.h index 5fc37ff7..59517ede 100644 --- a/src/sys_types.h +++ b/src/sys_types.h @@ -41,7 +41,7 @@ SysType msdos_systypes[] = { { 0x04, N_("Small FAT16") }, { 0x05, N_("Extended") }, { 0x06, N_("FAT16") }, - { 0x07, N_("NTFS") }, + { 0x07, N_("NTFS/exFAT/UDF") }, { 0x08, N_("AIX") }, { 0x09, N_("AIX Bootable") }, { 0x0a, N_("OS/2 Boot Manager") },