[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
This commit is contained in:
Pete Batard 2013-07-09 00:14:29 +01:00
parent 081f1eefe7
commit 4ac182830d
6 changed files with 146 additions and 96 deletions

View File

@ -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:

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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"

View File

@ -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") },