DISK GC on archlinux

10月になってTVの番組編成が変わり、女房がぶつぶつ言ってます。

半ば唐突と言っていいぐらいで終了しちゃった『梅ちゃん先生』。女房は女医版の赤ひげ先生 みたいのを期待してたようなんですが。。。何だか、昭和を紹介する、3丁目の夕日 の リメークだったんですかね?

昼の時間帯にやってる、アニメ3本立てが無くなってしまって悲しんでおります。他にどんな 番組が無くなったか、TV版のHTMLを真剣に表情で眺めてましたよ。

おいらが気になるのは、各局間での音量の差低減、およびCM時のわめき声撲滅なんですが、確認は これからですかね。1年も待ったんだぞーーー。

vmware-toolbox-cmd on ArchLinux

Archのパッケージになってるやつを入れて、disk shrink すると、えらーで実行出来なかった。 そう、こやつは俺的に言うと、DISKのGCなんだな。GC大好き人間としては、是非動かしてみたい訳よ。 そんな下心があって、ArchLinuxをVMWARE上にインストールしたんだ。

ウブでソースを御取り寄せして、ソース嫁したら、大体あの片を追えばよさそうってのがあった ので、今回はそれをやってみる。

付属文書のINSTALLを読むと、configure、make、make installで行けるはず。

[sakae@arch open-vm-tools-2011.12.20-562307]$ ./configure
checking build system type... i686-pc-linux-gnu
checking host system type... i686-pc-linux-gnu
configure: error: Can't find include dir under /lib/modules/3.5.4-1-ARCH

あれ、頭っからエラったるな。カーネルべったりがお好きなんだな。モジュールも作るんかいな。 それには、カーネルの使用(仕様)書が欲しいとな。そんなの入れてないから、探してみる。

[sakae@arch open-vm-tools-2011.12.20-562307]$ pacman -Ss header
  :
core/linux-api-headers 3.5.1-1 [installed]
    Kernel headers sanitized for use in userspace
core/linux-headers 3.5.4-1
    Header files and scripts for building modules for linux kernel
core/linux-lts-headers 3.0.43-1
    Header files and scripts for building modules for linux-lts kernel
  :
[sakae@arch open-vm-tools-2011.12.20-562307]$ sudo pacman -S linux-headers

どれを入れればいいんかいな。3種出てきたんで、いいかげんに真ん中のやつを取り合えず 選んでみる。で、早速、先ほどと続きを実行。

configure: error: gtkmm library not found. Please install the libgtkmm devel package(s), or re-configure using --without-gtkmm.

またconfiguのエラーですよ。gtkなんて嫌いなんで、スキップさせましょ。こんな調子で、もう一発 configureをやり直しましたよ。そしてmake。今度は、コンパイルエラー発症。

でもいいんだ。vmware-toolbox-cmdはちゃんと出来てるっぽい。

[sakae@arch toolbox]$ ls -F
COPYING          toolboxcmd-devices.c  vmware_toolbox_cmd-toolbox-cmd.o
Makefile         toolboxcmd-scripts.c  vmware_toolbox_cmd-toolboxcmd-devices.o
Makefile.am      toolboxcmd-shrink.c   vmware_toolbox_cmd-toolboxcmd-scripts.o
Makefile.in      toolboxcmd-stat.c     vmware_toolbox_cmd-toolboxcmd-shrink.o
l10n             toolboxcmd-time.c     vmware_toolbox_cmd-toolboxcmd-stat.o
toolbox-cmd.c    toolboxcmd_version.h  vmware_toolbox_cmd-toolboxcmd-time.o
toolboxCmdInt.h  vmware-toolbox-cmd*

早速、gdbにかけて、追跡開始

[sakae@arch toolbox]$ sudo -s
[root@arch toolbox]# gdb vmware-toolbox-cmd disk shrink /
Excess command line arguments ignored. (shrink ...)
  :
"/home/sakae/open-vm-tools-2011.12.20-562307/toolbox/vmware-toolbox-cmd": not in executable format: File format not recognized
/home/sakae/open-vm-tools-2011.12.20-562307/toolbox/disk: そのようなファイルやディレクトリはありません.

あれ? アーギュメントが無効と言ってる。それから、コマンドは実行出来ないとも言ってるな。 はてな? の偉い人に聞いてみたいぞ。

聞くより自助努力。調べてみたら、コマンドはbashのスクリプトになってた。と言う事は、本体は、 .libsの中にあるんかな? やっぱりそうだったんで、下に下りて実行してみると、

おっとその前に、toolbox-cmd.cの中にmain中で、VMware worldの中に居るかのチェックを 外しておかないと、gdbが袋小路に入っちゃうよ。(ひょっとして、おいらのgdb操縦方法が悪い んだろうけど)

   /*
    * Check if we are in a VM
    */
 /*  if (!VmCheck_IsVirtualWorld()) {
      g_printerr(SU_(error.novirtual, "%s must be run inside a virtual machine.\n"),
                 argv[0]);
      goto exit;
   }
*/

適当に nextで進めて行くと

481              ToolsCmd_MissingEntityError(argv[0], SU_(arg.command, "command"));
(gdb)
/home/sakae/open-vm-tools-2011.12.20-562307/toolbox/.libs/vmware-toolbox-cmd: コマンド が見つかりません
507              g_printerr(SU_(help.hint, "Try '%s %s%s%s' for more information.\n"),
(gdb)
詳細については、「/home/sakae/open-vm-tools-2011.12.20-562307/toolbox/.libs/vmware-toolbox-cmd help」を参照してください。

何か、怪しい動きしてるな。そもそも、bashのスクリプトを噛ませてるって事は、いろいろと環境を 整えているんだろうな。それをすっかり無視して強引に実行しちゃったから、どんなエラーを 喰らってもしょうがナイト。。。

コマンドの動きを追いかける

ええい、強引にインストールしちゃえ。

[sakae@arch open-vm-tools-2011.12.20-562307]$ sudo make -k install

vmware-toolbox-cmdは、i10n仕様になってて、ちょいと日本語がうざいので、LANGをCに切り替えて おきます。(気分の問題だけど)そして、実行してみると、

[root@arch bin]# gdb vmware-toolbox-cmd disk shrink /
Excess command line arguments ignored. (shrink ...)
  :
Reading symbols from /usr/local/bin/vmware-toolbox-cmd...done.
/usr/local/bin/disk: No such file or directory.

何かおかしいね。vmware-toolbox-cmd内で、引数のポインタを一つずらしてるっぽい。 しょうがないので、

[root@arch bin]# gdb -q vmware-toolbox-cmd
Reading symbols from /usr/local/bin/vmware-toolbox-cmd...done.
(gdb) set args disk shrink /
(gdb) b main
Breakpoint 1 at 0x8049270: file toolbox-cmd.c, line 404.

引数は、gdbを起動した後、set args を使って設定してみます。そして、next してきます。

(gdb)
483           } else if ((cmd = ParseCommand(argv, argc)) == NULL) {
(gdb)
486           } else if (cmd->requireRoot && !System_IsUserAdmin()) {
(gdb)
499           } else if (cmd->requireArguments && ++optind >= argc) {
(gdb)
503              retval = cmd->func(argv, argc, gQuiet);
(gdb) s
Disk_Command (argv=0x8050250, argc=4, quiet=0) at toolboxcmd-shrink.c:528
528     {

関数ポインターで disk関係のコマンドを呼び出しているので、そこだけは、stepに切り替え ました。これで、舞台は、toolboxcmd-shrink.c の中に移ります。

Diskコマンドには、Listとshrinkが有り、ShrinkDoWipeAndShrinkが、shrinkルーチンです。 これ、ファイルシステム内の使われなくなったファイル(ようするに、rmされたファイル)のエリアを マーキング宜しくゼロクリア(Wipeって言うらしい)、それが終わった後VMware側の力を借りて 圧縮(shrink)する。Disk側から見れば、sweepしてるって事になる。

366     ShrinkDoWipeAndShrink(char *mountPoint,         // IN: mount point
367                           gboolean quiet,           // IN: verbosity flag
368                           gboolean performShrink)   // IN: perform a shrink operation
369     {
370        int i;
371        int progress = 0;
372        unsigned char *err;
373        WiperPartition *part;
(gdb) n
379        signal(SIGINT, ShrinkWiperDestroy);
(gdb)
382        part = ShrinkGetPartition(mountPoint);
(gdb)
379        signal(SIGINT, ShrinkWiperDestroy);
(gdb)
369     {
(gdb)
371        int progress = 0;
(gdb)
379        signal(SIGINT, ShrinkWiperDestroy);
(gdb)
382        part = ShrinkGetPartition(mountPoint);
(gdb)
383        if (part == NULL) {
(gdb)
390        if (part->type == PARTITION_UNSUPPORTED) {
(gdb)
391           ToolsCmd_PrintErr(SU_(disk.shrink.partition.unsupported,
(gdb)
394           rc = EX_UNAVAILABLE;

前回の見立て通りに、エラーを検出してますな。

それじゃ上の部分でさっと通り過ぎちゃった部分を見て気ます。

(gdb) bt
#0  ShrinkGetMountPoints (pl=0xbffffb4c) at toolboxcmd-shrink.c:142
#1  0x0804a71a in ShrinkGetPartition (mountPoint=0x80504f0 "/")
    at toolboxcmd-shrink.c:168
#2  0x0804aa1a in ShrinkDoWipeAndShrink (mountPoint=0x80504f0 "/", quiet=0,
    performShrink=1) at toolboxcmd-shrink.c:382
#3  0x0804ad8d in Disk_Command (argv=0x8050250, argc=4, quiet=0)
    at toolboxcmd-shrink.c:535
#4  0x0804956e in main (argc=4, argv=0x8050250) at toolbox-cmd.c:503
(gdb) n
ShrinkGetPartition (mountPoint=0x80504f0 "/") at toolboxcmd-shrink.c:172
172        DblLnkLst_ForEach(curr, &plist.link) {
(gdb)
173           p = DblLnkLst_Container(curr, WiperPartition, link);
(gdb)
174           if (toolbox_strcmp(p->mountPoint, mountPoint) == 0) {
(gdb)
175              part = p;
(gdb)
180              DblLnkLst_Unlink1(&part->link);
(gdb)
181              break;
(gdb)
185        WiperPartition_Close(&plist);
(gdb) p *p
$10 = {mountPoint = "/", '\000' <repeats 254 times>, type =
    PARTITION_UNSUPPORTED, attemptUnmaps = 1 '\001', comment =
    0x8071510 "Unknown filesystem. Contact VMware.", link = {prev = 0x8071504,
    next = 0x8071504}}

丹念の追っていくと、lib/wiper/wiperPosix.cの中にある、WiperPartitionFilterにたどり着く。

   item->type = PARTITION_UNSUPPORTED;

   for (i = 0; i < ARRAYSIZE(gKnownPartitions); i++) {
      info = &gKnownPartitions[i];
      if (strcmp(info->name, fsname) == 0) {
         item->type = info->type;
         comment = info->comment;
         break;
      }
   }

   if (i == ARRAYSIZE(gKnownPartitions)) {  
      comment = "Unknown filesystem. Contact VMware.";

上記は、あらかじめ登録されたファイルシステムから、現ファイルシステムを検索してる。 登録されたファイルシステムってのは、こういうのね。

static const PartitionInfo gKnownPartitions[] = {
   { "autofs",    PARTITION_UNSUPPORTED,  "autofs filesystem.",   FALSE       },
   { "ext3",      PARTITION_EXT3,         NULL,                   TRUE        },
   { "ext4",      PARTITION_EXT4,         NULL,                   TRUE        },

左側から、登録名、タイプ、コメント、shrinkの可否。

(gdb)
432           if (strcmp(info->name, fsname) == 0) {
(gdb) p fsname
$5 = 0x8071628 "rootfs"

登録名にrootfsなんてのは、無いんで、そんなファイルシステム知らんと、コメントに書かれ、 最後にコメントが書かれてると、サポートしないよってtypeになっちまう。

マウントポイントは、/ なのに、途中で/etc/mtabあたりを検索して、rootfsなんてのを 引っ張ってきてるみたいだ。

そこで、検索テーブルに、rootfsなんてのを増やしてあげる。(あるいは、ext4を書き換える) そして、再コンパイルしてから実行すると、

(gdb) n
449              if (Posix_Stat(MNTINFO_NAME(mnt), &s) < 0) {
(gdb) n
450                 comment = "Unknown device.";
(gdb) p s
$12 = {st_dev = 13257260167990345984, __pad1 = 64368, __st_ino = 0, st_mode =
    3086802932, st_nlink = 256, st_uid = 0, st_gid = 3086532626, st_rdev =
    1102732852032, __pad2 = 16354, st_size = -4611691134807180436,
  st_blksize = -1208164364, st_blocks = 0, st_atim = {tv_sec = -1073742968,
    tv_nsec = -1208014912}, st_mtim = {tv_sec = -1208270878, tv_nsec =
    -1208164364}, st_ctim = {tv_sec = 0, tv_nsec = 0}, st_ino =
    13256559649160690568}
(gdb) p *mnt
$14 = {mnt_fsname = 0x8071e28 "rootfs", mnt_dir = 0x8071e38 "/", mnt_type =
    0x8071e48 "rootfs", mnt_opts = 0x8071e58 "rw", mnt_freq = 0, mnt_passno =
    0}

まだ、コメント欄にエラーを書き込んでいるよ。雰囲気からすると、Posix_Statが失敗してるな。 どんな事をやってるか、後で見る事にして、先へ進みます。

(gdb) n
464           if (comment != NULL) {
(gdb) set comment=0
(gdb) n
469        if (item->type == PARTITION_UNSUPPORTED) {
(gdb) n
473     }
(gdb) n
WiperPartition_Open (pl=0xbffffbcc) at wiperPosix.c:661
661           DblLnkLst_LinkLast(&pl->link, &part->link);
(gdb) c
Progress: 100 [===========>]
ディスクの圧縮が完了しました。
[Inferior 1 (process 20187) exited normally]
(gdb) q

もう、強引に、エラーは無かった事にして継続したら、GCが始まりましたよ。

Quick Hackで良しとするか。

Posix_Statって調べてみたら、ただのstatだった。これがエラーを返すって何だろう? 次の9のうちから、一つを選べ、って、LPCだかの問題に出るんでしょうか?

ERRORS
       EACCES Search  permission  is  denied for one of the directories in the
              path prefix of path.  (See also path_resolution(7).)

       EBADF  fd is bad.

       EFAULT Bad address.

       ELOOP  Too many symbolic links encountered while traversing the path.

       ENAMETOOLONG
              path is too long.

       ENOENT A component of path does not exist, or path is an empty string.

       ENOMEM Out of memory (i.e., kernel memory).

       ENOTDIR
              A component of the path prefix of path is not a directory.

       EOVERFLOW
              (stat()) path refers to a file whose size cannot be  represented
              in  the type off_t.  This can occur when an application compiled
              on a 32-bit platform without -D_FILE_OFFSET_BITS=64 calls stat()
              on a file whose size exceeds (1<<31)-1 bits.