ZFSプールを壊してみた

# はじめに
ZFSにはファイルシステムそのものにチェックサムが用意されていて、データを読みだす際に常時チェックサムとの整合性を確認してデータが化けいてないかどうかを確認しています。

zpool scrub というコマンドを実行すると、書き込まれているデータ部をすべて読みだしてチェックサムとの整合性を確認します。さらにscrubでエラーが発見された場合には、修復可能(ミラーやRAID-Z等の冗長性ある構成の場合)であればエラーは自動的に修復されます。

ここでは、実験的に書き込んだデータをZFS管理外のところで書き換えることチェックサムとの不整合後を故意に発生させてデータエラーを起こしてみます。

この実験ではFreeBSDを利用しましたが、Ubuntu等のLinux系でのZFSであれば、パーティション作成やディスクのデバイス名が変わりますが、ZFSの操作は同じコマンドでできます。

# 淡々とZFSプールを作って壊してみる

## 実験用のディスクにパーティションを作成する

まずは実験用のプールの作成です。ここでは USB ディスクの da1 で実験を行います。

GPTラベルのディスクを用意します。
```
$ gpart create -s gpt da1
```
4GBのパーティションを作成します。

```
$ gpart add -t freebsd-zfs -a 4k -s 4g -l ttt da1
```

出来上がったパーティションを確認します

```
$ gpart show da1
=>       40  312581728  da1  GPT  (149G)
         40    8388608    1  freebsd-zfs  (4.0G)
    8388648  304193120       - free -  (145G)

$ gpart show -l da1
=>       40  312581728  da1  GPT  (149G)
         40    8388608    1  ttt  (4.0G)
    8388648  304193120       - free -  (145G)

```
## ZFSプールの作成

通常であればディスク容量節約のため ZFSプールを作成する場合 `-O compression=lz4` 等を指定して圧縮機能を有効にするのですが、今回はゼロデータを書き込むため指定しません。指定するとデータの圧縮機能によってデータサイズ分の書き込みが行われなくなってしまうからです。
```
$ zpool create -O atime=off ztest gpt/ttt
```
出来上がったZFSプールを確認します。

```
$ zpool status ztest
  pool: ztest
 state: ONLINE
config:

        NAME        STATE     READ WRITE CKSUM
        ztest       ONLINE       0     0     0
          gpt/ttt   ONLINE       0     0     0

errors: No known data errors
```
## ダミーのデータを書き込む

scrubでデータの整合性の確認が行われるのは、データが実際に書き込まれている部分だけです。書き込まれているデータを壊すために、あらかじめ内容が0のファイルを作成します。

今回用意したZFSプール内のディレクトリに移動します。
```
$ cd /ztest
```
ddコマンドを使って/dev/zeroから読みだしたダミーデータ(つまり0データ)でプール全体を埋めます。

```
$ dd if=/dev/zero of=dummy bs=4m
dd: dummy: No space left on device
928+0 records in
927+1 records out
3891134464 bytes transferred in 157.300331 secs (24736976 bytes/sec)
$ 
```
この通りプールを使い切っています。ここではブロック数を確認したかったので明示的に `-b` オプションを指定しています。


```
$ df -b /ztest
Filesystem 512-blocks    Used Avail Capacity  Mounted on
ztest         7601024 7601024     0   100%    /ztest
```

## データに不整合が起きるように壊してみる

プール内が埋まったところで、ZFSのデータ整合性にエラーが起きるように壊してみます。ZFS経由での変更では壊せませんので、ここではZFS外部からディスクにゴミデータを書き込んでみます。

一旦プールをエクスポートします

```
$ zpool export ztest
```

該当パーティションの適当なところに1ブロック分ゴミデータを書き込みます。

```
$ dd if=/dev/random of=/dev/gpt/ttt count=1 oseek=4000000
1+0 records in
1+0 records out
512 bytes transferred in 0.382043 secs (1340 bytes/sec)
$ 
```

## 壊れたかどうかの確認

プールをインポートして、先ほど作ったファイルを読みだしてみます

```
$ zpool import ztest
$ cat /ztest/dummy > /dev/null
cat: /ztest/dummy: Input/output error
$ 
```
この通りエラーが発生しました。プールの状態は次の通りで一見するとエラーは無いように見えます。

```
$ zpool status ztest
  pool: ztest
 state: ONLINE
config:

        NAME        STATE     READ WRITE CKSUM
        ztest       ONLINE       0     0     0
          gpt/ttt   ONLINE       0     0     0

errors: No known data errors
$ 
```

scrub を実行してみます。

```
$ zpool scrub ztest
$ zpool status ztest
  pool: ztest
 state: ONLINE
  scan: scrub in progress since Mon Aug 29 18:17:33 2022
        3.62G scanned at 412M/s, 550M issued at 61.1M/s, 3.62G total
        0B repaired, 14.81% done, 00:00:51 to go
config:

        NAME        STATE     READ WRITE CKSUM
        ztest       ONLINE       0     0     0
          gpt/ttt   ONLINE       0     0     0

errors: No known data errors
$ 
```

scrub が終了するのを待って、status で確認してみます。

```
$ zpool status ztest
  pool: ztest
 state: ONLINE
status: One or more devices has experienced an error resulting in data
        corruption.  Applications may be affected.
action: Restore the file in question if possible.  Otherwise restore the
        entire pool from backup.
   see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-8A
  scan: scrub repaired 0B in 00:01:05 with 1 errors on Mon Aug 29 18:18:32 2022
config:

        NAME        STATE     READ WRITE CKSUM
        ztest       ONLINE       0     0     0
          gpt/ttt   ONLINE       0     0     2

errors: 1 data errors, use '-v' for a list
$
```
この通りCKSUMでエラーが発生していることがわかります。実際に壊れているファイルは -v オプションで確認できます。

```
$ zpool status -v ztest
  pool: ztest
 state: ONLINE
status: One or more devices has experienced an error resulting in data
        corruption.  Applications may be affected.
action: Restore the file in question if possible.  Otherwise restore the
        entire pool from backup.
   see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-8A
  scan: scrub repaired 0B in 00:01:05 with 1 errors on Mon Aug 29 18:18:32 2022
config:

        NAME        STATE     READ WRITE CKSUM
        ztest       ONLINE       0     0     0
          gpt/ttt   ONLINE       0     0     2

errors: Permanent errors have been detected in the following files:

        /ztest/dummy
$
```
改めて /ztest/dummy を読みだしてみても、エラーはエラーのままです。

```
$ cat /ztest/dummy > /dev/null
cat: /ztest/dummy: Input/output error
$
```
# 壊した部分の修復と確認

では壊した部分に改めて0を書きこむことで本来のデータに戻してみます。

```
$ zpool export ztest
$ dd if=/dev/zero of=/dev/gpt/ttt count=1 oseek=4000000
1+0 records in
1+0 records out
512 bytes transferred in 0.382715 secs (1338 bytes/sec)
$
```
これで元のデータに書き戻せたはずなので、改めて読みだしてみます。

```
$ zpool import ztest
$ cat /ztest/dummy > /dev/null
$
```
この通りエラーも発生せずに読みだすことができました。zpool status ではエラーメッセージが残ったままになりますが、CKSUMのエラーカウントは0になっています。

```
$ zpool status ztest
  pool: ztest
 state: ONLINE
status: One or more devices has experienced an error resulting in data
        corruption.  Applications may be affected.
action: Restore the file in question if possible.  Otherwise restore the
        entire pool from backup.
   see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-8A
  scan: scrub repaired 0B in 00:01:05 with 1 errors on Mon Aug 29 18:18:32 2022
config:

        NAME        STATE     READ WRITE CKSUM
        ztest       ONLINE       0     0     0
          gpt/ttt   ONLINE       0     0     0

errors: 1 data errors, use '-v' for a list
$ 
```
改めて scrub を実行してみます

```
$ zpool scrub ztest
```
scrub が終了したらプールの状態を確認してみます。

```
$ zpool status ztest
  pool: ztest
 state: ONLINE
  scan: scrub repaired 0B in 00:01:05 with 0 errors on Mon Aug 29 18:34:27 2022
config:

        NAME        STATE     READ WRITE CKSUM
        ztest       ONLINE       0     0     0
          gpt/ttt   ONLINE       0     0     0

errors: No known data errors
$
```
この通りエラーは無くなりました。