Advent Calendar 2019: advfs
Day 3: ファイルの定義
今回から数日間メモリ上にデータを配置する ramfs を実装していきます。 ramfs は FUSE でファイルシステムを作成する上で最も基本的な構造だと思われるので,今回はまず ramfs を実装します。最終的なゴールについては,Twitter で dedup (重複排除)するファイルシステムであったり,投機的プリフェッチを行うファイルシステムなど,最終的なゴールアイディアをいくつか頂いているので,今週末くらいまでにはなにかしらの方針を決めようかと思います。
1ファイル /test
の最上位ディレクトリへの追加
まず,ファイルの読み書きを実装する前に,ファイルシステムの最上位ディレクトリに1つの通常ファイル /test
を追加します。このファイルの属性情報や実体として以下の変数をグローバル変数として定義します。
static const char *ramfs_file_path = "/test";
static uint8_t *ramfs_file_buf = NULL;
static size_t ramfs_file_size = 0;
static size_t ramfs_file_max_size = 1 * 1024 * 1024;
static time_t ramfs_file_timestamp = 1575370225;
ramfs_file_path
はファイルパスを指定します。 ramfs_file_buf
はこのファイルの実体のメモリバッファです。 ramfs_file_size
,ramfs_file_max_size
はファイルの中身のサイズおよびバッファの最大サイズを定義します。 ramfs_file_timestamp
はこのファイルの作成・アクセス・変更時刻の Unix タイムスタンプであり,今回は Tue Dec 3 19:50:25 JST 2019
を表す 1575370225
を使います。
このファイルをファイルシステムの最上位ディレクトリに追加するために, advfs_readdir()
関数を以下のように書き換えます。
if ( strcmp(path, "/") == 0 ) {
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
filler(buf, ramfs_file_path + 1, NULL, 0);
return 0;
}
return -ENOENT;
昨日からの追加は
filler(buf, ramfs_file_path + 1, NULL, 0);
の行です。この行で test
をディレクトリに追加します。
また, advfs_getattr()
関数に以下の else if
のブロック内に定義したように /test
のファイル属性を取得するロジックを実装します。 st_mode
はファイルタイプ(S_IFREG
は通常ファイル)とパーミッション(0666
)を定義します。また,st_nlink
は通常ファイルの場合は 1
とします。 st_size
はファイル名の長さを代入します。 st_atime
, st_mtime
, st_ctime
, st_birthtime
(macOSのみ) はそれぞれファイルの最終アクセス時刻,更新時刻,作成時刻,macOSにおける作成時刻を表します。st_uid
, st_gid
はファイルの所有者(ユーザ・グループ)を表します。最後に,st_size
はファイルサイズ,st_blksize
はブロックサイズ,st_blocks
はブロック数を表します。
これに加え, /
から参照されるファイル・ディレクトリが .
, ..
, test
の3つになったため,この属性を表す st_nlink
も 2
から 3
に変更しています。
if ( strcmp(path, "/") == 0 ) {
stbuf->st_mode = S_IFDIR | 0777;
stbuf->st_nlink = 3;
stbuf->st_uid = ctx->uid;
stbuf->st_gid = ctx->gid;
status = 0;
} else if ( strcmp(path, ramfs_file_path) == 0 ) {
stbuf->st_mode = S_IFREG | 0666;
stbuf->st_nlink = 1;
stbuf->st_atime = (time_t)ramfs_file_timestamp;
stbuf->st_mtime = (time_t)ramfs_file_timestamp;
stbuf->st_ctime = (time_t)ramfs_file_timestamp;
#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
stbuf->st_birthtime = (time_t)ramfs_file_timestamp;
#endif
stbuf->st_uid = ctx->uid;
stbuf->st_gid = ctx->gid;
stbuf->st_rdev = 0;
stbuf->st_size = ramfs_file_size;
stbuf->st_blksize = 4096;
stbuf->st_blocks = (ramfs_file_size + 4095) / 4096;
status = 0;
} else {
status = -ENOENT;
}
また,ファイルシステムの状態を返す advfs_statfs()
関数の中身を以下のように書き換えます。
ssize_t used;
memset(buf, 0, sizeof(struct statvfs));
used = (ramfs_file_size + 4096 - 1) / 4096;
buf->f_bsize = 4096;
buf->f_frsize = 4096;
buf->f_blocks = 1024; /* in f_frsize unit */
buf->f_bfree = 1024 - used;
buf->f_bavail = 1024 - used;
buf->f_files = 1000;
buf->f_ffree = 100 - 1;
buf->f_favail = 100 - 1;
buf->f_fsid = 0;
buf->f_flag = 0;
buf->f_namemax = 255;
return 0;
/test
で利用済みのブロックを used
変数で計算し,空き領域から引いています。また,利用可能 inode の数を1減らしています(現状は適当に定義しているのであまり意味はありませんが)。
この時点での具体的な実装は src/main.c を参照してください。
実行
昨日同様に実装したプログラムをビルド・実行するために
$ docker-compose build && docker-compose up -d
を実行し,
$ docker exec -it advfs_advfs_1 bash
と実行することで, /mnt
に実装したファイルシステムをマウントしたコンテナで bash
を実行できます。
/mnt
に対して ls
コマンドを実行すると,以下の通り, .
および ..
ディレクトリと今回追加した /test
ファイルが追加されていることがわかります。
root@6561c12bfb9c:/usr/src# ls -al /mnt/
total 4
drwxrwxrwx 2 root root 0 Jan 1 1970 .
drwxr-xr-x 1 root root 4096 Dec 3 11:30 ..
-rw-rw-rw- 1 root root 0 Dec 3 10:50 test
今日のまとめと明日の予定
今日は昨日使った最低限の3 APIを使って,最上位ディレクトリに test
というファイルを追加しました。明日はこのファイルに対して読み書き(open
, read
, write
)を実装します。