みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

Yocto リードオンリーなルートファイルシステムを構築する

はじめに

組み込みシステムの場合、製品として使用するようなケースではルートファイルシステムをリードオンリーとして、 本体が持つデータを書き換えられないようにすることがある。

Yoctoでは簡単に実現することができる。

実機でやったほうが楽しいのでラズベリーパイ4で試す。

使用するブランチはgatesgarth。

環境構築

下記のコマンドを実行し、環境を構築する。

ソース取得

$ mkdir rpi-gatesgarth && cd rpi-gatesgarth
$ git clone git://git.yoctoproject.org/poky.git -b gatesgarth
$ source layers/poky/oe-init-build-env build
$ bitbake-layers layerindex-fetch meta-raspberrypi

local.confの修正

conf/local.confに下記を追記する。

MACHINE = "raspberrypi4-64"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

# connman
IMAGE_INSTALL_append = " connman \
                 connman-client \
"

EXTRA_IMAGE_FEATURES += "read-only-rootfs"

肝となるのはEXTRA_IMAGE_FEATURES += "read-only-rootfs"の部分。

ビルドおよび動作確認

下記のコマンドでビルドする。bitbakeは時間がかかる。

$ bitbake core-image-base

ビルドが完了したら、下記のコマンドでマイクロSDにイメージを書き込む。

$ cd tmp/deploy/images/raspberrypi4-64/
$ sudo bmaptool copy core-image-base-raspberrypi4-64.wic.bz2 /dev/sdX

/dev/sdXは環境に応じて適宜読み替える。

イメージを書き込んだマイクロSDでラズベリーパイ4を起動する。

試しに何かファイルを作成する。

root@raspberrypi4-64:~# echo hello > hello.txt
-sh: can't create hello.txt: Read-only file system

正しくエラーになる。これでルートファイルシステムがリードオンリーであることがわかる。

書き込み可能な領域

mega-manualを参照すると下記のようになっている。

With the "read-only-rootfs" feature enabled, any attempt by the target to write to the root filesystem at runtime fails. Consequently, you must make sure that you configure processes and applications that attempt these types of writes do so to directories with write access (e.g. /tmp or /var/run).

書き込みを伴うアプリケーションは/tmp/var/runのような書き込み可能な領域に配置する必要がある。

そのようなアプリケーションが/tmp/var/runでしか実行できないのは不便すぎる。

通常このような場合はoverlayfsやunionfsなどを使用して、書き込み可能な領域をオーバーレイすることによって問題を回避する。

オーバーレイする方法

単純に考えてオーバーレイする方法は下記のような感じになる。

  1. meta-readonly-rootfs-overlay
  2. aufs-utilやfuse-overlayfsを使って自前でなんとかする。

meta-readonly-rootfs-overlayは古すぎてメンテされいない。自前でなんとかするのは必要でなければできる限りやりたくない。

製品でYoctoを使用してリードオンリーなルートファイルシステムを使用したい場合、意外とこのへんで苦労すると思う。

VOLATILE_BINDSを使う

変更が揮発して良ければ、つまり、電源を切ったあとに保存される必要がなければVOLATILE_BINDSを使用して、書き込み不能ディレクトリに対して、書き込み可能な領域をバインドすることができる。

local.confに下記のように追加することで、任意のディレクトリを書き込み可能にすることができる。

VOLATILE_BINDS_append = " \
    /var/volatile/root /home/root\n\
"

ここではrootユーザーのホームディレクトリを書き込み可能にしている。

これは"read-only-rootfs"を有効化すると組み込まれるvolatile-bindsというレシピが提供している機能で、書き込み可能領域 バインド指定ディレクトリの順で記述すると、そのようにバインドしてくれる。

本来はリードオンリーである/home/rootを揮発するが書き込み可能である/var/volatile/rootでバインドすることにより、/home/rootを書き込み可能としている。

注意点としては、local.confでVOLATILE_BINDSを書き換えただけではvolatile-bindsパッケージは再ビルドされないので、下記のように明示的にビルドし直す必要がある。

$ bitbake volatile-binds -c clean
$ bitbake core-image-base

動作を確認する

先ほど書き込みできなかった/home/rootでファイルを作成してみる。

root@raspberrypi4-64:~# echo hello > hello.txt
root@raspberrypi4-64:~# cat hello.txt
hello

再起動したあとにlsを実行すると、ファイルが存在しないことがわかる。

まとめ

リードオンリーなルートファイルシステムIMAGE_FEATURES += "read-only-rootfs"で簡単に作れる。

任意のディレクトリを書き込み可能にしたい場合はvolatile-bindsVOLATILE_BINDS変数で可能。

電源を切ったあとも保存して置きたいデータは書き込み可能なパーティションを用意するか、USBメモリなどの外部メディアを用意する必要がある。

組み込み機器としてルートファイルシステムがリードオンリーであることの利点としては下記が考えられる。

  1. ファイル改ざんによる不具合の防止
  2. ストレージへの書き込み寿命による故障の防止
  3. セキュアアップデートなどでの環境の同一性の検証がしやすい

ファイルに書いたデータを保存するためには別途準備が必要となるが、リードオンリーなルートファイルシステムを持つ組み込み機器の用途を考えると、Yoctoが標準で用意する機能としては十分だと考える。