Linux-4.19.6でEROFS(Enhanced Read-Only File System)な環境を作る

[Linux Advent Calendar 2018](https://qiita.com/advent-calendar/2018/linux) 3日目の記事です。
今日はLinux-4.19.6で追加された、EROFS(Enhanced Read-Only File System)が利用できる環境を作る話をしようと思います。

## EROFSとは？

Linux-4.19.6で搭載された機能で、ライトウェイトでリードオンリーなファイルシステムとのことです。
競合するファイルシステムは他にも存在しますが、EROFSはファームウェアやスマートフォン、LiveCDといった、リードオンリーなファイルシステムに対して高パフォーマンスが要求される場面での利用を想定しているようです。
(というよりも、Kconfigの説明が正確で手っ取り早いですね...)

```
config EROFS_FS
        tristate "EROFS filesystem support"
        depends on BLOCK
        help
          EROFS(Enhanced Read-Only File System) is a lightweight
          read-only file system with modern designs (eg. page-sized
          blocks, inline xattrs/data, etc.) for scenarios which need
          high-performance read-only requirements, eg. firmwares in
          mobile phone or LIVECDs.

          It also provides VLE compression support, focusing on
          random read improvements, keeping relatively lower
          compression ratios, which is useful for high-performance
          devices with limited memory and ROM space.

          If unsure, say N.
```

今回はこのEROFSを利用可能にする手順と、実際にEROFSなファイルシステムを作成できるまでをゴールにしてみます。

## 今回の環境

今回EROFSを試した環境はCentOS-7です。

```sh 
$ cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
```

## Linux-4.19.6のビルド

まずはEROFSを利用できるLinux-4.19.6をビルドします。
余談ですがEROFSのソースコードは `linux-4.19.6/drivers/staging/erofs` 以下に置かれており、まだ実験・テスト的な位置付けという扱いであるようです。

```sh 
# curl -O https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.6.tar.xz
# tar Jxf linux-4.19.6.tar.xz
# cd linux-4.19.6
```

### 必要なカーネルコンフィグ

EROFSを使用するために最低限必要なカーネルコンフィグは `STAGING` と `EROFS_FS` です。それぞれカーネルコンフィグの以下の場所で設定します。

```
STAGING
  -> Device Drivers

EROFS_FS
  -> Device Drivers
    -> Staging Drivers
```

先ほど展開したlinux-4.19.6のディレクトリに移動し、カーネルコンフィグの設定とビルドを実行します。

```sh 
# cd linux-4.19.6
# make defconfig
# make manuconfig
# make
# make modules_install
# cp arch/x86_64/boot/bzImage /boot/vmlinuz-4.19.6.x86_64
# mkinitrd /boot/initramfs-4.19.6.x86_64.img 4.19.6
# grub2-mkconfig -o /boot/grub2/grub.cfg
```

あとは再起動した後、grubからlinux-4.19.6を選択して起動します。
カーネルバージョンが4.19.6かどうかは、 `uname` コマンドで確認できます。

```sh 
$ uname -a
Linux linuxadvcal 4.19.6 #2 SMP Mon Dec 3 18:47:26 JST 2018 x86_64 x86_64 x86_64 GNU/Linux
```

## EROFSを試してみる

Linuxカーネル側の準備も完了し、EROFSを試せる状態になりました。


### EROFS対応版のmkfsコマンド

さっそく `mkfs` コマンドを実行したいところですが、先にEROFS対応版の `mkfs` コマンドを用意する必要があります。
EROFS対応版 `mkfs` コマンドは、以下のGitHubリポジトリにて公開されているので、そこから入手します。

 * [hsiangkao/erofs_mkfs_binary](https://github.com/hsiangkao/erofs_mkfs_binary)

```sh 
# curl -s -L -O https://github.com/hsiangkao/erofs_mkfs_binary/raw/master/mkfs.erofs.x86_64
# file mkfs.erofs.x86_64
mkfs.erofs.x86_64: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=c8fff00e3d9261ad4d6937f50c0664cb3bce74ae, stripped
# chmod +x mkfs.erofs.x86_64  # 実行属性をつける必要あり
```

コマンド名は `mkfs.erofs` で、指定可能なオプションは以下のようです。

```sh 
# ./mkfs.erofs.x86_64
[2018-12-03 21:01:39 205797] Err: MKFS: mkfs_parse_options_cfg() Line[151]Expected filename after options

mkfs.erofs.x86_64 version:rel v3.0.180606.0 buildTime:Jul  4 201821:15:42
    [-z <compr algi>] [-d <debug level>]
    [-h <shared xattr threhold>] [-x <debug level>]
    [image name] [target_out_directory]
```

そして、ちょっと注意が必要なのは、 `mkfs.erofs` には **デバイス名ではなくイメージファイルを指定する** 点です。
`mkfs.btefs` とか `mkfs.ext4` 等ではデバイス名を指定してmkfsするのですが、EROFSではちょっと異なるようです。
( `mkfs.erofs` 用に別途 `/dev/sdb` なストレージを用意したのですが不要でした...)

EROFSの利用例として、LinuxカーネルソースツリーをEROFSでフォーマット(イメージ作成)してみます。

```sh 
# ./mkfs.erofs.x86_64 erofs_sample.img work/linux-4.19.6
[2018-12-03 21:07:23 694510] Err: EROFS: erofs_adjust_compress_level() Line[77]Compress level(9) is greater than max level(0), adjust it to default level(9).

        c_version:             [version:rel v3.0.180606.0 buildTime:Jul  4 201821:15:42]
        c_src_path:            [/root/linux-4.19.6]
        c_dbg_lvl:             [       0]
        c_dry_run:             [       0]
        c_alg_name:            [    none]
        c_compr_maxsz:         [  921600]
        c_compr_lvl:           [       0]
        c_compr_boundary:      [     128]
        c_compr_ratio_limit:   [     100]
        c_cust_xattr:          [       0]
        c_shared_xattr_thres:  [       4]
[2018-12-03 21:07:23 695102] Err: MKFS-XATTR:mkfs_add_xattr() Line[266][/root/linux-4.19.6] has no attributes.
...中略...
[2018-12-03 21:07:02 557461] Err: MKFS-XATTR:mkfs_add_xattr() Line[266][/root/linux-4.19.6/drivers/gpu/drm/v3d/v3d_trace.h] has no attributes.
[2018-12-03 21:07:02 557539] Err: MKFS-XATTR:mkfs_add_xattr() Line[266][/root/linux-4.19.6/drivers/gpu/drm/v3d/v3d_trace_points.c] has no attributes.
```

attributeまわりでエラーが出ているのが不安ですが(もしかしたらカーネルコンフィグの設定に漏れがあるのかも)、 `mkfs.erofs` でイメージファイルが作成できました。

```sh 
# ls -lh erofs_sample.img
-rw-r--r-- 1 root root 759M 12月  3 21:10 erofs_sample.img
#
# # 元のディレクトリツリーよりもサイズが小さくなっているようです。
# du -h linux-4.19.6/ | tail -n1
896M    linux-4.19.6/
```

さっそくイメージをmountしてみましょう。

```sh 
# # "-t erofs"が指定可能になっている。
# mount -o loop -t erofs erofs_sample.img /mnt
#
# # mountできているか確認する。
# mount | grep \/mnt
/root/erofs_sample.img on /mnt type erofs (ro,relatime,user_xattr,acl)
#
# # ファイルシステムのサイズを確認する。
# df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
...中略...
/dev/loop0       759M  759M     0  100% /mnt
#
# # ファイルシステムタイプはerofsになっている。
# df -T
ファイルシス   タイプ   1K-ブロック    使用   使用可 使用% マウント位置
...中略...
/dev/loop0     erofs         776684  776684        0  100% /mnt
```

(念のため) `mkfs.erofs` 実行時にattribute周りのエラーが出ていましたが、生成されたイメージに含まれるファイルに抜けは無いようです。

```sh 
# # 個数が同じであることをもって、ファイルに抜けがないとみなす。
# find /mnt/ | wc -l
65825
# find ./linux-4.19.6 | wc -l
65825
```

## まとめ

Linux-4.19.6で追加されたEROFSが利用できる環境の構築手順を紹介しました。
`mkfs.erofs` で指定するフォーマット先がデバイス名ではなくファイル名なのは意外でしたが、これはファームウェアへのイメージ書き込みを想定した仕様なのかもしれません。