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;
}
コンパイル
実行
ファイルシステムのターミナル
別のターミナル
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;
}
コンパイル
実行
ファイルシステムのターミナル
別のターミナル
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;
}
コンパイル
実行
ファイルシステムのターミナル
別のターミナル
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;
}
コンパイル
実行
ファイルシステムのターミナル
別のターミナル
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()」に対して正しくデータを返す
コンパイル
実行
ファイルシステムのターミナル
別のターミナル
a.txt の読み込みに応えることができています.