【问题标题】:Win32: Accessing a partition beyond the end of a volume?Win32:访问超出卷末尾的分区?
【发布时间】:2019-01-11 21:35:16
【问题描述】:

可以使用类似的函数调用将磁盘卷作为文件打开

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...) 来计算,但是超出卷末尾的读/写操作不起作用。

【问题讨论】:

  • 这让我想起了以前的 DOS 时代,您可以使用 INT13 按磁头/磁道/扇区访问磁盘。
  • 我的建议是打开整个磁盘,自己做分区识别。
  • 您可以将IOCTL_STORAGE_GET_DEVICE_NUMBER 发送到卷设备堆栈并获取STORAGE_DEVICE_NUMBER。然后使用 STORAGE_DEVICE_NUMBER.DeviceNumber 格式 \\\\?\\PhysicalDrive%u 并打开物理设备对象并使用它

标签: c winapi disk-partitioning


【解决方案1】:

对于整个磁盘 (fdo) 和分区 (pdo) 存在不同的设备对象。第一个为整个磁盘创建的设备。它具有规范的名称格式

#define FDO_NAME_FORMAT "\\Device\\Harddisk%d\\DR%d"

也为它创建了众所周知的符号链接

"\\Device\\Harddisk%d\\Partition0"
"\\DosDevices\\PhysicalDrive%d"

那么已经,如果磁盘已格式化并且上面存在分区 - 创建了额外的卷设备 (pdo)。此设备的本机名称具有格式

"\\Device\\HarddiskVolume%d"

以及众所周知的分区符号链接格式 -

"\\Device\\HarddiskX\\PartitionY" 

其中X 与整个磁盘相同,Y 始终不为 0。因此磁盘设备为 \Device\HarddiskX\DRX\Device\HarddiskX\Partition0\DosDevices\PhysicalDriveX,并且此磁盘上的分区名称为 \Device\HarddiskX\PartitionY(符号链接到\Device\HarddiskVolume%d)。挂载卷时 - 挂载管理器可以为设备分配字母,例如 \\\?\c: 但这只是指向某些卷设备的符号链接。

在给定卷的驱动器号的情况下,是否可以打开句柄 到整个分区?

是的。更正确的说法是处理整个磁盘(或分区 0)。我们可以通过将IOCTL_STORAGE_GET_DEVICE_NUMBER 发送到卷设备并使用STORAGE_DEVICE_NUMBER 中的DeviceNumber 来构造该分区所在的磁盘名称("\\\\?\\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 标签

【讨论】:

    【解决方案2】:

    我发现对于我的用例来说似乎是一个更简单的解决方案:FSCTL_ALLOW_EXTENDED_DASD_IO 控制代码消除了对卷句柄的限制,允许访问文件系统之外的分区上的扇区。所以除了错误检查之外,它是我代码中的一行代码:

    DeviceIoControl(hDisk, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &dwBytesRead, NULL);
    

    【讨论】:

      猜你喜欢
      • 2013-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 2015-09-08
      • 1970-01-01
      • 2021-08-02
      • 2020-10-29
      相关资源
      最近更新 更多