無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

fuse


ユーザプロセスはファイルをオープンする毎に、マウントされたファイルシステムのinodeのコールバック関数がfileにバインドされ、そのコールバック関数をコールする事で、ユーザプロセスから掛かるファイルシステムを参照できるわけです。このinodeのコールバック関数は、それぞれのカーネル下で動作するファイルシステム処理の実装としてインプリメントされています。

fsueはFilesystem in Userspaceと言うことで、このカーネル下でのインプリメントをユーザプロセスとして実現すると言う物です。

下記はsshによる応用例でsshfsと言うコマンドでのサンプルです。127.0.0.1:/home/kitamuraのネットワークのリモートのファイルを/tmp/hoge0にマウントしています。
[root@localhost ~]# ls /home/kitamura/
Desktop    Downloads  Pictures  Templates  etc   lkm       script.php  test
Documents  Mail       Public    Videos     hoge  rpmbuild  setuid
[root@localhost ~]# ls /tmp/hoge0

[root@localhost ~]# sshfs 127.0.0.1:/home/kitamura /tmp/hoge0
root@127.0.0.1's password:
[root@localhost ~]# ls /tmp/hoge0
Desktop    Downloads  Pictures  Templates  etc   lkm       script.php  test
Documents  Mail       Public    Videos     hoge  rpmbuild  setuid
nfsと何が違うのか?ということですが、fuseモジュールがインストールされていれば、sshfsにかかるモジュールは必要ありません。sshfsのマウント先のファイル操作にかかる処理は、ユーザプロセスとして普通のコマンドとして実現しているのです。従ってこの処理を自由に書き換える事で、通常のアプリを作成するようにファイルシステムを実装出来るという事です。

sshfsをstraceしてみると、/dev/fuseをオープンし、デバイス名:127.0.0.1:/home/kitamura、マウントポイント:/tmp/hoge0、ファイルシステムタイプ:fuse.sshfs、肝となるvoid __user *, dataに、/dev/fuseをオープンしたfile id=3を引数として、mountシステムコールをコールしています。

要はファイル操作のfile operationにこの/dev/fuseを結びつけます。/dev/fuseのfile operationの読み書きのコールバックは、ユーザプロセスから渡されたユーザ空間のメモリに対して、読み書きする通常の操作で、従ってこのfileとやり取りすることで、ファイルシステムの操作にかかる処理をインプリメントする事ができるわけです。
SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
               char __user *, type, unsigned long, flags, void __user *, data)

[root@localhost ~]# strace sshfs 127.0.0.1:/home/kitamura /tmp/hoge0
  :
open("/dev/fuse", O_RDWR|O_LARGEFILE)   = 3
getgid32()                              = 0
getuid32()                              = 0
mount("127.0.0.1:/home/kitamura", "/tmp/hoge0", "fuse.sshfs", MS_NOSUID|MS_NODEV,  "fd=3,rootmode=40000,user_id=0,gr"...) = 0
geteuid32()                             = 0
lstat64("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=24576, ...}) = 0
lstat64("/etc/mtab", {st_mode=S_IFLNK|0777, st_size=12, ...}) = 0
mountの引数fuse.sshfsの"."まで文字列(fuse)が、ファイルシステムタイプとなります。そのファイルシステムからfile_system_type(この場合fuse_fs_type)を取得し、fuse_fs_type->mointをコールする事で、マウント処理を行います。
static struct file_system_type fuse_fs_type = {
       .owner          = THIS_MODULE,
       .name           = "fuse",
       .fs_flags       = FS_HAS_SUBTYPE,
       .mount          = fuse_mount,
       .kill_sb        = fuse_kill_sb_anon,
};

const struct file_operations fuse_dev_operations = {
       .owner          = THIS_MODULE,
       .llseek         = no_llseek,
       .read           = do_sync_read,
       .aio_read       = fuse_dev_read,
       .splice_read    = fuse_dev_splice_read,
       .write          = do_sync_write,
       .aio_write      = fuse_dev_write,
       .splice_write   = fuse_dev_splice_write,
       .poll           = fuse_dev_poll,
       .release        = fuse_dev_release,
       .fasync         = fuse_dev_fasync,
};
fuse_mount()はスーパブロック処理のコールバック関数fuse_fill_superを引数にして、mount_nodev()をコールします。raw_dataには/dev/fuseのfile idが設定されています。なお、mount_nodev()は、デバイスブロックを有しないブロックデバイスに対してコールされます。ブロックデバイス有する場合、mount_dev()がコールされ、ブロックサイズ等実際のデバイスから取得されます。CONFIG_BLKでコンパイルすると、/dev/fuseblkというのが作成され、/dev/sdb等のブロックデバイスに対しても対応できるようになっています。
static struct dentry *fuse_mount(struct file_system_type *fs_type,
                      int flags, const char *dev_name,
                      void *raw_data)
{
       return mount_nodev(fs_type, flags, raw_data, fuse_fill_super);
}

struct dentry *mount_nodev(struct file_system_type *fs_type,
       int flags, void *data,
       int (*fill_super)(struct super_block *, void *, int))
{
       int error;
       struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);

       if (IS_ERR(s))
               return ERR_CAST(s);

       s->s_flags = flags;

       error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
       if (error) {
               deactivate_locked_super(s);
               return ERR_PTR(error);
       }
       s->s_flags |= MS_ACTIVE;
       return dget(s->s_root);
}
EXPORT_SYMBOL(mount_nodev);
fuse_fill_super()はstruct super_block *sbに所定の設定をする事です。エッセンスと言うべき事は、file = fget(d.fd);で/dev/fuseのfile idとsbを結びつけると言う事です。fuse_dev_operationsのread/writeはdo_sync_read/do_sync_writeで、通常のファイル読み書きとなるわけで、ユーザプロセスとしては、ファイルシステム内操作としての結果を、このfile idに対して読み書きすればいいわけです。
static int fuse_fill_super(struct super_block *sb, void *data, int silent)
{
       struct fuse_conn *fc;
       struct inode *root;
       struct fuse_mount_data d;
       struct file *file;
       struct dentry *root_dentry;
       struct fuse_req *init_req;
       int err;
       int is_bdev = sb->s_bdev != NULL;

       err = -EINVAL;
       if (sb->s_flags & MS_MANDLOCK)
               goto err;

       sb->s_flags &= ~MS_NOSEC;

       if (!parse_fuse_opt((char *) data, &d, is_bdev))
               goto err;

       if (is_bdev) {
#ifdef CONFIG_BLOCK
               err = -EINVAL;
               if (!sb_set_blocksize(sb, d.blksize))
                       goto err;
#endif
       } else {
               sb->s_blocksize = PAGE_CACHE_SIZE;
               sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
       }
       sb->s_magic = FUSE_SUPER_MAGIC;
       sb->s_op = &fuse_super_operations;
       sb->s_maxbytes = MAX_LFS_FILESIZE;
       sb->s_export_op = &fuse_export_operations;

       file = fget(d.fd);
       err = -EINVAL;
       if (!file)
               goto err;

       if (file->f_op != &fuse_dev_operations)
               goto err_fput;

       fc = kmalloc(sizeof(*fc), GFP_KERNEL);
       err = -ENOMEM;
       if (!fc)
               goto err_fput;

       fuse_conn_init(fc);

       fc->dev = sb->s_dev;
       fc->sb = sb;
       err = fuse_bdi_init(fc, sb);
       if (err)
               goto err_put_conn;

       sb->s_bdi = &fc->bdi;

       if (sb->s_flags & MS_POSIXACL)
               fc->dont_mask = 1;
       sb->s_flags |= MS_POSIXACL;

       fc->release = fuse_free_conn;
       fc->flags = d.flags;
       fc->user_id = d.user_id;
       fc->group_id = d.group_id;
       fc->max_read = max_t(unsigned, 4096, d.max_read);

       sb->s_fs_info = fc;

       err = -ENOMEM;
       root = fuse_get_root_inode(sb, d.rootmode);
       if (!root)
               goto err_put_conn;

       root_dentry = d_alloc_root(root);
       if (!root_dentry) {
               iput(root);
               goto err_put_conn;
       }
       sb->s_d_op = &fuse_dentry_operations;

       init_req = fuse_request_alloc();
       if (!init_req)
               goto err_put_root;

       if (is_bdev) {
               fc->destroy_req = fuse_request_alloc();
               if (!fc->destroy_req)
                       goto err_free_init_req;
       }

       mutex_lock(&fuse_mutex);
       err = -EINVAL;
       if (file->private_data)
               goto err_unlock;

       err = fuse_ctl_add_conn(fc);
       if (err)
               goto err_unlock;

       list_add_tail(&fc->entry, &fuse_conn_list);
       sb->s_root = root_dentry;
       fc->connected = 1;
       file->private_data = fuse_conn_get(fc);
       mutex_unlock(&fuse_mutex);
       fput(file);

       fuse_send_init(fc, init_req);

       return 0;

err_unlock:
       mutex_unlock(&fuse_mutex);
err_free_init_req:
       fuse_request_free(init_req);
err_put_root:
       dput(root_dentry);
err_put_conn:
       fuse_bdi_destroy(fc);
       fuse_conn_put(fc);
err_fput:
       fput(file);
err:
       return err;
}

補足

linux.2.6.0のfile_system_type
struct file_system_type {
       const char *name;
       int fs_flags;
       struct super_block *(*get_sb) (struct file_system_type *, int,
                                      const char *, void *);
       void (*kill_sb) (struct super_block *);
       struct module *owner;
       struct file_system_type * next;
       struct list_head fs_supers;
};
linux.3.3.8のfile_system_type
struct file_system_type {
       const char *name;
       int fs_flags;
       struct dentry *(*mount) (struct file_system_type *, int,
                      const char *, void *);
       void (*kill_sb) (struct super_block *);
       struct module *owner;
       struct file_system_type * next;
       struct hlist_head fs_supers;

       struct lock_class_key s_lock_key;
       struct lock_class_key s_umount_key;
       struct lock_class_key s_vfs_rename_key;

       struct lock_class_key i_lock_key;
       struct lock_class_key i_mutex_key;
       struct lock_class_key i_mutex_dir_key;
};
struct dentry *(*mount)()が追加されています。ファイルシステム毎に独自のマウント処理を実装できるという事で、この機能によりfuseが実現可能となったわけです。

fuseでの処理は、通常ファイルの読み書きに過ぎません。従って、その権限はファイル処理の権限に順ずるということです。たぶん。(一般ユーザでsshfsしたファイルを、rootで参照できませんでした。)

最終更新 2013/07/03 18:33:25 - north
(2013/07/03 18:11:06 作成)