Skip to content

Commit

Permalink
[ext2fs] fix creation of persistent partitions for pre-1703 platforms
Browse files Browse the repository at this point in the history
* Windows platforms prior to Windows 10 1703 cannot access any logical partition besides the
  first one (we don't even get a volume for those).
* This fix enables the use of physical + offset for ext# formatting to work around this,
  which is file since we don't actually need to mount the partition.
* Also fix ext2fs_open2() not handling normalized versions of Windows drive paths ("\\?\...")
* Also fix an issue where we would make the drive letter unavailable after formatting a
  standalone partition to ext#.
* Also ensure that we return an error if the drive we attempt to locate a partition on
  through an offset does not match the currently selected one.
* Also remove some unused calls in drive.c.
* Closes pbatard#1374
  • Loading branch information
pbatard committed Sep 14, 2019
1 parent 0a24940 commit bf8d888
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 83 deletions.
87 changes: 38 additions & 49 deletions src/drive.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,23 +242,6 @@ char* GetPhysicalName(DWORD DriveIndex)
return (success)?safe_strdup(physical_name):NULL;
}

/*
* Return the path to access a partition on a specific disk, or NULL on error.
* The string is allocated and must be freed (to ensure concurrent access)
* If PartitionOffset is 0, the offset is ignored and the first partition found is returned.
*/
char* GetPartitionName(DWORD DriveIndex, uint64_t PartitionOffset)
{
char partition_name[32];
DWORD i = MAX_PARTITIONS + 1;

CheckDriveIndex(DriveIndex);
for (i = 1; (i <= MAX_PARTITIONS) && (PartitionOffset != 0) && (SelectedDrive.PartitionOffset[i - 1] != PartitionOffset); i++);
static_sprintf(partition_name, "\\Device\\Harddisk%lu\\Partition%lu", DriveIndex, i);
out:
return (i <= MAX_PARTITIONS) ? safe_strdup(partition_name) : NULL;
}

/*
* Return a handle to the physical drive identified by DriveIndex
*/
Expand Down Expand Up @@ -387,11 +370,14 @@ char* GetLogicalName(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bKeepTrail
// Now process all the volumes we found, and try to match one with our partition offset
for (i = 0; (i < found_name.Index) && (PartitionOffset != 0) && (PartitionOffset != found_offset[i]); i++);

if (i < found_name.Index)
if (i < found_name.Index) {
ret = safe_strdup(found_name.String[i]);
else
} else {
// NB: We need to re-add DRIVE_INDEX_MIN for this call since CheckDriveIndex() substracted it
ret = AltGetLogicalName(DriveIndex + DRIVE_INDEX_MIN, PartitionOffset, bKeepTrailingBackslash, bSilent);
if ((ret != NULL) && (strchr(ret, ' ') != NULL))
uprintf("Warning: Using physical device to access partition data");
}

out:
if (hVolume != INVALID_HANDLE_VALUE)
Expand All @@ -409,26 +395,38 @@ char* GetLogicalName(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bKeepTrail
*/
char* AltGetLogicalName(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bKeepTrailingBackslash, BOOL bSilent)
{
BOOL matching_drive = (DriveIndex == SelectedDrive.DeviceNumber);
DWORD i;
char *ret = NULL, volume_name[MAX_PATH], path[MAX_PATH];
char *ret = NULL, volume_name[MAX_PATH], path[64];

CheckDriveIndex(DriveIndex);

// Match the offset to a partition index
for (i = 0; (i < MAX_PARTITIONS) && (PartitionOffset != 0) && (PartitionOffset != SelectedDrive.PartitionOffset[i]); i++);
if (i >= MAX_PARTITIONS) {
suprintf("Error: Could not find a partition at offset %lld on this disk", PartitionOffset);
if (PartitionOffset == 0) {
i = 0;
} else if (matching_drive) {
for (i = 0; (i < MAX_PARTITIONS) && (PartitionOffset != SelectedDrive.PartitionOffset[i]); i++);
if (i >= MAX_PARTITIONS) {
suprintf("Error: Could not find a partition at offset %lld on this disk", PartitionOffset);
goto out;
}
} else {
suprintf("Error: Searching for a partition on a non matching disk");
goto out;
}
static_sprintf(path, "Harddisk%luPartition%lu", DriveIndex, i + 1);
static_strcpy(volume_name, groot_name);
if (!QueryDosDeviceA(path, &volume_name[groot_len], (DWORD)(MAX_PATH - groot_len)) || (strlen(volume_name) < 20)) {
suprintf("Could not find the DOS volume name for '%s': %s", path, WindowsErrorString());
} else {
if (bKeepTrailingBackslash)
static_strcat(volume_name, "\\");
ret = safe_strdup(volume_name);
suprintf("Could not find a DOS volume name for '%s': %s", path, WindowsErrorString());
// If we are on the right drive, we enable a custom access mode through physical + offset
if (!matching_drive)
goto out;
static_sprintf(volume_name, "\\\\.\\PhysicalDrive%lu%s %I64u %I64u", DriveIndex, bKeepTrailingBackslash ? "\\" : "",
SelectedDrive.PartitionOffset[i], SelectedDrive.PartitionSize[i]);
} else if (bKeepTrailingBackslash) {
static_strcat(volume_name, "\\");
}
ret = safe_strdup(volume_name);

out:
return ret;
Expand Down Expand Up @@ -772,24 +770,6 @@ HANDLE GetLogicalHandle(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bLockDr
return hLogical;
}

/*
* Similar to the above, but use the partition name instead
*/
HANDLE GetPartitionHandle(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare)
{
HANDLE handle = INVALID_HANDLE_VALUE;
char* volume_name = GetPartitionName(DriveIndex, PartitionOffset);

if (volume_name == NULL) {
uprintf("Could not get partition volume name");
return NULL;
}

handle = GetHandle(volume_name, bLockDrive, bWriteAccess, bWriteShare);
free(volume_name);
return handle;
}

/*
* Who would have thought that Microsoft would make it so unbelievably hard to
* get the frickin' device number for a drive? You have to use TWO different
Expand Down Expand Up @@ -984,7 +964,7 @@ BOOL GetDriveLabel(DWORD DriveIndex, char* letters, char** label)
if (GetVolumeInformationByHandleW(h, VolumeName, 64, &VolumeSerialNumber,
&MaximumComponentLength, &FileSystemFlags, FileSystemName, 64)) {
wchar_to_utf8_no_alloc(VolumeName, VolumeLabel, sizeof(VolumeLabel));
*label = VolumeLabel;
*label = (VolumeLabel[0] != 0) ? VolumeLabel : STR_NO_LABEL;
}
// Drive without volume assigned - always enabled
return TRUE;
Expand Down Expand Up @@ -1167,6 +1147,7 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys

SelectedDrive.nPartitions = 0;
memset(SelectedDrive.PartitionOffset, 0, sizeof(SelectedDrive.PartitionOffset));
memset(SelectedDrive.PartitionSize, 0, sizeof(SelectedDrive.PartitionSize));
// Populate the filesystem data
FileSystemName[0] = 0;
volume_name = GetLogicalName(DriveIndex, 0, TRUE, FALSE);
Expand Down Expand Up @@ -1255,8 +1236,10 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys
break;
}
}
if (i < MAX_PARTITIONS)
if (i < MAX_PARTITIONS) {
SelectedDrive.PartitionOffset[i] = DriveLayout->PartitionEntry[i].StartingOffset.QuadPart;
SelectedDrive.PartitionSize[i] = DriveLayout->PartitionEntry[i].PartitionLength.QuadPart;
}
// NB: MinGW's gcc 4.9.2 broke "%lld" printout on XP so we use the inttypes.h "PRI##" qualifiers
suprintf(" Type: %s (0x%02x)\r\n Size: %s (%" PRIi64 " bytes)\r\n Start Sector: %" PRIi64 ", Boot: %s",
((part_type==0x07||super_floppy_disk)&&(FileSystemName[0]!=0))?FileSystemName:GetPartitionType(part_type), super_floppy_disk?0:part_type,
Expand All @@ -1282,8 +1265,10 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys
suprintf("Max parts: %d, Start Offset: %" PRIi64 ", Usable = %" PRIi64 " bytes",
DriveLayout->Gpt.MaxPartitionCount, DriveLayout->Gpt.StartingUsableOffset.QuadPart, DriveLayout->Gpt.UsableLength.QuadPart);
for (i=0; i<DriveLayout->PartitionCount; i++) {
if (i < MAX_PARTITIONS)
if (i < MAX_PARTITIONS) {
SelectedDrive.PartitionOffset[i] = DriveLayout->PartitionEntry[i].StartingOffset.QuadPart;
SelectedDrive.PartitionSize[i] = DriveLayout->PartitionEntry[i].PartitionLength.QuadPart;
}
SelectedDrive.nPartitions++;
isUefiNtfs = (wcscmp(DriveLayout->PartitionEntry[i].Gpt.Name, L"UEFI:NTFS") == 0);
suprintf("Partition %d%s:\r\n Type: %s\r\n Name: '%S'", i+1, isUefiNtfs ? " (UEFI:NTFS)" : "",
Expand Down Expand Up @@ -1551,6 +1536,7 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m
}
memset(partition_offset, 0, sizeof(partition_offset));
memset(SelectedDrive.PartitionOffset, 0, sizeof(SelectedDrive.PartitionOffset));
memset(SelectedDrive.PartitionSize, 0, sizeof(SelectedDrive.PartitionSize));

// Compute the start offset of our first partition
if ((partition_style == PARTITION_STYLE_GPT) || (!IsChecked(IDC_OLD_BIOS_FIXES))) {
Expand All @@ -1575,6 +1561,7 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m
if (!ClearPartition(hDrive, DriveLayoutEx.PartitionEntry[pn].StartingOffset, size_to_clear))
uprintf("Could not zero %S: %s", extra_part_name, WindowsErrorString());
SelectedDrive.PartitionOffset[pn] = DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart;
SelectedDrive.PartitionSize[pn] = DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart;
pn++;
DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart = DriveLayoutEx.PartitionEntry[pn-1].StartingOffset.QuadPart +
DriveLayoutEx.PartitionEntry[pn-1].PartitionLength.QuadPart;
Expand Down Expand Up @@ -1657,6 +1644,7 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m
wcsncpy(DriveLayoutEx.PartitionEntry[pn].Gpt.Name, main_part_name, ARRAYSIZE(DriveLayoutEx.PartitionEntry[pn].Gpt.Name));
}
SelectedDrive.PartitionOffset[pn] = DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart;
SelectedDrive.PartitionSize[pn] = DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart;
partition_offset[PI_MAIN] = SelectedDrive.PartitionOffset[pn];
pn++;

Expand All @@ -1670,6 +1658,7 @@ BOOL CreatePartition(HANDLE hDrive, int partition_style, int file_system, BOOL m
uprintf("● Creating %S Partition (offset: %lld, size: %s)", extra_part_name, DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart,
SizeToHumanReadable(DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart, TRUE, FALSE));
SelectedDrive.PartitionOffset[pn] = DriveLayoutEx.PartitionEntry[pn].StartingOffset.QuadPart;
SelectedDrive.PartitionSize[pn] = DriveLayoutEx.PartitionEntry[pn].PartitionLength.QuadPart;
if (extra_partitions & XP_CASPER)
partition_offset[PI_CASPER] = SelectedDrive.PartitionOffset[pn];
else if (extra_partitions & XP_ESP)
Expand Down
3 changes: 1 addition & 2 deletions src/drive.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ typedef struct {
int PartitionStyle;
int nPartitions; // number of partitions we actually care about
uint64_t PartitionOffset[MAX_PARTITIONS];
uint64_t PartitionSize[MAX_PARTITIONS];
int FSType;
char proposed_label[16];
BOOL has_protective_mbr;
Expand All @@ -366,14 +367,12 @@ extern uint64_t partition_offset[3];
BOOL SetAutoMount(BOOL enable);
BOOL GetAutoMount(BOOL* enabled);
char* GetPhysicalName(DWORD DriveIndex);
char* GetPartitionName(DWORD DriveIndex, uint64_t PartitionOffset);
BOOL DeletePartitions(DWORD DriveIndex);
HANDLE GetPhysicalHandle(DWORD DriveIndex, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare);
char* GetLogicalName(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bKeepTrailingBackslash, BOOL bSilent);
char* AltGetLogicalName(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bKeepTrailingBackslash, BOOL bSilent);
BOOL WaitForLogical(DWORD DriveIndex, uint64_t PartitionOffset);
HANDLE GetLogicalHandle(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare);
HANDLE GetPartitionHandle(DWORD DriveIndex, uint64_t PartitionOffset, BOOL bLockDrive, BOOL bWriteAccess, BOOL bWriteShare);
int GetDriveNumber(HANDLE hDrive, char* path);
BOOL GetDriveLetters(DWORD DriveIndex, char* drive_letters);
UINT GetDriveTypeFromIndex(DWORD DriveIndex);
Expand Down
39 changes: 25 additions & 14 deletions src/ext2fs/nt_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ typedef struct _NT_PRIVATE_DATA {
ULONG buffer_size;
BOOLEAN read_only;
BOOLEAN written;
// Used by Rufus
__u64 offset;
__u64 size;
} NT_PRIVATE_DATA, *PNT_PRIVATE_DATA;

//
Expand Down Expand Up @@ -199,7 +202,7 @@ static NTSTATUS _OpenNtName(IN PCSTR Name, IN BOOLEAN Readonly, OUT PHANDLE Hand
UnicodeString.MaximumLength = sizeof(Buffer); // in bytes!!!

// Initialize object
InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL );
InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL);

// Try to open it in initial mode
if (ARGUMENT_PRESENT(OpenedReadonly))
Expand Down Expand Up @@ -286,13 +289,17 @@ static __inline NTSTATUS _CloseDisk(IN HANDLE Handle)
return (pfNtClose == NULL) ? STATUS_DLL_NOT_FOUND : pfNtClose(Handle);
}

static PCSTR _NormalizeDeviceName(IN PCSTR Device, IN PSTR NormalizedDeviceNameBuffer)
static PCSTR _NormalizeDeviceName(IN PCSTR Device, IN PSTR NormalizedDeviceNameBuffer, OUT __u64 *Offset, OUT __u64 *Size)
{
*Offset = *Size = 0ULL;
// Convert non NT paths to NT
if (Device[0] == '\\') {
if ((strlen(Device) < 4) || (Device[3] != '\\'))
return Device;
strcpy(NormalizedDeviceNameBuffer, Device);
// Handle custom paths of the form "<Physical> <Offset> <Size>" used by Rufus to
// enable multi-partition access on removable devices, for pre 1703 platforms.
if (sscanf(Device, "%s %I64u %I64u", NormalizedDeviceNameBuffer, Offset, Size) < 1)
return NULL;
if ((NormalizedDeviceNameBuffer[1] == '\\') || (NormalizedDeviceNameBuffer[1] == '.'))
NormalizedDeviceNameBuffer[1] = '?';
if (NormalizedDeviceNameBuffer[2] == '.')
Expand Down Expand Up @@ -341,7 +348,8 @@ static VOID _GetDeviceSize(IN HANDLE h, OUT unsigned __int64 *FsSize)
}
}

static BOOLEAN _Ext2OpenDevice(IN PCSTR Name, IN BOOLEAN ReadOnly, OUT PHANDLE Handle, OUT PBOOLEAN OpenedReadonly OPTIONAL, OUT errcode_t *Errno OPTIONAL)
static BOOLEAN _Ext2OpenDevice(IN PCSTR Name, IN BOOLEAN ReadOnly, OUT PHANDLE Handle,
OUT __u64 *Offset, OUT __u64 *Size, OUT PBOOLEAN OpenedReadonly OPTIONAL, OUT errcode_t *Errno OPTIONAL)
{
CHAR NormalizedDeviceName[512];
NTSTATUS Status;
Expand All @@ -358,7 +366,7 @@ static BOOLEAN _Ext2OpenDevice(IN PCSTR Name, IN BOOLEAN ReadOnly, OUT PHANDLE H
(':' == *(Name + 1)) && ('\0' == *(Name + 2))) {
Status = _OpenDriveLetter(*Name, ReadOnly, Handle, OpenedReadonly);
} else {
Name = _NormalizeDeviceName(Name, NormalizedDeviceName);
Name = _NormalizeDeviceName(Name, NormalizedDeviceName, Offset, Size);
if (Name == NULL) {
LastWinError = ERROR_INVALID_PARAMETER;
if (ARGUMENT_PRESENT(Errno))
Expand Down Expand Up @@ -438,12 +446,13 @@ static BOOLEAN _SetPartType(IN HANDLE Handle, IN UCHAR Type)
errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags)
{
errcode_t errcode = 0;
__u64 Offset, Size;
HANDLE h;
BOOLEAN Readonly;

*mount_flags = 0;

if (!_Ext2OpenDevice(file, TRUE, &h, &Readonly, &errcode))
if (!_Ext2OpenDevice(file, TRUE, &h, &Offset, &Size, &Readonly, &errcode))
return errcode;

*mount_flags &= _IsMounted(h) ? EXT2_MF_MOUNTED : 0;
Expand All @@ -463,18 +472,19 @@ errcode_t ext2fs_check_mount_point(const char *file, int *mount_flags, char *mtp
// different removable devices (e.g. UFD) may be remounted under the same path.
errcode_t ext2fs_get_device_size2(const char *file, int blocksize, blk64_t *retblocks)
{
errcode_t errcode;
__int64 fs_size = 0;
errcode_t errcode = 0;
__u64 Offset, Size = 0;
HANDLE h;
BOOLEAN Readonly;

if (!_Ext2OpenDevice(file, TRUE, &h, &Readonly, &errcode))
if (!_Ext2OpenDevice(file, TRUE, &h, &Offset, &Size, &Readonly, &errcode))
return errcode;

_GetDeviceSize(h, &fs_size);
if (Size == 0LL)
_GetDeviceSize(h, &Size);
_CloseDisk(h);

*retblocks = (blk64_t)(fs_size / blocksize);
*retblocks = (blk64_t)(Size / blocksize);
return 0;
}

Expand Down Expand Up @@ -529,7 +539,8 @@ static errcode_t nt_open(const char *name, int flags, io_channel *channel)
io->private_data = nt_data;

// Open the device
if (!_Ext2OpenDevice(name, (BOOLEAN)!BooleanFlagOn(flags, EXT2_FLAG_RW), &nt_data->handle, &nt_data->read_only, &errcode)) {
if (!_Ext2OpenDevice(name, (BOOLEAN)!BooleanFlagOn(flags, EXT2_FLAG_RW), &nt_data->handle,
&nt_data->offset, &nt_data->size, &nt_data->read_only, &errcode)) {
if (!errcode)
errcode = EIO;
goto out;
Expand Down Expand Up @@ -631,7 +642,7 @@ static errcode_t nt_read_blk(io_channel channel, unsigned long block, int count,

size = (count < 0) ? (ULONG)(-count) : (ULONG)(count * channel->block_size);

offset.QuadPart = block * channel->block_size;
offset.QuadPart = block * channel->block_size + nt_data->offset;

// If not fit to the block
if (size <= nt_data->buffer_size) {
Expand Down Expand Up @@ -686,7 +697,7 @@ static errcode_t nt_write_blk(io_channel channel, unsigned long block, int count


assert((write_size % 512) == 0);
offset.QuadPart = block * channel->block_size;
offset.QuadPart = block * channel->block_size + nt_data->offset;

if (!_RawWrite(nt_data->handle, offset, write_size, buf, &errcode)) {
if (channel->write_error)
Expand Down
3 changes: 2 additions & 1 deletion src/ext2fs/openfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
goto cleanup;
strcpy(fs->device_name, name);
cp = strchr(fs->device_name, '?');
if (!io_options && cp) {
// Don't process "?\" as an option since some Windows device paths use "\\?\..."
if (!io_options && cp && cp[1] != '\\') {
*cp++ = 0;
io_options = cp;
}
Expand Down
Loading

0 comments on commit bf8d888

Please sign in to comment.