WSLのファイルシステム
「WSL」では、様々なLinuxのファイルシステムの操作をWindows NTカーネルのファイルシステムの操作に変換する必要があります。また「WSL」は、Linuxのシステムファイルが存在するスペースを提供し、Linuxのパーミッションやシンボリックリンク、その他FIFOのような特殊なファイルなどLinuxで要求されるすべての機能も提供しなければなりません。
これに加え、Windowsのボリュームにアクセスする機能や、ProcFsなど特殊な仮想ファイルシステムも提供する必要があります。
このように「WSL」に求められる機能は多く、実現には相応のハードルが伴います。
実装の負担を軽減するため、「WSL」はLinuxのVFSを手本としたVFSコンポーネントを持っています。
このアーキテクチャーは、以下のようになっています。
Linuxアプリケーションがシステムコールを呼ぶと、そのシステムコールはシステムコールレイヤーによって処理されます。
このシステムコールレイヤーには、「open」「read」「chmod」「stat」など様々なカーネルのエントリーポイントが定義されています。
ファイル操作に関連したシステムコールは、システムコールレイヤーがVFSを呼び出して処理を転送します。
ディレクトリーエントリー
ファイルのパスに関する処理は、VFSがディレクトリーエントリーキャッシュを利用してパスの解決を行います。もしパスがディレクトリーエントリーキャッシュに存在していなかった場合、ディレクトリーエントリーを作成するため、まずinodeの作成処理を行います。
この処理は、目的ごとに用意されているファイルシステムプラグインを呼び出し、inodeの作成処理を行います。
これらのファイルシステムプラグインは、 「lookup」や「chmod」といったinodeを操作する機能を提供しており、Linuxカーネルで使われているinode操作に似た機能を提供します。
ファイルディスクリプター
ファイルが開かれた時VFSは、ファイルシステムのinodeのopen操作を利用し、ファイルオブジェクトを生成します。そして生成したファイルオブジェクトに対応したファイルディスクリプターをシステムコールの呼び出し側に返します。
ファイルディスクリプターが指定されたシステムコールは、ファイルシステムによって定義されているファイル操作を呼び出します。
この仕組みはLinuxでの振る舞いと非常に近い振る舞いになっており、WSLはLinuxでのファイル操作と同じ意味を持つファイル操作をサポートすることができます。
ファイルシステムプラグイン
VFSはいくつかのファイルシステムプラグインを定義しています。VolFsとDrvFsは、ディスク上のファイルを表現するために使用されます。
TmpFsはメモリー内でファイルシステムを構築します。
ProcFs、SysFs、CgroupFsは仮想ファイルシステムを構築します。
VolFsは、Linuxのファイルシステムが持つ機能を完全にサポートするよう設計されています。
DrvFsは、Windowsのファイルシステムとの相互運用のために設計されています。
VolFs
「WSL」で主に使用されるファイルシステムがこのVolFsです。VolFsはLinuxのシステムファイルを配置するために使用されるファイルシステムで、このファイルシステムにUbuntuのユーザースペースやユーザーのホームフォルダーが配置されます。
VolFsはLinuxのVFSが提供するほとんどの機能をサポートしており、 Linuxのパーミッションやシンボリックリンク、FIFO、ソケット、そしてデバイスファイルもサポートしています。
ファイルシステムのマウント
VolFsは、VFSのルートディレクトリーをマウントする時に使用されます。VFSのルートディレクトリーは「%LocalAppData%\lxss\rootfs」であり、このディレクトリーを永続ストレージとしてマウントします。
加えて「%LocalAppData%\lxss\root」を「/root」にマウントし、「%LocalAppData%\lxss\home」を「/home」にマウントします。
「/」や「/root」、「/home」が異なるディレクトリーに分かれている理由は、WSLをアンインストールする時にrootや他のユーザーの固有のファイルを削除せず維持しておくためです。
こうすることでUbuntuのユーザースペースの再インストール及び環境構築の手間を削減できます。
もちろんこれらのユーザー固有のファイルを完全に削除することも可能です。
上記のWindows側のディレクトリー(%LocalAppData%\lxss・・・)は、Windowsのユーザーのフォルダー内に配置されています。
各Windowsのユーザーは、自身のWSL環境を持っているということになります。
従って他のWindowsのユーザーの影響を受けること無く、Ubuntuのユーザースペースを活用することができます。
inodeとファイルオブジェクト
Windowsはファイルシステムにinodeという仕組みを持っていません。そのためVolFsは、inode内にWindowsのファイルオブジェクトのハンドルを保持していなければなりません。
VFSがlookupコールバックを使用して新しいinodeを要求した時に、VolFsは親のinodeと子の名称からハンドルを利用します。
このハンドルは、要求された新しいinodeのハンドルを相対的に開き取得するために使用します。
これらのハンドルはファイルへのアクセスなしに開かれ、 メタデータを取得するためだけに利用されます。
ファイルを開いた時にVolFsは、inodeを参照するLinuxのファイルオブジェクトを作成します。
またVolFsは、ファイルへの読み書きの要求やファイルオブジェクト内に新しいハンドルを保存する際、inodeのファイルハンドルを再度開き処理を行います。
このハンドルは、読み書きのようなファイル操作を行う時に使用されます。
Linuxの機能のエミュレーション
Windowsのファイルシステムが持つ機能とLinuxのファイルシステムが持つ機能が異なっているため、VolFsはWindowsが直接的にサポートしていないLinuxのファイルシステムの機能をサポートし提供しなければなりません。大文字・小文字の区別
ファイル名の大文字・小文字の区別は、Windows自身によって扱われます。WindowsとNTFSは、大文字・小文字の区別をサポートしています。
VolFsはObject Managerに大文字・小文字を区別してファイル名やパスを扱うように要求します。
ファイル名
Linuxファイル名には、Windowsでは許可されない文字を使用することができます。WindowsはLinuxよりファイル名に使用できる文字により厳しい制限を行っており、いくつかの文字はファイル名に使用できず、いくつかの文字は特別な意味を持っています。
例えばWindowsにおいて「:」は、代替データストリームを意味します。
Linuxのファイル名で使用できる文字をすべてサポートするため、VolFsはWindowsで使用できない文字をエスケープしてファイル名に使用します。
unlinkとrename
Linuxのファイルのunlinkとrenameの意味は、Windowsのそれと意味が異なります。特に異なる点は、ファイルが開かれていてもそのファイルをunlinkできるという点です。
同様に開かれているファイルのファイル名を変更でき、そのファイルを上書きできます。
Windowsではファイルの削除要求が合った時、そのファイルを参照している最後のハンドルが閉じられた時にのみ削除でき、それまでそのファイルはファイルシステムに存在します。
Linuxのunlinkと同じ機能を提供するためにVolFsは、unlinkされたファイルのファイル名を一時的な隠しディレクトリー名に変更します。
ファイルの属性
Linuxのinodeには、Windowsにはなし様々な属性が含まれています。例えばファイルの所有者やグループ、ファイルモードなどです。
これらの属性は、NTFSの拡張属性領域に保存されます。
以下の属性が、拡張属性として保存されます。
■モード:
ファイルの種類(レギュラー、シムリンク、FIFOなど)やファイルのパーミッションの情報です。
■オーナー:
ファイルの所有者であるLinuxのユーザー及びグループに対応したユーザーIDやグループIDの情報です。
■デバイスID:
デバイスファイル用です。
デバイスのメジャー番号とマイナー番号の情報です。
現在WSLでは、ユーザーがVolFs上にデバイスファイルを作成することは許可していません。
■ファイルのタイムスタンプ:
ファイルにアクセスした時間、ファイルを編集した時間、ファイルを変更した時間です。
Windowsと異なるフォーマットであるため、これらの情報が拡張属性に保存されます。
■ケイパビリティー
ファイルのケイパビリティーは、代替データストリームに保存されます。
現在WSLでは、ユーザーがケイパビリティーを変更することは許可していません。
上記以外のinode番号やファイルサイズのようなinodeに含まれる属性は、NTFSによって提供される情報です。
Windowsとの相互運用時の注意点
Windowsから見た場合、上記で挙げたWindowsのディレクトリー(%LocalAppData%\lxss)内に通常のファイルとしてVolFsのファイル群が配置されます。もしWindowsのアプリからVolFsが使用する「%LocalAppData%\lxss」内にWindowsのアプリから新規にファイルを追加した場合、そのファイルは相互運用できません。
これはVolFsが必要とするNTFSの拡張属性情報が存在していないためです。
そのためVolFsはそれらのファイルをどのように見せれば良いのか、どのように扱えば良いのかが分からないため、それらのファイルの存在は無視されます。
また「%LocalAppData%\lxss」内のファイルをWindowsのアプリで開き、そのファイルを上書き保存した場合、多くのWindowsのアプリは拡張属性を切って捨てるため、ファイル保存時にVolFsが作成した拡張属性が失われてしまいます。
結果、それらのファイルはWSLから利用できなくなってしまいます。
加えて、VFSはディレクトリーエントリーをキャッシュしているため、Windowsからそれらのディレクトリーを変更してもそのことを検出できずキャッシュに反映されません。
キャッシュの整合性が取れなくなります。
つまり
「%LocalAppData%\lxss」内のファイルやディレクトリーをWindowsからいじらないようにしてください。DrvFs
Windowsとの相互運用性を実現するため、「WSL」はDrvFsというファイルシステムを使用します。「WSL」は自動的にWindowsのすべてのボリューム(固定ディスク)をマウントします。
それらのボリュームは、「/mnt」以下にマウントされます。
例えばCドライブは、「/mnt/c」にマウントされます。
現在サポートされているボリュームのファイルシステムは、NTFSとReFSのみです。
DrvFsはVolFsと似たような操作を行います。
inodeとファイルオブジェクト作成時ハンドルを開きますが、このハンドルはWindowsのファイルを指すハンドルです。
Windowsのルールに従う
VolFsと対照的な点は、DrvFsはいくつかの例外を除いてWindowsのファイルシステムのルールに従うという点です。Windowsのパーミッションに影響を受け、NTFSで利用可能な文字のみファイル名に使用できます。
またFIFOやソケットなど特殊なファイルは利用できません。
DrvFsのパーミッション
Linuxのパーミションはシンプルな仕組みであり、ファイルの読み書き及び実行権限を、ユーザー、グループ、その他のユーザーに対して指定します。Windowsでは、パーミッションの仕組みにACL(Access Control List)が使用されます。
ACLは個々のファイルやディレクトリーに複雑なパーミッションを指定することができます。
LinuxでもACL相当の機能が利用できますが、現在WSLではサポートしていません。
DrvFsにあるファイルを開くと、bash.exeを起動したユーザーの権限を基づいたパーミッションがそのファイルに適用されます。
従って「C:\Windows」内のファイルにアクセスするためにbashで「sudo」をしてWSL内でroot権限を取得しても、それらのファイルにはアクセスできません。
この時Windowsユーザーの権限が適用されるため、DrvFs内のファイル操作に必要な権限を得るには、bash.exeを管理者ユーザーで起動する必要があります。
DrvFsは「ls -l」などLinuxのソフトウェアにファイルの権限を見せる際、WindowsのパーミッションをLinuxのパーミッションに変換します。
しかしWindowsのパーミッションとLinuxのパーミションは1対1で対応していません。
例えばWindowsでは、ファイルの作成権限とサブディレクトリーの作成権限は個別に指定できます。
もしユーザーがどちらかの権限を持っている場合、DrvFsはディレクトリーに書き込み権限があると報告します。
でも実際Windowsから見て権限のない操作を行おうとすると、アクセス拒否のエラーになります。
Windowsで読み込み専用の属性が指定されたファイルはWSLから参照できますが、書き込み権限はありません。
chmodを使用して書き込み権限を付与したり、読み込み専用に変更することが可能です。
bash.exeをどのように起動したかで権限が変わる
DrvFs内のファイルのアクセス権限は、bash.exeをどのように起動したかで権限が変わるということになります。つまり管理者ユーザーでbash.exeを起動したのか、それとも通常のユーザーでbash.exeを起動したのかで、ファイルのアクセス権限が変わります。
大文字・小文字の区別
DrvFsは、大文字・小文字の区別をサポートします。そのため同じディレクトリー内に、大文字・小文字のみが異なるファイルを作成することができます。
しかしWindowsのアプリは大文字・小文字の区別に対応していないため、大文字・小文字のみが異なるファイルを正常に扱うことができません。
大文字・小文字の区別は、ボリュームのルートでは無効になっています。
そのためボリュームのルート直下(/mnt/cなど)にファイルは作成せず、ルート内のサブディレクトリー以下のディレクトリー内にファイルを作成すると良いでしょう。
シンボリックリンク
Windowsはシンボリックリンクをサポートしていますが、「WSL」はWindowsのシンボリックリンクの機能をそのまま利用できません。WSLでは「/proc」のようなパスにリンクを張れますが、Windowsにとってそのリンクは意味を持ちません。
またWindowsはシンボリックリンクの作成に管理者権限を要求します。
従ってWSLでシンボリックリンクをサポートするために他の方法が必要になります。
またVolFsと異なりDrvFsでは、シンボリックリンクの情報を拡張属性に保存することができません。
そこで「WSL」は、シンボリックリンクを表すために新しいタイプのリパースポイントを使用します。
従ってそれらのシンボリックリンクは、「WSL」でのみ機能します。
Windowsのソフトウェアからそれらのシンボリックリンクは解釈できず、利用することはできません。
ReFSではシンボリックリンクが利用できない
ReFSではリパースポイントがサポートされていないため、ReFSのボリューム内では「WSL」はシンボリックリンクが作成できません。NTFSのボリュームでのみ「WSL」はシンボリックリンクが作成できます。
Windowsとの相互運用
DrvFsはWindowsとの相互運用のためのファイルシステムですが、Windowsのルールに影響を受けるWindows寄りのファイルシステムです。従ってVolFsと異なり拡張属性にWSL固有の情報は保存されません。
パーミッションなどinodeの属性情報は、Windowsのファイル情報から取得されます。
DrvFsはディレクトリーエントリーをキャッシュせず、Windowsのプロセスがディレクトリー内のファイルを変更したとしても、常に正確で最新の情報を取得します。
この時DrvFsで操作が行われている最中でも、Windowsのプロセスに影響はありません。
ファイルの削除もWindowsのルールに従う
ファイルの削除もWindowsのルールに従うため、プロセスが開いているファイルを削除することはできません。ProcFsとSysFs
Linux同様これらの特殊なファイルはディスク上に存在していません。これらの特殊なファイルは、カーネルがプロセスやスレッド、デバイスの情報をソフトウェアに提供するためのファイルです。
これらのファイルは自動的に生成され、読み込み時にファイルの内容がカーネルにより生成されます。
いくつかのファイルの内容はlxcore.sysによって提供され、CPUの使用率等の情報はWSLがWindowsカーネルに問い合わせうことで提供されます。
いずれにせよ、Windowsのファイルシステムにアクセスすることはありません。