Rust-FUSEで遊んでみる (ver.2019)

# 概要
「[RustでFUSE](https://qiita.com/hhatto/items/8bcb89d76eef69bc36ba)」な記事は既出ですがバージョンが古くてそのままだとどうにもならないので、遊ぶついでに自分の記事を書こうと思いました。
FUSEが何なのかは上記の記事など私より賢い人たちの説明がたくさんあるのでそちらを参照してください。

## 今回やること

* Rust-FUSEの環境を用意する
* examples/hello.rs を試してみる
* ちょっと改造してみる

## 実施した環境

* Windows10 (Pro 64bit)
* Hyper-V
    * Ubuntu : 19.04
    * Rust : 1.36.0 (stable)

WSLではFUSEは使えないらしいです。（2019/8段階で一般公開されていないWSL2ならどうかわかりませんが）

# 内容
## Rust-FUSEを持ってくる
repositoryは下記です。
https://github.com/zargony/rust-fuse
READMEに書いてありますが、fuse、libfuse-dev、pkg-configをapt-getとかしておく必要があります。
Rust-FUSEのcrateを使うだけならrepositoryのソースコードは不要かもしれませんが、repositoryにあるexamples/hello.rsをビルドするためには未公開の最新版が必要です（repositoryはバージョン0.4とかになりましたがcrate.ioの方は0.3.1で放置されている……）。
なのでrepositoryをcloneしたらまず`cargo build`してみるのがいいかもしれません。

## examples/hello.rsを試す
repositoryにあるexampleなhello.rsをbuildするにはCargo.tomlに下記を追記します。

```diff:Cargo.toml
  log = "0.4.6"
  thread-scoped = "1.0.2"

- #[dev-dependencies]
+ #[dev-dependencies]
  env_logger = "0.6.0"

+ [[bin]]
+ name = "hello_fs"
+ path = "examples/hello.rs"

```

`dev-dependencies`はなんかうまいやり方があるのかもしれませんがめんどうなので上記のようになりました。
これで`cargo build --bin hello_fs`とかすれば`target/debug/hello_fs`とかが生成されます。
下記の要領でまずマウント動作を確認できます。

```shell
> cd target/debug
> mkdir /tmp/hello_dir
> ./hello_fs /tmp/hello_dir
```

3つ目のコマンドをたたくとプロンプトが返ってこなくなりますがそういうものっぽいです。ソースコードにそんな感じで書いてあります。なので別のコンソールを開いたりして下記を試します。（別のコンソール開けない人はゴメンナサイ。ctrl+cで止めるとマウントしたディレクトリは死にます。）

```shell
> ls -ln /tmp
...
drwxr-xr-x 2  501   20    0  1月  1  1970 hello_dir
...
> cat /tmp/hello_dir/hello.txt
Hello World!
> sudo umount /tmp/hello_dir
```

アンマウントするときは普通にumountなんですよね。面白いです。

## ちょっと遊んでみる
上に示したようにuid/gidが変な数字で固定されているので、マウントした人のuid/gidを答えるようにしてみます。

### 基本方針

ディレクトリの属性を参照したときの値は`HELLO_DIR_ATTR`定数から取られています。
今、この`HELLO_DIR_ATTR`にあるuidとgidを別の値にします。
しかし可変の値はグローバル定数にはできない＆Rustはグローバル変数に厳しいので、HelloFSでちゃんと値を持つようにして初期化しつつ……という手順を取ります。
説明すると長くなるので下記のソースコードを見てもらいつつ、かいつまんで説明していきます。
https://github.com/hakua-doublemoon/rust-fuse

### uid/gidの取得
ググるとnixというcrateが出てきたのでこれを使ってみます。
https://docs.rs/nix/0.15.0/nix/
Cargo.tomlに下記を追記します。

```Cargo.toml
nix = "0.15.0"
```

これを使って下記のようにuid/gidを取得します。

```Rust:examples/hello_attr.rs
use nix::unistd;
// (中略) 
pub fn dir_attr_get() -> FileAttr
{
    FileAttr {
// (中略) 
        uid: libc::uid_t::from(unistd::Uid::current()),
        gid: libc::gid_t::from(unistd::Gid::current()),
// (中略) 
    }
}
```

`libc::uid_t::from`とかやっているのは、Rustが型変換にも厳しく`as u32`でキャスティングできないので、一度uid_tとかの型にしてから……という手順を踏んでいるためです。

### 取得したuid/gidを聞かれたときに返せるようにする

まずファイルシステムのインスタンス作成時に属性を持てるように拡張します。

```Rust:examples/hello.rs
struct HelloFS {
    pub attr: FileAttr, // 追加
}
```

この`attr`を初期化します。

```Rust:examples/hello.rs
mod hello_attr;
    // （中略）
fn main() {
    // （中略）
    let hello_fs = HelloFS { attr : hello_attr::dir_attr_get() };
    fuse::mount(hello_fs, mountpoint, &options).unwrap();
}
```

これで属性を聞かれたときに答えられるようになります。

```Rust:examples/hello.rs
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        match ino {
            1 => reply.attr(&TTL, &(self.attr)), // ここを改造
            2 => reply.attr(&TTL, &HELLO_TXT_ATTR),
            _ => reply.error(ENOENT),
        }
    }
```

### 改造結果

```shell
> ls -ln /tmp
...
drwxrwxr-x 1 1000 1000    0  1月  1  1970 hello_dir
...
```

# おわりに
今回はRust-FUSEを持ってきてサンプルコードの属性の返し方をちょっと改造してみたりしました。
属性ではino、たぶんinode番号を見てるっぽく、ここらへんに変な値を入れるとIOエラーになったりしました。ストレージが壊れるとそういう状況がナチュラルに起こるでしょうから、ファイルが壊れるってそういうことなのかとそのとき思いました。
<br>
今後はストレージにアクセスしてデータの不揮発化などに取り組んでいくつもりです。