Advent Calendar 2019: advfs
Day 6: ファイルの作成
今日はディレクトリツリーに対し,ファイルの作成を実装します。ファイルを作成する create
API に加え,今回はファイルを作成するために touch
コマンドを使うため,タイムスタンプを更新する utimens
APIを実装します。
エントリ作成オプションの追加
昨日実装した advfs_path2ent()
関数で,ファイル作成オプションを追加した場合にエントリーを新たに作成するようにします。ただし,2段以上のディレクトリ・ファイルの作成には対応していません。実装は以下の通り,再帰的にディレクトリを辿り,当該エントリがあれば当該エントリを返します。パスの最後で指定されたエントリが存在せずに create
フラグが有効なときはその名前のエントリーを作成し,返すようにします。
static advfs_entry_t *
_path2ent_rec(advfs_t *advfs, advfs_entry_t *cur, const char *path, int create)
{
advfs_entry_t *e;
char name[ADVFS_NAME_MAX + 1];
char *s;
size_t len;
int i;
if ( cur->type != ADVFS_DIR ) {
return NULL;
}
/* Remove the head '/'s */
if ( '/' != *path ) {
return NULL;
}
while ( '/' == *path ) {
path++;
}
/* Get the file/directory entry name */
s = index(path, '/');
if ( NULL == s ) {
len = strlen(path);
} else {
len = s - path;
}
if ( len > ADVFS_NAME_MAX ) {
/* Invalid path name */
return NULL;
} else if ( len == 0 ) {
return cur;
}
memcpy(name, path, len);
name[len] = '\0';
path += len;
/* Resolve the entry */
for ( i = 0; i < cur->u.dir.nent; i++ ) {
e = &advfs->entries[cur->u.dir.children[i]];
if ( 0 == strcmp(name, e->name) ) {
/* Found */
if ( e->type == ADVFS_DIR ) {
return _path2ent_rec(advfs, e, path, create);
} else if ( '\0' == *path ) {
return e;
} else {
/* Invalid file type */
return NULL;
}
}
}
/* Not found */
if ( '\0' == *path && create ) {
/* Create */
if ( cur->u.dir.nent >= ADVFS_MAX_CHILDREN ) {
return NULL;
}
/* Search unused inode */
for ( i = 0; i < ADVFS_NUM_ENTRIES; i++ ) {
if ( advfs->entries[i].type == ADVFS_UNUSED ) {
break;
}
}
if ( i >= ADVFS_NUM_ENTRIES ) {
/* Not found */
return NULL;
}
cur->u.dir.children[cur->u.dir.nent] = i;
e = &advfs->entries[i];
memset(e, 0, sizeof(advfs_entry_t));
memcpy(e->name, name, len + 1);
cur->u.dir.nent++;
return e;
}
return NULL;
}
advfs_entry_t *
advfs_path2ent(advfs_t *advfs, const char *path, int create)
{
advfs_entry_t *e;
e = &advfs->entries[advfs->root];
return _path2ent_rec(advfs, e, path, create);
}
create APIの実装
ファイルの作成は create
APIを通じて行われます。第1引数にファイルパス,第2引数にモード(パーミッション),第3引数にはファイル情報を保持する struct fuse_file_info
構造体へのポインタが渡されます。まず, advfs_path2ent()
関数を create
オプションなしで呼び出すことで,ファイルの存在を確認します。ファイルが存在した場合は -EEXIST
を返します。ファイルが存在しない場合は advfs_path2ent()
関数を create
オプションを付け呼び出し,エントリが作成できれば,ファイル属性を設定します。実装は以下の通りです。
int
advfs_create(const char *path, mode_t mode, struct fuse_file_info *fi)
{
struct fuse_context *ctx;
advfs_t *advfs;
advfs_entry_t *e;
struct timeval tv;
/* Get the context */
ctx = fuse_get_context();
advfs = ctx->private_data;
gettimeofday(&tv, NULL);
e = advfs_path2ent(advfs, path, 0);
if ( NULL != e ) {
/* Already exists */
return -EEXIST;
}
e = advfs_path2ent(advfs, path, 1);
if ( NULL == e ) {
/* No entry found or non-directory entry */
return -EACCES;
}
e->type = ADVFS_REGULAR_FILE;
e->mode = mode;
e->atime = tv.tv_sec;
e->mtime = tv.tv_sec;
e->ctime = tv.tv_sec;
return 0;
}
utimens APIの実装
次に,ファイルのタイムスタンプを更新する utimens
API を実装します。第1引数にはパス,第2引数には struct timespec
構造体の配列が入ります。第2引数の配列はそれぞれ,アクセス時刻および更新時刻を表します。これらの情報を用い,以下のように対象のエントリの atime
および mtime
を更新します。
int
advfs_utimens(const char *path, const struct timespec tv[2])
{
struct fuse_context *ctx;
advfs_t *advfs;
advfs_entry_t *e;
/* Get the context */
ctx = fuse_get_context();
advfs = ctx->private_data;
e = advfs_path2ent(advfs, path, 0);
if ( NULL == e ) {
/* No entry found or non-directory entry */
return -ENOENT;
}
if ( NULL != tv ) {
e->atime = tv[0].tv_sec;
e->mtime = tv[1].tv_sec;
}
return 0;
}
実行
いつものように実装したプログラムをビルド・実行するために
$ docker-compose build && docker-compose up -d
を実行し,
$ docker exec -it advfs_advfs_1 bash
と実行し, /mnt
に実装したファイルシステムをマウントしたコンテナの bash
を起動します。
2日目同様,ls -al /mnt
を実行します。2日目とは違い,今回は最上位ディレクトリのタイムスタンプを起動時刻にしたため,下記のように .
のタイムスタンプが起動時の時刻になっていることが確認できるかと思います。
root@95298be9d52a:/usr/src# ls -al /mnt
total 4
drwxrwxrwx 2 root root 0 Dec 6 14:58 .
drwxr-xr-x 1 root root 4096 Dec 6 14:58 ..
root@95298be9d52a:/usr/src# touch /mnt/test
root@95298be9d52a:/usr/src# ls -al /mnt
total 4
drwxrwxrwx 3 root root 0 Dec 6 14:58 .
drwxr-xr-x 1 root root 4096 Dec 6 14:58 ..
-rw-r--r-- 1 root root 0 Dec 6 14:59 test
今日のまとめと明日の予定
今日はファイルの作成を実装しました。明日はこのファイルの読み書きを実装します。