質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.12%

ダイナミックディスクでGPTなPhysicalDriveから パーティションの空き位置の情報の取得方法について

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,279

juner

score 17

 前提・実現したいこと

現在、ディスクの管理にて次の様に表示されるダイナミックディスクでGPTなドライブよりパーティションの空き情報を取得方法を探しています。
[100MB 正常(EFI システムパーティション)][97.66GB 未割り当て][D: 833.63GB NTFS 正常]
※ソフトウェアRAIDを使用している4つのディスクの一つとなっております。

 発生している問題

パーティションの空き情報はパーティションの情報からることができる思い、 GPTな物理ドライブの \\.\PhysicaDrive0 より DeviceIoControl の IOCTL_DISK_GET_DRIVE_LAYOUT_EX 経由でパーティション間の空き領域情報を取得しようとしてみたのですが、それにより取得される。 DRIVE_LAYOUT_INFORMATION_EX の PartitionEntry[] こと PARTITION_INFORMATION_EX に設定される パーティションの各情報は次の様な形となります。

PartitionNumber StartOffset PartitionLength StartOffset+PartitionLength Type
1 1048576 104857600 105906176 c12a7328-f81f-11d2-ba4b-00a0c93ec93b
2 105906176 1048576 106954752 5808c8aa-7e8f-42e0-85d2-e1e90434cfb3
3 106954752 133169152 240123904 e3c9e316-0b5c-4db8-817d-f92df00215ae
4 240123904 999964745216 1000204869120 af9b60a0-1431-4f62-bc68-3311714a69ad

想定していた隙間がどこにも空いていませんでした。

 試したこと

以下の方法を試しました。

 list partition

尚、disikpart で list partition すると、次の様な表示となりました。

  Partition ###  Type                Size     Offset
  -------------  ------------------  -------  -------
  Partition 1    システム               100 MB  1024 KB
  Partition 2    予約済みダイナミック        1024 KB   101 MB
  Partition 3    予約                 127 MB   102 MB
  Partition 4    ダイナミック データ         931 GB   229 MB

FindVolume で取得されたVolumeに対しての読み込み

FindVolume で取得されたVolumeに対して IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTSにより読み込んだ場合対象のドライブに関しての情報は次のものだけとなった。

StartOffset ExtentLength StartOffset+ExtentLegnth
105097723904 895106416640 1000204140544

マウントしているボリュームのみ取得される様で、RAID構成されているDドライブの位置だけわかる状態となっておりました。(計4ディスクの位置も取得されているがここでは必要な情報だけ載せる形で省略。

使用したコード
juner/IoControl#93f655872fb7bda6e60d0f0ca13ff96c47680e30
IoControlPhysicalExample

QueryDosDevice() より取得されたパス群の中より、ボリュームを探して取得

QueryDosDevice()より取得したデバイス名群より、HarddiskVolumeX 形式の名前を探し、それを元にIOCTL_VOLUME_GET_VOLUME_DISK_EXTENTSを実施した。

FilePath StartOffset ExtentLength StartOffset+ExtentLegnth
\\.\HarddiskVolume1 1048576 104857600 105906176
\\.\HarddiskVolume2 106954752 133169152 240123904
\\.\HarddiskVolume10 105097723904 895106416640 1000204140544

一部のパーティションが取得できていないが、これがほしかった情報であろうか?

このコードは FindVolume で取得されたVolumeに対しての読み込み の確認に使ったコードに含まれる。

 GPTPartition情報を直に読み込む

PartitionNumber First(LBA8) Last(LBA8) SectorSize First*SectorSize Last*SectorSize
1 0x800 0x327FF 512 1048576 105905664
2 0x32800 0x32FFF 512 105906176 106954240
3 0x33000 0x727FF 512 106954752 240123392
4 0x72800 0x74706D8E 512 240123904 1000204868608

First*SectorSize が合致しているのでこの情報はほぼ同じ情報と想定される。Last*SectorSize がずれるのは誤差?

確認に使用したコード 
juner/ReadHeader#8591d9704b9b5dd4c2ffdc2192e9c779563c89e5
の ReadHeaderExample

 補足情報(FW/ツールのバージョンなど)

OS: Windows10 (1803)

以上、よろしくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • okrt

    2018/08/14 22:47

    C:ではなくD:で\\.\PhysicaDrive0というのが気になるところではありますが、ここが問題ではないとすれば「ダイナミックディスクを使用しているということで合ってますか?」合ってるとすれば、それが要因かもしれません。 https://stackoverflow.com/questions/33384413/how-to-open-a-partition-handle-to-get-its-information-on-a-dynamic-disk の回答のコメント欄に何か書かれてます。ちなみに、「ダイナミック ディスクは Windows では推奨されなくなりました。 」だそうです。 https://docs.microsoft.com/ja-jp/windows-server/storage/disk-management/change-a-dynamic-disk-back-to-a-basic-disk

    キャンセル

  • juner

    2018/08/16 11:54

    今回のこのディスクは ソフトウェアRAID構成を行った4つのディスクの一つな為、ダイナミックディスクであることが必須な為、上記の様な状態となっております。

    キャンセル

回答 1

+1

ダイナミックディスクに関してstackoverflowのコメント欄では「VDSかWindows Storage Management API」が提案されてますけど、具体的にどれをどのように使えばいいのか、それで本当に正しい情報が得られるのかどうかは私にはわかりません。

また別の方法としては、FindFirstVolume~FindNextVolume~FindVolumeCloseでボリュームを列挙して、各ボリュームに対してIOCTL_VOLUME_GET_VOLUME_DISK_EXTENTSで情報を取得して「ディスク全体から全ボリュームの分を引き算した結果が空き位置」という手法が使えるかもしれません。少し手間はかかりそうですが。

(ここから下は2018/08/20に追記)
QueryDosDevice()を利用する場合、HarddiskVolumeX形式だけで良いかどうかを調査したほうが良いかもしれません。
(昔のWindowsのダイナミックディスクでストライピングか何かにすると、普通のとは少し違った形式になってた物もあったかも……)
そこさえクリアすれば、以下の内容は無視していいかもしれません。

GitHubのソースはきちんと理解できていませんが、
・FindVolumes()の中でFindFirstVolume~FindNextVolume~FindVolumeCloseを使用している
・FindVolumes()ではD:の部分は得られたけど(EFI システムパーティション)の部分が得られなかった
ということですよね?
一応、以下のソースではドライブレターが割り当てられてなくても列挙できています。C言語またはC++のソースとして適当なWindows用のコンパイラでコンパイルできるはずです。

#include <Windows.h>
#include <tchar.h>
#include <stdio.h>

void print_volume_info(TCHAR szVolumeName[])
{
    HANDLE hV;
    VOLUME_DISK_EXTENTS VolumeDiskExtents[16]; /* 本当はVOLUME_DISK_EXTENTSの配列じゃないけど適当 */
    BOOL Bret;
    DWORD lBytesReturned;
    int loop;

    _tprintf(_T("%s\n"), szVolumeName);
    szVolumeName[_tcslen(szVolumeName)-1] = _T('\0'); /* 末尾1文字を削除する */
    hV = CreateFile(
        szVolumeName,
        0,
        FILE_SHARE_READ|FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_READONLY,
        NULL );
    if (hV == INVALID_HANDLE_VALUE) {
        return;
    }
    Bret = DeviceIoControl(
        hV,
        IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
        NULL,
        0,
        &VolumeDiskExtents,
        sizeof(VOLUME_DISK_EXTENTS)*16,
        &lBytesReturned,
        NULL);
    if (Bret != 0) {
        _tprintf(_T("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS\n"));
        _tprintf(_T("%d NumberOfDiskExtents\n"), VolumeDiskExtents[0].NumberOfDiskExtents);
        for(loop=0; loop<(int)VolumeDiskExtents[0].NumberOfDiskExtents; loop++) {
            _tprintf(_T(" %d DiskNumber\n"), VolumeDiskExtents[0].Extents[loop].DiskNumber);
            _tprintf(_T(" %f StartingOffset\n"), VolumeDiskExtents[0].Extents[loop].StartingOffset.QuadPart/1.0);
            _tprintf(_T(" %f ExtentLength\n"), VolumeDiskExtents[0].Extents[loop].ExtentLength.QuadPart/1.0);
        }
        _tprintf(_T("OK\n\n"), szVolumeName);
    }
    /* DVDドライブも列挙されたりするのでOKが出ない物もある */
    CloseHandle(hV);
}

int list_all_volumes(void)
{
    HANDLE hFV;
    TCHAR szVolumeName[MAX_PATH];

    hFV = FindFirstVolume(szVolumeName, MAX_PATH);
    if (hFV == INVALID_HANDLE_VALUE) {
        return 1;
    }
    do {
        print_volume_info(szVolumeName);
    } while(FindNextVolume(hFV, szVolumeName, MAX_PATH));
    FindVolumeClose(hFV);
    return 0;
}

int main(int argc, TCHAR **argv)
{
    return list_all_volumes();
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる