TimeMachineサーバを動かしてわかった事

ZFS/FreeBSD上でTimeMachineを動かそうとして[七転八倒した過程](https://qiita.com/ftns/items/13aa5783610d9dd2b84f)
でわかったことを、メモとしてまとめておく。

設定等は以下のページにまとめた。

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

なおこの知見は公開情報とMacのログからの**推測**である。当然一切の保証等は存在しない。参考にされる場合は、Your own riskでお願いする。

# 調査環境
* Server:
    - OS: FreeBSD 12.2-RELEASE-p11
    - samba: 4.13.14 (ports)
* Client 1: MacBook (Intel Core m7 Early 2016)
    - OS:
        - （内蔵SSD）Mojave(10.14.6)
        - （予定：外部SSD）Catalina(10.l5) 
* Client 2: MacBook Pro（M1Max 14inch 2021）
    - OS: Monterey (12.1)
* Client 3: MacBook Air (M1 2020) 
    - OS: Big Sur (11.6.2)

# TimeMachineの動作
## samba側

* __1.__ sambaは起動時に`smb4.conf`を調べ、`fruit:time machine = yes`のボリュームがあればそれを**mDNS**(avahi, mDNSResponder)に**登録**する。
* __2.__ TimeMachine用ボリュームを公開する。動作は普通のSMBボリュームと同一であり、特別扱いはしない。


## Mac側
* __1.__ MacはmDNS情報より、sambaが公開しているTimeMachineボリュームを知る。
* __2.__ Macはsambaが公開しているTimeMachineボリュームをマウントする。このボリュームを本稿では**rawボリューム**と呼ぶ。rawボリュームのpathはMac 側のOSにより異なる。

| OS | path |
|:---:|:---|
| Mojave | `/Volumes/com.apple.TimeMachine.<vol名>-<UUID>` |
| Catalina, Big Sur, Monterey | `/Volumes/.timemachine/<サーバ名>._smb._tcp.local./<UUID>/<Vol名>`[^1] |

[^1]: マウントポイントは `/Volumes/.timemachine/<サーバ名>._smb._tcp.local./<UUID>/` だが、umountすると<UUID>ディレクトリは消えて、`/Volumes/.timemachine/<サーバ名>._smb._tcp.local./`のみが残る。<br />またマウント中は`/Volumes/.timemachine/<サーバ名>._smb._tcp.local./<UUID>/` と言うディレクトリが２つ できる。一方はrawボリュームが入っているが、もう一方は空のようである。

* __3.__ Macはrawボリューム直下にある`<mac名>.sparsebundle`サブディレクトリをsparsebundleと解釈してマウント[^5]する。このボリュームを本稿では**TMボリューム**と呼ぶ。TMボリュームのpathもOSにより異なる。

| OS | path |
|:---:|:---|
| Mojave, Catalina | `/Volumes/Time Machineバックアップ` |
| Big Sur, Monterey | `/Volumes/<mac名>のバックアップ` |

[^5]: この為samba側からTMボリュームの内容を操作する事はできない。LinuxでTimeMachineボリュームをsparsebundleとして解釈し、read onlyでマウントする方法は存在する（[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) を使う）。よってFreeBSDでも同様に行う事は可能と思われる。

* __4.__ **Mojave, Catalina**では、TMボリュームに対して `fsck_hfs -q`を実行する。エラー（filesystemがdirtyである）が返ってくると**新規バックアップを作成する必要があります**と表示され、バックアップは中断する。
**Big Sur, Monterey**ではこのチェックは行われない[^6]。

[^6]: ログに`Skipping periodic backup verification: not needed for an APFS sparsebundle`と明示される。

* __5.__ TMボリュームに対してバックアップデータを書き込む。それと同時に**TMボリュームに対してSpotlight index作成**を行う。

* __6.__ バックアップの書き込みが終了すると、TimeMachine環境設定は**バックアップが終了したように表示**する。しかし大抵の場合、**index作成はその時点では終了していない。**
    - **Monterey**ではSpotlight index作成が終了するのを待ってからTMボリューム、rawボリュームをumountする。**Big Sur**も同様と思われる[^4]。
    - **Mojave**では、Spotlight index作成中にも関わらずumountしようとするので、**umountを失敗する事がある**。<br /> これが原因で**TMボリュームが正常にcloseされずdirtyとなってしまう**事があり、上記 __4.__ の通りバックアップは終了してしまう。

[^4]: Montereyではログから確認できたが、Big Surでは確認できなかった。

:::note warn
<b>Monterey, Big Sur</b>でもWiFi切断等の原因でSpotlight index作成が終了する前にTMボリューム、rawボリュームが<b>強制的にumountされてしまう事があり得る</b>。<br >Mac上のTimeMachine関連ログを見ると、Big Sur, MontereyではTMボリュームが正常にマウントしているか何度も確認している。よって何らかの対処を行なっていると思われるが、現状では判明していない。
:::

:::note info
USBで繋いだ外部SSDにTimeMachineバックアップを行った場合、MontereyではSpotlight index作成が終わってからTimeMachine環境設定の表示も終わっているようである。この為**外部ディスクへのバックアップは事故が少ない**のだろう。
:::

# raw ボリュームの構造

TimeMachineバックアップを行なったrawボリュームは、以下のような構成となる。

```
<timemachine path>/<mac名1>.sparsebundle/bands/...
                                       (/mapped/... Catalina以降)
                                        /com.apple.TimeMachine.MachineID.bckup
                                        /com.apple.TimeMachine.MachineID.plist
                                        /com.apple.TimeMachine.Results.plist
                                        /com.apple.TimeMachine.SnapshotHistory.plist
                                        /Info.bckup
                                        /Info.plist
                                        /token
                                          :
                                          :
 
                  /<mac名2>.sparsebundle/...
                                          :
                                          :
```
TimeMachineのpath直下には、`<mac名>.sparsebundle`と言うサブディレクトリが（TimeMachine バックアップを行なったMacの数だけ）ある。
このサブディレクトリはsparsebundleの構造[^2]となっている。

[^2]: sparsebundleの仕様を見つける事はできなかった。公開情報としては存在しないのか？

`bands`の中にはファイル名が16進数で構成されるファイルが多数存在する。各ファイルの大きさはほぼ同一（少数の例外あり）であるが、Wikipediaの記述[^3]のように8Mbyteとは限らない。確認できた範囲では、Mojaveでは8Mbyte, Catalinaでは64Mbyte, Big Sur, Montereyでは256Mbyteであった。なおOSバージョンアップ時にどうなるかは未確認である。
なおCatalina以降では`mapped`と言うディレクトリがあり、`bands`の内容と同じ名前のファイルが同数存在する。ファイルサイズは皆8Kbyteであった。

[^3]: Wikipedia：[Appleディスクイメージ](https://ja.wikipedia.org/wiki/Appleディスクイメージ)の記述より（1/8/2022閲覧）

このように一つのボリューム内に複数のmacのバックアップイメージが作られるので、複数人で一つのTimeMachineボリュームを使う場合は工夫が必要である。

筆者は以下の方法で対処した。

```conf:smb4.conf
[TimeMachine]
    path = <somewhere>
    read only = no
    browseable = no
    writable = yes
    fruit:time machine max size = <volume size>
    fruit:time machine = yes

    valid users = <user 1>, <user 2>, ...
    force user = tmbackup
    force group = tmbackup
```
`valid users`で使用するユーザを指定する。
サーバ側OSで擬似ユーザ（ここでは`tmbackup`）を作成し、rawボリュームの所有者をその擬似ユーザとする。
ユーザ間でTMボリュームが見えてしまう問題は、TMボリュームの暗号化で対処する。

# その他
## Macを特定するID
Catalinaで動作するMacが用意できないので、外部ディスクにCatalinaをインストールして試験をしようとした。ところがMojaveと同じボリュームしか選択できず、上書きしようとしてしまう。Macを異なるホスト名にしたり、ネットワークI/Fを別の物に変えようとも挙動は変わらない。
どうやらrawボリュームの`com.apple.TimeMachine.MachineID.plist`内にある`com.apple.backupd.HostUUID`で識別しているようである。
この為Catalinaでの挙動を調べる為に別の<del>サーバ</del>ボリュームを用意する必要が生じてしまった。

## samba1つで複数のTimeMachineボリュームを作れるか？
FreeBSDのports/pkgのsamba4.13のデフォルト設定では、TimeMachineボリュームを２つ以上作成しても１つしかMacから見れない。

これはmDNSによって広告されるTimeMachineボリュームの情報(`_adisk._tcp`)に、ボリューム一つ分しか登録できない為である。この情報は**sambaからavahiに自動的に設定される**ので、普通の方法では変更できない。

可能性としてはsambaからavahiサポートを外した上で、avahiを適切に設定する方法があり得る<del>が、未検証である。</del>

成功したので、以下のページにまとめた。

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

## 調査時に使ったscript
TimeMachineの動作を調べるために大量のlogを読む必要があったが、`/usr/bin/log`コマンドが出力するログは一行が非常に長く読みづらい。このため[こちらのページ](https://superuser.com/questions/1126990/how-to-view-time-machine-log-in-macos-sierra)を参考に、以下のようなscriptを作成した。
なおzsh, bashのいずれでも動作する。`.zshrc`や`.bashrc`など、シェルが起動時に読み込むファイルに挿入する。

```shell:
# logコマンドの出力を整形
function tm_f(){
         /usr/bin/awk '{$4=""; $5=""; print; fflush()}'
}

# logコマンドの引数
TMARGS=( --style compact --predicate 'senderImagePath contains[cd] "TimeMachine"' )

# tmlog: 記録されているログを出力する
tmlog(){
        sudo /usr/bin/log show "${TMARGS[@]}" "$@" | tm_f
}

# tmview: 実行中に出力されるログを出力する
#         ^Cで終了する
tmview(){
        sudo /usr/bin/log stream "${TMARGS[@]}" "$@" | tm_f
}
```

```shell:使い方
# 現在発生しているエラーのみ表示
$ tmview

# エラーだけでなく、INFOレベルも表示
$ tmview --info

# 5分前から現在までのエラーログを表示
$ tmlog --last 5m

# 2022年1月9日の13:00:30から14:15:45までのエラー、INFOログを表示
$ tmlog --start '2022-01-09 13:00:30' --end '2022-01-09 14:15:45' --info
```

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