見出し画像

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

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

概要

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

前提知識

先にインストール,コンパイル,サンプル実行の方法を読んでおいてください.
特にコンパイル,実行方法

環境

Ubuntu 12.04.3 LTS, Linux 5.4.0-104-generic
FUSE 3.10.5

ver. 0

何もしないファイルシステム
(mountだけできて,lsもできない)

myfs.c

#define FUSE_USE_VERSION 31
#include <fuse.h>
static const struct fuse_operations my_operations = {
};
int main(int argc, char *argv[])
{
	int ret;
	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
	ret = fuse_main(args.argc, args.argv, &my_operations, NULL);
	fuse_opt_free_args(&args);
	return ret;
}

コンパイル

pkg-config fuse --cflags --libs
gcc -Wall myfs.c `pkg-config fuse3 --cflags --libs` -o myfs

実行

ファイルシステムのターミナル

./myfs -f /mnt/

別のターミナル

# cd /mnt
# ls
ls: cannot open directory '.': Function not implemented
# cd
# umount /mnt

lsを行い「ディレクトリ内のファイル一覧を入手」しようとするとエラーになります.
これは、readdir()関数を作っていないからです.

ver. 1

lsだけできるファイルシステム

myfs.c

#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <errno.h>
#include <string.h>
static int my_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
{
        int res = 0;
        memset(stbuf, 0, sizeof(struct stat));
        if (strcmp(path, "/") == 0) {
                stbuf->st_mode = S_IFDIR | 0755;
                stbuf->st_nlink = 2;
        } else
                res = -ENOENT;
        return res;
}
static int my_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                         off_t offset, struct fuse_file_info *fi,
                         enum fuse_readdir_flags flags)
{
        filler(buf, ".", NULL, 0, 0);
        filler(buf, "..", NULL, 0, 0);
        return 0;
}
static const struct fuse_operations my_operations = {
        .getattr        = my_getattr,
        .readdir        = my_readdir,
};
int main(int argc, char *argv[])
{
        int ret;
        struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
        ret = fuse_main(args.argc, args.argv, &my_operations, NULL);
        fuse_opt_free_args(&args);
        return ret;
}

コンパイル

pkg-config fuse --cflags --libs
gcc -Wall myfs.c `pkg-config fuse3 --cflags --libs` -o myfs

実行

ファイルシステムのターミナル

./myfs -f /mnt/

別のターミナル

# cd /mnt/
# ls -alF
total 4
drwxr-xr-x 2 root root 0 Jan 1 1970 ./
drwxr-xr-x 20 root root 4096 Mar 14 14:56 ../
# cd
# umount /mnt

lsができました.
ファイルがないです.

ver. 2.0

lsができて,ファイル一覧にファイル"a.txt"がある.
しかし,ファイル"a.txt"の情報(getattr)を取得できないファイルシステム

#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <errno.h>
#include <string.h>
static int my_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) {
	int res = 0;
	memset(stbuf, 0, sizeof(struct stat));
	if (strcmp(path, "/") == 0) {
		stbuf->st_mode = S_IFDIR | 0755;
		stbuf->st_nlink = 2;
	} else
		res = -ENOENT;
	return res;
}
static int my_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                         off_t offset, struct fuse_file_info *fi,
                         enum fuse_readdir_flags flags) {
	filler(buf, ".", NULL, 0, 0);
	filler(buf, "..", NULL, 0, 0);
	filler(buf, "a.txt", NULL, 0, 0);
	return 0;
}
static const struct fuse_operations my_operations = {
	.getattr        = my_getattr,
	.readdir        = my_readdir,
};
int main(int argc, char *argv[]) {
	int ret;
	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
	ret = fuse_main(args.argc, args.argv, &my_operations, NULL);
	fuse_opt_free_args(&args);
	return ret;
}

コンパイル

pkg-config fuse --cflags --libs
gcc -Wall myfs.c `pkg-config fuse3 --cflags --libs` -o myfs

実行

ファイルシステムのターミナル

./myfs -f /mnt/

別のターミナル

# cd /mnt/
# ls
ls: cannot access 'a.txt': No such file or directory
a.txt
# ls -l
ls: cannot access 'a.txt': No such file or directory
total 0
?????????? ? ? ? ? ? a.txt
# cd
# umount /mnt

lsを行うと、a.txt の存在を確認できるが,a.txt の情報が得られていない.

ver. 2.1

lsができて,ファイル一覧にファイル"a.txt"がある.

#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <errno.h>
#include <string.h>
char filename[] = "/a.txt";
static int my_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
{
        int res = 0;
        memset(stbuf, 0, sizeof(struct stat));
        if (strcmp(path, "/") == 0) {
                stbuf->st_mode = S_IFDIR | 0755;
                stbuf->st_nlink = 2;
        } else if (strcmp(path,filename) == 0) {
                stbuf->st_mode = S_IFREG | 0754;
                stbuf->st_nlink = 1;
                stbuf->st_size = 0;
        } else {
                res = -ENOENT;
        }
        return res;
}
static int my_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                         off_t offset, struct fuse_file_info *fi,
                         enum fuse_readdir_flags flags)
{
        filler(buf, ".", NULL, 0, 0);
        filler(buf, "..", NULL, 0, 0);
        filler(buf, filename+1, NULL, 0, 0);
        return 0;
}
static const struct fuse_operations my_operations = {
        .getattr        = my_getattr,
        .readdir        = my_readdir,
};
int main(int argc, char *argv[])
{
        int ret;
        struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
        ret = fuse_main(args.argc, args.argv, &my_operations, NULL);
        fuse_opt_free_args(&args);
        return ret;
}

コンパイル

gcc -Wall myfs.c `pkg-config fuse3 --cflags --libs` -o myfs

実行

ファイルシステムのターミナル

./myfs -f /mnt/

別のターミナル

# ls /mnt/
a.txt
# ls -l /mnt/
total 0
-rwxr-xr-- 1 root root 0 Jan 1 1970 a.txt
# cat /mnt/a.txt
cat: /mnt/a.txt: Function not implemented
# umount /mnt

lsを行うと、a.txt がある.
ただし a.txt は読めない.(read()を実装していないので)

ver. 3

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

#define FUSE_USE_VERSION 31
#include <fuse.h>
#include <errno.h>
#include <string.h>
char filename[] = "/a.txt";
char filedata[] = "Hello,World!\n";
static int my_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
{
	int res = 0;
	memset(stbuf, 0, sizeof(struct stat));
	if (strcmp(path, "/") == 0) {
		stbuf->st_mode = S_IFDIR | 0755;
		stbuf->st_nlink = 2;
	} else if (strcmp(path,filename) == 0) {
		stbuf->st_mode = S_IFREG | 0754;
		stbuf->st_nlink = 1;
		stbuf->st_size = strlen(filedata);
	} else {
		res = -ENOENT;
	}
	return res;
}
static int my_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                         off_t offset, struct fuse_file_info *fi,
                         enum fuse_readdir_flags flags)
{
	filler(buf, ".", NULL, 0, 0);
	filler(buf, "..", NULL, 0, 0);
	filler(buf, filename+1, NULL, 0, 0);
	return 0;
}
static int my_read(const char *path, char *buf, size_t size, off_t offset,
                      struct fuse_file_info *fi)
{
	size_t len;
	if(strcmp(path, filename) != 0){
		return -ENOENT;
	}
	len = strlen(filedata);
	if (offset < len) {
		if (offset + size > len){
			size = len - offset;
		}
		memcpy(buf, filedata + offset, size);
	} else {
		size = 0;
	}
	return size;
}
static const struct fuse_operations my_operations = {
	.getattr	= my_getattr,
	.readdir	= my_readdir,
	.read   	= my_read,
};
int main(int argc, char *argv[])
{
	int ret;
	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
	ret = fuse_main(args.argc, args.argv, &my_operations, NULL);
	fuse_opt_free_args(&args);
	return ret;
}

ポイントは2点.
(1) my_getattr で a.txt のサイズを正しく返す
(2) my_read() で 「a.txt  への read()」に対して正しくデータを返す

コンパイル

gcc -Wall myfs.c `pkg-config fuse3 --cflags --libs` -o myfs

実行

ファイルシステムのターミナル

./myfs -f /mnt/

別のターミナル

# ls -l /mnt/
total 0
-rwxr-xr-- 1 root root 13 Jan 1 1970 a.txt
# cat /mnt/a.txt
Hello,World!
# umount /mnt

a.txt の読み込みに応えることができています.

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