この前から続けてunionfsをいじってたらpanicにやられた。
panicの中身からは分かりづらかったけど、VOP_CREATE
に先んじてVOP_LOOKUP
を呼んでいなかったことが問題らしい。
1ヶ月以上はまっていたので記録を残しておくことにした。
きっと誰の役にも立たないけど。
panic時のbacktraceはこんな感じ。
kdb_backtrace+0x8d vpanic+0x1df kproc_shutdown+0 ufs_direnter+0xbb2 ufs_makeinode+0x786 ufs_create+0x4a VOP_CREATE_APV+0x16b VOP_CREATE+0x51 (以下略)
ufs_direnter
はsys/ufs/ufs/ufs_lookup.cで定義されていて、panicの条件はこうなっている。
if (spacefree < newentrysize) panic("ufs_direnter: compact2");
ファイルを作る場合は、新しいファイルそのものに対応するエントリを生成するほか、それを配置するディレクトリも更新する。
ディレクトリの更新を行っているのがufs_direnter
。そしてspacefree
はinode(?)の空き領域、newentrysize
は更新後の書き込みたいディレクトリ情報のサイズを表す。
さて、普通のメモリの管理(mallocとか)を考えると、newentrysize
を格納できる空き領域を探す処理があって、続けてその領域を使う(使用中フラグを立てるとか)処理があるはずだ。
しかし、ufsの場合この2つは遠く離れていて、前者がufs_lookup_ino
(ufs_lookup
)をCREATEモードで呼んだ場合、後者がufs_direnter
にいる。
ufsのコードはまだあまり読んでないので、正直なところ理由は良く分からない。
とにかく、正しくVOP_LOOKUP
を呼ばないでVOP_CREATE
すると、楽しくないことが起きるということだ。
ちなみに、過去にunionfsで似たようなバグがあった模様。(FreeBSD unionfsの改善提案および修正状況のpatch13)
unionfsはrename時にファイルを生成する場合がある。
が、その後再度VOP_LOOKUP
をせずにVOP_RENAME
していたのが問題らしいように見える。LOOKUP
し忘れたという意味では完全に同じ事象といえる。