panic: ufs_direnter: compact2

この前から続けて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し忘れたという意味では完全に同じ事象といえる。