FreeBSD と Linux の rmt

研究室のファイルサーバは幾つかあるが、メジャーな奴は (残念なことに) Linux で、xfs を使っている。で、テープドライブのつながっているホストは FreeBSD なのだが、こいつに xfsdump しようとすると、
rmtopen: remote host type, “FreeBSD”, is unknown to librmt.
というメッセージが出て、dump は始まるものの、ファイルサイズなどは無視されて、テープの終わりまでに dump が終了しなければそのまま abort 、という結末になってしまう。で、昔 Solaris と FreeBSD と Linux をごっちゃにして使っていた頃は、そんなの気にしたことなかったので、なんでだろうなー、と調べていると、librmt というのはどうやら、xfsdump のソースに含まれているライブラリらしい。それに、Linux の rmt はどうやら BSD 由来のものであり、rmt が理解するコマンドは Linux でも FreeBSD でも全く同じようだ。rmt のコマンドには、uname を返すようなものはないので、
ということで、xfsdump のソース取ってきて眺めてみた。librmt/rmtopen.c が怪しい。
まず、uname は rsh 経由で呼ばれているようだ。

170 snprintf(cmd, sizeof(cmd), “%s %s uname”, rsh_path, system);
173 rmt_f = popen(cmd, “r”);
183 char *c = fgets(uname, sizeof(uname), rmt_f);

検索に使われているテーブルはたぶんこれ。

50 struct uname_table uname_table[] =
51 { {UNAME_LINUX, “Linux”}, {UNAME_IRIX, “IRIX”}, {0,0} };

で、ここまでは簡単そうなのだが、rmt が I とか S で扱う構造体には互換性がなさそうだ。rmtioctl.c を見る限り、I (MTIOCOP) は使っていなそうだが、S (MTIOCGET) のほうは使う模様。これは、mtget 構造体をそのまま返すんじゃないかと思う。
rmtioctl.c には、

47 struct linux32_mtget
48 {
49 int32_t mt_type; /* Type of magtape device. */
50 int32_t mt_resid; /* Residual count: */
51 /* The following registers are device dependent. */
52 int32_t mt_dsreg; /* Status register. */
53 int32_t mt_gstat; /* Generic (device independent) status. */
54 int32_t mt_erreg; /* Error register. */
55 /* The next two fields are not always used. */
56 int32_t mt_fileno; /* Number of current file on tape. */
57 int32_t mt_blkno; /* Current block number. */
58 };

というのがあり、一方 FreeBSD の /usr/include/sys/mtio.h

110 struct mtget {
111 short mt_type; /* type of magtape device */
112 /* the following two registers are grossly device dependent */
113 short mt_dsreg; /* “drive status” register */
114 short mt_erreg; /* “error” register */
115 /* end device-dependent registers */
122 short mt_resid; /* residual count */
123 #if defined (__FreeBSD__)
124 int32_t mt_blksiz; /* presently operating blocksize */
125 int32_t mt_density; /* presently operating density */
126 u_int32_t mt_comp; /* presently operating compression */
127 int32_t mt_blksiz0; /* blocksize for mode 0 */
128 int32_t mt_blksiz1; /* blocksize for mode 1 */
129 int32_t mt_blksiz2; /* blocksize for mode 2 */
130 int32_t mt_blksiz3; /* blocksize for mode 3 */
131 int32_t mt_density0; /* density for mode 0 */
132 int32_t mt_density1; /* density for mode 1 */
133 int32_t mt_density2; /* density for mode 2 */
134 int32_t mt_density3; /* density for mode 3 */
135 /* the following are not yet implemented */
136 u_int32_t mt_comp0; /* compression type for mode 0 */
137 u_int32_t mt_comp1; /* compression type for mode 1 */
138 u_int32_t mt_comp2; /* compression type for mode 2 */
139 u_int32_t mt_comp3; /* compression type for mode 3 */
140 /* end not yet implemented */
141 #endif
142 int32_t mt_fileno; /* relative file number of current posit
ion */
143 int32_t mt_blkno; /* relative block number of current posi
tion */
144 };

みたいな感じで、圧倒的に FreeBSD のほうが詳しい情報が返ってくるのであり、こりゃ互換性はないね。というわけで、大幅に手を加える必要がありそうだ。IRIX の場合、最終的には

364 if (RMTHOST(fildes) == UNAME_IRIX) {
365 struct mtget *dstp = (struct mtget *)arg;
366 struct irix_mtget *srcp = (struct irix_mtget *)irixget;
367 short status = srcp->mt_dsreg;
368
369 dstp->mt_type = srcp->mt_type;
370 dstp->mt_erreg = srcp->mt_erreg;
371 dstp->mt_resid = srcp->mt_resid;
372 dstp->mt_fileno = srcp->mt_fileno;
373 dstp->mt_blkno = srcp->mt_blkno;
374 dstp->mt_dsreg = srcp->mt_dsreg; /* different semantics */
375
376 /* need to do tape status conversions */
377 dstp->mt_gstat = 0;
378 if (status & IRIX_MT_EOT)
379 dstp->mt_gstat |= GMT_EOT(0xffffffff);
380 if (status & IRIX_MT_BOT)
381 dstp->mt_gstat |= GMT_BOT(0xffffffff);
382 if (status & IRIX_MT_WPROT)
383 dstp->mt_gstat |= GMT_WR_PROT(0xffffffff);
384 if (status & IRIX_MT_ONL)
385 dstp->mt_gstat |= GMT_ONLINE(0xffffffff);
386 if (status & IRIX_MT_EOD)
387 dstp->mt_gstat |= GMT_EOD(0xffffffff);
388 if (status & IRIX_MT_FMK)
389 dstp->mt_gstat |= GMT_EOF(0xffffffff);
390 if (status & IRIX_MT_EW)
391 ;/* No GMT_ to map it to */
392 }

みたいにして、32bit Linux な mtget に反映されるし、32bit Linux の場合は、

393 else if (islinux32) {
394 struct mtget *dstp = (struct mtget *)arg;
395 struct linux32_mtget *srcp = (struct linux32_mtget *)linux32get;
396
397 dstp->mt_type = srcp->mt_type;
398 dstp->mt_erreg = srcp->mt_erreg;
399 dstp->mt_resid = srcp->mt_resid;
400 dstp->mt_fileno = srcp->mt_fileno;
401 dstp->mt_blkno = srcp->mt_blkno;
402 dstp->mt_dsreg = srcp->mt_dsreg;
403 dstp->mt_gstat = srcp->mt_gstat;
404 }

とするだけだ。こんな感じのを書いてやれば良さそう (ほんとはこの手前で、必要な場合にはバイトオーダの変換を行っているのだけれど、僕が使うのは今のところぜんぶ x86 だから手抜きをすることにする。Solaris なファイルサーバがあるという理想的な環境では、話が変わるわけだが…)。
FreeBSD の mtget から Linux の mtget にそのまま値を渡して良さそうなのは、
– mt_type (short → int32)
– mt_resid (short → int32: でもこれって 32kB だとすぐあふれるから、違う IOCTL を使うべきらしい)
– mt_dsreg (short → int32)
– mt_erreg (short → int32)
– mt_fileno (int32 → int32)
– mt_blkno (int32 → int32)
で、変換しなければならない (かもしれない) のは、
– mt_gstat ( ? → int32)
なのだが、これは FreeBSD の mtget には、該当する値がない気がする。これはつまり、テープの終わりとかが検出できないのであり、何本ものテープにまたがって dump する場合には非常に問題なような気もする。しかし、実際のところ xfsdump はこの変数を使っていないように見えるので、とりあえず 0 にしときゃいいんではないだろうか…?
(きっと、続く)

コメントを残す