ZFS/FreeBSD上でsambaを使ってTimeMachineを作ろうとしたら、壮絶にハマった件

ZFSが動いているFreeBSDサーバ上で、sambaを使ってTimeMachineを作ろうとしたら壮絶にハマった。忘れない内にメモを作ろうと思う。

## まとめ
一通り終了したので、以下のページにまとめ直した。今後の更新は原則以下のページで行う。
なお本ページは見苦しい物となってしまったが、記録として保存しておく。

https://qiita.com/ftns/items/b5f368673a4b21270fdb

https://qiita.com/ftns/items/47a60e918a1cdd49fa9d

https://qiita.com/ftns/items/b023160d5a759eec2b77

## ESET CyberSecurity Proとの問題

:::note warn
ここでESET CyberSecurity Proを取り上げたのは、たまたま筆者が使っているからである。同製品に問題があるとは思わないし、推奨するものでも無い。なお他製品でも同種の問題があり、似たような方法で対処可能であるようだ。
:::

M1 MacBook Air(2020) Big Sur 11.6.2（[動作環境](#動作環境)のClient 3）でTimeMachineを実行すると極端に動作が重くなり、勝手にリブート（kernel panic？）を起こす事もある。
CPU負荷やメモリ消費は問題なさそうだったので、ディスクI/Oが原因であろうと推測した。ディスクI/Oで最初に疑わしいのはアンチウィルスである。
[ESETのサポート情報](https://eset-support.canon-its.jp/faq/show/17398)を参考にして、以下の2つをスキャナの除外に登録した所、問題なく動いているようだ。

```
/System/Applications/Time Machine.app/*.*
/Volumes/.timemachine/*.*
```

なお[動作環境](#動作環境)のClient 1, 3ではこのような設定は必要ない。

## 12/24/2021 現在で分かった事
sambaのTimeMachineボリュームに対するSpotlightサポートは、TimeMachineバックアップとは無関係であると確認できた。

## 12/23/2021 現在でわかっている事
Monterey12.1 （MacBook Pro 14inch M1Max）で試した所、TimeMachineバックアップが終わった時点ではまだSpotlightがindexを作成しているが、終わるまで待ってからumountしている。
よって事故が起こる可能性はまだ0ではないが、**問題は解決したと判断した。**
> Spotlightのindex作成が終了してから、TimeMachineバックアップが終了したと表示する方がより安全だと思うのだが...

なおsamba側でのspotlight supportについては、まだ検証の余地がある。現状ではelasticsearchによるspotlight supportをTimeMachine ボリュームに対して行っている。これを外した状態でどうなるか、`spotlight backend`を`elasticsearch`ではなく`noindex`で動作するか、そもそもsambaにspotlight supportは必須なのかと言う点について、調べる価値があるだろう。

なおelasticsearchによるSpotlight supportについては、別のページにまとめる予定。

https://qiita.com/ftns/items/b023160d5a759eec2b77

## 12/20/2021 現在でわかっている事
* TimeMachineボリュームの内容に対しMac上のSpotlightがindexを作っているにも関わらずTimeMachineバックアップを終了するので、TimeMachineボリュームのumountを失敗しているようである。これは[Mojave(10.14)の問題である可能性](#バックアップイメージが壊れる原因)がある。

## 10/30/2021 現在でわかっている事
* **バックアップイメージが壊れる原因は、SpotlightがTimeMachineボリュームを掴んで離さない為**らしい。TimeMachineボリュームでSpotlightをサポートしてみた所、<del>良好に動作しているように見える。</del>
やはりumountに失敗する。

## 10/13/2021 現在でわかっている事
* Macのログによれば、<del> `file:///`や`/`（実体は謎） への拡張属性の書き込みに失敗しているため</del> 、Time Machineボリュームのumountが失敗している。このためバックアップ開始時に毎回 `fsck_hfs -q` が実行されている模様。何らかのきっかけでこれがエラー`（status 3)`となり、"新規バックアップを作成する必要があります”となってしまう。<del>[AntiVirusのせい？](https://discussions.apple.com/thread/8331542)</del>

* アンチウイルスとしてESET Cyber Security Proを使っているが、以下のようなサポート情報があった。現在検証中。
<blockquote> https://eset-support.canon-its.jp/faq/show/17398
Macの「Time Machine」機能によるバックアップが失敗する | ESETサポート情報
</blockquote>

... 現状関係なさそう

* ZFSを使う場合、拡張属性は必須であるように見える。FreeBSD12以前ではサポートされていない事になっているが、事実上使える。

* ACLは`vfs_zfsacl`があるにも関わらずMacとFreeBSDで共有されない。samba側で必要なら設定する。

* `vfs_freebsd`を使うとかえって正常動作しないように見える。
拡張属性がsambaとMacで共有できない。

* 以下は全て誤り
    * <del>問題はtimemachineを置くdirectoryのpermissionだった。0700でないと、ディスクイメージが壊れたとMacが判断してしまう</del>
    * <del>ZFS特有の設定は特に必要ない。もちろん設定したからと言って、 必ずしも動かなくなる訳では無いだろう。</del>
    * <del>... FreeBSDではZFS上にTimeMachineを作れない？</del>

## 経緯
せっかくZFS/FreeBSDでファイルサーバを運用しているのに、MacbookのTimemachine backupを遅い2.5inch HDDで行っているのがバカバカしくなったのがきっかけである。sambaで作れるのは知っていたので、試行錯誤してみたのだが... 気がついたら1年以上経ってしまったw

<blockquote>
この試行錯誤の間、SATAケーブルのコネクタ部摩耗が原因で接触不良が発生し、その結果HDDが故障したように見えたり、まさかのCPU故障のせいでSATA拡張ボード、マザーボード、RAM、CPUとほぼ全て交換になってしまったのは余談であろう ...orz
</blockquote>

## smb4.conf
12/24/2021現在の試行錯誤の結果の抜粋を以下に示す。

```conf:smb4.conf
[global]
    workgroup = WORKGROUP
    security = USER
    server min protocol = SMB2
    server string = <your server>
    server role = standalone server
    dos filemode = yes
    store dos attributes = yes
    map archive = no
    unix extensions = no
    
    # veto files
    delete veto files = yes
    veto files = /._*/.DS_Store/

    # ACL
    # 不要の可能性があるが、未確認
    force unknown acl user = yes
    inherit acls = yes
    map acl inherit = yes

    # Spotlight
    # TimeMachineには不要
    # spotlight backend = elasticsearch 
    # elasticsearch:address = 127.0.0.1
    # elasticsearch:port = 9200

    # vfs
    vfs objects = zfsacl catia fruit streams_xattr full_audit

    # vfs_zfsacl
    nfs4:chown = yes

    # vfs_fruit
    fruit:zero_file_id = yes
    fruit:metadata = stream
    fruit:veto_appledouble = no
    fruit:resource = xattr
    fruit:model = PowerMac

    # vfs_streams_xattr
    streams_xattr:store_stream_type = no
    streams_xattr:prefix = user.

[TimeMachine]
    comment = TimeMachine
    path = <somewhere on ZFS>
    read only = no
    valid users = <yourname>
    fruit:time machine max size = <volume size>
    fruit:time machine = yes
    browseable = no
    writable = yes
    # spotlight = yes
```

## 何故かバックアップイメージが壊れる
これで動くだろうと思える設定で運用してみた所、段々とバックアップ失敗が増え始め、概ね１ヶ月程度で
![crush-timemachine.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428443/35f02597-af45-8043-5f2e-3c39c79168f1.png)
となり、TimeMachineイメージを一から作り直すはめになる。
最初は単なる偶然かと思ったが、何度も同じ結果となってしまう。

色々検索してみたところ、Appleのサポートページ（参考URL.1）に

> 新規バックアップを作成する必要性を最小限に抑えるため、以下に従うようにしてください:
>（中略）
> ・ネットワーク・ストレージ・デバイスで書き込みキャッシュが有効になっている場合は、無効にします。

とあるのを見つけた。

この記述より、oplock関連の設定を行ったり、zfs poolに加えていたcache SSDを取り除いてみたが、症状は変わらなかった。

> その後散々試行錯誤を重ねた結果、oplockについては設定不要、zfsのSSDキャッシュは問題ないと判明した。

### 拡張属性（xattr, EA）[^1]が原因か？
`vfs_full_audit` の出力を眺めていると、
<pre><code>Jun 28 20:09:21 <サーバ名> smbd_audit: <ユーザ名> | <クライアントIPアドレス> |getxattr|fail (Attribute
 not found)|.|user.org.netatalk.Metadata
</code></pre>
と言った拡張属性関連のエラーが多数出ている。
これを修正したいのだが、FreeBSDのzfsのマニュアルには、
<pre><code>xattr=off | on
         The xattr property is currently not supported on FreeBSD.
</code></pre>とあった。

**これは詰んだか？**

[^1]: 拡張属性(extended attributes)は、xattr 又は EA と略されるようである。

### その後...
しばらく放り出していたが、以下のような事に気がついた。

* zfs上ではFreeBSD 12以前でも[拡張属性が使えるらしい](https://www.caspernet.net/~wata/?p=1801\)
* ACLを設定していない為、拡張属性が読み書きできないのでは？
* TimeMachineに拘らず、通常の共有ディレクトリでACLやEAの動作を試してみるべき

##### ACL関連

現状では**MacとSambaの間でACLは共有されない。**
結論としては、TimeMachineを使う上で**ACLは不要だと思われる[^2]。**

[^2]: ただしACLをサポートしない環境に戻してテストしていないので、サポートが必要である可能性は否定できない。

ACLを使うなら、以下の設定が必要。

```shell:
# zfsへの設定 
$ sudo zfs set aclmode=passthrough <pool>/<filesystem>
$ sudo zfs set aclinherit=passthrough <pool>/<filesystem>
```

##### 拡張属性関係
FreeBSD 12以前では、`options UFS_EXTATTR`を追加して[カーネル再構築](https://docs.freebsd.org/ja/books/handbook/kernelconfig/)する。
... ZFSで完結すれば不要かもしれないが、未調査。

##### なお残る問題
一般ディレクトリで正常に拡張属性が共有されるようになっても、なお拡張属性関連のエラーが出る。
<del>これが[AntiVirusのせい](https://discussions.apple.com/thread/8331542)かもしれない</del>ので、検証中 → 問題と思われる挙動は、AntiVirusをアンインストールしても変わらなかった。
推測だが、正常な動作として拡張属性を消去しようとして存在しないと言う動作が、ログにエラーとして現れているようである。

```
# ログの例（Mac側）
2021-12-19 21:47:02.130 E   Failed to remove attribute 'com.apple.backupd.SnapshotVolumeFSEventStoreUUID' from 'file:///', error: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"
2021-12-19 21:47:02.131 E   Failed to remove attribute 'com.apple.backupd.SnapshotVolumeLastFSEventID' from 'file:///', error: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"
2021-12-19 21:47:02.132 E   Failed to remove attribute 'com.apple.backupd.SnapshotVolumeUUID' from 'file:///', error: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"
```

### バックアップイメージが壊れる原因
Mac側のログによれば、バックアップイメージが壊れる原因は以下の通り。

バックアップ終了後に、`/Volumes/Time Machineバックアップ` と `/Volumes/com.apple.TimeMachine.<volume名>
-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` のアンマウントに失敗している。
バックアップ開始時には`fsck_hfs -q` が実行されている。前回のバックアップでバックアップイメージをアンマウントできていないので、何らかのきっかけでエラーになる事がある。これが **新規バックアップを作成する必要があります** と言うエラーになっている。

アンマウントが失敗する理由はなかなか判明しなかったが、ある時 `/Volumes/Time Machineバックアップ/` をアンマウントしようとした所：

```shell:
$ diskutil umount /Volumes/Time\ Machineバックアップ/
Volume Time Machineバックアップ on disk2s2 failed to unmount: dissented by PID 232
(/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Support/mds_stores)
```

と言うエラーが出た。この`mds_stores`とはSpotlightの実体であるので、Spotlightが`Time Machineバックアップ`ボリュームのindexを作成している為アンマウントに失敗していると思われる。
この対処として最も簡単な方法は**sambaでSpotlightをサポート**する事かと思ったが、**残念ながらそれでは解決しない。**

Mojave(10.14)において、 `/Volumes/com.apple.TimeMachine.<volume名>
-＊`はsambaで公開しているTimeMachineボリュームをそのままマウントしている。Macはこの中身をsparsebundleとして解釈し、`/Volumes/Time Machineバックアップ/`としてマウントしている。
したがってindexを作っているのはMacであり、sambaからはどうしようもない。

> LinuxでTimeMachineボリュームをsparsebundleとして解釈し、マウントする方法は存在する（[FUSE filesystem for reading macOS sparse-bundle disk images](https://github.com/torarnv/sparsebundlefs) と [Apple's Time Machine fuse read only file system](https://github.com/abique/tmfs) を使う）[^3]ので、FreeBSDでも同様に行う事が可能であろう。
> しかしFreeBSD上でindexを作成しても、Mac側に伝える方法がない。

この問題は本来ならindex作成が終わるまでTimeMachineが待ってから、TimeMachineボリュームをアンマウントすれば良いだけである。これを実現するプログラムを書くことは困難だが、TimeMachineやSpotlightを改造できるなら容易である。
Appleがこの問題に気が付いていないとは考えにくい。実際に[Mojave(10.14)で起きるTimeMachineエラーを解消するには、Catalina(10.15)にバージョンアップせよと言う情報](https://www.reddit.com/r/applehelp/comments/eij9nx/time_machine_backups_failing_with_network_error/)もあるようだ。

これまで使って来たMacBookのOSをバージョンアップすれば確認できるが、都合[^4]により当分行えない。近日中に新しいMacBookを入手できる筈なので、そちらで検証を続ける予定である。

workaroundとして以下のようなshell script`(workaround.sh)`を作り、cron[^5]で動かしている。
運頼みなのでこれで壊れなくなる訳ではないが、無いよりはマシである。

<details><summary>`workaround.sh`</summary><div>

```shell:tm_workaround.sh
#!/bin/bash

isAC(){
  pmset -g ac | awk '/No adapter attached/ { exit 1 }'
}

if [ -f $HOME/.tm_running ]; then
  exit 0
fi

if ! isAC; then
  exit 0
fi

touch $HOME/.tm_running >/dev/null 2>&1

tmutil startbackup --block >/dev/null 2>&1
while [ -d /Volumes/Time\ Machineバックアップ ]; do
  sleep 60
  diskutil umount /Volumes/Time\ Machineバックアップ >/dev/null 2>&1
done
if [ -d /Volumes/com.apple.TimeMachine.* ]; then
  diskutil umount force /Volumes/com.apple.TimeMachine.<TMvolname>-* >/dev/null 2>&1
  # <TMvolname>はTimeMachineのボリューム名に置き換える
fi

rm $HOME/.tm_running
```
</div></details>

なおこのスクリプトはMojaveでしか動作検証していない。少なくともBig Sur(11.6.2), Monterey(12.1)ではTimeMachineボリューム名が違う[^6]ので、全く意味が無い。

[^3]: [Can Linux mount a normal Time Machine sparse bundle disk image directory? - Super User](https://superuser.com/questions/306497/can-linux-mount-a-normal-time-machine-sparse-bundle-disk-image-directory)より
[^4]: 32bit アプリケーションが必要な物で。
[^5]: <del> MacOSにcronは付属していない。Homebrewに存在するが、launchdへの登録は自分で行う必要があるようだ。</del>
[^6]: Big Sur(11.6.2), Monterey (12.1)では、sambaが公開しているボリュームは`/Volumes/.timemachine/<サーバ名>._smb._tcp.local./＊/<TimeMahineボリューム名>/`にマウントされており、その中のsparsebundleをMacが解釈して、`/Volumes/<mac名>のバックアップ/`にマウントしている。


### 動作環境
* Server:
  - OS: FreeBSD 11.4-RELEASE-p12 → 12.2-RELEASE-p11
  - samba: 4.13.8 → 4.13.14
* Client 1: MacBook (Retina 12inch Early 2016)
  - CPU: Intel Core m7 (1.3GHz)
  - RAM: 8Gbyte 
  - OS: Mojave(10.14.6)
  - Anti Virus: ESET Cyber Security Pro 6.8.1.0 → 6.10.700.0
* Client 2: MacBook Pro（14inch 2021）
  - CPU: Apple M1 Max (10core 32GPU)
  - RAM: 64Gbyte
  - OS: Monterey (12.1)
  - Anti Virus: ESET Cyber Security Pro 6.11.2.0
* Client 3: MacBook Air (M1 2020) 
  - CPU: Apple M1 (8core 7GPU)
  - RAM: 16Gbyte
  - OS: Big Sur (11.6.2)
  - Anti Virus: ESET Cyber Security Pro 6.11.2.0

### 参考URL
1. [**Macから新しいバックアップを作成する必要がある場合 - Apple サポート**] (https://support.apple.com/ja-jp/guide/mac-help/mh34042/11.0/mac/11.0)<br>
TimeMachineのイメージが壊れた時の対処法
4. [Samba4を「ふつうに」使おう！(2015/08/08 OSC 2015 Kansai@Kyoto)](https://www.slideshare.net/takahashimotonobu/samba420150809-osc-2015-kansaikyoto)<br>
Samba設定の常識・定石が解説されており、大変参考になった。
2. [FreeBSD ZFS で Samba | Netsphere Laboratories](https://www.nslabs.jp/freebsd-zfs-samba.rhtml)<br>
ZFSのACL関係で大変参考になった。
3. Sambaのマニュアル<br>
  [**`man smb.conf`**]
(http://www.samba.gr.jp/project/translation/current/htmldocs/manpages/smb.conf.5.html) — Samba の設定ファイル
<br>
  [**`man vfs_fruit`**](http://www.samba.gr.jp/project/translation/4.2/htmldocs/manpages/vfs_fruit.8.html) — OS X と Netatalk との相互運用性を強化する
<br>
  [**`man vfs_streams_xattr`**]
(http://www.samba.gr.jp/project/translation/current/htmldocs/manpages/vfs_streams_xattr.8.html) — 代替データストリームを POSIX の拡張属性に格納する
<br>
  [**`man vfs_zfsacl`**]
(http://www.samba.gr.jp/project/translation/current/htmldocs/manpages/vfs_zfsacl.8.html) — ZFS ACL samba モジュール