shimada-kの日記

ソフトウェア・エンジニアのブログです

FUSEで遊んでみました

これは「カーネル/VM Advent Calendar2012」17日目の記事です。

今回はFUSEを使って画像の中にデータを記録していくファイルシステムを作ってみました。

基本的にファイルシステムを作るときはシステムコールの実装になります(これはOS直書きのファイルシステムでもFUSEでも一緒です)。

ただ、FUSEの場合はシステムコールを直接フックするわけではなく、カーネルモジュールとglibcを間に挟んでいます。FUSEのすばらしいところはユーザランドでFSが書けるのところで、豊富なライブラリを遠慮なくリンクできるのです。

しかもRubyPHPPythonなど主要なLLでのバインディングも充実しているのでLLでファイルシステムを書くことができるわけです。RubyPHPなどのLLを使う場合はさらに言語のエクステンションを間に挟むことになります。

実装はRuby + FUSE + Imlib2です。ソースはgithubにあります。画像の1ピクセル(R・G・B)3バイトにデータを格納する仕組みです。

スクリプトを実行するとデフォルトの設定なら自動でデスクトップにマウントされます。

環境:Linux 2.6.32-5-amd64 [debian squeeze]

./base.rb "マウントポイントのパス" hoge.yml

マウントされたファイルシステムnautilusで表示させたスクリーンショットです。ディレクトリも作ることができます。

f:id:shimada-k:20121217220814p:plain

FUSEはオンメモリのファイルシステムなのでアンマウントするとデータが消えてしまいます。なのでYAMLにデータを保存して永続性を担保しています。この辺はfusefsをインストールした段階でサンプルも入っているのでそちらを参考にしました。

nautilusで開くと上のスクリーンショットのような状態になります。nautilusには「画像のデータである」と返しているのでnautilusは画像データと判断してサムネイルを表示しています。

ファイルブラウザで見る限り一見画像に見えますが、実体はテキストなので通常のテキストエディタで開くことができますし、コンパイルすることができます。

f:id:shimada-k:20121217220834p:plain

上の画像は以下のソースをコンパイルしたものです。透過的な実装というやつです。

#include<stdio.h>

int main(int argc, char *argv[])
{
	puts("笑い男、それがお前の正体か");
	return 0;
}

今回は縦200×横182ピクセルのサイズの画像を扱っています。

テキストにすると上記のソースは120バイト程の内容なので、そのまま画像に格納すると120 / 3 = 40で、横が182ピクセルなので画像のピクセル上1行の4分の1もいかない程度のデータ量ですがコンパイルすると標準ライブラリをリンクしている都合上、バイナリのサイズが大きくなっている様子が可視化できます。

画像の中にピクセルの中にデータを保存しているのでノイズ(というかデータ)が多く入っています。

ただ現状、拡張子がついているとnautilus拡張子の辞書を見に行って、辞書に登録された拡張子ならファイルの内容まで見に行かずにアイコンを決定してしまうという課題があります。

なのでtest.cというファイルを作ると画像形式の内容をnautilusに返してもC言語のソースと判断されてアイコンが目的の画像にはならないということです。

ファイルの拡張子というものはあったらあったで不便ですが、無いとそれはそれで不便なものですね。

FUSEは色んな言語のバインディングがリリースされててアイディア勝負でサクッとファイルシステムが作れるので面白かったです。