cloopfs: mkuzip と create_compressed_fs の欠陥2006年05月10日 18時25分11秒

前回は、cloopfs の形式を解析した。まとめると以下の形である。

[header][block-indexes][blocks...]

これが前回見た write() と lseek() が mkuzip が普通のファイルのみしか扱えない原因である。最初のブロックを書く前に、ポインタを置く場所を用意しなければいけない。そのために、stat を呼んでいるのだが、パイプやデバイスの大きさは stat.st_size は判らないのである。加えて適切な、エラーの処理をしていない。それゆえ、6.1-RELEASE の mkuzip はデバイスを与えられると、違法なメモリアクセスを起こすまで、適当に動くのである。

それと比べると、cloop で配布されている create_compressed_fs は標準入力からでも扱える。 陰でこっそり悪口を言うようで、あまり心地よくないが、 このコードはとてつもなく酷いので、指摘しておく。このような、コードは書いては ならない。

一番目に、なぜ標準入力を扱えるかと言うと、全ての圧縮されたブロックを malloc で保持しているからである。このように、たいした意味も持たず、特大なメモリが必要となるような、資源の使い方をしてはならない。ただの無駄使いである。CD であれば数百 MB で済むであろうが、DVD だったらいくら必要になるのだろうか。メモリなど数 TB ぐらいは入っているのは当り前だと、仮定されているようなコードである。しっかりと裏は取っていないが、現在でもこのようなのから予想すると、これの書かれた 2000 年ぐらいからこのような設計だったのだろう。その当時から、Linux はそのような巨大なメモリや、スワップ空間を扱えたのであろうか。その当時は処事情で Linux を使っていた時期だったはずだが、それよりもファイル自体に 2GB の壁があって、とても頭を悩ませたのを覚えている。

スタイルもかなり複写してここに載せづらい書き方なので、興味があるならば直接見て欲しい。create_compressed_fs.c のcreate_compressed_blocks の関数を見ると、malloc を繰り返しているのが判る。

この後を見ると、


 /* Write offsets */
 for (i=0,cbp=compressed_blocks; i < numblocks+1; i++)
  {
   loff_t tmp;
   tmp = __cpu_to_be64(bytes_so_far);
   write(STDOUT_FILENO, &tmp, sizeof(tmp));
   if(cbp) { bytes_so_far += cbp->size; cbp=cbp->next; }
  }

 /* Now write blocks and free them. */
 for (i = 0, cbp=compressed_blocks; cbp && i < numblocks; i++)
  {
   if (write(STDOUT_FILENO, cbp->data, cbp->size) != cbp->size)
    {
     perror("writing block");
     free_cb_list(compressed_blocks);
     return 1;
    }
   cbp=cbp->next;
   free(compressed_blocks); compressed_blocks=cbp;
  }

とあるのを見ると、malloc で巨大なメモリを取っている理由は、他にはない。

この問題に嫌気が差し、 解決案を提示した者もいたようだ。基本的には mkuzip がやっていることと同じである。いい感触の返事が原作者から返ってきたみたいだが、cloopfs の中には取り込まれていない。

こちらのプログラムも駄作だ。こちらは、300 行ぐらいあるファイルの中に main() しかない。このような書き方をすると読みづらいし、変更も加えにくくなる。いくつかの関数に分けて書くべきだ。

ずいぶん批判ばかりになってしまったが、根本的な問題は cloopfs のインデックスを最初に書き込む形式にある。cloopfs の記録方式の設計自体が間違っているのが、根本的な問題である。次回はこの部分を取り上げたいと思う。