見出し画像

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

FUSEでファイルシステムを作ります.
今回はopen処理,close処理(正しくはflushとrelease)を作ります.

概要

ファイルのopen要求,close要求が出たら,デバッグメッセージを出す(だけでなにもしない)ファイルシステムを作ります.
記事1の続きです.そちらを先に読んでください.

ファイルシステムを作るとは

FUSEでファイルシステムを作ります.
「ファイルシステムを作る」とは,
ある操作(要求)が発行されたときに,何をするかを定義していくことになります.
例えば「ファイル読込要求」が出たら実行する関数(callback関数という)や,
「ファイル作成要求」が出たら実行する関数を定義して,登録していきます.
作れる(作るべき)関数はこちら

ファイルopenに反応するファイルシステム

記事1のプログラムに,以下の関数を追加します.
このopen関数は,デバッグメッセージの表示以外に何もしていません.

static int my_open(const char *path, struct fuse_file_info *fi){
  printf("%s() path=%s\n", __func__, path);
  return 0;
}

fuse_my_operations に登録します.

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

プログラム全体は記事の末尾にあります.

コンパイル&実行結果

ファイルシステムのターミナルで以下を実行します.

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

別のターミナルで以下を実行します.

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

cat hoge に対応して,ファイルシステムプログラムが以下の様に出力します

my_getattr() path=/hoge
my_open() path=/hoge
my_read() path=/hoge size=4096 offset=0

つまり,cat でファイルを表示すると,
「ファイルのopen」→「ファイルのread」の順で行われていることが分かります.

ファイルclose(flushとrelease)に反応するファイルシステム

上のプログラムに,以下の関数を追加します.
これらのrelease関数とflush関数は,デバッグメッセージの表示以外に何もしていません.

static int my_release(const char *path, struct fuse_file_info *fi){
  printf("%s() path=%s\n", __func__, path);
  return 0;
}
static int my_flush(const char *path, struct fuse_file_info *fi){
  printf("%s() path=%s\n", __func__, path);
  return 0;
}

fuse_my_operations に登録します.

static struct fuse_operations fuse_my_operations = {
  .readdir = my_readdir,
  .getattr = my_getattr,
  .read = my_read,
  .open = my_open,
  .release = my_release,
  .flush = my_flush,
};

プログラム全体は記事の末尾にあります.

open処理やclose処理で行うべきこと

実は,open処理やclose処理の関数でやらなくてはならないことはないです.

一般に,ファイルをread()する前にopen()しなくてはならないですが,
このopen()処理は何をしていて,なぜ必要なのでしょうか?
open()処理は,これからアクセスする予定のファイルをOSに登録します.
登録したら,そのファイルの情報をメモリにコピーしたりして,高速に処理できます.
close()処理は,そのファイルを使わないことをOSに知らせます.OSはそのファイルの情報をメモリから削除したりできます.
つまり,open() や close() で使用開始や使用終了と伝えると,OSが対応しやすくなりますが,これがないと原理的にファイルアクセスができない分けではありません.

一方、read()処理の関数に「プログラムに対応するデータを渡す」処理を記述すること必須です.これを記述しないとファイル読み込みができません.

プログラム全体

#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 int my_open(const char *path, struct fuse_file_info *fi){
  printf("%s() path=%s\n", __func__, path);
  return 0;
}
static int my_release(const char *path, struct fuse_file_info *fi){
  printf("%s() path=%s\n", __func__, path);
  return 0;
}
static int my_flush(const char *path, struct fuse_file_info *fi){
  printf("%s() path=%s\n", __func__, path);
  return 0;
}


static struct fuse_operations fuse_my_operations = {
  .readdir = my_readdir,
  .getattr = my_getattr,
  .read = my_read,
  .open = my_open,
  .release = my_release,
  .flush = my_flush,
};

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

コンパイル&実行

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

別のターミナルで

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

cat hogeを行うと,ファイルシステム側には

my_getattr() path=/hoge
my_open() path=/hoge
my_read() path=/hoge size=4096 offset=0
my_flush() path=/hoge
my_release() path=/hoge

と,表示されます.
つまり,cat hogeを行うと,
getattr, open, read, flush, releaseの順で行われている事が分かります.

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