fsprotect再び [プログラム]
BeagleBoneBlackのDebianで、aufsがサポートされ始めました。ですので、fsprotectが、パッケージをインストールするだけで使えるようになりました。
で、早速使ってみました。SD上のrootfsはext4フォーマットになっています。
tune2fs -i0 -c0 -m0 /dev/mmcblk0p1
したあと、uEnv.txtのcmdlineに「fsprotect ro」を追加して再起動しました。
ふむ。問題なさそうです。
...と思ったのですが、その後、丸1日、「なぜprotectしてくれずに書き込みやがるのか」という大問題にぶつかることになりました。
そうです、linuxがrootfsに「書き込む」のです。しかしながら、書き込んだ形跡をfindで探しても見つかりません。また書き込んで削除したらディレクトリの日付が変わるはずなので、そういうことでもないようです。
調査対象をファイルシステム全体に広げると、見つかりました。ちょうど1バイトだけ変更されていました。
それは、ファイルシステムの先頭から1401バイトめでした。
何が書き込まれた(変更された)のか? それを知るには、まず、ext4の構造(レイアウト)を知らなければなりません。
ext4の構造は
https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout
などにあります。
1024バイトまでが Group0 paddingで、次からsuperblockになっています。
1401バイトは、superblockの中です。superblockの1401-1024=377バイトめ、つまり、0x179バイトめは
「このファイルシステムへ書き込んだKiB数」だということですから、「間違いなく書き込んでいます」。protectしてくれてません。はて?
なお、このs_kbytes_writtenは、「ext4をマウントした後では」、sysfsから読むことができます。たとえば、
cat /sys/fs/ext4/mmcblk0p1/lifetime_write_kbytes
とすればわかります。さらに、これを下記のように直接/dev/mmcblk0p1から読むこともできますが、この場合、注意すべき点があります。
dd if=/dev/mmcblk0p1 bs=1024 count=2 | hexdump -C |grep 0000570
注意点は、「ReadWriteでマウントしているとき」、実際にファイルシステムへ書き込んでも、この値は「変わりません」。アンマウントするときに、ここへ書き込まれるようです。
さて、誰かが書き込んでいるわけですが、それがなかなか分かりませんでした。
まず、書き込みが、起動時なのか終了時なのかについては、他のマシンでSDカード上のrootfsのlifetime_write_kbytesを調べることにより、「起動時」と分かりました。
起動時のどこでしょうか? initramfs内でしょうか? fsprotectの前でしょうか? fsprotect時でしょうか?
initramfs(/boot/initrd.img-*)をgunzip+cpio展開して、中を調べていくと...
ひょっとして!? fsckしてからマウントするようになっています。もっともそれはUN*X時代からの流儀で、突然の電源断などのあとでは、そのfsckが大活躍したものです。/fastboot というファイルがあると、/etc/fstabでfsckするように設定されていもfsckを行いません。ただ、/etc/fstabは、通常rootfs上にありますので、rootfsについては、カーネルの起動パラメータ(fastboot)で指定するようです。
(書き込まない)ReadOnlyのrootfsに対してfsckを実行することが正しいのかどうかについては、NANDのデータ保持期間など議論があるところでしょうが、ともかく、このfsckを「止める」ことにします。
uEnv.txtのcmdlineにfastbootを追加して、結局、「fsprotect ro fastboot」の3つを追加しました。
また、起動時にfsckをしないため、他のPC上でfsck -fをして問題ないのを確認してから、BeagleBoneBlackに差し込んで起動しました。
そして、
cat /sys/fs/ext4/mmcblk0p1/lifetime_write_kbytes
を見たあと、再起動して、再度
cat /sys/fs/ext4/mmcblk0p1/lifetime_write_kbytes
したとき、同じ値になっていることを確認しました。
今回の検討点としては、
個人的には、
今回の問題では、「起動するときにだけ」SDに書き込むので、もしも基本24時間稼働であれば、再起動は半年〜1年に1回くらい(経験的な値)なので、書き込む回数としてはまったく問題がない程度でしょう。
で、早速使ってみました。SD上のrootfsはext4フォーマットになっています。
tune2fs -i0 -c0 -m0 /dev/mmcblk0p1
したあと、uEnv.txtのcmdlineに「fsprotect ro」を追加して再起動しました。
ふむ。問題なさそうです。
...と思ったのですが、その後、丸1日、「なぜprotectしてくれずに書き込みやがるのか」という大問題にぶつかることになりました。
そうです、linuxがrootfsに「書き込む」のです。しかしながら、書き込んだ形跡をfindで探しても見つかりません。また書き込んで削除したらディレクトリの日付が変わるはずなので、そういうことでもないようです。
調査対象をファイルシステム全体に広げると、見つかりました。ちょうど1バイトだけ変更されていました。
それは、ファイルシステムの先頭から1401バイトめでした。
何が書き込まれた(変更された)のか? それを知るには、まず、ext4の構造(レイアウト)を知らなければなりません。
ext4の構造は
https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout
などにあります。
1024バイトまでが Group0 paddingで、次からsuperblockになっています。
1401バイトは、superblockの中です。superblockの1401-1024=377バイトめ、つまり、0x179バイトめは
0x178 __le64 s_kbytes_written Number of KiB written to this filesystem over its lifetime.の下位から2バイトめです。
「このファイルシステムへ書き込んだKiB数」だということですから、「間違いなく書き込んでいます」。protectしてくれてません。はて?
なお、このs_kbytes_writtenは、「ext4をマウントした後では」、sysfsから読むことができます。たとえば、
cat /sys/fs/ext4/mmcblk0p1/lifetime_write_kbytes
とすればわかります。さらに、これを下記のように直接/dev/mmcblk0p1から読むこともできますが、この場合、注意すべき点があります。
dd if=/dev/mmcblk0p1 bs=1024 count=2 | hexdump -C |grep 0000570
注意点は、「ReadWriteでマウントしているとき」、実際にファイルシステムへ書き込んでも、この値は「変わりません」。アンマウントするときに、ここへ書き込まれるようです。
さて、誰かが書き込んでいるわけですが、それがなかなか分かりませんでした。
まず、書き込みが、起動時なのか終了時なのかについては、他のマシンでSDカード上のrootfsのlifetime_write_kbytesを調べることにより、「起動時」と分かりました。
起動時のどこでしょうか? initramfs内でしょうか? fsprotectの前でしょうか? fsprotect時でしょうか?
initramfs(/boot/initrd.img-*)をgunzip+cpio展開して、中を調べていくと...
ひょっとして!? fsckしてからマウントするようになっています。もっともそれはUN*X時代からの流儀で、突然の電源断などのあとでは、そのfsckが大活躍したものです。/fastboot というファイルがあると、/etc/fstabでfsckするように設定されていもfsckを行いません。ただ、/etc/fstabは、通常rootfs上にありますので、rootfsについては、カーネルの起動パラメータ(fastboot)で指定するようです。
(書き込まない)ReadOnlyのrootfsに対してfsckを実行することが正しいのかどうかについては、NANDのデータ保持期間など議論があるところでしょうが、ともかく、このfsckを「止める」ことにします。
uEnv.txtのcmdlineにfastbootを追加して、結局、「fsprotect ro fastboot」の3つを追加しました。
また、起動時にfsckをしないため、他のPC上でfsck -fをして問題ないのを確認してから、BeagleBoneBlackに差し込んで起動しました。
そして、
cat /sys/fs/ext4/mmcblk0p1/lifetime_write_kbytes
を見たあと、再起動して、再度
cat /sys/fs/ext4/mmcblk0p1/lifetime_write_kbytes
したとき、同じ値になっていることを確認しました。
今回の検討点としては、
- initramfs内のスクリプトで、ReadOnlyのrootfsに対してfsckを実行して書き込みをするのが正しいのかどうか
- 一般的にReadOnlyのrootfsに対してfsckを実行して書き込みをすることが正しいのかどうか
個人的には、
- ReadOnlyのrootfsについては、まず、fsck -nで書き込まずエラーの有無を調べる。
エラーがあったときは、fsckを行うか、起動中止するか、のいずれかを
「なんらかの方法で選択して」行うようにする。
今回の問題では、「起動するときにだけ」SDに書き込むので、もしも基本24時間稼働であれば、再起動は半年〜1年に1回くらい(経験的な値)なので、書き込む回数としてはまったく問題がない程度でしょう。