10

Windows システム (XP 以降) の物理ディスク、パーティション、およびボリュームに関する情報を抽出するためのライブラリを作成しています。

ボリュームの容量を取得しようとしています。私が知っているアプローチと、それぞれが失敗する理由は次のとおりです。

  • GetDiskFreeSpaceEx-- ユーザー クォータの影響を受けます。
  • IOCTL_DISK_GET_DRIVE_GEOMETRY_EX-- ボリューム ハンドルを使用して呼び出された場合でも、物理ディスク全体のサイズを取得します。
  • IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS-- RAID オーバーヘッドを考慮していません。
  • IOCTL_DISK_GET_LENGTH_INFO-- アクセスが拒否されて失敗します。(実際にはGENERIC_READ、他のすべてのクエリとは異なり、アクセスGENERIC_READが必要であり、管理者アクセスが必要です。)
  • IOCTL_STORAGE_READ_CAPACITY-- XP では利用できませんIOCTL_DISK_GET_LENGTH_INFOIOCTL_DISK_GET_DRIVE_GEOMETRY_EX
  • FSCTL_GET_VOLUME_BITMAP+GetFreeDiskSpaceクラスター サイズ -- GENERIC_READ(管理者アクセス) が必要であり、ボリューム全体ではなく、ファイル システムのデータ領域のサイズを指定します。
  • IOCTL_DISK_GET_PARTITION_INFO-- GENERIC_READ(管理者アクセス) が必要であり、USB 接続のディスクでも失敗しました (おそらくスーパーフロッピー パーティショニングを使用)

奇妙なことに、 のクラスタ数FSCTL_GET_VOLUME_BITMAPと WMI のCIM_LogicalDisk.Sizeプロパティは一致しており、どちらも の値よりも 4096 バイト小さくなっていますIOCTL_DISK_GET_LENGTH_INFO

ボリューム容量を取得する正しい方法は何ですか? 他のすべてのクエリは管理者アクセスなしで機能するため、これについても最小権限のソリューションを探しています。

4

1 に答える 1

3

いったい何を手に入れたいのですか?

  • 1) 物理ディスク容量

    また

  • 2) ディスク上のパーティションの容量

    また

  • 3) パーティション上のファイル システムの容量

物理ディスク用の PDO があります。これは、disk.sys が FDO を作成してアタッチするためです ( \Device\Harddisk<I>\DR0- 名前または\Device\Harddisk<I>\Partition0- シンボリック リンク。ここで、0、1、2 のディスク番号)。

物理ディスク上のすべてのパーティションに対して disk.sys は PDO を作成します ( \Device\Harddisk<I>\Partition<J>- (J in {1,2,3..}) - some へのシンボリックリンク\Device\HarddiskVolume<X>)

1) 物理ディスクの容量を取得するには、いくつかの方法があります。

  • a)

\Device\Harddisk<I>\Partition<J>いずれかのデバイス ({0,1,..} の J - ディスク FDO または任意のパーティション PDO) を開き、 and/or -でIOCTL_SCSI_PASS_THROUGH_DIRECT(FILE_READ_ACCESS | FILE_WRITE_ACCESS)送信します。SCSIOP_READ_CAPACITYSCSIOP_READ_CAPACITY16SCSIOP_READ_CAPACITYSCSIOP_READ_CAPACITY16

READ_CAPACITY_DATA_EX rcd;
SCSI_PASS_THROUGH_DIRECT sptd = {
    sizeof(sptd), 0, 0, 0, 0, CDB12GENERIC_LENGTH, 0, SCSI_IOCTL_DATA_IN, 
    sizeof(rcd), 1, &rcd, 0, {SCSIOP_READ_CAPACITY16}
};

if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SCSI_PASS_THROUGH_DIRECT,
    &sptd, sizeof(sptd), &sptd, sizeof(sptd)))
{
    DbgPrint("---- SCSIOP_READ_CAPACITY16 ----\n");
    rcd.BytesPerBlock = _byteswap_ulong(rcd.BytesPerBlock);
    rcd.LogicalBlockAddress.QuadPart = _byteswap_uint64(rcd.LogicalBlockAddress.QuadPart) + 1;
    DbgPrint("%I64x %x\n", rcd.LogicalBlockAddress, rcd.BytesPerBlock);
    rcd.LogicalBlockAddress.QuadPart *= rcd.BytesPerBlock;
    DbgPrint("%I64x %I64u\n", rcd.LogicalBlockAddress.QuadPart, rcd.LogicalBlockAddress.QuadPart);
}

また

    READ_CAPACITY_DATA rcd;
    SCSI_PASS_THROUGH_DIRECT sptd = {
        sizeof(sptd), 0, 0, 0, 0, CDB10GENERIC_LENGTH, 0, SCSI_IOCTL_DATA_IN, 
        sizeof(rcd), 1, &rcd, 0, {SCSIOP_READ_CAPACITY}
    };

    if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SCSI_PASS_THROUGH_DIRECT,
        &sptd, sizeof(sptd), &sptd, sizeof(sptd)))
    {
        DbgPrint("---- SCSIOP_READ_CAPACITY ----\n");
        rcd.BytesPerBlock = _byteswap_ulong(rcd.BytesPerBlock);
        rcd.LogicalBlockAddress = _byteswap_ulong(rcd.LogicalBlockAddress) + 1;
        DbgPrint("%x %x\n", rcd.LogicalBlockAddress, rcd.BytesPerBlock);
        ULARGE_INTEGER u = {rcd.LogicalBlockAddress};
        u.QuadPart *= rcd.BytesPerBlock;
        DbgPrint("%I64x %I64u\n", u.QuadPart, u.QuadPart);
    }
  • b)

\Device\Harddisk<I>\Partition<J>いずれかのデバイスを開き、IOCTL_STORAGE_READ_CAPACITYFILE_READ_ACCESSを送信します- a) と同じ結果になるはずです - このリクエストは classpnp.sys で処理され内部で SCSI リクエスト ( ) がディスク PDO に送信されます。この方法は XP では機能しませんでした。ClassReadDriveCapacitySCSIOP_READ_CAPACITY

STORAGE_READ_CAPACITY sc;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_STORAGE_READ_CAPACITY, 0, 0, &sc, sizeof(sc)))
{
    DbgPrint("---- IOCTL_STORAGE_READ_CAPACITY ----\n");
    DbgPrint("%I64x %I64x %x \n", sc.DiskLength.QuadPart, sc.NumberOfBlocks.QuadPart, sc.BlockLength);
    sc.NumberOfBlocks.QuadPart *= sc.BlockLength;
    DbgPrint("%I64x %I64u\n", sc.NumberOfBlocks.QuadPart, sc.NumberOfBlocks.QuadPart);
}
  • c)

\Device\Harddisk<I>\Partition<J>任意のアクセスでいずれかを開き、IOCTL_DISK_GET_DRIVE_GEOMETRY_EXを送信して使用しますDISK_GEOMETRY_EX.DiskSize。これが最善の方法だと思います。権利を必要とせず、XP で動作する

DISK_GEOMETRY_EX GeometryEx;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, 0, 0, &GeometryEx, sizeof(GeometryEx)))
{
    DbgPrint("---- IOCTL_DISK_GET_DRIVE_GEOMETRY ----\n");

    ULONG BytesPerCylinder = GeometryEx.Geometry.TracksPerCylinder * GeometryEx.Geometry.SectorsPerTrack * GeometryEx.Geometry.BytesPerSector;

    DbgPrint("%I64x == %I64x\n", GeometryEx.Geometry.Cylinders.QuadPart, GeometryEx.DiskSize.QuadPart / BytesPerCylinder);
    DbgPrint("%I64x <= %I64x\n", GeometryEx.Geometry.Cylinders.QuadPart * BytesPerCylinder, GeometryEx.DiskSize.QuadPart);
}
  • d)

開く\Device\Harddisk<I>\Partition0\Device\Harddisk<I>\Dr0IOCTL_DISK_GET_LENGTH_INFOFILE_READ_ACCESSを使用します

  • 2)

ディスク上のパーティションの容量を取得するには - オープン\Device\Harddisk<I>\Partition<J>( {1,2..} の J ) または X 文字がパーティションに割り当てられている場合 - IOCTL_DISK_GET_LENGTH_INFO\GLOBAL??\X:を使用します。再び必要FILE_READ_ACCESS

GET_LENGTH_INFORMATION gli;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_DISK_GET_LENGTH_INFO, 0, 0, &gli, sizeof(gli)))
{
    DbgPrint("---- IOCTL_DISK_GET_LENGTH_INFO ----\n");
    DbgPrint("%I64x %I64u\n", gli.Length.QuadPart, gli.Length.QuadPart);
}
  • 3)

パーティション上のファイル システムの容量を取得するには、任意のファイル (\GLOBAL??\X:\たとえば) を開き、 NtQueryVolumeInformationFile ( FileFsSizeInformation )を使用します。

FILE_FS_SIZE_INFORMATION fsi;
if (0 <= NtOpenFile(&hFile, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_FREE_SPACE_QUERY|FILE_SYNCHRONOUS_IO_NONALERT))
{
    if (0 <= NtQueryVolumeInformationFile(hFile, &iosb, &fsi, sizeof(fsi), FileFsSizeInformation))
    {
        DbgPrint("%I64x %x %x\n", fsi.TotalAllocationUnits.QuadPart, fsi.SectorsPerAllocationUnit, fsi.BytesPerSector);
        fsi.TotalAllocationUnits.QuadPart *= fsi.SectorsPerAllocationUnit * fsi.BytesPerSector;
        DbgPrint("%I64x %I64u\n", fsi.TotalAllocationUnits.QuadPart, fsi.TotalAllocationUnits.QuadPart);
    }
    NtClose(hFile);
}

またはGetDiskFreeSpaceExを使用します- 内部的にも呼び出しますNtQueryVolumeInformationFile( FileFsSizeInformation)が flagFILE_DIRECTORY_FILEを使用するため、入力パラメーターとしてディレクトリのみを使用できます

于 2016-08-09T16:23:27.007 に答える