Win32:访问超出卷末尾的分区?
Win32: Accessing a partition beyond the end of a volume?
可以使用像
这样的函数调用将磁盘卷作为文件打开
HANDLE hDisk = CreateFile("\\.\G:", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, NULL);
但是,这只允许访问卷占用的分区区域 - 这可能不是全部。
是否可以根据卷的驱动器号打开整个分区的句柄?分区的真实大小可以通过在卷句柄上调用 DeviceIoControl(...IOCTL_GET_DRIVE_GEOMETRY...)
来计算,但是超出卷末尾的 reads/writes 不起作用。
整个磁盘 (fdo) 和分区 (pdo) 存在不同的设备对象。为整个磁盘创建的第一个设备。它具有规范的名称格式
#define FDO_NAME_FORMAT "\Device\Harddisk%d\DR%d"
也为它创建了众所周知的符号 link
"\Device\Harddisk%d\Partition0"
"\DosDevices\PhysicalDrive%d"
然后,如果磁盘已格式化且其上存在分区 - 创建了额外的卷设备 (pdo)。此设备的本机名称具有
形式
"\Device\HarddiskVolume%d"
和众所周知的分区符号 links 格式 -
"\Device\HarddiskX\PartitionY"
其中 X
与整个磁盘相同,并且 Y
始终不为 0。因此磁盘设备是 \Device\HarddiskX\DRX
或 \Device\HarddiskX\Partition0
或 \DosDevices\PhysicalDriveX
和分区因为在这个磁盘上有名称 \Device\HarddiskX\PartitionY
(符号 link 到 \Device\HarddiskVolume%d
)。挂载卷时 - 挂载管理器可以为设备分配字母,例如 \\?\c:
但这只是符号 link 到某些卷设备。
Is it possible, given the drive letter of the volume, to open a handle
to the entire partition?
是的。更正确地说是整个磁盘(或分区 0)的句柄。我们可以通过发送 IOCTL_STORAGE_GET_DEVICE_NUMBER
to volume device and use DeviceNumber
from STORAGE_DEVICE_NUMBER
来构造这个分区所在的磁盘 ("\\?\PhysicalDrive%d"
) 的名称。所以代码看起来像
ULONG dv(PCWSTR VolumeName)
{
HANDLE hFile = CreateFile(VolumeName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
union {
STORAGE_DEVICE_NUMBER sdn;
DISK_GEOMETRY_EX dg;
};
OVERLAPPED ov = {};
ULONG dwError = DeviceIoControl(hFile, IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, &sdn, sizeof(sdn), 0, &ov)
? NOERROR : GetLastError();
CloseHandle(hFile);
if (dwError == NOERROR)
{
WCHAR name[32];
swprintf(name, L"\\?\PhysicalDrive%d", sdn.DeviceNumber);
hFile = CreateFile(name, FILE_GENERIC_READ,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
if (DeviceIoControl(hFile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, 0, 0, &dg, sizeof(dg), 0, &ov))
{
FILE_ALIGNMENT_INFO fai;
if (GetFileInformationByHandleEx(hFile, FileAlignmentInfo, &fai, sizeof(fai)))
{
if (fai.AlignmentRequirement < 2*sizeof(void*))
{
fai.AlignmentRequirement = 0;
}
ULONG_PTR a = fai.AlignmentRequirement;
if (PVOID buf = _malloca(dg.Geometry.BytesPerSector + a))
{
PVOID pv = (PVOID)(((ULONG_PTR)buf + a) & ~a);
LARGE_INTEGER ByteOffset;
ByteOffset.QuadPart = dg.DiskSize.QuadPart - dg.Geometry.BytesPerSector;
ov.Offset = ByteOffset.LowPart;
ov.OffsetHigh = ByteOffset.HighPart;
ReadFile(hFile, pv, dg.Geometry.BytesPerSector, 0, &ov);
_freea(buf);
}
}
}
CloseHandle(hFile);
}
}
return dwError;
}
这里我读取了磁盘的最后一个扇区。通常我在这里将 EFI PART
视为缓冲区中的前 8 个。在一些(可移动的)闪光灯上,我查看 ...NTFS
标签
我发现对于我的用例来说似乎是一个更简单的解决方案:FSCTL_ALLOW_EXTENDED_DASD_IO 控制代码取消了对卷句柄的限制,允许访问文件系统外部分区上的扇区。所以除了错误检查之外,它是我代码的一行补充:
DeviceIoControl(hDisk, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &dwBytesRead, NULL);
可以使用像
这样的函数调用将磁盘卷作为文件打开HANDLE hDisk = CreateFile("\\.\G:", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, NULL);
但是,这只允许访问卷占用的分区区域 - 这可能不是全部。
是否可以根据卷的驱动器号打开整个分区的句柄?分区的真实大小可以通过在卷句柄上调用 DeviceIoControl(...IOCTL_GET_DRIVE_GEOMETRY...)
来计算,但是超出卷末尾的 reads/writes 不起作用。
整个磁盘 (fdo) 和分区 (pdo) 存在不同的设备对象。为整个磁盘创建的第一个设备。它具有规范的名称格式
#define FDO_NAME_FORMAT "\Device\Harddisk%d\DR%d"
也为它创建了众所周知的符号 link
"\Device\Harddisk%d\Partition0"
"\DosDevices\PhysicalDrive%d"
然后,如果磁盘已格式化且其上存在分区 - 创建了额外的卷设备 (pdo)。此设备的本机名称具有
形式"\Device\HarddiskVolume%d"
和众所周知的分区符号 links 格式 -
"\Device\HarddiskX\PartitionY"
其中 X
与整个磁盘相同,并且 Y
始终不为 0。因此磁盘设备是 \Device\HarddiskX\DRX
或 \Device\HarddiskX\Partition0
或 \DosDevices\PhysicalDriveX
和分区因为在这个磁盘上有名称 \Device\HarddiskX\PartitionY
(符号 link 到 \Device\HarddiskVolume%d
)。挂载卷时 - 挂载管理器可以为设备分配字母,例如 \\?\c:
但这只是符号 link 到某些卷设备。
Is it possible, given the drive letter of the volume, to open a handle to the entire partition?
是的。更正确地说是整个磁盘(或分区 0)的句柄。我们可以通过发送 IOCTL_STORAGE_GET_DEVICE_NUMBER
to volume device and use DeviceNumber
from STORAGE_DEVICE_NUMBER
来构造这个分区所在的磁盘 ("\\?\PhysicalDrive%d"
) 的名称。所以代码看起来像
ULONG dv(PCWSTR VolumeName)
{
HANDLE hFile = CreateFile(VolumeName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
union {
STORAGE_DEVICE_NUMBER sdn;
DISK_GEOMETRY_EX dg;
};
OVERLAPPED ov = {};
ULONG dwError = DeviceIoControl(hFile, IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, &sdn, sizeof(sdn), 0, &ov)
? NOERROR : GetLastError();
CloseHandle(hFile);
if (dwError == NOERROR)
{
WCHAR name[32];
swprintf(name, L"\\?\PhysicalDrive%d", sdn.DeviceNumber);
hFile = CreateFile(name, FILE_GENERIC_READ,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
if (DeviceIoControl(hFile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, 0, 0, &dg, sizeof(dg), 0, &ov))
{
FILE_ALIGNMENT_INFO fai;
if (GetFileInformationByHandleEx(hFile, FileAlignmentInfo, &fai, sizeof(fai)))
{
if (fai.AlignmentRequirement < 2*sizeof(void*))
{
fai.AlignmentRequirement = 0;
}
ULONG_PTR a = fai.AlignmentRequirement;
if (PVOID buf = _malloca(dg.Geometry.BytesPerSector + a))
{
PVOID pv = (PVOID)(((ULONG_PTR)buf + a) & ~a);
LARGE_INTEGER ByteOffset;
ByteOffset.QuadPart = dg.DiskSize.QuadPart - dg.Geometry.BytesPerSector;
ov.Offset = ByteOffset.LowPart;
ov.OffsetHigh = ByteOffset.HighPart;
ReadFile(hFile, pv, dg.Geometry.BytesPerSector, 0, &ov);
_freea(buf);
}
}
}
CloseHandle(hFile);
}
}
return dwError;
}
这里我读取了磁盘的最后一个扇区。通常我在这里将 EFI PART
视为缓冲区中的前 8 个。在一些(可移动的)闪光灯上,我查看 ...NTFS
标签
我发现对于我的用例来说似乎是一个更简单的解决方案:FSCTL_ALLOW_EXTENDED_DASD_IO 控制代码取消了对卷句柄的限制,允许访问文件系统外部分区上的扇区。所以除了错误检查之外,它是我代码的一行补充:
DeviceIoControl(hDisk, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &dwBytesRead, NULL);