2009年11月20日金曜日

自分で作るネットワークストレージ (2)

ファイルシステムを作ると言っても、OSネイティブのファイルシステムを作るのは大変です。しかしながら、最近はFUSE (Filesystem in Userspace)を使って手軽にファイルシステムを試作できるようになりました。以前だったら躊躇していたところですが、気軽に試せそうです。

FUSEはOS内部で発生したファイルシステムイベントをユーザー空間のプログラムに通知し、そこで必要な操作を代行させることでOSネイティブのファイルシステムと同様の操作性をファイルシステム利用者に提供しています。イベントの処理といっても、実際はイベントに対応したコールバック関数を記述する形になりますから、特に実装が複雑なわけではありません。FUSEでは様々なイベントに対応するコールバック関数が定義されていますが、すべての関数を実装しなければならないわけではありません。たとえば、シンボリックリンクをサポートしないのであれば、シンボリックリンクに関係する関数を実装する必要はありません。とりあえず今回対応するのは以下の関数です。試作にMacOS XのXcodeテンプレートを使うので、関数名がCocoa APIっぽく書かれていますが、簡単に読み替えることができると思います。

  • ディレクトリ一覧 (contentsOfDirectoryAtPath:error:, opendir(2), readdir(2)など)
  • ファイル情報取得 (attributesOfItemAtPath:userData:error:, stat(2))
  • ファイル情報設定 (setAttributes:ofItemAtPath:userData:error:, chmod(2)など)
  • ファイルオープン (openFileAtPath:mode:userData:error:, open(2))
  • ファイルクローズ (releaseFileAtPath:userData:, close(2))
  • ファイル読み込み (readFileAtPath:userData:buffer:size:offset:error:, read(2))
  • ファイル書き込み (writeFileAtPath:userData:buffer:size:offset:error:, write(2))
  • 新規ディレクトリ作成 (createDirectoryAtPath:attributes:error:, mkdir(2))
  • 新規ファイル作成 (createFileAtPath:attributes:userData:error:, open(2))
  • ファイル移動 (moveItemAtPath:toPath:error:, rename(2))
  • ディレクトリ削除 (removeDirectoryAtPath:error:, rmdir(2))
  • ファイル削除 (removeItemAtPath:error:, unlink(2))

FUSEで読み書き可能なファイルシステムを作る場合、上記の関数を実装すればとりあえず動きます。あとは、これらのイベントの裏で、適切に版管理システムと連携させてあげればよいわけです。版管理システムが提供するインターフェースは以下のようなものになるでしょう。

  • チェックアウト (checkout)
  • 更新 (update)
  • 追加 (add)
  • 複製 (copy)
  • 移動 (move)
  • 削除 (delete)
  • コミット (commit)

今回、版管理システムとしてSubversionを念頭に置いています。ですが、Subversionと同様の概念で構築されているシステムであれば、上記のような抽象化された版管理インターフェースと実際の動作を対応させることで、差し替えもできるのではないかと思います。

FUSEが提供するファイル操作と、版管理システムが提供するファイル操作をじっと見比べてみると、お互いの機能が似ていることがわかります。FUSE側に存在せず、版管理側に存在する操作としてチェックアウトとコミット、逆にFUSEには存在し、版管理システムに存在しない操作としてはファイルのオープンやクローズ、ファイル情報の操作などがあります。これらの差を埋めれば二つのシステムが相互に繋がりそうです。Subversionを前提にがっつり実装してもよいのですが、将来の拡張性を考えて、版管理抽象層を定義し、以下のAPIを版管理抽象層からFUSEファイルシステム側に提供することにしました。

  • 初期化 (revisionControlSetup:)
  • チェックアウト (revisionControlSetup:)
  • 追加 (revisionControllAdd:)
  • 削除 (revisionControlRemove:)
  • 複製 (revisionControlCopy:)
  • 更新 (revisionControlUpdate:)
  • コミット (revisionControlCommit:)
  • タッチ (revisionControlTouch:)
  • 終了処理 (revisionControlCleanup:)
  • 実パス名取得 (revisionControlRealPathForPath:)
  • 制限ファイル名取得 (revisionControlNameIsReservedAtPath:)

いくつか、版管理システムの操作一覧にないAPIを追加しています。初期化と終了処理はよいとして、タッチ、実パス名取得、制限ファイル名取得は、手元の作業コピーでの操作と版管理されているデータを結びつけるために必要となります。

今回、版管理システムを基礎としてファイルシステムを構築するので、ファイルへの操作はオリジナルのファイルではなく、手元に作られた作業コピーに対して行われます。作業結果は、最終的に版管理システムにコミットしなければならないので、どのファイルが操作されたのか履歴を記録しておく必要があります。タッチAPIは、作業コピーで修正されたファイルを版管理抽象層に通知します。

実パス名取得APIは、版管理システム内で使われているパス名を、作業コピー上でのパス名に変換するためのAPIです。今回の試作では、版管理システム内のディレクトリ構造を、手元のPCの特定ディレクトリにマウントして使うことを想定しています。FUSEから通知されるパスはマウントされたディレクトリからの総体パスになりますが、実際に操作できるファイルは作業コピーとして取り出したファイルパスになります。作業コピーを取り出した場所は版管理抽象層しか知らないので、実際のファイルパス名を知りたい場合に抽象層に問い合わせるAPIが必要になります。

制限ファイル名取得APIは、ファイルシステムとして利用できないファイル名を判断するためのAPIです。たとえば、版管理システムとしてSubversionを使う場合、.svnという名前のファイル名、ディレクトリ名は使えません。このAPIを通じて、後ろで使われている版管理システムで禁止されているファイル名を取得します。

とりあえずFUSE関数を上述の版管理抽象層APIで実装してみると、以下のようになります。

  • (FUSE初期化関数)
    • 初期化
    • チェックアウトあるいは更新
  • ディレクトリ一覧
    • 更新
  • ファイル情報取得
    • (作業コピーのファイル情報を取得)
  • ファイル情報設定
    • (作業コピーのファイル情報を取得)
  • ファイルオープン
    • タッチ
  • ファイルクローズ
    • コミット
  • ファイル読み込み
    • (作業コピーのファイルを読み込み)
  • ファイル書き込み
    • (作業コピーのファイルに書き込み)
  • 新規ディレクトリ作成
    • (作業コピーにディレクトリ作成)
    • 追加
  • 新規ファイル作成
    • (作業コピーにファイル作成)
    • 追加
  • ファイル移動
    • (移動元を移動先に)複製
    • (移動元を)削除
  • ディレクトリ削除
    • 削除
  • ファイル削除
    • 削除
  • (FUSE終了関数)
    • コミット
    • 終了処理

まぁ、いろいろと問題はあるのですが、一応動くものにはなります。ソースコードsourceforgeに公開していますので、興味のある人は覗いてみてください。sourceforgeに登録しているコードは、上記以上の機能も実装していますが、それはまた別の機会に。

次回に続きます。

2009年11月11日水曜日

自分で作るネットワークストレージ (1)

ネットワークストレージという考え方は昔からありましたが、それが単体サービスとして有償で提供されるようになったのはここ数年でしょうか。Dropboxなどに代表されるネットワークストレージサービスには一定の利用者がいるようで、解説記事もよく見るようになりました。

データをサーバに保持しておいて、ネットワーク経由でどこからでもアクセスするという考え方は新しいものではありません。ただ、そういったサービスを構築するには、高度なサーバ設定、ネットワーク設定の知識が必要だったため、一般の人が手軽に始めるというわけにはいきませんでした。近年のネットワークストレージサービスは、サーバ設定をサービス提供者側に分離し、クライアントとネットワークの設定を専用アプリケーションにすることでその敷居を下げ、技術者以外の利用者の取り込みに成功したのだと思います。

さて、特に昔からコンピュータネットワークを使っている人には共感してもらえると思いますが、自分のデータを自分の管理の行き届かない場所に預けるのは心配なものです。便利なネットワークストレージサービスですが、大切なデータをそこに保存しても大丈夫なのかどうか、つい考えてしまいます。そうはいっても便利なサービスは使いたい、ということで、自分で運用できないものかと考えるのは技術者として自然なことです。

では、自分にとってネットワークストレージサービスに必要な機能は何か、と考えてみると、以下のような要求がでてきました。

  1. 自分で運用できる
  2. ファイルシステムとして操作できる
  3. ネットワークに接続していなくてもある程度操作できる

要求1は、そもそもの発端なので当然です。要求2は利便性を確保するために重要です。ネットワーク上にデータを保存できても、それを直接ローカルアプリケーションで操作できなければ面倒です。OSのファイルシステムの一部として見えている、もしくは同等の操作性が提供されている必要があります。要求3も、現実の環境を考えると必須といえます。ネットワーク環境は日々向上しているものの、ネットワークに接続できない場所は必ずあるものです。その間、フル機能ではないにせよ、ある程度の操作ができないと使い勝手が落ちてしまいます。

これらの条件を満たすもので、現状一番近いものは版管理システムです。旧くはRCSやCVS、その後現れたSubversion、最近使われる事が多いGitなどが有名です。これらの版管理システムは一種のファイルシステム機能を提供しています。ただ、元々プログラムのソースコードの改変履歴を記録する目的で設計されたシステムですから、OSのファイルシステムと同様には操作できません。ファイルを更新しようとした場合、まず版管理システムに保存されているデータを、通常のファイルシステム上に複製(チェックアウト)し、その複製に対して編集を行い、編集完了後に書き戻し(チェックイン)といった、通常のファイル操作とは異なる粒度でのファイル操作が必要になります。

それならば、その部分をうまく隠蔽し、通常のOSレベルでのファイル操作と同等のインターフェースを提供できれば、自分の欲しいネットワークストレージになるのではないか、しかも版管理機能のおまけ付き、という考えが浮かびます。

実際、そういう試みは過去にありました。かなり古いプロジェクトですが、CVSサーバをNFSマウントするVirtual CVS Filesystemというソフトウェアがsourceforgeに登録されています。ただし、プロジェクトが活動停止してからかなり時間が経っているようです。また、FUSEを用いたSVNレポジトリアクセス用ファイルシステムSvnFSというのもあります。こちらは開発が継続しているようです。ただ、どちらも読み取り専用ファイルシステムで、ネットワークストレージとしては中途半端な状態です。ここはやはり、読み書きできるストレージシステムが欲しいところではないでしょうか。

ということで次回に続きます。