Blog:
SquashfsとOverlayfsを使用して組み込みLinux
読み取り専用の利用共にファイルシステムの信頼性を高める

Wednesday, December 23, 2020

組み込みLinuxシステムを使用する際、デバイスの不測の電源障害が原因で発生したファイルシステムの損傷により、デバイスが起動しない現象が発生します。通常はこの問題に対処するため、リチウム電池または超コンデンサなどの予備電源を使用するといったハードウェア設計またはシステムソフトウェアによる回避対策を取ります。本稿では、squashfsの読み込み専用ファイルシステムを使用してLinuxシステムファイルを作成し、overlayfsを使ってユーザーディレクトリへの書き込み権限を追加する方法を紹介します。デモンストレーションには、Colibri iMX6コンピューターモジュールを使います。この手法は、iMX8コンピューターモジュールなど、ほかのToradex製品にも適用可能です。

Squashfsは、読み込み専用の圧縮されたファイルシステムです。通常は、データのバックアップやLinuxディストリビューションのLiveCDといったシステムリソースに限りのあるコンピューターシステムに利用されます。OpenWRTシステムもsquashfsを使用しています。Overlayfsは、ほかのファイルシステムと組み合わされたジョイントマウントで、1つのディレクトリへのマウントポイントを複数重ね合わせます。Overlayfsがよく使われるのは、読み込み専用のパーティションに別の読み書き用パーティションを重ね合わせる用途です。普通、組み込みLinuxデバイス機能は優れた設計を備えています。後の段階になってからほかのソフトウェアをインストールしたり、Linuxシステムソフトウェアを変更したりする必要があることは滅多になく、デバイスアプリケーションや関連データを変更する必要性が出てくることの方が多いのです。このため、squashfsベースの読み込み専用ファイルシステムとoverlayfsとを組み合わせ、ユーザーアプリケーションとデータへの読み書き操作を可能にすることで、組み込みLinuxファイルシステムの信頼性を向上させることができます。

Colibri iMX6のeMMCでは、次のパーティションプランを採用します。BootFSはFAT32形式です。このパーティションは、Linuxカーネルファイル、デバイスツリーおよびそのほかの起動ファイルを含みます。iMX8では、そのほかのファームウェアファイルも含まれます。このパーティションへの書き込みが行われるのは、通常はファイルシステムをプログラミングする段階のみです。RootFSパーティションは、Linuxが動作するファイルシステムで、usr、bin、lib、etcやhomeといったディレクトリはすべてこのパーティションにあります。一般に、このパーティションはEXT3やEXT4形式となっており、ファイルの書き込みと削除をサポートします。ファイルシステムの損傷はここで発生することが多く、そのうちにデバイスが起動しなくなる問題の原因となります。このため、ここで読み込み専用形式のsquashfsを使います。UserDataはEXT4のパーティションで読み書きが可能です。このパーティションは、overlayfsを利用して、元々は読み込み専用のsquashfsに位置している/home/rootディレクトリにマウントします。ユーザーアプリケーションは、通知なしでこのディレクトリを使用してファイルの書き込みや削除を行えますが、読み込み専用のsquashfsファイルシステムを壊すことなく、すべての操作はUserDataパーティションに移されます。ユーザーのアプリケーションもUserDataパーティションに保存され、アプリケーションの起動時にはそこから読み込まれます。パーティションは書き込み可能で、その上にあるすべてのファイルは変更が可能です。

下記では、Yocto環境で上記のプランに適合するBSPを生成する方法について詳しく説明し、これらのパーティションを実装してToradex Easy Installerツールによりファイルを書き込みます。

まず、Yocto環境でsquashfs形式のシステムファイル、rootfsを生成する必要があります。build/conf/local.confを変更し、ファイルの最後に次を追加します。

IMAGE_FSTYPES_append = " squashfs"

Linuxカーネルのデフォルト設定では、squashfsはカーネルドライバーモジュールを読み込むことでサポートします。モジュールドライバーファイルはrootfsにあります。Linuxカーネルが起動時にこの形式を正常に処理できるようにするためには、squashfsのカーネルドライバーモジュールを静的ドライバーとして設定し、カーネル内に直接コンパイルする必要があります。カーネル用に設定されたファイルシステムは、squashfsとオーバーレイ形式の追加をサポートします。

$ MACHINE=colibri-imx6 bitbake -c menuconfig virtual/kernel
→ File systems 
<*> Overlay filesystem support 

→ File systems → Miscellaneous filesystems 
<*>   SquashFS 4.0 - Squashed file system support  

EXT4形式のUserDataパーティションは、オーバーレイの仕組みにより読み込み専用rootfsの/home/rootにマウントされるのですが、その前にfstabで、/dev/mmcblk0p3 -> /media/data-> /home/rootのようにして、このパーティションをシステムにマウントする必要があります。Yoctoでは、layers/meta-toradex-demos/recipes-core/base-files/base-files/fstabがColibri iMX6 BSPにコンパイルされます。このファイルに次を追加します。

/dev/mmcblk0p3       /media/data         auto       defaults,sync,noauto  0  0

次に、オーバーレイを使って/media/dataを/home/rootにマウントするブートスクリプトを追加します。コンパイルに必要なbbファイルとsystemdサービスを含むlayers/meta-toradex-bsp-common/recipes-coreディレクトリにmount-overlayfsフォルダーを追加します。Mount-overlayfs.bbでは、install -d ${D}/media/dataが/mediaディレクトリにdataフォルダーを作成し、FILES_${PN} = "/media/data"が空のフォルダをBSPに追加します。mount-overlayfs.serviceは、RequiresMountsForを使い、実行前にfstabによって/media/dataディレクトリがマウントされるようにします。オーバーレイのディレクトリ構造は、lowerdirは/home/root、upperdirは/media/data/home/root/upper、workdirは/media/data/home/root/workになります。このようにして、アプリケーションが/home/rootの下にあるファイルの読み込みおよび書き込みを行えるようになり、アプリケーションとユーザーにとってoverlayfsは透過的になります。

[Service]
Type=simple
ExecStart=/bin/sh -c 'mount -t overlay -o lowerdir=/home/root,upperdir=/media/data/home/root/upper,workdir=/media/data/home/root/work overlay /home/root'

build/conf/local.confでBSPにmount-overlayfsを追加します。

IMAGE_INSTALL_append = " mount-overlayfs"

rootfsは読み込み専用の形式なため、開発ボードでsystemctlコマンドを直接実行してブートスクリプトを追加することはできません。上記のようにYoctoにtest-appを追加し、UserDataパーティションにあるtestというテストプログラムを自動的に実行する必要があります(パーティションは、オーバーレイにより、ここでようやく/home/rootディレクトリにマウントされます)。コンパイルに必要なbbファイルとsystemdサービスを含むlayers/meta-toradex-bsp-common/recipes-coreディレクトリにtest-appフォルダーを追加します。

build/conf/local.confでBSPにtest-appを追加します。

IMAGE_INSTALL_append = " mount-overlayfs test-app"

ここまでで、必要な構造のBSPを生成するため、Yoctoに変更を加えました。次のコマンドを実行してコンパイルのタスクを完了します。

$ MACHINE=colibri-imx6 bitbake tdx-reference-minimal-image

build/deploy/images/colibri-imx6に、関連するファイルが生成されます。次の2つのファイルが必要になります。ファイル名のタイムスタンプは、コンパイルした特定の日付に相応します。

  • Colibri-iMX6_Reference-Minimal-Image-Tezi_5.1.0-devel-20201112022057+build.0.tar
  • Colibri-iMX6_Reference-Minimal-Image.rootfs.squashfs

Colibri-iMX6_Reference-Minimal-Image-Tezi_5.1.0-devel-20201112022057+build.0.tarを解凍します。rootfsはsquashfs形式のため、起動にはカスタマイズしたboot.srcを使う必要があります。

$ cd ~/
$ tar vxf Colibri-iMX6_Reference-Minimal-Image-Tezi_5.1.0-devel-20201112022057+build.0.tar
$ cd Colibri-iMX6_Reference-Minimal-Image-Tezi_5.1.0-devel-20201112022057+build.0
$ tar vxf Reference-Minimal-Image-colibri-imx6.bootfs.tar.xz

emmcargs_setパラメータを変更します。

env set emmcargs_set 'env set rootfsargs root=/dev/mmcblk0p2 rootfstype=squashfs ro rootwait'

これで、boot.cmdを使って相応するboot.srcを直接生成、解凍ディレクトリにある同名のファイルと置き換えてReference-Minimal-Image-colibri-imx6.bootfs.tar.xzにパッケージ化し直せるようになります。

$ cd Reference-Minimal-Image-colibri-imx6.bootfs
$ rm boot.src
$ mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n "Distro boot script" -d boot.cmd boot.scr
$ cd ..
$ tar cJf Reference-Minimal-Image-colibri-imx6.bootfs.tar.xz -C Reference-Minimal-Image-colibri-imx6.bootfs .

squashfsのrootfsファイルシステムをColibri-iMX6_Reference-Minimal-Image-Tezi_5.1.0-devel-20201112022057+build.0ディレクトリにコピーします。

$ cp Colibri-iMX6_Reference-Minimal-Image.rootfs.squashfs Colibri-iMX6_Reference-Minimal-Image-Tezi_5.1.0-devel-20201112022057+build.0

次に、UserDataパーティションにコピーしなければならないファイルを作成します。先述したように、オーバーレイによるマウントでは相応するディレクトリ構造が必要です。次のようにして順にディレクトリを作成し、テストプログラムであるtestをupperディレクトリにコピーします。ここで使っているテストプログラムのtestは、シンプルなC言語アプリケーションで、システムログに「Hello Toradex!」と出力します。

$ cd ~/Colibri-iMX6_Reference-Minimal-Image-Tezi_5.1.0-devel-20201112022057+build.0
$ mkdir rootfs
$ cd rootfs
$ mkdir -p home/root/upper
$ mkdir -p home/root/work
$ cp ~/test home/root/upper
$ tree

rootfs.tar.xzとしてパッケージ化します。

$ cd ..
$ sudo tar cJf rootfs.tar.xz -C rootfs .

image.jsonファイルを変更して、squashfsシステムファイルに直接書き込むためのRAW形式のパーティションをblockdevsに追加し、さらに上記で生成したrootfs.tar.xzを書き込むためのEXT4形式のDATAパーティションを追加します。

最終的に、Colibri-iMX6_Reference-Minimal-Image-Tezi_5.1.0-devel-20201112022057+build.0には、次のファイルが含まれます。

Toradex Easy Installerを使い、上記のBSPをColibri iMX6に再インストールします。インストールする際には、EraseをクリックしてeMMC上の内容を消す必要があります。

プログラミングが完了したら、モジュールを再起動します。マウントコマンドを使えば、マウントされたパーティションを表示できます。

root@colibri-imx6:~# mount
/dev/mmcblk0p2 on / type squashfs (ro,noatime)
……
/dev/mmcblk0p3 on /media/data type ext4 (rw,relatime,sync)
overlay on /home/root type overlay (rw,relatime,lowerdir=/home/root,upperdir=/media/data/home/root/upper,workdir=/media/data/home/root/work)

mmcblk0p2は、読み込み専用のsquashfsファイルシステムに書かれたパーティションなので、例えば/etcディレクトリにファイルを作成することはできません。

root@colibri-imx6:/etc# mkdir test-folder
mkdir: can't create directory 'test-folder': Read-only file system

EXT4形式の/dev/mmcblk0p3パーティション上にあるユーザーファイルは、/media/dataにマウントされています。さらに、オーバーレイにより、/home/rootには読み込みと書き込みの権限があります。

root@colibri-imx6:~# cd ~/
root@colibri-imx6:~# pwd
/home/root
root@colibri-imx6:~# mkdir test-folder
root@colibri-imx6:~# ls
test         test-folder
root@colibri-imx6:~# ls -lh
-rwxrwxr-x    1 1000     1000       11.3K Nov 11  2020 test
drwxr-xr-x    2 root     root        4.0K Feb  7 16:25 test-folder

起動時には、UserDataにあるtestというテストプログラムも自動的に実行されます。

root@colibri-imx6:~# journalctl -u test-app
-- Logs begin at Fri 2020-02-07 15:50:53 UTC, end at Fri 2020-02-07 16:11:49 UTC. --
Feb 07 15:50:57 colibri-imx6 systemd[1]: Started start a demo on overlay mount folder.
Feb 07 15:50:57 colibri-imx6 test[456]: Hello Toradex!
Feb 07 15:50:57 colibri-imx6 systemd[1]: test-app.service: Succeeded.

まとめ

上記では、squashfsとオーバーレイを組み合わせることで、システムファイルを読み込み専用のパーティションに置き、別のパーティションで読み書きの作業をする使用方法を詳しく紹介しました。不測の電源障害によってシステムファイルが起動できなくなるリスクを削減できます。読み込み専用のsquashfsファイルシステムを使用する際には、rootfsを可能な限りシンプルにする必要があります。Reference-Minimal-Imageに基づいて独自のBSPを構築するか変更することが推奨されます。

Author: Shanfeng Hu, FAE, Toradex
Share this on:

Leave a comment

Please login to leave a comment!
Have a Question?