ミルク色の記録

やったこと、やってみたこと

Raspbianのinitramfsをカーネルのアップデートに追従するようにした

Raspberry PIでrootfsのReadOnly化やUSB OTGとかで試してきて、ちょっとだけinitramfs使ってやりたいことやれるようになった。

だけど、apt-get upgradeカーネルが更新されると、既存のinitramfsを作りなおさなければならないことに気がついた。 しばらくはカーネルの更新があるたびに手作業で作りなおしていたんだけど、これなんとかならんかな…と思っていた。

ある日PCのubuntuでパッケージの更新をしていた時に、流れてくる出力にinitramfsを作りなおしているらしき表示があることに気がついた。 で、もしかしてaptの更新で動くフックがあるんじゃないかと思って探してみた。

対象はRaspbian Busterの2019-07-10のやつ。

initramfs-toolsのフック

目的のフックは/etc/kernel/postinst.d/initramfs-toolsにあった。

中身は次のようになっていた。

#!/bin/sh -e

version="$1"
bootopt=""

command -v update-initramfs >/dev/null 2>&1 || exit 0

# passing the kernel version is required
if [ -z "${version}" ]; then
        echo >&2 "W: initramfs-tools: ${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number"
        exit 2
fi

# exit if kernel does not need an initramfs
if [ "$INITRD" = 'No' ]; then
        exit 0
fi

# absolute file name of kernel image may be passed as a second argument;
# create the initrd in the same directory
if [ -n "$2" ]; then
        bootdir=$(dirname "$2")
        bootopt="-b ${bootdir}"
fi

# avoid running multiple times
if [ -n "$DEB_MAINT_PARAMS" ]; then
        eval set -- "$DEB_MAINT_PARAMS"
        if [ -z "$1" ] || [ "$1" != "configure" ]; then
                exit 0
        fi
fi

# we're good - create initramfs.  update runs do_bootloader
# shellcheck disable=SC2086
INITRAMFS_TOOLS_KERNEL_HOOK=1 update-initramfs -c -k "${version}" ${bootopt} >&2

ざっと見た感じ、この仕組みに乗っかるならupdate-initramfsで作ったほうが良さそう(自分はmkinitramfsコマンドで作っていた)。 とりあえずmkinitramfsで作っておいたinitrd.imgupdate-initramfs -c -k $(uname -r)で作りなおした。

ファイル名はinitrd.img-x.y.z-v7+のようになるので、/boot/config.txtinitramfs設定も修正しておいた。

また、スクリプトを見るに$INITRDの値が設定されていないと更新はキャンセルされそう。 そこでこの値を設定する方法を探した。これは/etc/default/raspberrypi-kernelにあった。

中身こんな感じ。

# Defaults for raspberrypi-kernel

# Uncomment the following line to enable generation of
# /boot/initrd.img-KVER files (requires initramfs-tools)

#INITRD=Yes

# Uncomment the following line to enable generation of
# /boot/initrd(7).img files (requires rpi-initramfs-tools)

#RPI_INITRD=Yes

rpi-initramfs-toolsってなんだ?と思ったけど、特にそういうパッケージがあるわけではないみたい。 今のところ取り置きされているような感じなのだろうか…?とりあえずINITRD=Yesコメントアウトを解除した。 その状態でapt-get upgradeカーネルの更新をしてやると、出力に次のような表示が出てきて新しいカーネルバージョンでinitramfsが作りなおされていることが解る。

run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.19.66+ /boot/kernel.img
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.19.66+ /boot/kernel.img
/etc/kernel/postinst.d/initramfs-tools:
update-initramfs: Generating /boot/initrd.img-4.19.66+
run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.19.66-v7+ /boot/kernel7.img
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.19.66-v7+ /boot/kernel7.img
/etc/kernel/postinst.d/initramfs-tools:
update-initramfs: Generating /boot/initrd.img-4.19.66-v7+
run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.19.66-v7l+ /boot/kernel7l.img
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.19.66-v7l+ /boot/kernel7l.img
/etc/kernel/postinst.d/initramfs-tools:
update-initramfs: Generating /boot/initrd.img-4.19.66-v7l+

実際に/bootの中身を見てみると

$ ls /boot/initrd.img-*
/boot/initrd.img-4.19.66+  /boot/initrd.img-4.19.66-v7+  /boot/initrd.img-4.19.66-v7l+

となって、新たなバージョンのinitramfsができていることがわかった(手で作った旧バージョンは削除されていた)。

ただし、Raspbianの場合このままだと/boot/config.txtinitramfs設定は古いままになってしまうので、 そこはひと手間かけてやる必要がありそう。

/etc/kernel/postinst.d/initramfs-toolsをカスタマイズ

ひとまず/etc/kernel/postinst.d/initramfs-toolsの末尾に次の処理を追加した。

# replace /boot/config.txt
current_version="$(uname -r)"
old_version_number=${current_version%-*}
if echo $version | grep -Pq "^\d+\.\d+\.\d+\+$"; then
        old_version="$old_version_number+"
else
        old_version="$old_version_number-${version#*-}"
fi
sed -i "s/initramfs initrd.img-$old_version/initramfs initrd.img-$version/g" "$bootdir/config.txt"

現在動作しているOSのバージョンを調べて更新対象のライブラリバージョンで/boot/config.txtinitramfs設定を書き換える。

これで改めて確認し、Busterではアップデートに追従できるようになった。

Stretchに対応する

上記の修正をStretchでもやってみたところ、次のような出力が表示されて作りなおしに失敗した。

run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.19.66+ /boot/kernel.img
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.19.66+ /boot/kernel.img
/etc/kernel/postinst.d/initramfs-tools:
update-initramfs: Generating /boot/initrd.img-4.19.66+
run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.19.66-v7+ /boot/kernel7.img
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.19.66-v7+ /boot/kernel7.img
/etc/kernel/postinst.d/initramfs-tools:
update-initramfs: Generating /boot/initrd.img-4.19.66-v7+
run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.19.66-v7l+ /boot/kernel7l.img
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.19.66-v7l+ /boot/kernel7l.img
/etc/kernel/postinst.d/initramfs-tools:
update-initramfs: Generating /boot/initrd.img-4.19.66-v7l+
WARNING: missing /lib/modules/4.19.66-v7l+
Ensure all necessary drivers are built into the linux image!
depmod: ERROR: could not open directory /lib/modules/4.19.66-v7l+: No such file or directory
depmod: FATAL: could not search modules: No such file or directory
depmod: WARNING: could not open /var/tmp/mkinitramfs_L5yf77/lib/modules/4.19.66-v7l+/modules.order: No such file or directory
depmod: WARNING: could not open /var/tmp/mkinitramfs_L5yf77/lib/modules/4.19.66-v7l+/modules.builtin: No such file or directory

*** 中略 ***

Processing triggers for initramfs-tools (0.130) ...
ln: failed to create hard link '/boot/initrd.img-4.19.66-v7l+.dpkg-bak' => '/boot/initrd.img-4.19.66-v7l+': Operation not permitted
cp: error writing '/boot/initrd.img-4.19.66-v7l+.dpkg-bak': No space left on device
dpkg: error processing package initramfs-tools (--configure):
 subprocess installed post-installation script returned error exit status 1
Processing triggers for libc-bin (2.24-11+deb9u4) ...
Errors were encountered while processing:
 initramfs-tools
E: Sub-process /usr/bin/dpkg returned an error code (1)

メッセージを読みながら調べてみると、更新処理がRaspberry PI4用のv7l+系のinitramfsを作ろうとするが、 StretchはRaspberry PI4に対応していないのでv7l+用のモジュールライブラリが見つからずにdepmodのエラーが出ている。失敗に終わったinitrd.img-4.19.66-v7l+ファイルはそのまま/bootに残留する。 更にバックアップを取って新しく作ろうとするので、/bootパーティションのディスクスペースが圧迫されて作成できずに落ちる…ということらしい。

そこで/etc/kernel/postinst.d/initramfs-tools$INITRDのチェックの次に、下のような処理を追加した。

# skip ignore version lib
if [ ! -e "/lib/modules/$version" ]; then
        echo "Not exist module libraries. [$version]"
        exit 0
fi

これでv7l+のような対応していないライブラリバージョンの生成はスキップされる。

また、/bootパーティションの空き容量が深刻なので、/usr/sbin/update-initramfsbackup_initramfsbackup_booted_initramfsの方にも手を入れた。

# backup initramfs while running
backup_initramfs()
{
        [ ! -r "${initramfs}" ] && return 0
        #initramfs_bak="${initramfs}.dpkg-bak"
        initramfs_bak="/var/tmp/$(basename initramfs).dpkg-bak"
        [ -r "${initramfs_bak}" ] && rm -f "${initramfs_bak}"
        #ln -f "${initramfs}" "${initramfs_bak}" \
        #       || cp -a "${initramfs}" "${initramfs_bak}"
        mv "${initramfs}" "${initramfs_bak}"
        verbose "Keeping ${initramfs_bak}"
}

# keep booted initramfs
backup_booted_initramfs()
{
        #initramfs_bak="${initramfs}.dpkg-bak"
        initramfs_bak="/var/tmp/$(basename initramfs).dpkg-bak"

        *** 以下略 ***

デフォルトでは/bootパーティションに作成されていたバックアップファイルを/var/tmpに作成するように変更し、 既存バージョンのファイルを直接バックアップファイルにすることで、既存バージョンのファイルと、バックアップファイル、新しく作成するファイルで一時的に3つできてしまうことを避けるようにした。

これでapt-get upgradeカーネルの更新をして結果は次のようになった。

run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.19.66+ /boot/kernel.img
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.19.66+ /boot/kernel.img
/etc/kernel/postinst.d/initramfs-tools:
update-initramfs: Generating /boot/initrd.img-4.19.66+
run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.19.66-v7+ /boot/kernel7.img
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.19.66-v7+ /boot/kernel7.img
/etc/kernel/postinst.d/initramfs-tools:
update-initramfs: Generating /boot/initrd.img-4.19.66-v7+
run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.19.66-v7l+ /boot/kernel7l.img
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.19.66-v7l+ /boot/kernel7l.img
/etc/kernel/postinst.d/initramfs-tools:
Not exist module libraries. [4.19.66-v7l+]

** 以下省略 **

4.19.66-v7l+用のライブラリは存在しないのでスキップされ、/bootパーティション内でファイル容量食い過ぎることもないので、エラーになることなくinitramfsの更新までできた。

パッチファイル

それぞれのバージョン用に作ったパッチをインストールスクリプトともにリポジトリに置いておいた。

github.com