FUSE を使用して独自のファイルシステムを開発する

カーネル・プログラミングは必要ありません

FUSE (Filesystem in Userspace) を使用すると、ファイルシステムの内部について理解したり、カーネル・モジュールのプログラミングを学習したりしなくても、ユーザー空間のファイルシステム・フレームワークを開発することができます。この簡単なステップ・バイ・ステップのガイドに従い、FUSE と AFS のインストール、カスタマイズ、および有効化を行うことによって、完全に機能するユーザー独自のファイルシステムを Linux のユーザー空間に作成することができます。

2015年 2月 06日: 「参考文献」を含む全体を更新しました。

Sumit Singh, Software Engineer, IBM

Sumit Singhは、IBMのエンタープライズ・ファイルシステム・グループのソフトウェア技術者です。主な業務として、AFS用のL3サポートがあります。



2015年 2月 19日 (初版 2006年 2月 28日)

ファイルシステムは、コンピューターのファイルおよびディレクトリー、そしてそれらに含まれるデータを格納、編成するための手段です。ファイルシステムを使用すると、ファイルやディレクトリー、データなどを簡単に見つけてアクセスできるようになります。コンピューターを使用している場合、おそらく 2 種類以上のファイルシステムを使用していることになります。ファイルシステムは拡張機能を提供することができ、ファイルシステムのラッパーとして拡張機能を作成してデータを管理することや、拡張機能によって豊富な機能を持つように強化したファイルシステムを提供することができます (たとえば、CVS 用のファイルシステム・インターフェースを提供する cvsfs-fuse や、古いデータのコピーを保持するためのバックアップ・メカニズムを提供する Wayback ファイルシステムなどが挙げられます)。

ユーザー空間ファイルシステムが登場する以前は、ファイルシステムの開発はカーネル開発者の仕事でした。ファイルシステムを作成するにはカーネル・プログラミングとカーネル・テクノロジー (vfs など) の知識が必要で、デバッグには C および C++ の高度な専門知識が必要でした。しかし、他の開発者もファイルシステムを操作して、パーソナライズされた機能を追加したり (履歴機能やフォワード・キャッシュ機能を追加するなど)、機能強化したりする必要がありました。

FUSE の概要

FUSE を使用すると、フル機能のファイルシステムを開発することができ、そのファイルシステムは簡単な API ライブラリーを備え、セキュアな実装を提供し、非特権ユーザーもアクセスできるものにすることができます。そしてなにより、FUSE は安定性に実績があります。

FUSE では、ファイルシステムを FUSE ライブラリーにリンクされた実行可能バイナリーとして開発することができます。言い換えると、このファイルシステム・フレームワークでは、ファイルシステムの内部について学んだり、カーネル・モジュール・プログラミングを学んだりする必要がありません。

ファイルシステムに関して言えば、ユーザー空間ファイルシステムは新しい設計というわけではありません。ユーザー空間ファイルシステムの商用実装および学術用実装の例には、次のようなものがあります。

  • LUFS: あらゆるアプリケーションに対してトランスペアレントかつ無制限にファイルシステムをサポートする、ハイブリッドなユーザー空間ファイルシステム・フレームワークであり、カーネル・モジュールとユーザー空間デーモンで構成されています。LUFS では基本的に、VFS 呼び出しを専門で扱うデーモンに VFS 呼び出しの大部分を任せています。
  • UserFS: ユーザー・プロセスを通常のファイルシステムとしてマウントできるようにします。この概念検証プロトタイプは、ファイルシステム・インターフェースを使用した匿名 FTP を可能にする ftpfs を提供します。
  • Ufo Project: Solaris 用のグローバルなファイルシステムであり、リモート・ファイルをローカル・ファイルとまったく同様に扱うことができます。
  • OpenAFS: Andrew FileSystem のオープン・ソース版です。
  • CIFS: Common Internet FileSystem です。

これらの商用実装および学術用の例と異なり、FUSE はこのファイルシステム設計の機能を Linux で実現します。FUSE では、(たとえば LUFS で使用しているような共有オブジェクトではなく) 実行ファイルを使用することから、デバッグと開発が容易になります。FUSE はカーネル 2.4.x と 2.6.x のどちらでも動作し、Java バインディングもサポートするようになったので、ファイルシステムのプログラミングが C と C++ に限定されません (FUSE を使用するユーザーランド・ファイルシステムの詳細については、「参考文献」を参照してください)。

FUSE でファイルシステムを作成するには、FUSE カーネル・モジュールをインストールし、FUSE ライブラリーと API のセットを使用してファイルシステムを作成する必要があります。


FUSE の解凍

ファイルシステムを開発するには、まず FUSE ソース・コードをダウンロードして (「参考文献」を参照)、コマンド tar -zxvf fuse-2.2.tar.gz を実行することでパッケージを解凍します。これにより、ソース・コードを含む形で FUSE のディレクトリーが作成されます。作成された fuse-2.2 ディレクトリーは、以下のディレクトリーで構成されます。

  • ./doc: FUSE 関連のドキュメントが含まれています。現時点で含まれているファイルは how-fuse-works のみです。
  • ./kernel: FUSE カーネル・モジュールのソース・コードが含まれています (もちろん、FUSE を使用してファイルシステムを開発する上で、このソース・コードの内容を理解している必要はありません)。
  • ./include: ファイルシステムを作成する上で必要な FUSE API ヘッダーが含まれています。今のところ必要なのは fuse.h のみです。
  • ./lib: FUSE ライブラリーを作成するためのソース・コードが含まれています。このライブラリーをバイナリーにリンクさせることによってファイルシステムを作成します。
  • ./util: FUSE ユーティリティー・ライブラリーのソース・コードが含まれています。
  • ./example: もちろん、参照用のサンプル・コードが含まれています。xmp (fusexmp.c)、null (null.c)、hello (hello.c) といったサンプル・ファイルシステムのコードが含まれています。

FUSE のビルドとインストール

  1. fuse-2.2 ディレクトリーから構成スクリプト ./configure を実行します。このスクリプトによって、必要な makefile などが作成されます。
  2. ./make を実行して、ライブラリー、バイナリー、およびカーネル・モジュールをビルドします。kernel ディレクトリーにファイル ./kernel/fuse.ko (これがカーネル・モジュール・ファイルです) があることを確認します。また、lib ディレクトリーに fuse.o、mount.o、および helper.o があることを確認します。
  3. ./make install を実行して、FUSE のインストールを完了します。

    別の手段: insmod を使用して (たとえば /usr/local/sbin/insmod ./kernel/fuse.ko を実行するか、/sbin/insmod ./kernel/fuse.ko を実行することで)、自分でカーネル内のモジュールをインストールしたい場合は、このステップをスキップしてください。必須モジュールは、root 権限でインストールしてください。

お望みの場合は、上記の全ステップを 1 つのステップで実行することができます。それには fuse-2.2 ディレクトリーから、./configure; make; make install; を実行します。

重要: FUSE を作成するときには、カーネル・ヘッダーやソース・コードが所定の場所になければなりません。作業を単純にするには、カーネル・ソース・コードを /usr/src/ ディレクトリーに配置してください。


ファイルシステムのカスタマイズ

ここで、ファイルシステムを作成し、古い Linux カーネルを使用して最新のカーネルがある Linux ボックス上の AFS 空間にアクセスできるようにしましょう。それには 2 つのプロセスを使用します。1 つは古い Linux カーネル上で実行されるサーバー・プロセスで、もう 1 つは最新のカーネルがある Linux ボックス上で実行される FUSE クライアント・プロセスです。要求が FUSE クライアント・プロセスに到達すると、このクライアント・プロセスはリモートのサーバー・プロセスに接続します。このファイルシステムは、通信に AFS の一部である RX RPC コードを使用するので、OpenAFS をビルドする必要があります (図 1 に、この AFS ファイルシステムの概要を示します)。

図 1. AFS-FUSE ファイルシステムの概要
AFS-FUSE ファイルシステムの概要を示す図

OpenAFS のビルド

  1. OpenAFS Linux ソースをダウンロードして解凍します。

    ソースの解凍先ディレクトリーで、./make ./configure --enable-transarc-paths を実行します。./configure がビルドする sysname を認識できない場合は、--with-afs-sysname オプションを使用して、適切な sysname を指定してください。

    Linux 2.4 カーネルをビルドする場合は、コマンド ./configure --enable-transarc-paths --with-afs-sysname=i386_linux24 を使用します。

  2. ./make を実行し、続いて ./make dest を実行します。ビルド中にエラーがなかったかどうかを確認します。

    ビルドに問題がなければ、AFS ソース・ツリーを使用することができます。この段階で、afsfuse という名前の開発用ディレクトリーを準備する必要があります。このディレクトリーに、さらに以下の 2 つのディレクトリーを作成します。

    • include ディレクトリーには、OpenAFS と FUSE からインクルードするヘッダー・ファイルを格納します。
    • lib ディレクトリーには、OpenAFS と FUSE のライブラリーを格納します。
  3. ヘッダーとライブラリーをコピーします。

    まず、OpenAFS ディレクトリーから AFS ヘッダーをコピーします。すなわち、dest\i386_linux24\include 内のディレクトリーとファイルを include ディレクトリーにコピーします。次に、fuse-2.2 ディレクトリーの FUSE ヘッダーを include ディレクトリーにコピーします。ライブラリーに関しても、これと同様のステップを行って lib ディレクトリーにコピーします。

  4. アプリケーションの構造を作成します。

    2 組のプロセスのために 2 組のファイルが必要です。クライアント・プロセス・ファイルには、afsfuse_client. * という命名スキームに従って名前を付けます。サーバー・プロセス・ファイルには、afsfuse_server. * という命名スキームで名前を付けます。

    このようにして、FUSE プロセス・コードを含んだ afsfuse_client.c ファイルと、リモート・マシン上で実行されるプロセス用のサーバー・コードを含んだ afsfuse_server.c ファイル、さらには makefile、そして RPC ヘッダーを作成するための rxgen ファイル (afsfuse.xg など) を用意します。

    afsfuse_client.c ファイルには、独自のファイルシステムを作成するために FUSE ファイルシステムによって呼び出される afsfuse_client プロセス・コードを含めるようにします (この afsfuse_client.c ファイルを作成するために、サンプル・コード fuse-2.2/example/fusexmp.c を使用してください)。

必要な関数の定義

FUSE を使用してファイルシステムを作成するには、fuse_operations 型の構造体変数を宣言して、この構造体変数を fuse_main 関数に渡す必要があります。fuse_operations 構造体には、適切な処置が必要なときに呼び出される関数のポインターが含まれます。リスト 1 に fuse_operations 構造体を示します。

リスト 1. fuse_operation 構造体内の必要な関数
struct fuse_operations {
    int (*getattr) (const char *, struct stat *);
    int (*readlink) (const char *, char *, size_t);
    int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
    int (*mknod) (const char *, mode_t, dev_t);
    int (*mkdir) (const char *, mode_t);
    int (*unlink) (const char *);
    int (*rmdir) (const char *);
    int (*symlink) (const char *, const char *);
    int (*rename) (const char *, const char *);
    int (*link) (const char *, const char *);
    int (*chmod) (const char *, mode_t);
    int (*chown) (const char *, uid_t, gid_t);
    int (*truncate) (const char *, off_t);
    int (*utime) (const char *, struct utimbuf *);
    int (*open) (const char *, struct fuse_file_info *);
    int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
    int (*write) (const char *, const char *, size_t, off_t,struct fuse_file_info *);
    int (*statfs) (const char *, struct statfs *);
    int (*flush) (const char *, struct fuse_file_info *);
    int (*release) (const char *, struct fuse_file_info *);
    int (*fsync) (const char *, int, struct fuse_file_info *);
    int (*setxattr) (const char *, const char *, const char *, size_t, int);
    int (*getxattr) (const char *, const char *, char *, size_t);
    int (*listxattr) (const char *, char *, size_t);
    int (*removexattr) (const char *, const char *);
};

これらの操作はどれも絶対不可欠というものではありませんが、その多くはファイルシステムが適切に機能する上で必要です。この構造体を使用すれば、.flush.release.fsync といった特殊な用途のメソッドを備えたフル機能のファイルシステムを実装することができます (この記事では、xattr 関数には触れません)。リスト 1 に示した関数を以下で説明します。

  • getattr: int (*getattr) (const char *, struct stat *);
    この関数は、stat() と似ており、st_dev フィールドと st_blksize フィールドは無視され、use_ino マウント・オプションが指定されなかった場合、st_ino フィールドは無視されます。
  • readlink: int (*readlink) (const char *, char *, size_t);
    この関数は、シンボリック・リンクのターゲットを読み取ります。バッファーは、null 終端文字列で埋められます。バッファー・サイズの引数には、null 終端文字用のスペースも含めたサイズを渡します。リンク名が長すぎてバッファーに収まらない場合、リンク名は切り詰められます。読み取りに成功した場合、戻り値は 0 になります。
  • getdir: int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
    この関数は、ディレクトリーの中身を読み取ります。この操作は、opendir()readdir()、...closedir() のシーケンスを 1 回の呼び出しで行います。ディレクトリー・エントリーごとに filldir() 関数が呼び出されます。
  • mknod: int (*mknod) (const char *, mode_t, dev_t);
    この関数は、ファイル・ノードを作成します。create() 関数を呼び出す操作はありません。ディレクトリー・ノードとシンボリック・リンク・ノード以外のノードを作成する場合、mknod() が呼び出されます。
  • mkdir: int (*mkdir) (const char *, mode_t);
    rmdir: int (*rmdir) (const char *);
    これらの関数は、それぞれ、ディレクトリーの作成、ディレクトリーの削除を行います。
  • unlink: int (*unlink) (const char *);
    rename: int (*rename) (const char *, const char *);
    これらの関数は、それぞれ、ファイルの削除、ファイル名の変更を行います。
  • symlink: int (*symlink) (const char *, const char *);
    この関数は、シンボリック・リンクを作成します。
  • link: int (*link) (const char *, const char *);
    この関数は、ファイルのハード・リンクを作成します。
  • chmod: int (*chmod) (const char *, mode_t);
    chown: int (*chown) (const char *, uid_t, gid_t);
    truncate: int (*truncate) (const char *, off_t);
    utime: int (*utime) (const char *, struct utimbuf *);
    これらの関数は、それぞれ、ファイルのパーミッション・ビットの設定変更、ファイルの所有者およびグループの設定変更、ファイルのサイズの設定変更、ファイルのアクセス日時/更新日時の設定変更を行います。
  • open: int (*open) (const char *, struct fuse_file_info *);
    この関数は、ファイルを開く操作を行います。作成フラグ、排他フラグ、切り詰めフラグ (O_CREATO_EXCLO_TRUNC) は、open() には渡されません。この関数は、ファイルを開く操作が許可されているかどうか、ポインター引数として渡された構造体内のフラグをチェックします。open() はオプションで、fuse_file_info 構造体内のファイルハンドル fh を返すこともできます。このファイルハンドルは、すべてのファイル操作に渡されます。
  • read: int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
    この関数は、開いているファイルからデータを読み取ります。read() は EOF またはエラーの場合を除き、要求されたバイト数を返します。要求されたバイト数に満たない場合、読み出されたデータが格納されたバッファーの残りのバイトはゼロに置き換えられます。ポインター引数として渡された構造体内で direct_io マウント・オプションが指定されている場合は、この関数による操作の戻り値が read() システム・コールの戻り値に反映されるという、例外的な処理が行われます。
  • write: int (*write) (const char *, const char *, size_t, off_t, struct fuse_file_info *);
    この関数は、開いているファイルにデータを書き込みます。write() はエラーの場合を除き、要求されたバイト数を返します。ポインター引数として渡された構造体内で direct_io マウント・オプションが指定されている場合は、(read() での操作と同様に) 例外的な処理が行われます。
  • statfs: int (*statfs) (const char *, struct statfs *);
    この関数は、ファイルシステム統計を取得します。f_type フィールドと f_fsid フィールドは無視されます。
  • flush: int (*flush) (const char *, struct fuse_file_info *);
    この関数は、キャッシュされたデータをフラッシュします。この操作は fsync() と同じ内容ではありません (ダーティー・データを同期する要求をするのではありません)。ファイル記述子を閉じるための close() が呼び出されるたびに、flush() は呼び出されます。そのため、ファイルシステムが close() の中で書き込みエラーを返す必要があって、キャッシュされたダーティー・データがファイルにある場合には、flush() でデータを書き戻してエラーを返すとよいでしょう。多くのアプリケーションが close() エラーを無視するので、この方法が常に有効なわけではありません。

: flush() メソッドは、1 つの open() に対して複数回呼び出すことができます。このメソッドが複数回呼び出されるのは、dup()dup2()fork() などを呼び出すことにより、複数のファイル記述子が 1 つの開かれたファイルを参照するような場合です。どのフラッシュが最後のフラッシュかを判断するのは不可能なので、すべてのフラッシュが同等に扱われます。「書き込み―フラッシュ」のシーケンスが複数回行われるのは比較的まれなので、これが問題になることはないはずです。

  • release: int (*release) (const char *, struct fuse_file_info *);
    この関数は、開いているファイルを解放します。release() が呼び出されるのは、開いているファイルがこれ以降参照されないとき、すなわち、すべてのファイル記述子が閉じられてすべてのメモリー・マッピングが解除されたときです。open() 呼び出しのそれぞれに対し、同じフラグと同じファイル記述子での release() 呼び出しが 1 つずつ存在することになります。ファイルが複数回オープンされるような場合には、最後の release() だけが解放処理とみなされ、そのファイルに対してはそれ以降の読み取りや書き込みは行われません。release() の戻り値は無視されます。
  • fsync: int (*fsync) (const char *, int, struct fuse_file_info *);
    この関数は、ファイルの中身を同期させます。datasync パラメーターの値がゼロでない場合は、ユーザー・データのみがフラッシュされ、メタ・データはフラッシュされません。
  • setxattr: int (*setxattr) (const char *, const char *, const char *, size_t, int);
    getxattr: int (*getxattr) (const char *, const char *, char *, size_t);
    listxattr: int (*listxattr) (const char *, char *, size_t);
    removexattr: int (*removexattr) (const char *, const char *);
    これらの関数は、それぞれ、拡張属性を設定、取得、一覧表示、削除します。

結果としてのファイルシステム

ファイルシステムは次のようになります。

afsfuse_client   <--RX[RPC]-->   afsfuse_server

afsfuse_client は、渡されたファイルシステム呼び出しを別のマシン上の afsfuse_server に転送します。afsfuse_server はクライアントから送られたすべてのリクエストを処理して、結果をクライアントに返します。afsfuse_server は必要なすべての処理を行います。RPC の場合に使用されるメカニズムは RX です。データとメタ・データのいずれについても、キャッシングは行われません。


RX RPC 層の定義

先へ進む前に、RX RPC 層を定義する必要があります。そのためには、rxgen の .xg ファイルを作成して、afsfuse_client.c と afsfuse_server.c にリンクされるプロキシーおよびスタブ・コードを記述します。リスト 2 に、afsfuse.xg ファイルを作成する方法として、以下の内容のコードを示します。

リスト 2. afsfuse.xg ファイルを作成するためのコード
#define MYMAXPATH 512
%#include <rx/rx.h>
%#include </rx_null.h >
%#define SAMPLE_SERVER_PORT 5000
%#define SAMPLE_SERVICE_PORT 0
/* i.e. user server's port */
%#define SAMPLE_SERVICE_ID 4 /* Maximum number of requests that will be handled by this
                                service simultaneously */
/* This number will also be guaranteed to execute in parallel if no services' requests
   are being processed */
%#define SAMPLE_MAX 2 /* Minimum number of requests that are guaranteed to be handled
                         immediately */
%#define SAMPLE_MIN 1 /* Index of the "null" security class in the sample service. This
                         must be 0 (there are N classes, numbered from 0. In this case,
                         N is 1) */
%#define SAMPLE_NULL 0 /********************** fuse4_file_info taken from fuse.h the
                        rxgen does not understands fuse.h  mystat taken from man 2
                        mystat these are required again rxgen does not understand the
                        struct paras and will bump.. **********************/
struct my_file_info { /** Open flags. Available in open() and release() */
                     int flags; /** File handle. May be filled in by filesystem in
                                    open(). Available in all other file operations */
                     unsigned int fh; /** In case of a write operation indicates if
                                          this was caused by a writepage */
                     int writepage;
                    };
struct mystatfs {
                    afs_uint32 f_type; /* type of filesystem (see below) */
                    afs_uint32 f_bsize; /* optimal transfer block size */
                    afs_uint32 f_blocks; /* total data blocks in file system */
                    afs_uint32 f_bfree; /* free blocks in fs */
                    afs_uint32 f_bavail; /* free blocks avail to non-superuser */
                    afs_uint32 f_files; /* total file nodes in file system */
                    afs_uint32 f_ffree; /* free file nodes in fs */
                    afs_uint32 f_fsid1; /* file system id */
                    afs_uint32 f_fsid2; /* file system id */
                    afs_uint32 f_namelen; /* maximum length of filenames */
                    afs_uint32 f_spare[6]; /* spare for later */
                };
struct mystat {
                    afs_uint32 st_dev; /* device */
                    afs_uint32 st_ino; /* inode */
                    afs_uint32 st_mode; /* protection */
                    afs_uint32 st_nlink; /* number of hard links */
                    afs_uint32 st_uid; /* user ID of owner */
                    afs_uint32 st_gid;/* group ID of owner */
                    afs_uint32 st_rdev; /* device type (if inode device) */
                    afs_uint32 st_size; /* total size, in bytes */
                    afs_uint32 st_blksize; /* blocksize for filesystem I/O */
                    afs_uint32 st_blocks; /* number of blocks allocated */
                    afs_uint32 st_atim; /* time of last access */
                    afs_uint32 st_mtim; /* time of last modification */
                    afs_uint32 st_ctim; /* time of last change */
                };
struct my_dirhandle{
                    afs_uint32 type;
                    afs_uint32 inode;
                    char name[MYMAXPATH];
                };
typedef my_dirhandle bulkmydirhandles<>;
 /*********************phase 1 functions *********************************************/
rxc_getattr(IN string mypath<MYMAXPATH>, IN int dummy) split = 1;
rxc_getdirWrapper(IN string path<MYMAXPATH>, OUT bulkmydirhandles *handles) = 2;
rxc_read(IN string path<MYMAXPATH>;, IN afs_uint32 size, IN afs_uint32 offset,
         IN struct my_file_info *fi) split = 3;
rxc_open(IN string path<MYMAXPATH>, IN int flags, OUT u_int *hd) = 4;
rxc_write(IN string path<MYMAXPATH>,IN afs_uint32 size, IN afs_uint32 offset,
          IN struct my_file_info *fi) split = 5;
rxc_chmod(IN string path<MYMAXPATH>, IN afs_uint32 mode) = 6;
rxc_chown(IN string path<MYMAXPATH>, IN afs_uint32 uid, IN afs_uint32 gid) = 7;
rxc_utime(IN string path<MYMAXPATH>, IN afs_uint32 at,IN afs_uint32 mt) = 8;
rxc_mknod(IN string path<MYMAXPATH>, afs_uint32 mode, afs_uint32 rdev) = 9 ;
rxc_mkdir(IN string path<MYMAXPATH>, IN afs_uint32 mode) = 10;
rxc_unlink(IN string path<MYMAXPATH>) = 11 ;
rxc_rmdir(IN string path<MYMAXPATH>) = 12;
rxc_rename(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 13;
rxc_truncate(IN string path<MYMAXPATH>, IN afs_uint32 size) = 14;
rxc_release(IN string path<MYMAXPATH>, IN struct my_file_info *fi) = 15;
rxc_readlink(IN string path<MYMAXPATH>, IN afs_uint32 size,OUT string
             data<MYMAXPATH>) = 16;
rxc_symlink(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 17;
rxc_link(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 18;
rxc_statfs(IN string path<MYMAXPATH>, OUT struct mystatfs *stbuf) = 19;
rxc_fsync(IN string path <MYMAXPAT>, IN int isdatasync, IN struct my_file_info
          *fi) = 20 ;
rxc_flush(IN string path <MYMAXPATH>, IN struct my_file_info *fi) = 21 ;

RX RPC 層を定義するときには、次の点に注意してください。

  • mystatfsmystat、および my_file_info を statfsstat、および fuse_file_info 構造体のラッパーとして定義しました。これらは、生成された XDR (eXternal Data Representation) コードを使用して、ネットワーク上で変換されます。(XDR では、アーキテクチャーに依存しない方法でデータをラップすることができるので、異種混在のコンピューター・システム間でデータを転送することができます。)
  • afsfuse_client が行う処理は、FUSE ファイルシステムからの呼び出しを受け取って、それを afsfuse_server に渡すだけなので、構造体 fuse_operations のすべてのメンバーについて、ほとんど同じパラメーターを持つ関数をほぼ 1 つずつ定義しました。
  • MYMAXPATH など、システムから取得する値をいくつかハードコーディングしました。ハードコーディングしたのは、単純化するという目的のためです。

クライアントとスタブ・ファイルの作成

次に、rxgen を使用して afsfuse.xg ファイルをコンパイルし、クライアントとスタブ・ファイルを作成します。afsfuse_server と afsfuse_client のソースがあるディレクトリーから、コマンド openafs-1.2.13/i386_linux24/dest/bin/rxgen afsfuse.xg を実行します。これにより、次のようなファイルが作成されます。

  • afsfuse.cs.c: afsfuse_client.c にリンクされるクライアント・スタブ・コードです。
  • afsfuse.h: FUSE RX コードのさまざまな定義を含んでいるヘッダーです。
  • afsfuse.ss.c: afsfuse_server コードにリンクされるサーバー・スタブ・コード (プロキシー・コード) です。
  • afsfuse.xdr.c: afsfuse.xg の中で定義した 3 つの構造体をマーシャリングするコードを含んでいます。

ここで、実際の作業を行うためのコードを afsfuse_client.c と afsfuse_server.c に追加します。これらのファイルで呼び出されるのは、そのほとんどが次の関数のようです。

  • Our_call_in_afs_fuse_client(): パラメーターをマーシャリングして、RPC の準備をします。RX [RPC] 経由で afsfuse_server アプリケーションを呼び出します。パラメーターをデマーシャリングします。この関数に渡された正式なパラメーターに値をコピーします。
  • Our_call_in_afs_fuse_server(): パラメーターをデマーシャリングします。ローカル・ファイルシステムまたは AFS 固有の関数を呼び出します。パラメーターをマーシャリングして、RPC の準備をします。RX RPC 呼び出しを行います。

afsfuse_client.c の呼び出しは、次のようになります。

int afsfuse_readlink(const char *path, char *buf, size_t size){
    rx_connection *local& int ret& char *buffer = malloc (512)&
    memset(buffer,0,512)& memset(buf,0,size)& local = getconnection()&
    ret = rxc_rxc_readlink(local,path,512,&buffer) // rpc call
         relconnection(local)&
         strncpy(buf,buffer,512-1)&
        //<- demarshall the parametrs
        return ret&
    }

afsfuse_server.c の呼び出しは、次のようになります。

リスト 3. afsfuse_server.c の呼び出し
int rxc_rxc_readlink( struct rx_call *call, char * path, afs_uint32 size, char**data)
    { int ret& char lbuff[512] ={0}&
    translatePath(path,lbuff)& //<- make filesystem call
     *data = malloc(512)&
     res = readlink(lbuff, *data, 512-1)&
     if(res == -1) return -errno& (*data)[res] = '\0'& return 0&
    }

同様に、ファイルシステムを機能強化するためのコードをその他の関数に追加することができます。

この場合も、makefile を作成して、コードをコンパイルする必要があります。afsfuse_client のコードをコンパイルするときには、オプションとして -D_FILE_OFFSET_BITS=64-DFUSE_USE_VERSION=22 を指定するのを忘れないでください。

リスト 4. クライアント・コードをコンパイルするための makefile の生成
SRCDIR=./ LIBRX=${SRCDIR}lib/librx.a
LIBS=${LIBRX} ${SRCDIR}lib/liblwp.a
#CC = g++
CFLAGS=-g -I. -I${SRCDIR}include -I${SRCDIR}include/fuse/ -DDEBUG ${XCFLAGS}
    -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=22
afsfuse_client: afsfuse_client.o afsfuse.xdr.o ${LIBS} bulk_io.o afsfuse.cs.o
    ${CC} ${CFLAGS} -o afsfuse_client afsfuse_client.o ${SRCDIR}lib/fuse/fuse.o
    ${SRCDIR}lib/fuse/mount.o ${SRCDIR}lib/fuse/helper.o
    ${SRCDIR}lib/fuse/fuse_mt.o bulk_io.o afsfuse.cs.o afsfuse.xdr.o ${LIBS}
afsfuse_server: afsfuse_server.o afsfuse.xdr.o afsfuse.ss.o bulk_io.o ${LIBS}
    ${CC} ${CFLAGS} -o afsfuse_server afsfuse_server.o bulk_io.o afsfuse.ss.o
    afsfuse.xdr.o ${LIBS}
#afsfuse_client.o: afsfuse.h
#afsfuse_server.o: afsfuse.h
bulk_io.o: ${CC} -c -g -I${SRCDIR}include bulk_io.c afsfuse.cs.c afsfuse.ss.c
    afsfuse.er.c afsfuse.h afsfuse.xdr.c: afsfuse.xg rxgen afsfuse.xg
afsfuse.xdr.o: afsfuse.xdr.c ${CC} -c -g -I{SRCDIR}include afsfuse.xdr.c
all: afsfuse_server afsfuse_client
clean: rm *.o rm afsfuse_client rm afsfuse_server

librx.a と liblwp.a を使用して、RX および RX 用 LWP コードにリンクする必要があることを忘れないでください。また、fuse/fuse.o、fuse/helper.o、および fuse/mount.o は、コードをリンクするために必要な FUSE のライブラリーです。


まとめ

この記事では、FUSE と OpenAFS をインストールする方法と、これらを使用して Linux において完全に機能する安定したファイルシステムを独自のユーザー空間ファイルシステムとして、作成してカスタマイズする方法を説明しました。このファイルシステムは、パッチをあてる必要や、アクティブ・カーネルを再コンパイルする必要がないので、この作業をカーネル・モジュール・プログラマーが行う必要もありません。この記事ではさらに、FUSE ファイルシステムを有効にする上で重要な 2 つの概念の詳細についても説明しました。この 2 つの概念とは、FUSE カーネル・モジュールをインストールして構成する方法と、FUSE のライブラリーと API が提供する機能を活用する方法です。


ダウンロード

内容ファイル名サイズ
AFSFuse filesystem sample codel-fuse.zip9KB

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux
ArticleID=228240
ArticleTitle=FUSE を使用して独自のファイルシステムを開発する
publish-date=02192015