MyUtils

View on GitHub

ファイルシステムを自作する(FUSE)

FUSE(Filesystem in Userspace)とは、ユーザ空間内で独自の仮想ファイルシステムを作ることができる仕組み root権限が不要であることも特徴である。

通常のファイルシステムとの違いについて

ext4FUSEの動作の違い

ext4にアクセスする場合

  1. ext4でマウントされたディレクトリ内でls,catなどで実行されるファイルに関するシステムコールは、 仮想ファイルシステム(Virtual File System,VFS)レイヤで実行される。
  2. VFSは各ファイルシステムのモジュールを仮想化するもので、マウントしているファイルシステムごとのハンドラを呼び出す。なので、ext4カーネルモジュールが呼び出される
  3. ext4カーネルモジュールはファイルシステム毎のハンドラを実行する。
  4. デバイスドライバを通じてストレージデバイスにアクセスする

FUSEにアクセスする場合

  1. ext4同様にFUSEでマウントされたディレクトリ内でls,catなどで実行されるファイルに関するシステムコールは、 仮想ファイルシステム(Virtual File System,VFS)レイヤで実行される。
  2. VFSは各ファイルシステムのモジュールを仮想化するもので、 マウントしているFUSEカーネルモジュールごとのハンドラを呼び出す。
  3. FUSEカーネルモジュールストレージデバイスにアクセスする代わりに、/dev/FUSEというデバイスファイルを通じてユーザスペースにあるFUSEデーモンとやりとりをおこなう。 (カーネルをクライアント,FUSEデーモンをサーバとするクライアント/サーバモデル)
  4. FUSEデーモンが実行結果をカーネルに返す。FUSEデーモンはユーザスペースプログラムであるため、 ユーザ毎に実装できる。

FUSEプロトコルを実装する

FUSEプロトコルはLinuxカーネル内の/include/uapi/linux/fuse.hで提供されている。 FUSE_OPEN,FUSE_READといったリクエストが定義されている。 (詳しくはman 4 fuseで) libfuseを使いFUSEプロトコルをラップすることで、ファイルシステムを作成できる。

以下の構造体の関数ポインタを置き換えることで、自作の関数を作成できる。

struct fuse_operations {
 /** Get file attributes.
  */
 int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi);

 /** Read the target of a symbolic link
  */
 int (*readlink) (const char *, char *, size_t);

 /** Create a file node
  */
 int (*mknod) (const char *, mode_t, dev_t);

 /** Create a directory
  * */
 int (*mkdir) (const char *, mode_t);

 /** Remove a file */
 int (*unlink) (const char *);

 /** Remove a directory */
 int (*rmdir) (const char *);

 /** Create a symbolic link */
 int (*symlink) (const char *, const char *);

 /** Rename a file
  */
 int (*rename) (const char *, const char *, unsigned int flags);

 /** Create a hard link to a file */
 int (*link) (const char *, const char *);

 /** Change the permission bits of a file
  */
 int (*chmod) (const char *, mode_t, struct fuse_file_info *fi);

 /** Change the owner and group of a file
  */
 int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi);

 /** Change the size of a file
  */
 int (*truncate) (const char *, off_t, struct fuse_file_info *fi);

 /** Open a file
  */
 int (*open) (const char *, struct fuse_file_info *);

 /** Read data from an open file
  */
 int (*read) (const char *, char *, size_t, off_t,
       struct fuse_file_info *);

 /** Write data to an open file
  */
 int (*write) (const char *, const char *, size_t, off_t,
        struct fuse_file_info *);

 /** Get file system statistics
  */
 int (*statfs) (const char *, struct statvfs *);

 /** Possibly flush cached data
  */
 int (*flush) (const char *, struct fuse_file_info *);

 /** Release an open file
  */
 int (*release) (const char *, struct fuse_file_info *);

 /** Synchronize file content*/
 int (*fsync) (const char *, int, struct fuse_file_info *);

 /** Set extended attributes */
 int (*setxattr) (const char *, const char *, const char *, size_t, int);

 /** Get extended attributes */
 int (*getxattr) (const char *, const char *, char *, size_t);

 /** List extended attributes */
 int (*listxattr) (const char *, char *, size_t);

 /** Remove extended attributes */
 int (*removexattr) (const char *, const char *);

 /** Open directory */
 int (*opendir) (const char *, struct fuse_file_info *);

 /** Read directory  */
 int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
   struct fuse_file_info *, enum fuse_readdir_flags);

 /** Release directory  */
 int (*releasedir) (const char *, struct fuse_file_info *);

 /** Synchronize directory contents  */
 int (*fsyncdir) (const char *, int, struct fuse_file_info *);

 /**  */
 void *(*init) (struct fuse_conn_info *conn,
         struct fuse_config *cfg);

 /** Clean up filesystem  */
 void (*destroy) (void *private_data);

 /** Check file access permissions*/
 int (*access) (const char *, int);

 /** Create and open a file  */
 int (*create) (const char *, mode_t, struct fuse_file_info *);

 /** Perform POSIX file locking operation  */
 int (*lock) (const char *, struct fuse_file_info *, int cmd,
       struct flock *);

 /** Change the access and modification times of a file with
  * nanosecond resolution
  */
  int (*utimens) (const char *, const struct timespec tv[2],
    struct fuse_file_info *fi);

 /**
  * Map block index within file to block index within device
  *
  * Note: This makes sense only for block device backed filesystems
  * mounted with the 'blkdev' option
  */
 int (*bmap) (const char *, size_t blocksize, uint64_t *idx);

#if FUSE_USE_VERSION < 35
 int (*ioctl) (const char *, int cmd, void *arg,
        struct fuse_file_info *, unsigned int flags, void *data);
#else
 /** Ioctl  */
 int (*ioctl) (const char *, unsigned int cmd, void *arg,
        struct fuse_file_info *, unsigned int flags, void *data);
#endif

 /** Poll for IO readiness events*/
 int (*poll) (const char *, struct fuse_file_info *,
       struct fuse_pollhandle *ph, unsigned *reventsp);

 /** Write contents of buffer to an open file  */
 int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off,
     struct fuse_file_info *);

 /** Store data from an open file in a buffer  */
 int (*read_buf) (const char *, struct fuse_bufvec **bufp,
    size_t size, off_t off, struct fuse_file_info *);
 /** Perform BSD file locking operation  */
 int (*flock) (const char *, struct fuse_file_info *, int op);

 /** Allocates space for an open file*/
 int (*fallocate) (const char *, int, off_t, off_t,
     struct fuse_file_info *);

 /** Copy a range of data from one file to another*/
 ssize_t (*copy_file_range) (const char *path_in,
        struct fuse_file_info *fi_in,
        off_t offset_in, const char *path_out,
        struct fuse_file_info *fi_out,
        off_t offset_out, size_t size, int flags);

 /** Find next data or hole after the specified offset*/
 off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *);
};