0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自作スキャナのディスク使用量がWizTreeと11 GB違う — 指標・WOF圧縮・ハードリンクを分けて考える

0
Posted at

NTFSのMFT(マスターファイルテーブル)を直接読んでディスク使用量を集計するツールを個人で作っています。ある日、同じC:ドライブを見ているのに、自作スキャナの合計が WizTree より 11 GB 以上大きいことに気づきました。

こういうとき、真っ先に「自分の集計にバグがある」と考えたくなります。実際にはそう単純ではありませんでした。順に切り分けていくと、原因は少なくとも3層に分かれていました。

  1. そもそも比較していた数字が別の指標だった
  2. WOF圧縮されたファイルを、MFTから見ると圧縮前の大きさで拾ってしまう
  3. ハードリンクで共有されているクラスタを、パスごとに重複して数えている

この記事は、その切り分けの記録です。最終的にどのツールもバグではなく、「何を測っているか」が違っていました。

先に結論を書いておくと、私は最終的に「他ツールと1バイト単位で一致させる」ことをゴールから外しました。その方針をやめた理由も書きます。

最初の違和感

最初に見ていたのは、C:ドライブ全体の合計です。

C:合計
自作スキャナ 186.5 GB
WizTree "Allocated" 約 174.9 GB
差 +11.6 GB

自作スキャナのほうが約11.6 GB大きい。ドライブ1本の合計で10 GB以上ずれると、そのツールで「どのフォルダが大きいか」を判断する気になれません。

ここで「どこかで二重に足しているのだろう」と当たりを付けて集計コードを読み始めたのですが、これは遠回りでした。先にやるべきだったのは、そもそも比較している数字が同じ種類のものか確認することでした。

まず、比べている数字を揃える

差が一番目立っていた C:\Program Files (x86) を取り出して、手元にある値を全部並べてみます。

ツール 指標 値
エクスプローラー サイズ 15.2 GB(16,342,637,554 バイト)
エクスプローラー ディスク上のサイズ 11.0 GB(11,882,143,744 バイト)
WizTree Size 約 15.2 GB
WizTree Allocated 約 7.8 GB
自作スキャナ (アロケーション集計) 10.139 GB

1つのフォルダに対して、5つの数字が出てきました。しかも、どれか1つが単純に壊れている、という話ではありませんでした。

  • エクスプローラーのサイズと WizTree の Size は、どちらも論理サイズ(ファイルの中身の長さ)で、約15.2 GBで一致しています。
  • エクスプローラーのディスク上のサイズと WizTree の Allocated は、どちらもディスク上の割り当て量です。
  • 自作スキャナが出しているのは後者、つまりアロケーション側の値だけです。論理サイズは出していません。

つまり最初に「11 GB違う」と言っていたときの比較は、指標を揃えていませんでした。並べるべきは「ディスク上のサイズ」と「Allocated」と自作スキャナの値で、エクスプローラーの「サイズ」と混ぜてはいけません。

単位は疑わなくてよかった

もう1つ、先に潰しておいた候補があります。GB と GiB の取り違えです。

エクスプローラーが「15.2 GB」と表示している横のバイト数 16,342,637,554 を 2^30 で割ると 15.22 になります。「11.0 GB」の 11,882,143,744 も同様に 11.07 です。つまりエクスプローラーの GB 表記は 2^30 基準で、WizTree や自作スキャナと同じでした。表示ラベルは GB / GiB で違いますが、計算の基準は同じです。

ここは差の原因ではないと確認できたので、以降は考えないことにしました。合わない原因を探すときは、こういう「潰した候補」を記録しておくと後で楽になります。

揃えたうえで、まだ残っている差

指標を揃えて、アロケーション側の3つだけを並べ直します。

Program Files (x86)
エクスプローラー ディスク上のサイズ 11.0 GB
自作スキャナ 10.139 GB
WizTree "Allocated" 約 7.8 GB

同じ「ディスク上でどれだけ使っているか」を表しているはずの3つが、11.0 / 10.1 / 7.8 と並びました。自作スキャナが真ん中にいて、エクスプローラーとWizTreeの間だけでも3 GB以上開いています。

つまり「アロケーション」と呼ばれる値も、ツール横断で1つに決まってはいませんでした。ここからが本題です。

WOFという見えない圧縮層

C:\Program Files (x86) の中身は Microsoft Edge や Office です。この種のシステム/アプリケーション領域には、WOF(Windows Overlay Filter)による圧縮がかかっているファイルが大量にあります。Microsoft の Compact OS は「OSのファイルを圧縮ファイルとしてインストールする」機能ですが、その土台にあるのがこの仕組みです。

WOF圧縮されたファイルは、NTFS上で少し変わった形をしています。

  • 実際の圧縮データは WofCompressedData という名前付き $DATA ストリームに入っている
  • 一方、無名の $DATA 属性には、圧縮前の見かけ上の大きさに対応する割り当て(projected allocation)が残っていることがある
  • アプリケーションから読むときは、フィルタドライバが透過的に展開するので、利用者からは普通のファイルに見える

つまりWOF圧縮ファイルには、実質2つの「割り当て」が存在します。圧縮前に対応する無名 $DATA 側と、圧縮後の実体である WofCompressedData 側です。

これを踏まえて先ほどの3つを見ると、高いほう(11.0 / 10.1)と低いほう(7.8)に分かれています。

MFTを直接読む自作スキャナは、WOFのフィルタ層を経由しません。レコードに書かれている無名 $DATA の割り当て、つまり圧縮前側の値をそのまま拾います。高いほうに来るのは説明が付きます。WizTree が低いほうに来ているのは、圧縮後の実体側を見ていると考えると辻褄が合います。

一方、エクスプローラーの「ディスク上のサイズ」がなぜ11.0 GBという位置なのかは、今回確認できていません。WOF対応のレイヤを通しているなら低いほうに寄りそうですが、実際には高いほうにいます。ここは推測せず、未解決のまま残しました。

いずれにせよ、自作スキャナとWizTreeの差については仮説が立ちました。次はそれを確かめます。

ファイル単位で確認する

集計の話だけだと推測の域を出ないので、ファイル1個の粒度で見ることにしました。MFTから、ファイルごとに次を出す診断モードを足しています。

  • 無名 $DATA の割り当てサイズ
  • WofCompressedData ストリームの割り当てサイズ
  • ハードリンク数(link_count)
  • $FILE_NAME 属性の数
  • reparse tag、sparse / compressed などの属性フラグ

C:\Program Files (x86)\Microsoft\EdgeCore 配下で一番大きかったファイル(msedge.dll)を見ると、こうなっていました。

alloc=309MB  link=5  fn=4  wof=1  tag=0x00000000  wof_stream=179104KB

無名 $DATA の割り当ては約309 MB。ところが WofCompressedData ストリーム側は 179,104 KB、およそ175 MBです。1ファイルで130 MB以上の開きがあります。同じ規模のDLLがバージョン違いで複数並んでいるので、フォルダ合計で数GB動くのは納得できます。

ここまでで、仮説は具体的な数字に裏付けられました。

WOFかどうかの判定は、reparse tagだけでは足りなかった

ついでに分かったことがあります。同じフォルダの別のファイルはこうでした。

alloc=18MB  link=0  fn=2  wof=1  tag=0x80000017  wof_stream=0KB

こちらは reparse tag が 0x80000017(IO_REPARSE_TAG_WOF)で、明示的にWOFだと分かります。ところが WofCompressedData 側の割り当ては取れていません(0 KB)。

先ほどの msedge.dll はその逆で、tag は 0 なのに WofCompressedData ストリームは存在していました。

つまり「reparse tagがWOFであること」と「WofCompressedDataストリームの割り当てが読めること」は、実際には一致しませんでした。判定はどちらか一方ではなく、両方を見る必要がありました。そして後者が読めないファイルは、そもそも補正のしようがありません。この種のファイルは補正せず元の値のまま残す、というフォールバックが必要になります。

WOF補正を試す

そこで、次のルールで集計しなおす診断用のポリシーを追加しました。

WOFと確認でき、かつ WofCompressedData の割り当て > 0 のファイル:
    そのファイルのサイズ = WofCompressedData の割り当て
それ以外:
    従来どおり

C:ドライブ全体で、この条件に当てはまったファイルは 104,884 個でした。結果です。

対象 補正前 補正後 WizTree Allocated
C: 合計 186.5 GB 170.6 GB 約 174.9 GB
Program Files 29.693 GB 24.788 GB 24.6 GB
Program Files (x86) 10.139 GB 8.251 GB 約 7.8 GB
Program Files (x86)\Microsoft Office 4.250 GB 3.235 GB 3.2 GB

Program Files は差 0.2 GB弱、Microsoft Office は 0.04 GB弱まで縮みました。この2つについては、WOFの扱いが差の主因だったと言ってよさそうです。

かなり近づいたが、完全一致はしない

ただしC:全体を見ると、話はきれいに終わりません。

補正前は WizTree より 11.6 GB 大きかったのが、補正後は 4.3 GB 小さくなりました。行き過ぎています。

Program Files (x86) も、補正後 8.251 GB に対して WizTree は約 7.8 GB で、0.45 GB ほど残っています。

これは「補正で正確になった」とは言えない状態です。分かったのは次の2つだけです。

  • WOFの扱いは、差の大きな一因だった
  • しかしそれだけでは説明しきれない差が、方向を変えて残っている

WizTree が WofCompressedData の割り当てそのものではなく、クラスタ単位の別の数え方をしている可能性もあります。そこは外から確認できないので、断定していません。

対照として役に立ったフォルダ

ここで効いたのが C:\Users です。

値
エクスプローラー サイズ 85.5 GB
エクスプローラー ディスク上のサイズ 86.6 GB
WizTree Size 85.5 GB
WizTree Allocated 85.6 GB
自作スキャナ 85.0〜85.4 GB(日を分けた複数回の測定)

いずれも 85.0〜86.6 GB の範囲、最大でも 1.6 GB の開きに収まります。WOF圧縮されたファイルはC:全体で10万個以上あるのに、C:\Users 配下には 941 個しかありませんでした。ハードリンクもほとんどありません。

普通のユーザーデータでは各ツールが揃う、という事実は重要でした。もし集計そのものに素朴なバグがあるなら、こういうフォルダでもずれるはずです。ずれていないので、問題はパス固有であって、全体の足し算の話ではないと切り分けられました。

原因を追うとき、ずれている場所ばかり見てしまいがちですが、「ずれていない場所」を1つ確保しておくと、疑う範囲をかなり狭められます。

WinSxSに残る4.6 GB — ハードリンク

WOF補正をしても最後まで大きく残ったのが C:\Windows\WinSxS です。

値
自作スキャナ(補正前) 11.467 GB
自作スキャナ(WOF補正後) 8.729 GB
WizTree Allocated 4.1 GB
残差 約 4.6 GB

WOF補正で2.7 GBほど下がりましたが、まだ倍以上あります。ここはWOFの話ではありませんでした。

MFTから見えたもの

同じ診断で、WinSxS配下のレコードを数えました。

指標 件数
link_count > 1 のレコード 70,912
$FILE_NAME 属性を複数持つレコード 72,959
$FILE_NAME の親ヒントがWinSxS外を指すレコード 19,574
うち System32 を指すもの 10,676
うち SysWOW64 を指すもの 3,546

ハードリンクは、同じファイルレコード・同じクラスタに対する2つ目以降のディレクトリエントリです。クラスタはディスク上で共有されていて、複製されていません。ところがMFTを走査すると、それぞれのエントリが独立したファイルとして見えます。重複排除をしなければ、同じ実体をパスの数だけ足してしまいます。

$FILE_NAME の親ヒントが WinSxS の外(System32 など)を指しているレコードが約2万件あるという事実は、「WinSxSの中にあるように見えるファイルの多くが、実は他の場所とクラスタを共有している」ことと整合します。

そして、WOF補正後の値でハードリンク疑いのレコードだけを合計すると 5.810 GB でした。残差の約4.6 GBは、この規模で説明できる量です。

ここは表現に気をつけたいところで、「残差の原因をハードリンクだと特定した」とまでは言えません。クラスタ単位で「このクラスタは何回数えられたか」を追ったわけではないからです。言えるのは、残差を説明するのに十分な量のハードリンク共有が実在する、というところまでです。

そして自作スキャナ側では、ハードリンクの重複排除は実装していません。WinSxSの値は「参考値」として扱い、UIにもその旨を残しています。

公式にも、別の答えが用意されている

このあたりはMicrosoft自身が明記しています。Determine the Actual Size of the WinSxS Folder にはこうあります。

Some tools, such as the File Explorer, determine the size of directories without taking into account that the contained files might be hard linked, which might lead you to think that the WinSxS folder takes up more disk space than it really does.

エクスプローラーを含む一部のツールは、ハードリンクを考慮せずにディレクトリのサイズを決める、という説明です。

さらに同ページは、DISM の /Cleanup-Image /AnalyzeComponentStore が WinSxS について複数の数字を返すことを示しています。「エクスプローラーが報告するサイズ」「実際のサイズ」「Windowsと共有されているサイズ」「バックアップと無効な機能のサイズ」などです。

つまりWinSxSには、そもそも単一の正解となる数字が存在しないわけです。「見かけのサイズ」「実サイズ」「削除して空く量」は別の問いで、別の答えになります。ファイルシステムを走査するタイプのツールは、コンポーネントストアのマニフェストを見るDISMとは違うものを測っています。

なお今回、DISM側の値との突き合わせはしていません。上記は公式ドキュメントの内容であって、私の実測ではありません。

なぜ「完全一致」をゴールにしなかったか

ここまでで、方針を変えました。

当初は「WizTreeと合う数字を出す」を目標にしていました。しかし調べるほど、それが達成可能な目標ではないことが見えてきます。

  • エクスプローラー、WizTree、MFT直読みは、それぞれ違うレイヤに問い合わせている
  • 同じ「サイズ」という言葉で、論理サイズとアロケーションという別の量が呼ばれている
  • WinSxSのように、ツールごとに違って当たり前の領域がある
  • ハードリンク共有領域では「このフォルダのサイズ」自体が一意に決まらない

そこで目標を「差が出る理由を説明できる状態」に置き換えました。日々の判断基準としては、こう考えています。

最悪なのは「違う理由が分からない」。目指すのは「違うが、理由は分かる」。

実際にやったのは、精度改善ではなく次の2つです。

1. ラベルで指標を明示する

UIの表示を ALLOCATED ESTIMATE(推定アロケーション)のように変え、「エクスプローラーの "サイズ" ではなく "ディスク上のサイズ" か WizTree の "Allocated" と比較してください」という説明を添えました。値そのものは変えていません。

2. パス単位で差の理由を出す診断コマンドを作る

指定したパスについて、補正前後の推定値、WOFファイル数と WofCompressedData の合計、ハードリンク疑いレコード数、reparse / sparse / compressed の件数、寄与の大きい子ディレクトリなどをまとめて出すコマンドを追加しました。

出力する分類は「判定」ではなく「候補」にしています。このコマンドはエクスプローラーやWizTreeの値を読まないので、一致・不一致を自分で決めることはできないからです。できるのは「このパスにはWOFの影響が大きい」「このパスはハードリンクが多い」という材料を並べるところまでです。

今回わかっていないこと

正直に分けておきます。以下は今回確認できていません。

  • ハードリンクの重複排除:未実装です。クラスタまたはコンポーネントストアのマニフェストを見る仕組みが必要で、そこまでやっていません。
  • 「WinSxSを消したら実際に何GB空くか」:これは上記が必要で、範囲外にしています。
  • WOF補正を既定にすること:実装したのは診断用のオプションで、通常の表示は補正前のままです。C:全体で行き過ぎる以上、既定にはできません。
  • CompactOSの個別検証:WOFのタグ処理にまとめて含まれており、CompactOS単体では切り分けていません。
  • 代替データストリーム(ADS):名前付きストリームは集計から除いていますが、これが差に効いている実例は測っていません。
  • OneDrive等のクラウドプレースホルダー:今回の測定対象に含めていません。未検証です。
  • sparseファイル単独の影響:属性フラグとしては取得していますが、WOFと重なるため単独要因としては分離できていません。

もう1つ、コードを読んで見つけたが量を測れていない件があります。無名 $DATA の割り当てが 0 のWOFファイルについて、現在の集計では拡張レコード側の値を採らずに 0 として扱う経路があります。理屈のうえでは、これは逆方向、つまり過小計上になり得ます。ただし、それがどのパスでどれだけ効いているかは、今の診断では測れていません。

C:\Windows 全体も、WinSxS・servicing・ハードリンク・WOF・保護されたフォルダが混ざるため、きれいな検証対象になっていません。ここは「注意領域」として切り離しています。

サイズが合わないときに見る順番

自作のファイルサイズ/ディスク使用量ツールで他ツールと数字が合わないとき、私はこの順で見るようにしました。

1. 論理サイズとアロケーションを混ぜていないか

最初にこれです。エクスプローラーの「サイズ」と「ディスク上のサイズ」、WizTreeの "Size" と "Allocated" は別物です。自分のツールがどちらを出しているのかを先に決め、比較相手も揃えます。今回、最初の混乱の大半はここでした。

2. 単位の基準を潰す

GB表記が 10^9 基準なのか 2^30 基準なのかを、バイト値から確認します。数分で潰せて、以降考えなくて済みます。

3. 揃った状態でも合わないなら、圧縮レイヤを疑う

WOF圧縮されたファイルは、MFT上の無名 $DATA と WofCompressedData ストリームで別の大きさを持ちます。フォルダ合計だけ見ていても分からないので、一番大きいファイル数個を単体で見るのが早いです。

4. ハードリンクをどう数えているかを決める

link_count > 1 のレコードがどれくらいあるかを数えます。多い領域では「このフォルダのサイズ」が一意に決まりません。重複排除するのかしないのか、しないなら参考値だと明示するのか、方針を決めて書いておきます。

5. 走査している範囲が同じか確認する

指標を揃えても合わないケースの一部は、そもそも見ている対象が違っていました。今回 C:\Program Files では、エクスプローラーのプロパティが49,652ファイル、WizTreeが86,577ファイルと数えています。アロケーション側の値もエクスプローラーだけ大幅に小さく(19.5 GB に対して WizTree 24.6 GB)、これは圧縮やハードリンクでは説明できません。権限、reparse point、パッケージ済みアプリの扱いなど候補は挙がりましたが、原因の特定には至っていません。数え方を疑う前に、数えている対象が同じかを比べるほうが先です。

6. ずれていないフォルダを1つ確保する

C:\Users のような、各ツールが揃う対照を持っておくと「全体の集計バグ」か「パス固有の現象」かを分けられます。これが一番コスパが良かったです。

7. そのうえで、完全一致が本当に必要かを考える

必要ないことのほうが多いはずです。大きいフォルダの順位付けが目的なら、順位が正しく、差の理由が説明できれば足ります。

なお、上の3〜5に関連して、今回は実測していないが一般に確認候補になるものとして、クラウド同期のプレースホルダー(ローカルに実体があるかどうか)、sparseファイル、代替データストリーム、走査時の権限不足による欠落、スキャン中のファイル変更などがあります。これらは今回の差の説明には使っていません。

まとめ

「同じフォルダなのにツールごとにサイズが違う」の内訳は、私の環境では次のようになりました。

  • 最初の11.6 GBの差のうち、かなりの部分は比較する指標を取り違えていたことによるもの
  • 指標を揃えたうえで残る差の主因は、WOF圧縮ファイルをMFTから見たときに圧縮前側の割り当てを拾っていたこと。補正すると Program Files は0.2 GB弱、Microsoft Office は0.04 GB弱まで縮んだ
  • ただし補正はC:全体では行き過ぎ、今度はWizTreeより4.3 GB小さくなった。これは「正確になった」ではない
  • WinSxSに残った約4.6 GBは、ハードリンクによる多重計上で説明できる規模だった。ただし重複排除は実装しておらず、原因を特定したとも言っていない

どのツールもバグではありませんでした。エクスプローラーもWizTreeも間違っていません。「サイズ」という1つの言葉が、論理サイズ・割り当て・圧縮後の実体・共有クラスタの数え方という複数の量を指していただけです。

自分でこの種のツールを作るなら、精度を上げる前に、自分が何を測っているのかを言えるようにするほうが先だと思います。数字が他と違うこと自体は避けられません。避けられるのは、違う理由が分からないままにしておくことです。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?