tar コマンドを使ってテープにバックアップ・リストアしてみた

# はじめに
サーバ管理者なら誰でも実行したことがある ``tar`` コマンドですが、先輩から「実は Tape Archive なんだよ」と教わった記憶があります[^1]。

というわけで、テープを使ってバックアップ・リストアをしてみました。FreeBSD の tar[^2] でテープドライブを操作する様子を動画で紹介しながら、コマンドを解説していきます。

## テスト環境
* OS ... FreeBSD 12.1
* テープドライブ ... Quantum TC-L32AX (LTO3)
* SCSI カード ... Adaptec AHA-2940U2W
* SCSI ケーブル ... 内蔵用 68pin ケーブル

テープドライブは /dev/sa0, /dev/nsa0 として見えています。[man sa(4)](https://www.freebsd.org/cgi/man.cgi?sa(4)) にあるように、``sa`` は "Rewind on close" 、``nsa`` は "No rewind on close" なデバイスです。今回は複数のバックアップを取って行ったり来たりするので ``/dev/nsa0`` を使います。

## テープの操作
テープドライブを扱うには、テープにはどのような構造で記録されるかを理解しておく必要があります。

```
%<--file 0-->|<--file 1-->|
% ... BOP (beginning of the partition)
| ... EOF
```

``file``は名前のないデータのかたまりで、テープの中のファイル番号は 0 から始まります。また、EOF はファイルの末尾の次の位置に記録されるファイルの終端マークで、ファイルの一部ではありません。
file 0 の終端マークは、同時に file 1 の開始を表すのに用いられるようで、mt status の File Number は以下のように EOF からを次のファイルとして報告するようです。

```
%<--file 0-->|<--file 1-->|
<---- 0 ----><---- 1 ----><-- 2 ... File Number
```

書き込み・読み出しは、BOP または EOF にヘッドを移動させてから行います。そのため、``mt`` の fsf, bsf や、``tar`` コマンドを実行したあとに、テープヘッドがどの位置に移動されるのかを把握しておく必要があります。

| 操作    | 操作後のヘッド位置 |
|:--------|:-------------------|
| mt fsf  | 次のEOF |
| mt bsf  | 前のEOFを（巻き戻し方向に）超えたところ=前のファイルの末尾 |
| tar cvf | ファイルを書き込んだ直後のEOF |
| tar tvf | 内容を表示したファイルの末尾 |
| tar xvf | 展開したファイルの末尾 |

fsf, bsf は移動する数を指定することもできます。``EOF`` の位置から fsf で移動するときは、今いる EOF はカウント__されません__。しかし、``EOF`` の位置から bsf で移動するときは、今いる EOF がカウント__されます__ので注意が必要です。

```
%<--file 0-->|<--file 1-->|<--file 2-->|
                          ^               initial position
                                       ^  mt fsf
                         ^                mt bsf
            ^                             mt bsf 2
```

# 実際のバックアップ・リストア

## バックアップ

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">tar コマンドを使ってテープドライブのテープにバックアップする様子 <a href="https://t.co/g7UoiaVJta">pic.twitter.com/g7UoiaVJta</a></p>&mdash; maya_shack (@maya_shack) <a href="https://twitter.com/maya_shack/status/1316019618616500225?ref_src=twsrc%5Etfw">October 13, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

まずテープの状況を確認します。

```
# mt -f /dev/nsa0 status
(snip)
Partition:   0      Calc File Number:   0     Calc Record Number: 0
Residual:    0  Reported File Number:   0 Reported Record Number: 0
Flags: BOP
```

```
%
^
```

テープドライブのヘッドはテープの先頭（BOP）にあります。

backup1 をテープの今の位置から書き込みます。

```
# tar cvf /dev/nsa0 backup1
a backup1
# mt -f /dev/nsa0 status
(snip)
Partition:   0      Calc File Number:   1     Calc Record Number: 0
Residual:    0  Reported File Number:   1 Reported Record Number: 1026
Flags: None
```

```
%<--file 0-->|
             ^
```

tar で書き込みが終わると、テープドライブのヘッドはファイルを書き込んだ直後の EOF に来ています。

次に backup2 をテープの今の位置から書き込みます。

```
# tar cvf /dev/nsa0 backup2
a backup2
a backup2/file2
a backup2/file1
# mt -f /dev/nsa0 status
(snip)
Partition:   0      Calc File Number:   2     Calc Record Number: 0
Residual:    0  Reported File Number:   2 Reported Record Number: 1642
Flags: None
```

```
%<--file 0-->|<--file 1-->|
                          ^
```

テープドライブのヘッドはファイルを書き込んだ直後の EOF に来ています。


## リストア

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">tar コマンドを使ってテープドライブのテープからリストアする様子 <a href="https://t.co/Ee9ekMB8WT">pic.twitter.com/Ee9ekMB8WT</a></p>&mdash; maya_shack (@maya_shack) <a href="https://twitter.com/maya_shack/status/1316019715140022274?ref_src=twsrc%5Etfw">October 13, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

テープを先頭まで巻き戻します。

```
# mt -f /dev/nsa0 rewind
# mt -f /dev/nsa0 status
(snip)
Partition:   0      Calc File Number:   0     Calc Record Number: 0
Residual:    0  Reported File Number:   0 Reported Record Number: 0
Flags: BOP
```

テープのヘッドドライブはテープの先頭に来ています。

テープの今の位置（BOP）から抽出します。

```
# tar xvf /dev/nsa0
x backup1
# mt -f /dev/nsa0 status
(snip)
Partition:   0      Calc File Number:   0     Calc Record Number: 1025
Residual:    0  Reported File Number:   0 Reported Record Number: 1025
Flags: None
```

```
%<--file 0-->|<--file 1-->|
            ^
```

tar での抽出が終わると、テープドライブのヘッドは今（0番目）のファイルの最後尾（EOFではない）に来ています。

テープのヘッダを次の EOF に移動し、次のファイルを扱えるようにします。

```
# mt -f /dev/nsa0 fsf
# mt -f /dev/nsa0 status
(snip)
Partition:   0      Calc File Number:   1     Calc Record Number: 1026
Residual:    0  Reported File Number:   1 Reported Record Number: 1026
Flags: None
```

```
%<--file 0-->|<--file 1-->|
             ^
```

テープの今の位置からファイルの一覧を表示してみます。

```
# tar tvf /dev/nsa0
drwxr-xr-x  0 root   wheel       0 Sep 21 17:40 backup2/
-rw-r--r-x  0 root   wheel 4194304 Sep 21 17:40 backup2/file2
-rw-r--r-x  0 root   wheel 2097152 Sep 21 17:40 backup2/file1 
# mt -f /dev/nsa0 status
(snip)
Partition:   0      Calc File Number:   1     Calc Record Number: 615
Residual:    0  Reported File Number:   1 Reported Record Number: 1641
Flags: None
```

```
%<--file 0-->|<--file 1-->|
                         ^
```

tar でファイルの一覧を表示した後も、テープドライブのヘッドは表示したファイル（1番目）の最後尾に来ています。

もういちど同じファイルを操作するために、直前の EOF に移動します。これを 1 回で操作できるコマンドはない[^3]ので、mt bsf で一つ前（0番目）のファイル（の最後尾）に移動して、mt fsf で次の EOF に移動します。

```
# mt -f /dev/nsa0 bsf
# mt -f /dev/nsa0 status
(snip)
Partition:   0      Calc File Number:   0     Calc Record Number: 0
Residual:    0  Reported File Number:   0 Reported Record Number: 1025
Flags: None
```

```
%<--file 0-->|<--file 1-->|
            ^
```

```
# mt -f /dev/nsa0 fsf
# mt -f /dev/nsa0 status
(snip)
Partition:   0      Calc File Number:   1     Calc Record Number: 0
Residual:    0  Reported File Number:   1 Reported Record Number: 1026
Flags: None
```

```
%<--file 0-->|<--file 1-->|
             ^
```

テープの今の位置から抽出します。

```
# tar xvf /dev/nsa0
x backup2/
x backup2/file2
x backup2/file1
# mt -f /dev/nsa0 status
(snip)
Partition:   0      Calc File Number:   1     Calc Record Number: 615
Residual:    0  Reported File Number:   1 Reported Record Number: 1641
```

```
%<--file 0-->|<--file 1-->|
                         ^
```

tar での抽出が終わったときも、テープドライブのヘッドは今（1番目）のファイルの最後尾に来ています。

```
# mt -f /dev/nsa0 offline
```

テープを巻き戻して取り出します。


# わかったこと

Web を検索すると、Linux で mt を操作する資料はすぐ見つかりましたが、FreeBSD での資料はあまり見つかりませんでした。Linux の資料を見ながら試していたのですが、FreeBSD の [mt](https://www.freebsd.org/cgi/man.cgi?mt(1)) と Linux の [mt](https://linuxjm.osdn.jp/html/GNU_cpio/man1/mt.1.html) には違いがあり、出力やコマンドが異なります。

* FreeBSD ではテープヘッドが先頭にあるときの mt status の出力に、BOT (Beginning of Tape) ではなく BOP (beginning of the partition) が表示されます。
* FreeBSD ではテープヘッドが EOF にあっても、mt status の出力にそのことが表示されません。
* FreeBSD では bsfm, fsfm で移動することができません。

特に移動コマンドの移動先についての説明が違うため、動作の仕様を理解するのに時間がかかりました。この違いについては、別の記事で解説してみたいと思います。

（2020/12/20 追記）

「[mt コマンドの違いについて](https://qiita.com/maya_shack/items/99b2e44705df79cce97d)」を書きました。


[^1]: Linux の [man tar(1)](http://manpages.ubuntu.com/manpages/focal/en/man1/tar.1.html) では "an archiving utility" と紹介されていますが、FreeBSD の [man tar(1)](https://www.freebsd.org/cgi/man.cgi?tar(1)) では "manipulate tape archives" と紹介されています。 

[^2]: もちろん、Linux の tar でもテープにバックアップを取ることができます。

[^3]: Linux では ``mt bsfm 1`` で移動できます。