mt コマンドの違いについて

「[tar でテープにバックアップ・リストアしてみる](https://qiita.com/maya_shack/items/fca94e9c4f1b0b424289)」という記事で、FreeBSD の mt と Linux の mt に違いがある、と書きました。

FreeBSD 使いとして FreeBSD で動作している様子を解説することにしたのですが、FreeBSD で mt を操作する解説はあまり見つかりませんでした。Linux の mt を使っている解説と、FreeBSD と Linux の man ページを見ながらテープの操作を理解していったつもりですが、その際「mt status の表示の違い」と「man ページの説明の違い」が気になったので、この違いを整理するためにこの記事を書きました。

# mt の実装の種類

まず、Web 検索して出てくる Linux の man mt の内容が、ウェブサイトによって違うことに気づきました。そこで、OS に含まれている mt の実装の種類について調べました。

| OS                 | mt の実装             | この記事で使用                           |
|:-------------------|:----------------------|:---------------------------------------|
| FreeBSD[^1]        | BSD 4.4 Lite 由来[^2] | FreeBSD 12.1 の userland の mt          |
| CentOS, Debian[^3] | mt-st[^4]             | CentOS 8.2 に含まれる mt-st 1.1 の mt   |
| Ubuntu[^5]         | GNU cpio の mt[^6]    | Utuntu 20.04 に含まれる cpio 2.13 の mt |

Linux のディストリビューションによって、違う mt の実装が採用されていました。どうりで man ページの内容が違っているわけです。

# mt status の表示の違い
　
次に、テープの状態を表示する mt status コマンドの表示の違いを比較してみます。

## FreeBSD

```text:mt&nbsp;status
# mt -f /dev/nsa0 status
Drive: sa0: <QUANTUM ULTRIUM 3 1960> Serial Number: JN0750AME50062
---------------------------------
Mode      Density              Blocksize      bpi      Compression
Current:  0x44:LTO-3           variable       244805   enabled (0x1)
---------------------------------
Current Driver State: at rest.
---------------------------------
Partition:   0      Calc File Number:   0     Calc Record Number: 0
Residual:    0  Reported File Number:   0 Reported Record Number: 0
Flags: BOP
# mt -f /dev/nsa0 fsf
# mt -f /dev/nsa0 status
Drive: sa0: <QUANTUM ULTRIUM 3 1960> Serial Number: JN0750AME50062
---------------------------------
Mode      Density              Blocksize      bpi      Compression
Current:  0x44:LTO-3           variable       244805   enabled (0x1)
---------------------------------
Current Driver State: at rest.
---------------------------------
Partition:   0      Calc File Number:   1     Calc Record Number: 0
Residual:    0  Reported File Number:   1 Reported Record Number: 20501
Flags: None
```

テープヘッドがテープの先頭にあると、BOT ではなく ``BOP`` というフラグが表示されます。
テープヘッドが EOF にあっても、そのことはフラグに表示されません。
``Reported Record Number`` は BOP からのレコード番号を表し、``Calculated Record Number`` は EOF からのレコード番号を表します。

## mt-st

```text:mt&nbsp;status
# mt -f /dev/nst0 status
SCSI 2 tape drive:
File number=0, block number=0, partition=0.
Tape block size 512 bytes. Density code 0x44 (LTO-3).
Soft error count since last status=0
General status bits on (41010000):
 BOT ONLINE IM_REP_EN
# mt -f /dev/nst0 fsf
# mt -f /dev/nst0 status
SCSI 2 tape drive:
File number=1, block number=0, partition=0.
Tape block size 512 bytes. Density code 0x44 (LTO-3).
Soft error count since last status=0
General status bits on (81010000):
 EOF ONLINE IM_REP_EN
```

テープヘッドがテープの先頭にあると、``BOT`` フラグが表示されます。
テープヘッドが EOF にあると ``EOF`` フラグが表示されます。
``block number`` は file 内のブロック番号を表します。テープ先頭からのブロック番号は表示されません。

## GNU cpio

```text:mt&nbsp;status
# mt -f /dev/nst0 status
drive type = 114
drive status = 1140851200
sense key error = 0
residue count = 0
file number = 0
block number = 0
# mt -f /dev/nst0 fsf
# mt -f /dev/nst0 status
drive type = 114
drive status = 1140851200
sense key error = 0
residue count = 0
file number = 1
block number = 0
```

テープヘッドがテープの先頭にあっても、そのことはフラグに表示されません。
テープヘッドが EOF にあっても、そのことはフラグに表示されません。
``block number`` は file 内のブロック番号を表します。テープ先頭からのブロック番号は表示されません。


# man ページの巻き戻し系サブコマンドの説明の違い

各実装の man ページの説明を比較すると、同じサブコマンドが違う動きをするように読める部分がありました。
man ページはどのように説明しているのか、また各実装の動作が同じなのかを確かめます。

## FreeBSD

### bsf

> [man mt](https://www.freebsd.org/cgi/man.cgi?query=mt&sektion=1) より引用
>
         fsf    Forward space count files.
         bsf    Backward space count files.

戻ると説明していますが、ヘッドの位置については記述されていません。

```text:mt&nbsp;status
# mt -f /dev/nsa0 rewind
# mt -f /dev/nsa0 fsf 3
# mt -f /dev/nsa0 bsf
# mt -f /dev/nsa0 status
Drive: sa0: <QUANTUM ULTRIUM 3 1960> Serial Number: JN0750AME50062
---------------------------------
Mode      Density              Blocksize      bpi      Compression
Current:  0x44:LTO-3           variable       244805   enabled (0x1)
---------------------------------
Current Driver State: at rest.
---------------------------------
Partition:   0      Calc File Number:   2     Calc Record Number: 0
Residual:    0  Reported File Number:   2 Reported Record Number: 61502
Flags: None
```

```text:tape
%<--file 0-->|<--file 1-->|<--file 2-->|
                                       ^
mt bsf
%<--file 0-->|<--file 1-->|<--file 2-->|
                                      ^
```

ヘッドは前のファイルの末尾に移動しています。

### bsfm

FreeBSD の mt には bsfm コマンドは存在しません。

## mt-st（CentOS, Debian）

### bsf

> [man mt](https://manpages.debian.org/stretch/mt-st/mt.1.en.html) より引用
>
           fsf    Forward space count files. The tape is positioned on the
                  first block of the next file.
           bsf    Backward  space  count  files.  The tape is positioned on the
                  last block of the previous file.

``previous file`` の ``last block`` に来ると説明しています。

```text:mt&nbsp;status
# mt -f /dev/nst0 rewind
# mt -f /dev/nst0 fsf 3
# mt -f /dev/nst0 bsf
# mt -f /dev/nst0 status
SCSI 2 tape drive:
File number=2, block number=-1, partition=0.
Tape block size 512 bytes. Density code 0x44 (LTO-3).
Soft error count since last status=0
General status bits on (1010000):
 ONLINE IM_REP_EN
```

```text:tape
%<--file 0-->|<--file 1-->|<--file 2-->|
                                       ^
mt bsf
%<--file 0-->|<--file 1-->|<--file 2-->|
                                      ^
```

この説明は、動作と一致しています。
block number=-1 は「不明」という意味で、[man st](http://manpages.ubuntu.com/manpages/focal/en/man4/st.4.html) には「MTBSF などの後にこの値になる」と書かれています。巻き戻しているので、ファイルの先頭からのブロック番号は分からないということでしょう。

### bsfm

> [man mt](https://manpages.debian.org/stretch/mt-st/mt.1.en.html) より引用
>
           fsfm   Forward space count files, then backward space one record.
                  This leaves the tape positioned at the last block of the file
                  that is count - 1 files past the current file.
           bsfm   Backward space past count file marks, then forward space  one
                  file  record.   This  leaves the tape positioned on the first
                  block of the file that is count-1 files  before  the  current
                  file. 

``bsmf`` は count 回 mark (EOF) を戻ってから 1 つ進み ``first block`` に来ると説明しています。

```text:mt&nbsp;status
# mt -f /dev/nst0 rewind
# mt -f /dev/nst0 fsf 3
# mt -f /dev/nst0 bsfm 2
# mt -f /dev/nst0 status
SCSI 2 tape drive:
File number=2, block number=0, partition=0.
Tape block size 512 bytes. Density code 0x44 (LTO-3).
Soft error count since last status=0
General status bits on (81010000):
 EOF ONLINE IM_REP_EN
```

```text:tape
%<--file 0-->|<--file 1-->|<--file 2-->|
                                       ^
mt bsfm 2
%<--file 0-->|<--file 1-->|<--file 2-->|
                          ^
```

この説明も、動作と一致しています。

## GNU cpio

### bsf

> [man mt](http://manpages.ubuntu.com/manpages/focal/en/man1/mt.1.html) より引用
>
           fsf    Forward space count files.  The tape is positioned on the first
                  block of the next file.
           bsf    Backward space count files.  The tape is positioned on the first
                  block of the next file.

``bsf`` は ``next file`` の ``first block`` に来ると説明しています。

```text:mt&nbsp;status
# mt -f /dev/nst0 rewind
# mt -f /dev/nst0 fsf 3
# mt -f /dev/nst0 bsf
# mt -f /dev/nst0 status
drive type = 114
drive status = 1140851200
sense key error = 0
residue count = 0
file number = 2
block number = -1
```

```text:tape
%<--file 0-->|<--file 1-->|<--file 2-->|
                                       ^
mt bsf
%<--file 0-->|<--file 1-->|<--file 2-->|
                                      ^
```

mt status で確認すると、前のファイルの末尾に移動しています。これは mt-st の動作と同様です。

``mt-st の動作 == mt-st の説明 == GNU cpio の動作 != GNU cpio の説明`` というふうに見えます。この説明は、以下のように解釈すると整合性が保てるものと思われますが、前後を取り違えているように感じられます。

* bsf は巻き戻し操作なので、``next file`` というのは後ろから前に見て ``次のファイル``、つまりテープの先頭から見たら前のファイルのことである
* ``first block`` というのも後ろから前に見て ``最初のブロック``、つまりファイルの先頭から見たら最後のブロックのことである

### bsfm

> [man mt](http://manpages.ubuntu.com/manpages/focal/en/man1/mt.1.html) より引用
>
           fsfm   Forward space count file marks.  The tape is positioned  on  the
                  beginning-of-the-tape side of the file mark.
           bsfm   Backward  space count file marks.  The tape is positioned on the
                  beginning-of-the-tape side of the file mark.
> [man mt](http://manpages.ubuntu.com/manpages/focal/ja/man1/mt.1.html)（日本語）より引用
>
           fsfm   count  で指定された数のファイルマーク分だけ進める。  テープの位置は
                  ファイルマークのテープ先頭側になる。
           bsfm   count で指定された数のファイルマーク分だけ戻す。 テープの位置は
                  ファイルマークのテープ先頭側になる。

``bsfm`` は count 回 mark(EOF) を戻ってから、mark(EOF) のテープ先頭側に来ると説明しています。

```text:mt&nbsp;status
# mt -f /dev/nst0 rewind
# mt -f /dev/nst0 fsf 3
# mt -f /dev/nst0 bsfm 2
# mt -f /dev/nst0 status
drive type = 114
drive status = 1140851200
sense key error = 0
residue count = 0
file number = 2
block number = 0
```

```text:tape
%<--file 0-->|<--file 1-->|<--file 2-->|
                                       ^
mt bsfm 2
%<--file 0-->|<--file 1-->|<--file 2-->|
                          ^
```

実際に動かして mt status で確認すると file 2 の block 0 に移動しています。これも mt-st の bsfm と同じ動作です[^10]。

bsfm についても、``mt-st の動作 == mt-st の説明 == GNU cpio の動作 != GNU cpio の説明`` というふうに見えます。テープヘッドは mark のテープ終端側に停止しているので、man の説明の ``the beginning-of-the-tape side of the file mark`` は前後を取り違えているように感じられます。


## AIX

突然 AIX が出てきてましたが、"beginning-of-the-tape side of the file mark" とはいったいどこなのか調べるために Web を検索していたら、AIX の tctl コマンドの説明もヒットしました。

> [tctl](https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/t_commands/tctl.html) の説明より引用
>
    fsf     Moves the tape forward the number of file marks specified
            by the Count parameter and positions it on the end-of-tape
            (EOT) side of the file mark.
    bsf     Moves the tape backward the number of file marks specified
            by the Count parameter and positions it on the beginning-of-tape
            (BOT) side of the file mark.

この説明にも ``end-of-tape (EOT) side of the file mark``, ``beginning-of-tape (BOT) side of the file mark`` という、GNU cpio と同様の表現が出てきます。

AIX での動作を検証することはできませんでしたが、fsf, bsf の動作は FreeBSD や Linux (mt-st, GNU cpio) と同じだと思われます。bsfm の停止位置は mark のテープ終端側で fsf と同じですので、GNU cpio の man の bsfm の説明は正しくないように思えます。


## Solaris

Solaris も突然の登場ですが、Web 検索で Solaris の mt の man もヒットしました。

> [man mt](https://docs.oracle.com/cd/E26502_01/html/E29030/mt-1.html#scrolltoc) より引用
>
                      fsf       Forward  spaces  over count EOF marks. The tape is
                                positioned on the first block of the file.
                      bsf       Back spaces over count  EOF  marks.  The  tape  is
                                positioned  on  the  beginning-of-tape side of the
                                EOF mark.

この man にも ``beginning-of-tape side of the EOF mark`` という表現が出てきます。fsf では ``first block`` に、bsf では ``beginning-of-tape side of the EOF mark`` に来ると説明しています。

Solaris の動作も検証できていませんが、fsf, bsf の動作は FreeBSD や Linux (mt-st, GNU cpio) と同じだと思われます。bsfm の停止位置は mark のテープ終端側で fsf と同じですので、やはり GNU cpio の man の bsfm の説明は正しくないように思えます。

# まとめ

### テープの構造とテープヘッドの停止位置

細かく書くとこのようになっているものと思われます。

```
                                ___ position A:
                               /    the beginning-of-tape side of mark.
                              /     stop position by bsf (BSD style),
                             /      and called `last block' of file 1
                            /
                           /     __ position B:
                          /     /   the end-of-tape side of mark.
                         /     /    stop position by fsf,
                        /     /     and called `first block' (block 0) of file 2,
                       /     /      and EOF flag is displayed by `mt status' of mt-st
                      /     /
~--+-----+-----+-----+-----+-----+-----+-----+-~
  file 1 data blocks | EOF | file 2 data blocks
~--+-----+-----+-----+-----+-----+-----+-----+-~
     file 1             |    file 2               file number of mt status
~--+-----+-----+-----+-----+-----+-----+-----+-~
```

 * ファイルはテープ上に 1 個以上のブロックとして保存されます。
 * ファイルとファイルを区切るために EOF というブロックが記録されます。EOF ブロックを超えるとファイル番号がインクリメントされます。
 * テープヘッドはブロックとブロックの間に停止します。
   * mt status が block 0 を示しているとき、テープヘッドは block 0 の中ではなく block 0 の直前に停止しています。
 * mt-st の mt status で EOF フラグが表示されているとき、テープヘッドは EOF ブロックの直後（テープ終端側）に停止しています。EOF ブロックの直前（テープ先頭側）ではなく、EOF ブロックの中でもありません。
   * ブロックとしての EOF と、mt-st で EOF フラグが表示される位置に違いがあることに注意してください。
 * 位置としての `last block` は「ファイルの最後のブロックの前」ではなく「ファイルの最後のブロックと EOF ブロックの間（EOF のテープ先頭側）」を差しています。
 * ドキュメント（実装）によって呼び方が違う EOF mark と file mark は同じものを差しています。
 * ドキュメント（実装）によって呼び方が違う block と record は同じものを差しています。


今まで簡易的に書いてきた図は、こう書いた方がもっと正確かもしれません。

```
%<--file 0-->|M|<--file 1-->|M|<--file 2-->|
                            ^ position A
                              ^ position B
```

### 各実装の man ページの説明

| mt       | fsf                                          | bsf                                                | fsfm                                             | bsfm                                             |
|:---------|:---------------------------------------------|:---------------------------------------------------|:-------------------------------------------------|:-------------------------------------------------|
| FreeBSD  | Forward space count files.                   | Backward space count files.                        | -                                                | -                                                |
| mt-st    | the first block of the next file.            | the last block of the previous file.               | the last block of the file that is ...           | the first block of the file that is ...          |
| GNU cpio | the first block of the next file.            | the first block of the next file.                  | the beginning-of-the-tape side of the file mark. | the beginning-of-the-tape side of the file mark. |
| AIX tctl | the end-of-tape (EOT) side of the file mark. | the beginning-of-tape (BOT) side of the file mark. | -                                                | -                                                |
| Solaris  | the first block of the file.                 | the beginning-of-tape side of the EOF mark.        | -                                                | -                                                |

* FreeBSD の説明はヘッドの停止位置について述べていません。
* mt-st の説明は一貫して "block" を用いています。
* GNU cpio の説明が最も複雑に思えます。
    * fsf, bsf では "block" を用いて、fsfm, bsfm では "file mark" を用いて説明しています。
    * bsf の説明はヘッドの移動方向を考慮したものとして読まないと、説明が正しくなりません。
    * fsfm の説明は正しいように思えます[^11]。
    * bsfm の説明は正しいように思えません[^8]。
        * ちなみに [man st(4)](http://manpages.ubuntu.com/manpages/precise/en/man4/st.4.html) の説明では、MTFSFM は ``BOT side of the last filemark``, MTBSFM は ``EOT side of the last filemark`` に移動すると説明されています。
        * さらに [mtio.h](https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/gnu/sys/mtio.h;h=56cfcf13d0b7215a632b2e6b9334e49da1bd7a83;hb=HEAD) のコメントには MTFSFM, MTBSFM ともに ``position at FM`` と書かれています。file mark のどちら側に移動するということまでは書いていないものと思われます。
* AIX の説明は一貫して "file mark" を用いています。
* Solaris の説明には "block" と "EOF mark" が混在しています。


# おわりに

mt status コマンドによるステータスの表示のフォーマットは実装によって違いますが、慣れればテープヘッダがどこにあるのか読み取れるようになります。

実装によって使用できるサブコマンドの違いはあるものの、（確認できた範囲では）同じサブコマンドで違う動作をすることはありません。しかし、同じ動作を別の用語で説明していたり、説明が誤っているように思えるため、理解するのが難しく感じました。

自分がなにか説明の文章を書くとき、正しい説明であると同時に、別の意味に取られかねない表現になっていないか、これからも気をつけようと思いました。



[^1]: https://www.freebsd.org/cgi/man.cgi?query=mt&sektion=1
[^2]: https://svnweb.freebsd.org/base/head/usr.bin/mt/mt.c?view=log#rev1590
[^3]: https://manpages.debian.org/stretch/mt-st/mt.1.en.html
[^4]: ftp://ftp.ibiblio.org/pub/linux/system/backup
[^5]: http://manpages.ubuntu.com/manpages/focal/en/man1/mt.1.html
[^6]: https://www.gnu.org/software/cpio/
[^8]: この説明は少なくとも 1994 年まで[遡れます](https://git.savannah.gnu.org/cgit/cpio.git/commit/?id=63123af5d8b3c3ea5c7768214c12b3d58050de9f)。
[^10]: 同じ mtio.h を参照しているので、同じ結果になるのは当たり前と言えるかもしれません。
[^11]: 1995 年に fsfm が[登場したとき](https://git.savannah.gnu.org/cgit/cpio.git/commit/?id=411f84a01cb63cf15e5e72b1226aac510ca6a6ec)からこの説明をしています。

[^100]: [このページ](https://blog.yukirii.dev/linux-lto-tape-drive-management-commands/#テープの移動方法)の図における bsf 1 は、tar 1 の末尾ではなく tar 2 の末尾に来るのが正しいです。図の位置は bsf 2 を実行したときの結果です。

[^101]: [このページ](https://thinkit.co.jp/cert/compare/5/4/2.htm)の図における bsf 2 は、「DATA03の手前のファイルマークではなく、データそのものの第1ブロック」ではなく DATA03 の末尾に来るのが正しいです。図の位置にヘッドが来ることはありません。

[^102]: [Wikipedia](https://ja.wikipedia.org/wiki/Mt_(UNIX)) の図における bsf 1 は、File 2 の末尾ではなく File 3 の末尾に来るのが正しいです。図の位置は bsf 2 を実行したときの結果です。