WSLがサポートするファイルシステム
WSLがサポートするファイルシステムに関する紹介です。UbuntuのファイルシステムとWindowsファイルシステムは、大きく異なるファイルシステムです。
ここで言うファイルシステムには、「ext4」や「NTFS」でフォーマットされたボリュームだけでなく、「/proc」のような仮想ファイルシステムも含みます。
UbuntuのユーザースペースをWindows上で動作させるために、この違いをどうにかする必要があります。
さもないと、Ubuntuのソフトウェア群は期待通りに動作しません。
Linuxのファイルシステム
まずはLinuxのファイルシステムの紹介です。Linuxでのファイル操作
Linuxでのファイル操作は抽象化されており、VFS(Virtual File System)を通じてファイル操作を行います。VFSは、「open」「read」「chmod」「stat」などシステムコールを通じてユーザーモードのプログラムがファイルシステムにアクセスするインターフェースを提供し、またそのためにファイルシステムが実装しなければならないインターフェースも提供します。
VFSにより異なるファイルシステムを共存させることができ、ユーザーモードのプログラムは異なるファイルシステムに対し同じ操作方法でファイル操作を行うことができます。
ファイルシステムはその種類や目的に応じて異なるディレクトリーにマウントされます。
例えばLinuxがインストールされたファイルシステムは、「/」にマウントされます。
他にも「/dev」「/proc」「/sys」など様々なファイルシステムがマウントされます。
VFSはファイルシステムを操作するために、inodeやディレクトリーエントリー、そしてファイルのような数々のデータ構造を利用し、また、ファイルシステムが実装しなければならないコールバックを利用して様々なシステムコールを実装しています。
1.inode
inodeはVFSにおいて軸となるデータ構造です。inodeは一般的なファイルやディレクトリ、シンボリックリンクなどファイルシステム上のオブジェクトを表現するデータです。
例えばinodeには、以下の情報が含まれています。
- ファイルタイプ
- ファイルサイズ
- パーミッション
- 最終更新日時
ext4のような多くのLinuxで共通的に使用されるファイルシステムにとってディスク上のデータ構造は、ファイルのメタデータを表現するために使われ、Linuxカーネルによって使用されるinode構造と直接適合するようになっています。
inodeがファイルを表現しますが、inodeはファイル名を表現しません。
1つのファイルはハードリンクによって複数のファイル名を持つことができます。
しかしそのファイルに対応するinodeは1つです。
ファイルシステムはlookupコールバックをVFSに提供しており、このコールバックは親のinodeとこの名称に基づいて特定のファイルに対応したinodeを取得するために使用されます。
ファイルシステムは「chmod」「stat」「open」など様々なinodeの操作を実装しなければなりません。
2.ディレクトリーエントリー
VFSはファイルシステムの名前空間を表現するため、ディレクトリーエントリーのキャッシュを利用します。ディレクトリーエントリーはメモリー上にのみ存在し、ディレクトリーエントリーにはinodeへのポインターが含まれています。
例えば「/home/user/foo」というパスには、home、user、fooの3つのディレクトリーエントリーが存在します。
また各ディレクトリーエントリーには、inodeへのポインターが含まれています。
ディレクトリーエントリーはinodeの検索を高速化するためキャッシュされていますが、もしディレクトリーエントリーがまだキャッシュ内に存在していなかった場合、ファイルシステムからinodeを取得するためinodeの検索操作が行われます。
inodeが見つかれば新しいディレクトリーエントリーが作成され、その見つかったinodeのポインターと共にキャッシュされます。
3.ファイルオブジェクト
inodeを開いた時にファイルオブジェクトが生成されます。ファイルオブジェクトは、ファイルを読み書きなどどのようなモードで開いたのか、ファイルのオフセットが現在どこにあるかなどの情報を追跡するために使用されます。
ファイルシステムは、「read」「write」「sync」などの様々なファイル操作を提供しなければなりません。
4.ファイルディスクリプター(ファイル記述子)
アプリケーションは、ファイルディスクリプターを通じてファイルオブジェクトにアクセスします。ファイルディスクリプターは、プロセスごとに一意になる数値で表現されます。
プロセスで開いたすべてのファイルは、ファイルディスクリプターを経由してアクセスします。
またファイルディスクリプターは、ファイルのようなインターフェースが提供するオブジェクトを指すこともできます。
例えばttysやソケット、パイプをファイルディスクリプターで参照することができます。
さらに複数のファイルディスクリプターは、同じファイルオブジェクトを指すこともできます。
5.特殊なファイル
Linuxでは、一般的なファイルやディレクトリーだけでなく様々なファイルタイプをサポートしています。例えばデバイスファイルやFIFO、ソケット、シンボリックリンクです。
これらのファイルのいくつかは、パスの解釈方法に違いが出ます。
シンボリックリンクは異なるファイルやディレクトリーを参照する特殊なファイルです。
シンボリックリンクはVFSによって透過的に扱われます。
「/foo/bar/baz」パスにアクセスした際、もし「bar」が「/zed」を指すシンボリックリンクだった場合、実際にアクセスするパスは「/zed/baz」になります。
似たような事例として、ディレクトリーはファイルシステムをマウントするために使用されます。
このディレクトリーを含むパスにアクセスした場合は、すべてのinodeの操作はマウントポイントにマウントされているファイルシステムに対して行われます。
6.特殊な擬似ファイルシステム
Linuxでは、ディスクからファイルを読み込まない様々なファイルシステムを使用します。TmpFsは、メモリー上に構築される一時的なファイルシステムです。
ProcFsやSysFsは、プロセスやデバイス、ドライバーといったLinuxカーネルが提供する情報にアクセスするために提供されるファイルシステムです。
これらのファイルシステムは、ディスク(ストレージ)やネットワーク上のリソース、あるいは何かしらのデバイス上に構築されているファイルシステムではありません。
これらのファイルシステムはLinuxカーネルによって提供される仮想的なファイルシステムです。
Windowsのファイルシステム
次にWindowsのファイルシステムの紹介です。オブジェクト
Windowsではすべてのシステムリソースをオブジェクトで一般化します。このオブジェクトはファイルだけでなく、スレッドや共有メモリー、タイマーなど、OSで使用するリソースが対象になります。
ファイルを開く要求は、最終的にはWindows NTカーネルのObject Managerを通じ、要求がI/O Managerへ転送され、ファイルシステムのドライバーに到達します。
Windowsのファイルシステムドライバーが実装するインターフェースは、Linuxより一般的であり、実装しなければならない機能はLinuxより少なくなっています。
例えば、共通的なinode構造やそれに似た構造は存在せず、ディレクトリーエントリーも存在しません。
代わりに「ntfs.sys」のようなファイルシステムドライバーは、パスの解決やファイルオブジェクトを開く機能に対応しています。
ドライブレターはただのリンク
Windowsのファイルシステムは通常、C:やD:などのドライブレターにマウントされます。加えて他のファイルシステムを自身のファイルシステム上のディレクトリーにマウントすることもできます。
ドライブレターはWin32を構成する要素ではなく、Object Managerが直接扱っている要素でもありません。
Object Managerは、Linuxのファイルシステムの名前空間のような名前空間を保持しており、「\」をルートにしてデバイスオブジェクトによって表現されるファイルシステムのボリュームを管理しています。
例えばあるボリュームは、Object Managerでは「\Device\HarddiskVolume1」のように表現されます。
もし「C:\foo\bar」パスを指定してファイルを開く場合、Win32のCreateFileはこのパスを「\DosDevice\C:\foo\bar」形式のNTパスに変換します。
この時「\DosDevice\C:」は、 「\Device\HarddiskVolume1」などObject Managerが表現している名前空間へのシンボリックリンクになっています。
従って実際のファイルパスは、「\Device\HarddiskVolume1\foo\bar」となります。
Object Managerは各パスのコンポーネントを、それに対応するデバイスオブジェクトを見つけるまで解決していきます。
この点はLinuxのVFSと似ています。
そしてObject ManagerはI/O Managerにファイル操作の要求を転送し、I/O Managerは残りのパスと共にIRP(I/O Request Packet)を作成し、IRPを該当するデバイスのファイルシステムドライバーに送ります。
1.ファイルオブジェクト
ファイルが開かれた時、Object Managerはそのファイルに対応したファイルオジェクトを作成します。またObject Managerは、ファイルディスクリプターの代わりにファイルオブジェクトへのハンドルを提供します。
ハンドルはファイルオジェクトだけでなく、Object Managerによって管理されている様々なオブジェクトを指すことができます。
NtReadFileのようなシステムコール(Win32ではReadFile関数)が呼ばれた時に、I/O ManagerはIRPを作成し、ファイルオブジェクトに対応したファイルシステムドライバーにIRPを送ります。
こうしてファイル操作が行われます。
Windowsにはinodeやそれに似た仕組みが存在せず、多くのファイル操作はファイルオブジェクトを必要とします。
2.リパースポイント
Windowsでは2種類のファイルタイプのみがサポートされています。それは一般的なファイルとディレクトリーです。
ファイルやディレクトリーは、リパースポイントになることができます。
リパースポイントは特殊なファイルで、固定ヘッダーと任意のデータブロックを持っています。
ヘッダーにはリパースポイントの種類を識別するタグが含まれており、ファイルシステムフィルタードライバーや内蔵リパースポイントタイプや、I/O manager自身によって扱われます。
リパースポイントとは
リパースポイントはシンボリックリンクやマウントポイントの実装に使われています。この時リパースポイントを識別するタグは、シンボリックリンクもしくはマウントになります。
またリパースポイントに関連付けられるデータには、リンク先の情報やマウントポイントにマウントされているボリュームのボリューム名が含まれています。
またリパースポイントは、Windows 8で導入されたOneDriveのプレースホルダーファイルのような機能にも使用されます。
3.大文字・小文字の区別
Linuxと異なりWindowsのファイルシステムは、デフォルトで大文字・小文字の情報を保持しますが、大文字・小文字の区別は行いません。実際にWindowsとNTFSは大文字・小文字の区別をサポートしていますが、この機能はデフォルトで有効になっていません。