見出し画像

FUSE 2でファイルシステムを作る 1

FUSE 2 でファイルシステムを作る方法について説明します.
実用的なファイルシステムよりは学習用,検証用になります.

概要

FUSE 2.9で、ファイルシステムを自作す方法を説明します.
読み込み可能なディレクトリ、読み込み可能なファイルを作ります.

環境

Debian 5.10.0-11
FUSE 2.9.9-5

インストール

apt -y install fuse libfuse-dev

ver. 0

何もしないファイルシステム
myfs.c

#define FUSE_USE_VERSION 29

#include <fuse.h>

static struct fuse_operations fuse_my_operations = {
};

int main(int argc, char *argv[])
{
  return fuse_main(argc, argv, &fuse_my_operations, NULL);
}

コンパイル

pkg-config fuse --cflags --libs
gcc -Wall `pkg-config fuse --cflags --libs` mayfs.c -o hello -lfuse

もし

myfs.c:3:10: fatal error: fuse.h: No such file or directory
3 | #include <fuse.h>
| ^~~~~~~~

となってしまったら,

apt -y install libfuse-dev

を忘れています.

もし

/usr/bin/ld: /tmp/ccYWg90J.o: in function `main':
mayfs.c:(.text+0x2d0): undefined reference to `fuse_main_real'
collect2: error: ld returned 1 exit status

となってしまったら,

gcc に -lfuse をつける

のを忘れています.

実行

rootで実行します

mkdir /mnt/a
./myfs -f /mnt/a

これでマウントされています.以下を別のターミナルでやってみましょう.
太字がユーザ入力

# mount | grep myfs
myfs on /mnt/a type fuse.myfs (rw,nosuid,nodev,relatime,user_id=0,group_id=0)
# cd /mnt/a
# ls
ls: cannot open directory '.': Function not implemented

マウントできるが,lsすらできない.

ver. 1

lsができるファイルシステム.

lsをできるようにするには,readdir() という「ディレクトリにあるファイル一覧を問い合わせる関数」などを作る必要があります.
そして,それら関数をfuseに登録します.
以下のreaddir()は . と .. のみを登録して返します.

#define FUSE_USE_VERSION 29

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fuse.h>

static int my_getattr(const char *path, struct stat *stbuf) {
  printf("%s() path=%s\n", __func__, path);

  if (strcmp(path, "/") == 0) {
    stbuf->st_mode = S_IFDIR;
    return 0;
  }

  return -ENOENT;
}

static int my_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
  printf("%s() path=%s offset=%ld\n", __func__, path, offset);

  filler(buf, ".", NULL, 0);
  filler(buf, "..", NULL, 0);

  return 0;
}

static struct fuse_operations fuse_my_operations = {
  .readdir = my_readdir,
  .getattr = my_getattr,
};

int main(int argc, char *argv[])
{
  return fuse_main(argc, argv, &fuse_my_operations, NULL);
}

readdir の関数と,getattr の関数のみを登録.
それらが呼び出されば場合は,printf() でdebugメッセージが表示される様になっている.
readdir は . と .. のみを返すようになっている.
getattr は,"/" に対する返答として,"ディレクトリである"ことだけ登録して,後はなにも登録していない.

コンパイル & 実行

pkg-config fuse --cflags --libs
gcc -Wall `pkg-config fuse --cflags --libs` mayfs.c -o hello -lfuse
./myfs -f /mnt/a

↑全く同一

別ターミナルで実行

# cd /mnt/a
# ls
# ls -l
total 0
# ls -al
total 4
d--------- 0 root root 0 Jan 1 1970 .
drwxr-xr-x 3 root root 4096 Mar 13 14:15 ..

lsの結果,
. と .. があることが分かる.
. (カレントディレクトリ)は,一切情報を登録していないので,タイムスタンプは1970/01/01の00時00分になっている.

ver. 2

lsができるファイルシステム.その2

ls をすると,ファイルが1個だけ得られるファイルシステムを作ります.
readdir() の結果は . と .. と hoge です.

#define FUSE_USE_VERSION 29

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fuse.h>

static int my_getattr(const char *path, struct stat *stbuf) {
  printf("%s() path=%s\n", __func__, path);
  memset(stbuf, 0, sizeof(struct stat));

  if (strcmp(path, "/") == 0) {
    stbuf->st_mode = S_IFDIR;
    return 0;
  } else if(strcmp(path, "/hoge") == 0) {
    stbuf->st_mode = S_IFREG;
    return 0;
  }

  return -ENOENT;
}

static int my_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
  printf("%s() path=%s offset=%ld\n", __func__, path, offset);

  filler(buf, ".", NULL, 0);
  filler(buf, "..", NULL, 0);
  filler(buf, "hoge", NULL, 0);

  return 0;
}

static struct fuse_operations fuse_my_operations = {
  .readdir = my_readdir,
  .getattr = my_getattr,
};

int main(int argc, char *argv[])
{
  return fuse_main(argc, argv, &fuse_my_operations, NULL);
}

コンパイル & 実行

pkg-config fuse --cflags --libs
gcc -Wall `pkg-config fuse --cflags --libs` mayfs.c -o hello -lfuse
./myfs -f /mnt/a

↑全く同一

別ターミナルで実行

# cd /mnt/a
# ls
hoge
# ls -al
total 4
d--------- 0 root root 0 Jan 1 1970 .
drwxr-xr-x 3 root root 4096 Mar 13 14:15 ..
---------- 0 root root 0 Jan 1 1970 hoge
# cat hoge
cat: hoge: Function not implemented

lsの結果,
. と .. と hoge があることが分かる.
ファイルパーミッションは 0 で,タイムスタンプも 0 (Unixタイムの0).
catコマンドでファイルの中身を読み込もうとしてもできない.
読み込むためには,read() 関数を実装する必要があります.

注意:getattr()の引数 path は "/hoge" で,
readdir()の中のfiller()に与えるのは"hoge"です.

ver. 2.1

lsができるファイルシステム.その3
ファイルのパーミション情報のみ提供してみる。

以下の修正を行います.

16c16
<     stbuf->st_mode = S_IFREG;
---
>     stbuf->st_mode = S_IFREG | 0754;

つまり

static int my_getattr(const char *path, struct stat *stbuf) {
  printf("%s() path=%s\n", __func__, path);
  memset(stbuf, 0, sizeof(struct stat));

  if (strcmp(path, "/") == 0) {
    stbuf->st_mode = S_IFDIR;
    return 0;
  } else if(strcmp(path, "/hoge") == 0) {
    stbuf->st_mode = S_IFREG | 0754; /* ←ここだけ修正 */
    return 0;
  }

  return -ENOENT;
}

実行

# ls -al
total 4
d--------- 0 root root 0 Jan 1 1970 .
drwxr-xr-x 3 root root 4096 Mar 13 14:15 ..
-rwxr-xr-- 0 root root 0 Jan 1 1970 hoge

hogeというファイルのパーミションが -rwxr-xr-- になっている.

ver. 3

ファイルを読み込めるファイルシステム.

hoge という名のファイルがあり,
そのファイルを読み込めるファイルシステムを作ります.

#define FUSE_USE_VERSION 29

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fuse.h>

char txt[] = "hello, fuse!\n";
char filename[] = "/hoge";

static int my_getattr(const char *path, struct stat *stbuf) {
  printf("%s() path=%s\n", __func__, path);
  memset(stbuf, 0, sizeof(struct stat));

  if (strcmp(path, "/") == 0) {
    stbuf->st_mode = S_IFDIR;
    return 0;
  } else if(strcmp(path, filename) == 0) {
    stbuf->st_mode = S_IFREG | 0754;
    stbuf->st_size = strlen(txt);
    return 0;
  }

  return -ENOENT;
}

static int my_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
  printf("%s() path=%s offset=%ld\n", __func__, path, offset);

  filler(buf, ".", NULL, 0);
  filler(buf, "..", NULL, 0);
  filler(buf, filename+1, NULL, 0);

  return 0;
}

static int my_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
  printf("%s() path=%s size=%ld offset=%ld\n", __func__, path, size, offset);
  size_t len;

  if (strcmp(path, filename) != 0) {
    return -ENOENT;
  }

  len = strlen(txt);

  if (offset >= len) {
    return 0;
  }

  if (offset + size > len) {
    size = (len - offset);
  }

  memcpy(buf, txt + offset, size);

  return size;
}
static struct fuse_operations fuse_my_operations = {
  .readdir = my_readdir,
  .getattr = my_getattr,
  .read = my_read,
};

int main(int argc, char *argv[])
{
  return fuse_main(argc, argv, &fuse_my_operations, NULL);
}

コンパイル & 実行

pkg-config fuse --cflags --libs
gcc -Wall `pkg-config fuse --cflags --libs` mayfs.c -o hello -lfuse
./myfs -f /mnt/a

↑全く同一

別ターミナルで実行

# cd /mnt/a
# ls
hoge
# cat hoge
hello, fuse!

cat hoge を実行すると,my_read() が呼ばれて,そこで設定された内容がプログラム(今回は cat)に返される.

リンク

FUSE 2でファイルシステムを作る 2

FUSE 3.xでファイルシステムを作る 1


この記事が気に入ったらサポートをしてみませんか?