更新履歴(3件・最終更新 2026年08月02日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 知識マップの「$LogFileはボリューム破損を防ぐ」という関係を、「軽減する」に改めました。$LogFileの再生で回復できるのはメタデータ操作の中断による不整合で、不良セクタやメディア故障、$LogFile自体の損傷による破損までは回復できないためです。本文の説明は変えていません。
- 記事の冒頭に「この記事の知識マップ」を追加しました。本文で扱う概念どうしの関係を一枚の図で見渡せます。関係の全一覧(根拠のURL・確度・確認日つき)と主要概念の定義は知識マップ詳細ページにまとめ、機械可読データをJSON-LDとTurtleで公開しています。
- 連載第5回として前提になる用語(IRP、デバイススタック、キャッシュマネージャー)を、前回へのリンク付きで冒頭に定義しました。あわせて常駐と非常駐の対比図、確認コマンドごとの「ここを見る」注釈、2つのジャーナルの対比表、短縮名が環境依存である旨を追加しています。
- 初版公開
この記事を引用する(DOI(登録済みアーカイブ): 10.5281/zenodo.21739476)
以下のDOIは過去に登録されたアーカイブを指しており、現在の本文とは一致しない場合があります。現在の本文を参照するときは、このページのURLを使用してください。
小村 豪(2026)「Windows I/Oの深層(第5回) ── NTFSの内部構造:MFTから理解するファイルシステム」合同会社小村ソフト. https://comcomponent.com/blog/ntfs-internals-mft-structure/
- DOI(登録済みアーカイブ)
- 10.5281/zenodo.21739476
- DOI(前回登録した版)
- 10.5281/zenodo.21739477
ダウンロードしたファイルに付く見えない Zone.Identifier。同じ合計サイズでも、小さいファイル1万個のコピーが遅い理由。「NTFSはジャーナリングだから安心」という説明が守っている範囲。これらを、ディスク上のデータの置き方から整理します。
中心になるのは、ファイルを台帳管理するMFT(マスターファイルテーブル)です。まず「ファイルは属性の集合」と捉え、データ、名前、リンク、ジャーナル、ディスク領域の順に見ていきます。
連載の第1〜3回ではI/O要求の流れ、第4回ではキャッシュの働きを扱いました。今回は、要求が最後に届くファイルシステムの代表であるNTFSを取り上げます。「要求がどう流れるか」という動的な話から、「データがどう置かれるか」という静的な構造へ視点を移す回です。
連載「Windows I/Oの深層」の第5回です。
困っていることから読む
仕組みを順に学ぶなら2章から、調査中なら次の案内から読めます。確認コマンドと出力の見方は8章にまとめています。
| 知りたいこと・困っていること | 読むところ |
|---|---|
| MFTとは何か。ファイルを削除してもMFTが縮まないのはなぜか | 2.1節: ボリュームの台帳 |
| 小ファイルのコピーが遅い。常駐・非常駐や断片化を理解したい | 2.2節: 属性とデータの置き場所 |
| Zone.Identifierの正体や、コピーで失われる付加情報を知りたい | 3章: データストリーム |
| 別名から同じ内容が見える。名前を消しても実体が残る | 4.1節: ハードリンクと削除 |
| 短縮名が環境によって存在しない。除去の影響を調べたい | 4.2節: 8.3名 |
| フォルダーの走査がループする。開くと通信が走る | 5章: リパースポイント |
| 電源断で何が守られるか。変更履歴を調査したい | 6章: 2つのジャーナル |
| 「サイズ」と「ディスク上のサイズ」が合わない | 7章: スパースと圧縮 |
| 手元のWindowsで確認したい | 8章: コマンドと出力の見方 |
この回で使う前提用語
この回を読む前提: 第1回のIRPとデバイススタックの基本を押さえていると読みやすくなります。とはいえ単独で読んでも困らないよう、本文に出てくる前回までの用語を先に定義しておきます。
| 用語 | 一行で言うと | 詳しくは |
|---|---|---|
| IRP(I/O Request Packet) | ReadFile などのAPI呼び出しがカーネル内で変換される「I/O要求の伝票」。ドライバーはこの伝票を受け取って処理する |
第1回 |
| I/Oマネージャーとデバイススタック | IRPを作り、目的のデバイスに至るまで積み重なったドライバー(スタック)へ順に渡していくカーネルの部品と、その積み重ね | 第1回 |
| キャッシュマネージャー | ファイルの内容をメモリ上に保持し、WriteFile の内容を後からまとめてディスクへ書き出す部品。「書いた直後にディスクに届いているとは限らない」の元凶 |
第4回 |
表1: この回で前提にする前回までの用語
もう1つ、第1回で扱ったcleanup(最後のハンドルが閉じたとき)とclose(カーネル内の参照がすべて消えたとき)の2段階も、4章のファイル削除の説明で使います。
1. まず結論
ファイルの実体とデータの置き場所
- NTFSの中心はMFT(マスターファイルテーブル)です。全ファイルがMFT内のレコードとして台帳管理され、ファイルに関するすべての情報は「MFTエントリの中」か「エントリが指すMFT外の領域」にあります(2章)。1
- ファイルの実体は「属性の集合」です。小さいファイルはデータ本体ごとMFTレコード内に収まり(常駐)、大きいファイルはクラスター列への参照だけを持ちます(非常駐)。小ファイル大量処理の遅さはここから説明できます(2章)。1
- データは複数持てます(複数データストリーム)。普段のデータは「名前のないストリーム」で、
file.txt:名前で追加のストリームを持てます。Zone.Identifier(Mark of the Web)の正体です(3章)。2
名前と、開くときの処理
- 名前も属性です。同じレコードに複数の名前を付けたものがハードリンク。8.3短縮名も「もう一つの名前」として同居しています(4章)。34
- リパースポイントは「開いたら別の場所」の公式な仕掛けです。シンボリックリンク・ジャンクション・OneDrive のファイルオンデマンドは、みなこのタグ付きデータの応用です(5章)。56
障害回復と、ディスク領域の割り当て
- ジャーナルは2つあります。
$LogFileはメタデータ整合性の回復用(壊れないための先行ログ)、USNジャーナルは変更履歴の記録用(何が変わったかの台帳)。役割がまったく違います(6章)。78 - 「サイズ」と「ディスク上のサイズ」は別物です。スパースファイルと圧縮が乖離を生みます。第2回で見た「圧縮ファイルは非同期にならない」の背景もここにあります(7章)。910
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全35件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
2. すべてはMFTのレコードである
2.1. ボリュームの台帳
NTFSボリュームをフォーマットすると、MFT(master file table)と、$ で始まる一連のメタデータファイルが作られます。MFTには、ボリューム上のすべてのファイルについて少なくとも1つのエントリがあり、MFT自身のエントリも含まれます。1
flowchart TB
subgraph VOL["NTFSボリューム"]
MFT["$MFT ── マスターファイルテーブル<br/>全ファイルのレコードの台帳(自分自身も載っている)"]
LOG["$LogFile ── メタデータ操作の<br/>トランザクションログ(6章)"]
BITMAP["$Bitmap ── クラスターの使用状況"]
OTH["$Boot / $Secure / $UpCase など<br/>その他のメタデータファイル"]
DATA["ユーザーデータ領域<br/>(非常駐データの置き場所)"]
end
MFT -->|"レコードが位置を指す"| DATA
図1: NTFSボリュームの構造。「ファイルシステム自身の管理情報もファイルとして持つ」のがNTFSの設計
情報は、レコード内か、レコードが指す外部領域にある
サイズ、タイムスタンプ、アクセス許可、そしてデータの内容まで、ファイルの情報はMFTエントリの中か、エントリが位置を記述するMFT外の領域に格納されます。1
削除で空いたレコードは再利用されるが、MFT自体は縮まない
ファイルを削除すると、そのエントリは「空き」としてマークされ、再利用されます。ただし、MFT自体のサイズは縮みません。
MFTが大きくなるときにできるだけ連続した領域を使えるよう、MFTゾーンが予約されています。ボリュームが埋まっていくとMFTの断片化が始まる、という運用中の変化も公式ドキュメントで説明されています。1
2.2. ファイル=属性の集合、常駐と非常駐
ファイルレコードの中身は属性のリストです。標準情報(タイムスタンプ等)、ファイル名、セキュリティ、データを、このまとまりで管理します。
データの置き場所は、次の2つに分かれます。
| 置き方 | MFTレコードに入るもの | データ本体の場所 |
|---|---|---|
| 常駐(resident) | 小さいデータ本体 | MFTレコード内 |
| 非常駐(non-resident) | クラスター列への参照(データラン) | MFT外のユーザーデータ領域 |
図2で分岐を、図3でレコードの中身の違いを見てください。
flowchart TB
subgraph REC["MFTファイルレコード(1ファイルぶんの台帳)"]
STD["標準情報属性<br/>タイムスタンプ・属性フラグ"]
FN["ファイル名属性<br/>(複数持てる ── 4章)"]
DATA["データ属性"]
end
Q{"データは小さいか"}
RES["常駐(resident)<br/>データ本体がレコード内に収まる<br/>読み出しはMFTアクセスだけで完結"]
NONRES["非常駐(non-resident)<br/>レコードには「クラスター列への参照」だけ<br/>実データはユーザーデータ領域に"]
DATA --> Q
Q -->|"数百バイト程度まで"| RES
Q -->|"それ以上"| NONRES
図2: ファイルレコードは属性の集合。データが小さければレコード内に「常駐」する
同じレコードの中身が、常駐と非常駐でどう変わるかを並べて見ると分かりやすくなります。
flowchart LR
subgraph RES2["常駐(resident) ── 小さいファイル"]
RA["MFTファイルレコード(固定長)<br/>標準情報 / ファイル名 / セキュリティ<br/>─────────────<br/>データ属性 = 中身そのもの<br/>『設定値=1』がここに直接入る"]
RB["ディスク上に別の置き場所は無い<br/>読み出しはMFTへのアクセスだけで完結"]
RA --> RB
end
subgraph NON2["非常駐(non-resident) ── 大きいファイル"]
NA["MFTファイルレコード(固定長)<br/>標準情報 / ファイル名 / セキュリティ<br/>─────────────<br/>データ属性 = データランの表<br/>『どこから何クラスター』の並び"]
NB["ユーザーデータ領域<br/>ラン1: 連続クラスター"]
NC["ユーザーデータ領域<br/>ラン2: 別の場所の連続クラスター"]
NA -->|"位置を指す"| NB
NA -->|"位置を指す"| NC
end
図3: 常駐と非常駐の対比。非常駐では、レコードが持つのは「実データがどこにいくつあるか」の表(データラン)だけになる
データランの数が増えるほど、1つのファイルを読むために散らばった領域を渡り歩くことになります。これが次に挙げる断片化の正体です。
この構造から、現場で出会う現象がいくつも説明できます。
小ファイルのコピーでは、ファイルごとの台帳処理が積み重なる
1ファイルごとにMFTレコードの作成・名前の登録・セキュリティ設定というメタデータ操作が発生します。データ転送そのものより台帳仕事が支配的になるのです(そしてその1つ1つが、第6回で見るフィルターの検査対象にもなります)。
断片化とは、データランが複数の領域に分かれること
非常駐データは「クラスターの連続区間(ラン)の列」として記録されます。連続領域が取れないとランの数が増え、読み出しに必要なシークが増える──これが断片化です。ランの実際の並びは fsutil file layout で覗けます。
ディレクトリも、名前を引く索引を持ったファイル
ディレクトリは「ファイル名からMFTレコード番号への索引(インデックス)を持つファイル」です。台帳の上では、すべてが同じ仕組みの上に乗っています。
3. データは「ストリーム」の1つにすぎない
3.1. 1つのファイル、複数のバイト列
NTFSでは、1つのファイルが複数のデータストリームを持てます。普段 ReadFile/WriteFile で読み書きしているのは名前のない既定ストリームで、ファイル名:ストリーム名 という構文で代替データストリーム(ADS)を作れます。2
flowchart LR
subgraph F["report.docx というファイル(1つのMFTレコード)"]
D0["既定ストリーム(無名)<br/>= 普段見ている中身"]
D1[":Zone.Identifier<br/>出所情報(Mark of the Web)"]
D2[":任意の名前<br/>アプリ独自の付加情報"]
end
図4: 複数データストリーム。エクスプローラーのサイズ表示に出るのは既定ストリームだけ
Zone.Identifierは、出所情報を持つストリーム
身近な例が Zone.Identifier です。ブラウザーでダウンロードしたファイルの出所(インターネット由来である等)が記録され、SmartScreenの「WindowsによってPCが保護されました」やOfficeの保護ビューの判定材料になります。
警告の仕組みは「Windowsで「Windows によって PC が保護されました」が出る理由」で扱いました。その出所情報を保存する側の仕組みが、NTFSの追加ストリームです。
3.2. 開発者が踏む落とし穴
通常の一覧やサイズ表示だけでは見えない
エクスプローラーのサイズにも dir の一覧にも出ません。dir /r かSysinternalsの streams で確認できます。11
コピー先や経路によっては失われる
ADSはNTFSの機能なので、FATのUSBメモリやクラウドストレージ経由のコピーでは失われがちです。「ダウンロード警告がコピーしたら消えた」はこれです。
アプリから使えても、業務データ本体の保存先にはしない
CreateFile("data.txt:meta", ...) のようにパスにコロンを含めるだけで読み書きできます。2 便利ですが、前項の「運べない」性質ごと引き受けることになるため、業務データの本体を入れる場所ではありません。
4. 名前もまた属性である ── ハードリンクと8.3名
4.1. ハードリンク ── 同じレコードへの複数の名前
図2で「ファイル名属性は複数持てる」と書きました。同一ボリューム内で、複数のパスが単一のファイルを参照する──これがハードリンクです(CreateHardLink / mklink /H)。3
flowchart TB
subgraph DIR1["C:\app\ のインデックス"]
E1["config.json → レコード#1234"]
end
subgraph DIR2["C:\backup\ のインデックス"]
E2["config-link.json → レコード#1234"]
end
REC["MFTレコード#1234<br/>データ本体(またはランへの参照)<br/>リンク数: 2"]
E1 --> REC
E2 --> REC
図5: ハードリンク。ディレクトリの索引が同じMFTレコードを指しているだけで、どちらも「本物」
どの名前も、同じファイルを指している
どの名前から変更しても同じファイルなので、内容は即座に一致します。3 一方を本物、他方をコピーと考えるのではなく、同じ実体に同格の名前が付いていると捉えます。
名前を外すことと、実体が消えることを分ける
ハードリンクがあると、DeleteFile は「名前を1つ外す」という意味になります。実体が消えるのは、最後の名前が外れ、開いているハンドルが閉じ、メモリマップのセクションなどカーネル内の参照もすべて消えたときです。
第1回のcleanup(最後のハンドルが閉じたとき)とclose(最後の参照が消えたとき)の2段階は、この削除の寿命にも関係します。
内容の共有と、属性表示の更新は同じではない
あるリンクから属性を変更しても、別のリンクの見かけ上の属性表示が古いままになることがあります。これは公式ドキュメントにも注記された表示上の癖です。3
4.2. 8.3名 ── もう一つの隠れた名前
短縮名は互換性のための別名
NTFSは歴史的互換性のため、長いファイル名に REPORT~1.DOC のような8.3形式の短縮名を自動生成できます。これも、同じレコードに同居する「もう一つの名前」です。
大量のファイルがあるフォルダーでは、短縮名の生成や衝突回避にもコストがかかります。fsutil 8dot3name で生成を無効化したり、既存の短縮名を除去したりできます。4
ただし、短縮名をレジストリパスに記録する古いアプリがあると、除去によって壊れます。strip前に影響を検査する機能があるのは、そのためです。4
生成設定を確認してから、短縮名に依存する
短縮名が存在するかどうかは環境依存です。既定の動作を決めるレジストリ値 NtfsDisable8dot3NameCreation には、次の4通りがあります。4
| 値 | 短縮名の生成方法 |
|---|---|
0 |
全ボリュームで生成する |
1 |
全ボリュームで生成しない |
2 |
ボリューム単位で設定する |
3 |
システムボリューム以外では生成しない |
2 ならボリュームごとに切り替えられます。「Windowsなら PROGRA~1 のような短縮名が必ずある」とは限りません。
短縮名に依存するコードや手順を書く前に、fsutil 8dot3name query C: で状態を確認してください。ボリュームを省略すると、全ボリューム共通の既定設定を確認できます。
パスと名前をめぐる落とし穴(MAX_PATH、予約名、末尾ドット)は「MAX_PATHとWindowsのパス・ファイル名の落とし穴」で詳しく扱っています。第1回の名前解決(オブジェクトマネージャー)とこの章(ファイルシステム内の名前)を合わせると、Windowsの「名前」の全体像になります。
5. リパースポイント ── 「開いたら別の場所」の仕掛け
タグを見て、開くときの処理を切り替える
ファイルやディレクトリにはリパースポイントを付けられます。実体は、タグとユーザー定義データを持つ属性です。
リパースポイント付きのファイルを開くと、タグに応じて処理が切り替わります。タグを理解するフィルタードライバーが引き受ける場合と、名前の付け替え系のタグによりリンク先のパスで解決をやり直す場合があります。5
sequenceDiagram
participant App as アプリ
participant IOM as I/Oマネージャー
participant FS as NTFS
App->>IOM: CreateFile("C:\data\link.txt")
IOM->>FS: IRP_MJ_CREATE(第1回の世界)
Note over FS: 対象にリパースポイント発見<br/>タグとデータを返す
alt シンボリックリンク/ジャンクション(名前の付け替え)
FS-->>IOM: 「本当の場所はこちら」
IOM->>FS: リンク先のパスで解決をやり直す
else フィルター管理のタグ(クラウドファイル等)
Note over FS: タグを理解するフィルターが<br/>処理を引き受ける(第6回)
end
図6: リパースポイントの解決。「開く」という操作に割り込む公式のフックになっている
シンボリックリンク、ジャンクション、ファイルオンデマンド
この1つの仕掛けの上に、見慣れた機能が並んでいます。
- シンボリックリンク(
mklink)──リンク先パスを保持する標識。別ボリュームやUNCパスも指せます。6 - ジャンクション/マウントポイント──ディレクトリを別のローカルボリュームの位置へつなぐ古参の仕組み。3
- OneDriveのファイルオンデマンド──実データが手元にないファイルをリパースポイントで表現し、開かれた瞬間にフィルターがダウンロードして中身を差し出します。「エクスプローラーには見えるのに、開くと通信が走る」の正体です(フィルターの仕組み自体は第6回で)。
ツリーを辿るコードは、リパースポイントを確認する
実務では、パスの先が本当にローカルのその場所とは限らないことが重要です。存在を考慮しないコードでは、次のような問題を踏みます。
- 再帰的な走査がジャンクションでループする。
- サイズ集計が二重になる。
- バックアップがクラウドファイルの実体化を大量に引き起こす。
対策の入口は、FindFirstFile 系で FILE_ATTRIBUTE_REPARSE_POINT を確認することです。5
6. 2つのジャーナル ── $LogFileとUSN
「NTFSはジャーナリングファイルシステム」とよく言われますが、NTFSには役割の違う2つのジャーナルがあります。混同すると保証を読み違えます。
flowchart TB
subgraph J1["$LogFile ── 先行ログ(壊れないため)"]
A1["メタデータ操作(レコード更新・名前変更等)を<br/>実行前にログへ記録"]
A2["システム障害後の次回起動時に<br/>ログを再生して構造の整合性を回復"]
A1 --> A2
end
subgraph J2["USNジャーナル ── 変更履歴(何が変わったかを知るため)"]
B1["ファイル/ディレクトリへの変更のたびに<br/>変更内容と名前を記録"]
B2["バックアップ・検索インデックス・同期ツールが<br/>「前回から何が変わったか」を全走査なしで把握"]
B1 --> B2
end
図7: 2つのジャーナル。$LogFileは「壊さない」ため、USNは「変更を知る」ため
違いを表にすると次のようになります。
| 観点 | $LogFile(トランザクションログ) |
USNジャーナル(変更ジャーナル) |
|---|---|---|
| 目的 | 障害後にファイルシステムの構造を整合状態へ戻す7 | 「前回から何が変わったか」を後から知る8 |
| 記録する内容 | メタデータ操作(レコード更新・名前変更など)の先行ログ。ファイルの中身は対象外 | 変更のたびに、変更の内容と対象ファイル/ディレクトリの名前8 |
| 使うのは誰か | NTFS自身。次回マウント時の自動回復に使う | バックアップ・検索インデックス・同期ツールなどのアプリ |
| どこまで遡れるか | 回復に必要な範囲だけ。固定サイズを使い回すため、過去の履歴を辿る用途には使えない | 目標最大サイズ(MaximumSize)を超えるとチェックポイント時に古いレコードから切り捨てられる。遡れる範囲はサイズ設定とボリュームの更新量しだい12 |
| 参照方法 | 中身を読む公式な手段はない(サイズは chkdsk /L で確認できる) |
fsutil usn queryjournal で状態、fsutil usn readjournal で中身。プログラムからは FSCTL_QUERY_USN_JOURNAL / FSCTL_READ_USN_JOURNAL12 |
| 止められるか | 止められない(NTFSの一部) | 管理者が削除・無効化できる。ただし利用中のサービスに全走査を強いるので影響は大きい12 |
表2: 2つのジャーナルの対比
$LogFileは構造の整合性を回復する
$LogFileは、メタデータ操作の先行ログです。システム障害の後、NTFSは次回起動時にログとチェックポイント情報からファイルシステムの整合性を自動回復します。7
ここで守るのは構造の整合性であり、ファイルに書いたデータ内容そのものではありません。第4回で見たとおり、キャッシュ上のダーティなデータは電源断で失われることがあります。構造を回復できることと、最後の書き込み内容が残ることを分けて考えてください。
USNは、前回から何が変わったかを調べる台帳
USNジャーナルは、ボリューム内のファイルやディレクトリに変更が起きるたびに、変更の内容と対象の名前を記録します。8
バックアップやインデクサーが「前回から変わったものだけ」を全走査せずに拾うための仕組みで、障害後のインデックス再構築の回避にも使われます。8
調査では、FileSystemWatcher の取りこぼしを補う突き合わせ台帳として fsutil usn readjournal を使えます。監視の取りこぼし自体は「FileSystemWatcher実務ガイド」を参照してください。
7. スパースと圧縮 ── 「サイズ」が二つある話
NTFSでは、ファイルの論理的な長さと実際に割り当てられた領域が別々に管理されます。プロパティ画面の「サイズ」と「ディスク上のサイズ」です。乖離を生む代表が2つあります。
スパースは、ゼロの範囲を「穴」として持つ
スパースファイルは、ゼロが続く範囲に実領域を割り当てず「穴」として管理します。9 論理サイズ42GBの仮想ディスクファイルが、ディスク上では500MBしか使っていない──が普通に起こります。穴を読めばゼロが返り、書けばそのぶんだけ割り当てられます。
flowchart LR
subgraph L["論理的なファイル(サイズ: 1GB)"]
R1["データ 10MB"]
H1["穴(ゼロ) 500MB"]
R2["データ 5MB"]
H2["穴(ゼロ) 残り"]
end
subgraph P["ディスク上の割り当て(15MB+管理情報)"]
A1["ラン: R1の実体"]
A2["ラン: R2の実体"]
end
R1 --> A1
R2 --> A2
図8: スパースファイル。「穴」には割り当てが無く、論理サイズとディスク上のサイズが乖離する
圧縮は、使用量だけでなくI/Oの動作にも関わる
NTFS圧縮は、データを圧縮ユニット単位で圧縮して格納します。10 透過的で便利ですが、コストも透過的ではありません──読み書きのたびに展開・再圧縮が走り、断片化も進みやすくなります。そして第2回5章で見たとおり、圧縮ファイルへのアクセスは非同期になりません(ファイルシステムが同期に変換します)。「非同期I/Oにしたのに速くならないファイルがある」ときに疑う場所の一つです。
サイズが合わないときに見る4つの要因
割り当てベースの実サイズは GetCompressedFileSize で取得できます。「ファイルサイズの合計」と「ディスク使用量」が合わないときは、次の4つを順に確認します。
| 要因 | 確認すること |
|---|---|
| スパース | ゼロの範囲が、実領域のない「穴」になっていないか |
| 圧縮 | 圧縮後のデータとして格納されていないか |
| ADS | 既定ストリーム以外のデータがないか(3章) |
| クラスター切り上げ | クラスター単位の割り当てによる差ではないか |
論理的な長さと割り当てられた領域を分けると、表示の違いを追いやすくなります。
8. 自分の目で確かめる
今回も、手元のWindowsですべて観察できます(一部は管理者権限が必要です)。実行結果が正しいかを自分で判定できるよう、コマンドごとにどこを見れば何が分かるかを添えます。
8.1. ADSの有無を、ストリーム名付きの行で見る
:: 代替データストリームを見る
dir /r C:\Users\%USERNAME%\Downloads
ここを見る: 通常のファイル行の下に、字下げされた ファイル名:Zone.Identifier:$DATA の形の行が長さ付きで並びます。この行があれば、そのファイルにMark of the Webが付いている状態です(3章)。ブラウザーでダウンロードしたファイルには付き、自分で作ったファイルには付きません。両方の場所で実行して見比べると、ADSの有無がはっきりします。
8.2. 常駐・非常駐と、データの配置を見る
:: ファイルのMFT上の配置(ラン)や属性を見る
fsutil file layout C:\path\to\file.dat
:: エクステントだけを見る(公式ドキュメントに記載のあるサブコマンド)
fsutil file queryextents C:\path\to\file.dat
ここを見る: layout はストリームごとに、サイズ・割り当てサイズと、非常駐であればエクステント(VCN・LCN・クラスター数の組)の一覧を並べます。エクステントの行が出ないごく小さいファイルは常駐(2.2節)、複数行に分かれていれば断片化しています。数バイトのテキストファイルと数百MBのファイルで実行して見比べるのが、常駐/非常駐を最短で体感する方法です。
8.3. 短縮名の生成設定と、実際の名前を見比べる
:: 8.3短縮名の生成設定と、既存の短縮名
fsutil 8dot3name query C:
dir /x
ここを見る: query はそのボリュームで短縮名の生成が有効か無効かを返します(ボリュームを省略すると全ボリューム共通の既定設定)。4 dir /x は長い名前の隣に短縮名の列を表示するので、列が空なら短縮名が作られていないということです。4.2節の「短縮名があるとは限らない」を自分の環境で確かめられます。
8.4. USNジャーナルの状態と、記録の進み方を見る
:: USNジャーナルの状態
fsutil usn queryjournal C:
ここを見る: ジャーナルID、有効なUSNの範囲(First USN / Next USN)、目標最大サイズ(MaximumSize)と割り当て単位(AllocationDelta)が表示されます。12 何かファイルを作ってからもう一度実行すると Next USN が進んでいるはずで、これが「変更が記録されている」ことの確認になります。MaximumSizeは6章で触れた「どこまで遡れるか」の目安です。ジャーナルが無効なボリュームではエラーになります。
8.5. リパースポイントの属性とタグを見る
:: リパースポイントの確認(リンク先とタグ)
dir /aL C:\Users\%USERNAME%
fsutil reparsepoint query "C:\Users\%USERNAME%\OneDrive"
ここを見る: dir /aL に出てきたものがリパースポイント(FILE_ATTRIBUTE_REPARSE_POINT が立っているもの)です。シンボリックリンクやジャンクションは <SYMLINKD> <JUNCTION> のような種別付きで表示されます。fsutil reparsepoint query はリパースタグの値と、名前の付け替え系ならリンク先のパスを表示します。リパースポイントでない対象を指定するとエラーになるので、エラーになること自体が「ここは普通のフォルダー」の確認になります。
8.6. 実際のファイル操作はProcmonで追う
Procmonでファイル操作を追うと、今回の登場人物が実名で流れていきます($LogFile への書き込み、ストリーム名付きのパス、リパース処理)。使い方は「Process Monitor(ProcMon)実践ガイド」を参照してください。
9. まとめ
- NTFSの中心はMFT。全ファイルが台帳のレコードで、情報は「レコード内」か「レコードが指す外部領域」にあります。小さいデータは常駐、大きいデータはラン参照で、小ファイル大量処理の遅さや断片化はこの構造の帰結です。1
- データストリームは複数持てます。Zone.Identifier(Mark of the Web)はただのADSで、
dir /rで見え、NTFSの外へは運ばれません。211 - 名前は属性で、複数持てます。ハードリンクは同じレコードへの同格の名前、8.3名は互換用のもう一つの名前。「削除=名前を外す」であり、実体の消滅は最後の名前・ハンドル・カーネル内参照(マップ済みセクション等)がすべて無くなったときです。34
- リパースポイントは「開く」への公式フックで、シンボリックリンクもジャンクションもファイルオンデマンドもこの応用です。ツリーを辿るコードは
FILE_ATTRIBUTE_REPARSE_POINTを意識する必要があります。56 - ジャーナルは2つ。
$LogFileは構造の整合性回復(壊れない)、USNは変更履歴(何が変わったか)。「ジャーナリングだからデータも安全」ではありません──データの耐久性は第4回の道具で作ります。78 - 論理サイズと割り当ては別物。スパース・圧縮・ADS・クラスター切り上げが「サイズが合わない」の四大要因です。圧縮ファイルが非同期I/Oにならない件も含め、性能調査の引き出しになります。910
続きは最終回、第6回「フィルタードライバーとミニフィルター ── ProcmonとウイルススキャンがI/Oに割り込める理由」です。第1回から折に触れて登場してきた「間に挟まる者たち」──ウイルス対策、Procmon、OneDrive、暗号化──がどうI/Oに割り込んでいるのか。連載の総仕上げとして、デバイススタックの隙間に立つ住人たちの正体を明かします。
関連記事
- Windows I/Oの深層(第1回) ── すべての読み書きはIRPになる:I/Oシステムの全体像
- Windows I/Oの深層(第2回) ── 同期I/Oと非同期I/O:OVERLAPPEDの本当の意味
- Windows I/Oの深層(第4回) ── キャッシュマネージャー:あなたのWriteFileはいつディスクに届くのか
- Windowsで「Windows によって PC が保護されました」が出る理由
- MAX_PATHとWindowsのパス・ファイル名の落とし穴 ── 260文字制限、予約名、末尾ドット、大文字小文字
- FileSystemWatcher実務ガイド - 取りこぼしと重複対策
- ネットワークドライブとUNCパスの落とし穴 ── 業務アプリでファイルサーバー(共有フォルダ)を扱う実務
- Process Monitor(ProcMon)実践ガイド ── 「設定が読まれない」「ACCESS DENIED」を10分で特定する
関連する相談領域
合同会社小村ソフトでは、ファイルサイズやコピー性能の不可解な挙動、リンク・ストリーム絡みの不具合など、NTFSの仕組みに根ざしたWindows業務アプリの設計・調査を扱っています。
参考リンク
-
Microsoft Learn, Master File Table. NTFSボリューム上のすべてのファイルについてMFTに少なくとも1つのエントリがあり、MFT自身のエントリも含まれること、ファイルのサイズ・タイムスタンプ・アクセス許可・データ内容を含むすべての情報がMFTエントリ内かMFTエントリが位置を記述するMFT外の領域に格納されること、ファイル削除時にエントリが空きとしてマークされ再利用されるがMFTのサイズは縮まないこと、MFTを連続に保つためのMFTゾーンが予約されること、割り当ての進行によりMFTの断片化が起こることについて。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, File Streams. NTFSのファイルデータが1つ以上のストリームとして格納されること、既定の(名前のない)データストリームと名前付きの代替データストリームがあること、「ファイル名:ストリーム名」の形式でストリームを指定してCreateFileで開けることについて。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Hard links and junctions. ハードリンクが同一ボリューム内で複数のパスが単一のファイルを参照するファイルシステム上の表現であること、CreateHardLinkで作成すること、どのリンク経由の変更も他のリンクから即座に見えること、属性の変更がすべてのハードリンクに伝播する一方で、ディレクトリエントリ上の表示は変更を行ったリンクでのみ更新されるという表示上の癖があること、およびジャンクション(ディレクトリを別のローカルボリュームへつなぐ仕組み)について。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, fsutil 8dot3name. NTFSが長いファイル名に対して8.3形式の短縮名を生成できること、fsutil 8dot3nameで短縮名生成の有効/無効の照会と設定、既存の短縮名の除去(strip)、除去した場合に影響を受けるレジストリ参照の走査ができることについて。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Reparse points. リパースポイントがユーザー定義データとそのデータ形式を一意に識別するリパースタグの集まりであること、リパースポイント付きファイルのオープン時にファイルシステムがタグに対応する処理(タグを解釈するファイルシステムフィルターによる処理)を試みること、NTFSのファイルシステムリンクやリモートストレージ(階層記憶)の実装に使われていること、FILE_ATTRIBUTE_REPARSE_POINT属性で存在を確認できることについて。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Symbolic links. シンボリックリンクが別のファイルやディレクトリを指すファイルシステムオブジェクトであり、リンク先への透過的なリダイレクトとして機能すること、絶対・相対のリンクがあり、ボリュームをまたぐ参照やリモートパスへの参照が可能なことについて。 ↩ ↩2 ↩3
-
Microsoft Learn, NTFS overview. NTFSがログファイルとチェックポイント情報を使い、システム障害が起きた場合に次回起動時にトランザクションログを再生してファイルシステムの整合性を自動的に回復すること、不良セクターの動的な再マップや、バックグラウンドで軽微な破損を修復するself-healing NTFSを備えることについて。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Change Journals. ボリューム内のファイルやディレクトリに変更が加えられるたびに、そのボリュームのUSN変更ジャーナルに変更の内容と対象ファイル/ディレクトリの名前が記録されること、ボリュームごとにジャーナルが維持されること、障害後のファイルシステムインデックスの回復に使え、ボリューム全体の再インデックスを回避できることについて。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Sparse Files. スパースファイルにおいて、ゼロで構成される大きな範囲に物理的なディスク領域を割り当てず、データを含む部分にのみ領域を割り当てること、割り当てのない範囲を読むとゼロが返ることについて。 ↩ ↩2 ↩3
-
Microsoft Learn, File Compression and Decompression. NTFSのファイル圧縮が透過的に行われ、圧縮単位ごとにデータが圧縮・格納されること、GetCompressedFileSizeで圧縮(実割り当て)後のサイズを取得できること、圧縮ファイルの読み書きに展開・再圧縮のコストが伴うことについて。 ↩ ↩2 ↩3
-
Microsoft Learn, Streams - Sysinternals. Sysinternalsのstreamsユーティリティが、NTFSファイルの代替データストリームを列挙・削除できることについて。 ↩ ↩2
-
Microsoft Learn, Creating, Modifying, and Deleting a Change Journalおよびfsutil usn. 変更ジャーナルのMaximumSizeが目標値であり、サイズがMaximumSizeとAllocationDeltaの合計を超えるとNTFSのチェックポイント時に切り詰められること、AllocationDeltaがジャーナルの末尾への追加と先頭からの削除の単位であること、fsutil usn queryjournalでジャーナルの状態と容量を、readjournalで記録内容を参照できること、プログラムからはFSCTL_CREATE_USN_JOURNAL / FSCTL_QUERY_USN_JOURNAL / FSCTL_READ_USN_JOURNAL / FSCTL_DELETE_USN_JOURNALを使うこと、アクティブなジャーナルの削除・無効化がMFT全体の走査を伴い、ジャーナルを利用しているサービスにボリュームの再スキャンを強いることについて。 ↩ ↩2 ↩3 ↩4
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Windows I/Oの深層(第4回) ── キャッシュマネージャー:あなたのWriteFileはいつディスクに届くのか
Windowsのキャッシュマネージャーを図解で解説する連載の第4回です。ファイルマッピングとして実装されたキャッシュ、先読みと遅延書き込み、FlushFileBuffersやFILE_FLAG_NO_BUFFERINGの使い分け、電源断でデータが消える条件までを整理します。
Windows I/Oの深層(第1回) ── すべての読み書きはIRPになる:I/Oシステムの全体像
WindowsのI/Oシステムを底から解説する連載の第1回です。オブジェクトマネージャーの名前空間、ドライバー・デバイス・ファイルの3つのオブジェクト、IRPのライフサイクル、CloseHandleの裏側までを図解で整理します。
Windows I/Oの深層(第6回・最終回) ── ミニフィルターの仕組みとProcmonによる遅延調査
ミニフィルターがファイルI/Oを監視・制御する仕組みを解説します。FltMgr、アルティチュード、pre/postコールバック、fltmcの読み方を整理し、Procmonで遅い操作を特定する手順と、除外設定・Dev Driveの注意点をまとめます。
Windows I/Oの深層(第2回) ── 同期I/Oと非同期I/O:OVERLAPPEDの本当の意味
Windowsの同期I/Oと非同期I/O(オーバーラップI/O)を図解で解説する連載の第2回です。FILE_FLAG_OVERLAPPEDの意味、完了通知の4方式、非同期のはずが同期完了する条件、キャンセルの作法、.NETとの対応までを整理します。
Windowsのショートカットは、移動したファイルをどうやって見つけるのか? ── ファイルの「場所」と「同じファイルであること」は別の話
元のファイルを移動したのに、ショートカットから開けるのはなぜでしょうか。一つの企画書を移動・改名・コピーして考えながら、Windowsがパス以外の識別情報や特徴を手がかりにリンク先を探す仕組みと、その限界を解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
不具合調査 / 長期稼働テーマ
再現しにくい不具合、通信停止、長期稼働障害、失敗パス検証を整理するトピックです。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
不具合調査・原因解析
再現しにくい障害、長期稼働後の不具合、通信停止などの調査を支援します。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- MFT(マスターファイルテーブル)とは何ですか?
- NTFSボリュームの心臓部にあたるデータ構造で、ボリューム上のすべてのファイルについて少なくとも1つのエントリ(ファイルレコード)を持つ台帳です。MFT自身のエントリも含まれます。ファイルのサイズ、タイムスタンプ、アクセス許可、そしてデータ本体に至るまで、ファイルに関するすべての情報は、MFTエントリの中か、MFTエントリが指し示すMFT外の領域に格納されます。小さなファイルならデータ本体ごとMFTエントリ内に収まり(常駐)、大きなファイルはデータの置き場所(クラスターの並び)への参照だけがエントリに記録されます(非常駐)。ファイルを削除するとエントリは空きとしてマークされ再利用されますが、MFT自体のサイズは縮みません。
- ファイルに付いている「Zone.Identifier」という見えないデータは何ですか?
- NTFSの複数データストリーム(代替データストリーム)の一つです。NTFSでは1つのファイルが複数のバイト列(ストリーム)を持て、普段読み書きしているのは名前のない既定ストリームです。「file.txt:Zone.Identifier」のようにコロン区切りで指定する追加ストリームに、Windowsはファイルの出所(インターネットからダウンロードした等)を記録します。これがいわゆる「Mark of the Web」で、SmartScreenの警告やOfficeの保護ビューの判定材料になります。代替ストリームはエクスプローラーのサイズ表示に現れず、dir /rコマンドやSysinternalsのstreamsツールで確認できます。NTFS以外のファイルシステム(FATなど)へコピーすると保持されない点にも注意が必要です。
- ハードリンクとシンボリックリンクは何が違いますか?
- ハードリンクは「同じファイル実体(同じMFTレコード)を指す、同格の名前がもう一つ増える」ことです。同一ボリューム内でしか作れず、どの名前からアクセスしても同じファイルで、1つの名前を削除しても他の名前が残っていればファイルは消えません。シンボリックリンクは「別のパスへ案内する標識」で、リパースポイントとして実装されます。リンク先のパス文字列を保持しているだけなので、別ボリュームやリモートも指せますが、リンク先が消えれば行き止まりになります。実務では、ハードリンクは実体の共有(削除の意味が変わる)、シンボリックリンクはパスの付け替え(移設やリダイレクト)と使い分けるのが基本です。
- NTFSはジャーナリングファイルシステムだから、電源断でもデータは消えないのですか?
- 守られる範囲を正確に理解する必要があります。NTFSのトランザクションログ($LogFile)が守るのは、ファイルシステムの構造(メタデータ)の整合性です。システム障害が起きても、次回起動時にログを使って整合性を自動回復し、「ボリュームが壊れて読めない」事態を防ぎます。しかし、書き込み途中だったファイルのデータ内容そのものが復元されるわけではありません。連載第4回で見たとおり、キャッシュ上のダーティなデータは電源断で失われます。つまり「ボリュームは壊れないが、最後の書き込み内容は消えうる」が正しい理解で、データ自体の耐久性が必要ならFlushFileBuffersやWRITE_THROUGH、あるいはアプリ側の書き込み設計(一時ファイル+リネーム等)で作り込む必要があります。
- ファイルの「サイズ」と「ディスク上のサイズ」が違うのはなぜですか?
- NTFSでは、ファイルの論理的な長さと、実際に割り当てられたディスク領域が別々に管理されているからです。通常のファイルでもクラスター単位(既定4KB)で切り上げて割り当てるため差が出ますが、大きく乖離するのはスパースファイルと圧縮ファイルです。スパースファイルでは、ゼロが続く範囲に実領域を割り当てず「穴」として管理するため、論理サイズ数GBでもディスク上は数MBということが起こります。圧縮ファイルでは圧縮後のサイズだけが割り当てられます。逆に「ディスク上のサイズ」の方が大きく見える場合は、クラスター切り上げや代替データストリームが原因のことがあります。