はじめに
前回はkirkstoneでリードオンリーなファイルシステムを作成して、OverlayFSで書き込み可能な領域を追加する方法を紹介した。
kirkstoneではoverlayfs.bbclass
やoverlayfs-etc.bbclass
を使用することができるが、
それらは以前のバージョンのYoctoProjectでは存在しないため使用できない。
OverlayFSで書き込み領域を追加する簡単な方法はないだろうか。すこし検討してみる。
環境構築
作業環境
$ mkdir -p ~/yocto/rpi-hardknott $ cd ~/yocto/rpi-hardknott
pokyの取得
$ git clone -b hardknott git://git.yoctoproject.org/poky.git
環境変数の設定
$ source poky/oe-init-build-env
レイヤの取得
$ bitbake-layers layerindex-fetch meta-raspberrypi
local.confを編集
conf/local.conf
に下記の部分を追加
MACHINE = "raspberrypi4-64" DL_DIR = "${TOPDIR}/../downloads" # systemd DISTRO_FEATURES:append = " systemd" VIRTUAL-RUNTIME_init_manager = "systemd" DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit" VIRTUAL-RUNTIME_initscripts = "" # enable uart ENABLE_UART = "1" # read only root filesystem. EXTRA_IMAGE_FEATURES += "read-only-rootfs"
今回もsystemdの使用を前提とする。
書き込みパーティションの追加
wksの作成
既存のwksファイルをベースに新しくwksを作成し書き込み可能なパーティションを追加する。
$ bitbake-layers create-layer meta-work $ bitbake-layers add-layer meta-work $ mkdir meta-work/wic
meta-work/wic/sdimg-raspberrypi-overlay.wks
を次の内容で作成する。
part /boot --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 4096 --size 20 part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --label root --align 4096 --size 2048 part /data --ondisk mmcblk0 --fstype=ext4 --label data --fixed-size 20
local.confに下記を追加
WKS_FILE="sdimg-raspberrypi-overlay.wks"
マウントポイントの作成
$ recipetool newappend meta-work core-image-base
作成されたmeta-work/recipes-core/images/core-image-base.bbappend
に下記の内容を記述する。
create_data_dir() { mkdir -p ${IMAGE_ROOTFS}/data } ROOTFS_PREPROCESS_COMMAND += "create_data_dir;"
揮発しても良いデータ
/home/root
に対して、揮発しても良い領域を割り当てたい場合、local.conf
に下記のように記述する。
VOLATILE_BINDS_append = " \ /var/volatile/root /home/root\n\ "
/home/rootは書き込み可能になるが、実際の書き込み先として/var/volatile/root
が割り当てられるため、
/home/rootに書き込んだデータはターゲットの電源がオフになると消える。
これは、下記のように/var/volatile
がtmpfsの領域だから。
tmpfs on /var/volatile type tmpfs (rw,relatime)
以前にも記載したが、注意点として、local.confでVOLATILE_BINDSを書き換えただけではvolatile-bindsパッケージは再ビルドされないので、下記のように明示的にビルドし直す必要がある。
$ bitbake volatile-binds -c clean $ bitbake core-image-base
volatile-bindsの実装
poky/meta/recipes-core/volatile-binds/volatile-binds.bb
で、VOLATILE_BINDSは下記のように、定義されている。
VOLATILE_BINDS ?= "\ /var/volatile/lib /var/lib\n\ /var/volatile/cache /var/cache\n\ /var/volatile/spool /var/spool\n\ /var/volatile/srv /srv\n\ " VOLATILE_BINDS[type] = "list" VOLATILE_BINDS[separator] = "\n"
VOLATILE_BINDSに登録されたパスの分だけ、systemdのユニットファイルとして登録している。
def volatile_systemd_services(d): services = [] for line in oe.data.typed_value("VOLATILE_BINDS", d): if not line: continue what, where = line.split(None, 1) services.append("%s.service" % what[1:].replace("/", "-")) return " ".join(services) SYSTEMD_SERVICE_${PN} = "${@volatile_systemd_services(d)}"
実際にユニットファイルを作成するのは下記。
do_compile () { while read spec mountpoint; do if [ -z "$spec" ]; then continue fi servicefile="${spec#/}" servicefile="$(echo "$servicefile" | tr / -).service" sed -e "s#@what@#$spec#g; s#@where@#$mountpoint#g" \ -e "s#@whatparent@#${spec%/*}#g; s#@whereparent@#${mountpoint%/*}#g" \ volatile-binds.service.in >$servicefile done <<END ${@d.getVar('VOLATILE_BINDS').replace("\\n", "\n")} END
作成されたユニットファイルは下記のようになっている。
[Unit] Description=Bind mount volatile /home/root DefaultDependencies=false Before=local-fs.target RequiresMountsFor=/var/volatile /home ConditionPathIsReadWrite=/var/volatile ConditionPathExists=/home/root ConditionPathIsReadWrite=!/home/root [Service] Type=oneshot RemainAfterExit=Yes TimeoutSec=0 ExecStart=/sbin/mount-copybind /var/volatile/root /home/root ExecStop=/bin/umount /home/root [Install] WantedBy=local-fs.target
ターゲット上でに実行されるのは下記の行
ExecStart=/sbin/mount-copybind /var/volatile/root /home/root
mount-copybindはシェルスクリプトで、下記のように一度OverlayFSでマウントを試みて、
失敗した場合はmount -o bind
でマウントする様になっている。この時、マウントポイント内の既存データをbind先にコピーすることによって、擬似的にオーバーレイの様にしている。
... (snip) ... # Try to mount using overlay, which is must faster than copying files. # If that fails, fall back to slower copy. if ! mount -t overlay overlay -olowerdir="$mountpoint",upperdir="$spec",workdir="$overlay_workdir" "$mountpoint" > /dev/null 2>&1; then if [ "$specdir_existed" != "yes" ]; then cp -aPR "$mountpoint"/. "$spec/" fi mount -o "bind$options" "$spec" "$mountpoint" fi elif [ -f "$mountpoint" ]; then if [ ! -f "$spec" ]; then cp -aP "$mountpoint" "$spec" fi mount -o "bind$options" "$spec" "$mountpoint" fi
mount-copybind自体はパラメータとして渡されたディレクトリがvolatileかどうかは判断していない。
揮発してほしくないデータ
/home/root
に対して揮発しない領域を割り当てる場合にはlocal.conf
のVOLATILE_BINDSを下記のようにする。
VOLATILE_BINDS_append = " \ /data/home/root /home/root\n\ "
/data
はwksで追加した、書き込み可能なパーティションのマウントポイントとなる。
volatile-bindsの本体となるmount-copybindが、割当先がvolatileかどうかを判断しないため、
VOLATILE_BINDSのリストに揮発しない領域を記述することで、揮発しない書き込み可能領域を追加することができる。
注意点
OverlayFSが使用できる場合は、揮発する場合でもしない場合でもVOLATILE_BINDは期待通りに動作するが、 OverlayFSが使用できない場合は問題となる。
mount-copybindは、OverlayFSが使用できない場合は下記のような動作を行う。
例えば/home/root
を/data/home/root
にbindするケースで、予め/home/root
にデータが存在する場合。例えば/home/root/helloが存在する。
- /home/root内の全データを/data/home/rootにコピー
- /data/home/rootを/home/rootにバインド
ユーザーはバインド後の/home/rootにデータを書き込む。この時/home/root/helloの内容も書き換える。 そしてターゲットを再起動する。
mount-copybindによってコピーとバインドが行われる。
この時のコピーで、再起動前に書き換えた/data/home/root/hello
は/home/root/hello
で上書きされる。
つまり変更内容が失われてしまうのだ。
これは、名前の通りVOLATILE_BINDSが本来は揮発することを前提とした設計になっているためである。
まとめ
kirkstone以前のバージョンのYoctoProjectで、OverlayFSにより書き込み可能領域を追加するには、 VOLATILE_BINDSを言わば悪用することで実現できる。
VOLATILE_BINDSの実処理であるmount-copybindは処理対象がvolatileであるかどうかは判断しないが、 揮発することを前提して設計されている。OverlayFSが使えないケースでは割当先を不揮発な領域にに指定した場合は問題がある。
今回の目的はOverlayFSの使用が前提となっているため、基本的に問題はないのだが、 OverlayFSが意図せず使用できない場合、わかりにくい不具合となる可能性がある。
このことから、kirkstone以降ではVOLATILE_BINDSの悪用は避け、
overlayfs.bbclass
やoverlayfs-etc.bbclass
を使用するべき。