SierraでAPFSを試す

High Sierraが発表され，新しいファイルシステムであるAPFS（Apple File System）への期待が高まっています．
使っているという声は聞こえてきませんが，APFS自体はすでにSierraに実装されています．
そこで，APFSのRamDiskを作ってみたり，APFSの特徴であるスナップショットなどを試してみたいと思います．
（ググってもググっても出てこなかったので，断片的な情報をつなぎ合わせてみた，というのが正しいところです）

# 注意事項
本記事の内容は無保証です．
APFSのスナップショット機能は，Appleから正式にアナウンスのあったものではありません．
コマンド名が変更されることは容易に予想できますし，正式版のスナップショットとのデータの互換性も保証しません．
sudoを使用しているため，操作を間違うとあなたのmacOSに損害を与える可能性もあります．
上記のことを理解した上でお試しください．

# APFSフォーマットのRamDisk作成

まずは肩慣らしとして，APFSフォーマットのRamDiskを作成してみます．
なお，この作業は次節の内容とは無関係なので，興味がなければ読み飛ばしていただいて構いません．

基本的にはhdiutilコマンドでメモリ上にディスクイメージの容量を確保し，diskutilコマンドでパーティション作成とフォーマットを行いマウントします．
今回は実験用に10MBというごく小さなRamDiskを確保しています．
1セクタのサイズは512byteなので，hdiutilコマンドの```ram://```に続く数字と，diskutilコマンドの最後のパラメータ（10mは10MBを表す）を適当に計算して調整してください．

```bash:RamDiskの作成とマウント
$ hdiutil attach -nomount ram://20480
/dev/disk4         // ←の名前を↓に書く
$ diskutil partitionDisk /dev/disk4 1 apfs test 10m
Started partitioning on disk4
Unmounting disk
Creating the partition map
Waiting for partitions to activate
Formatting disk4s1 as APFS with name test
spaceman_metazone_init:262: not creating spaceman metazone, container is only 10444800 bytes, block_size 4096 block count 2550
spaceman_metazone_init:262: not creating spaceman metazone, container is only 10444800 bytes, block_size 4096 block count 2550
nx_mount:981: checkpoint search: largest xid 1, best xid 1 @ 1
nx_mount:1043: sanity checking all nx state... please be patient
apfs_newfs:14968: FS will NOT be encrypted
Mounting disk
Finished partitioning on disk4
/dev/disk4 (disk image):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        +10.5 MB    disk4
   1:                 Apple_APFS                         10.4 MB    disk4s1
$ diskutil ap list
APFS Container (1 found)
|
+-- Container disk5 89E211AE-CE3B-416D-B904-CDCE5A9C9AE6
    ====================================================
    APFS Container Reference:     disk5
    Capacity Ceiling (Size):      10444800 B (10.4 MB)
    Capacity In Use By Volumes:   376832 B (376.8 KB) (3.6% used)
    Capacity Available:           10067968 B (10.1 MB) (96.4% free)
    |
    +-< Physical Store disk4s1 F70DCEE3-D6EE-433E-A221-875C41E80DDF
    |   -----------------------------------------------------------
    |   APFS Physical Store Disk:   disk4s1
    |   Size:                       10444800 B (10.4 MB)
    |
    +-> Volume disk5s1 EC206B01-7A86-4F51-8A1B-DED10FF54493
        ---------------------------------------------------
        APFS Volume Disk (Role):   disk5s1 (No specific role)
        Name:                      test
        Mount Point:               /Volumes/test
        Capacity Consumed:         24576 B (24.6 KB)
        Capacity Reserve:          None
        Capacity Quota:            None
        Encrypted:                 No
$
```

エラーがなければ/Volumes/testにマウントされています．
lsコマンドやmountコマンドでご確認ください．

```bash:lsやmountコマンドで確認
$ ls -F /Volumes/
Macintosh HD@	Ramdisk/	test/
$ mount
/dev/disk2 on / (hfs, local, journaled)
devfs on /dev (devfs, local, nobrowse)
map -hosts on /net (autofs, nosuid, automounted, nobrowse)
map auto_home on /home (autofs, automounted, nobrowse)
/dev/disk3s1 on /Volumes/Ramdisk (hfs, local, nodev, nosuid, noowners, mounted by suda)
/dev/disk5s1 on /Volumes/test (apfs, local, nodev, nosuid, journaled, noowners, mounted by suda)
$
```

# APFSフォーマットのディスクイメージの作成

続いて，APFSフォーマットのディスクイメージ（dmgファイル）を作成してみます．
以下の例では前節で作成したRamDisk上にディスクイメージを置いていますが，どこに置いても構いません．
また，サイズを5MBとしていますが，適当に変更してください．

```bash:APFSフォーマットのディスクイメージの作成とマウント
$ hdiutil create -fs APFS -volname "APFS" -size 5MB /Volumes/test/test.dmg
WARNING: You are using a pre-release version of the Apple File System called
APFS which is meant for evaluation and development purposes only.  Files
stored on this volume may not be accessible in future releases of OS X.

You should back up all of your data before using APFS and regularly back up
data while using APFS, including before upgrading to future releases of OS X.

Continue? [y/N] y
created: /Volumes/test/test.dmg
$ hdiutil mount /Volumes/test/test.dmg
/dev/disk6          	GUID_partition_scheme
/dev/disk6s1        	Apple_APFS
/dev/disk7          	EF57347C-0000-11AA-AA11-0030654
/dev/disk7s1        	41504653-0000-11AA-AA11-0030654	/Volumes/APFS
$
```

エラーがなければ/Volumes/APFSにマウントされています．
lsコマンドやmountコマンドでご確認ください．

```bash:lsやmountコマンドで確認
$ ls /Volumes/
APFS		Macintosh HD	Ramdisk		test
$ mount
/dev/disk2 on / (hfs, local, journaled)
devfs on /dev (devfs, local, nobrowse)
map -hosts on /net (autofs, nosuid, automounted, nobrowse)
map auto_home on /home (autofs, automounted, nobrowse)
/dev/disk3s1 on /Volumes/Ramdisk (hfs, local, nodev, nosuid, noowners, mounted by suda)
/dev/disk5s1 on /Volumes/test (apfs, local, nodev, nosuid, journaled, noowners, mounted by suda)
/dev/disk7s1 on /Volumes/APFS (apfs, local, nodev, nosuid, journaled, noowners, mounted by suda)
$
```

mountコマンドの他に，diskutilコマンドを使用してAPFSファイルシステムのマウント状態を表示する方法があります．

```bash:diskutilコマンドによるAPFSファイルシステムのマウント状態の確認
$ diskutil apfs list
APFS Containers (2 found)
|
+-- Container disk5 89E211AE-CE3B-416D-B904-CDCE5A9C9AE6
|   ====================================================
|   APFS Container Reference:     disk5
|   Capacity Ceiling (Size):      10444800 B (10.4 MB)
|   Capacity In Use By Volumes:   5619712 B (5.6 MB) (53.8% used)
|   Capacity Available:           4825088 B (4.8 MB) (46.2% free)
|   |
|   +-< Physical Store disk4s1 F70DCEE3-D6EE-433E-A221-875C41E80DDF
|   |   -----------------------------------------------------------
|   |   APFS Physical Store Disk:   disk4s1
|   |   Size:                       10444800 B (10.4 MB)
|   |
|   +-> Volume disk5s1 EC206B01-7A86-4F51-8A1B-DED10FF54493
|       ---------------------------------------------------
|       APFS Volume Disk (Role):   disk5s1 (No specific role)
|       Name:                      test
|       Mount Point:               /Volumes/test
|       Capacity Consumed:         5267456 B (5.3 MB)
|       Capacity Reserve:          None
|       Capacity Quota:            None
|       Encrypted:                 No
|
+-- Container disk7 1A3CEC30-ADAE-4E3F-B350-68710E632A4D
    ====================================================
    APFS Container Reference:     disk7
    Capacity Ceiling (Size):      5201920 B (5.2 MB)
    Capacity In Use By Volumes:   405504 B (405.5 KB) (7.8% used)
    Capacity Available:           4796416 B (4.8 MB) (92.2% free)
    |
    +-< Physical Store disk6s1 45CBBF41-7B07-4BFB-AB8B-E8F02C087A34
    |   -----------------------------------------------------------
    |   APFS Physical Store Disk:   disk6s1
    |   Size:                       5201920 B (5.2 MB)
    |
    +-> Volume disk7s1 4A3DFDAB-92BB-4C8E-83E3-4963840C8DE2
        ---------------------------------------------------
        APFS Volume Disk (Role):   disk7s1 (No specific role)
        Name:                      APFS
        Mount Point:               /Volumes/APFS
        Capacity Consumed:         49152 B (49.2 KB)
        Capacity Reserve:          None
        Capacity Quota:            None
        Encrypted:                 No
```


# スナップショットを作成・復元する方法

まずは/Volumes/APFSにファイルを作っておきます．

```bash:ダミーファイルの作成
$ touch /Volumes/APFS/foo.txt
$ touch /Volumes/APFS/bar.txt
$ touch /Volumes/APFS/baz.txt
$ ls -l /Volumes/APFS/
total 0
-rw-r--r--  1 suda  staff  0  7  3 15:13 bar.txt
-rw-r--r--  1 suda  staff  0  7  3 15:13 baz.txt
-rw-r--r--  1 suda  staff  0  7  3 15:13 foo.txt
$
```

## スナップショットの作成

続けて，testsnapという名称のスナップショットを作成します．
警告が出るので，読んでおいてください．
なお，スナップショットは多数作成できます．

```bash:スナップショットの作成
$ sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs_snapshot -c testsnap /Volumes/APFS
WARNING: You are using apfs_snapshot utility on pre-release version of the
Apple File System called APFS which is meant for evaluation and development
purposes only. Please note that apfs_snapshot may be removed in a future
release and should only be used for testing.
$
```

## スナップショットの確認

作成したスナップショットを確認してみましょう．
警告のあとにtestsnapという文字が見えています．

```bash:スナップショットの確認
$ sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs_snapshot -l /Volumes/APFS
WARNING: You are using apfs_snapshot utility on pre-release version of the
Apple File System called APFS which is meant for evaluation and development
purposes only. Please note that apfs_snapshot may be removed in a future
release and should only be used for testing.
testsnap  (xid: 6)
$
```

## スナップショットをリードオンリーでマウント

下記のようにファイルを増やした（＝ファイルシステムの内容を書き換えた）場合でも，予め作成しておいたスナップショットをマウントすれば，スナップショット作成時点でのファイルを入手できます．

```bash:ダミーファイルを増やしておく
$ touch /Volumes/APFS/qux.txt
$ touch /Volumes/APFS/quux.txt
$ touch /Volumes/APFS/corge.txt
$ ls -l /Volumes/APFS/
total 0
-rw-r--r--  1 suda  staff  0  7  3 15:13 bar.txt
-rw-r--r--  1 suda  staff  0  7  3 15:13 baz.txt
-rw-r--r--  1 suda  staff  0  7  3 15:26 corge.txt
-rw-r--r--  1 suda  staff  0  7  3 15:13 foo.txt
-rw-r--r--  1 suda  staff  0  7  3 15:26 quux.txt
-rw-r--r--  1 suda  staff  0  7  3 15:26 qux.txt
$
```

それでは，スナップショットをリードオンリーで/tmp/testsnapにマウントしてみます．

```bash:スナップショットのマウント
$ mkdir /tmp/testsnap
$ sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/mount_apfs -s testsnap /Volumes/APFS/ /tmp/testsnap
Password:
mount_apfs: snapshot implicitly mounted readonly
$
```

あとは/tmp/testsnapから必要なファイルを救出してください．
ここでは，mountコマンドとlsコマンドでマウント状態とスナップショットの内容を確認するにとどめておきます．

```bash:mountとlsコマンドで確認
$ mount
/dev/disk2 on / (hfs, local, journaled)
devfs on /dev (devfs, local, nobrowse)
map -hosts on /net (autofs, nosuid, automounted, nobrowse)
map auto_home on /home (autofs, automounted, nobrowse)
/dev/disk3s1 on /Volumes/Ramdisk (hfs, local, nodev, nosuid, noowners, mounted by suda)
/dev/disk5s1 on /Volumes/test (apfs, local, nodev, nosuid, journaled, noowners, mounted by suda)
/dev/disk7s1 on /Volumes/APFS (apfs, local, nodev, nosuid, journaled, noowners, mounted by suda)
testsnap@/Volumes/APFS on /private/tmp/testsnap (apfs, local, read-only, journaled)
$ ls -l /tmp/testsnap
total 0
-rw-r--r--  1 suda  staff  0  7  3 15:13 bar.txt
-rw-r--r--  1 suda  staff  0  7  3 15:13 baz.txt
-rw-r--r--  1 suda  staff  0  7  3 15:13 foo.txt
$
```

## 禁断の奥義：ファイルシステムをスナップショット作成時に巻き戻す

スナップショットは内部の仕組みがどうであれ，バックアップです．
つまり，ある瞬間のバックアップの状態にファイルシステムを巻き戻すことも可能です．
あまり使う場面が想像できないのですが，一応その方法を書いておきます．




まずは，先ほどリードオンリーマウントした```/tmp/testsnap```をアンマウントしておきます．
```
$ sudo umount /tmp/testsnap
```

続けてファイルシステムを巻き戻します．
注意点として，巻き戻しただけではまだ新しいファイルが存在します．
このタイミングで新しいファイルを書き込んだときにどうなるのか興味がありますが，おとなしくアンマウント→マウントしておきましょう．

```bash:スナップショットの時点に巻き戻す
$ sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs_snapshot -b testsnap /Volumes/APFS
WARNING: You are using apfs_snapshot utility on pre-release version of the
Apple File System called APFS which is meant for evaluation and development
purposes only. Please note that apfs_snapshot may be removed in a future
release and should only be used for testing.
Revert will take place on remount of Volume
$ ls /Volumes/APFS/
bar.txt		baz.txt		corge.txt	foo.txt		quux.txt	qux.txt
$ umount /Volumes/APFS
$ hdiutil mount /Volumes/test/test.dmg
/dev/disk6          	GUID_partition_scheme
/dev/disk6s1        	Apple_APFS
/dev/disk7          	EF57347C-0000-11AA-AA11-0030654
/dev/disk7s1        	41504653-0000-11AA-AA11-0030654	/Volumes/APFS
$ ls /Volumes/APFS/
bar.txt	baz.txt	foo.txt
$
```

※スナップショットを複数撮った上で上記作業を行うと，巻き戻したファイルシステム以降に撮ったスナップショットが全て無くなります．

# あとがき

ファイルを書き換えた後に，やはり元のファイルが必要だったという経験は誰しもが通る道だと思います．
そのようなときのために，ZFSなどではスナップショット機能を利用した{アワリー|デイリー|ウィークリー}バックアップが役に立ちます．
実際に筆者も研究室のファイルサーバをZFSで運用しており，スナップショット機能を利用したデイリーバックアップを提供しています．
macOSにはTimeMachineという仕組みがあるので，わざわざスナップショットを撮る必要性があるか？と問われると何とも言えませんが，あれば何かの際に役に立つと思い，その方法を公開します．

# 参考

- [Testing out snapshots in Apple’s next-generation APFS file system](https://arstechnica.com/apple/2017/02/testing-out-snapshots-in-apples-next-generation-apfs-file-system/)
- [Appleの新しいファイルシステム「APFS」のチュートリアルまとめ。](http://applech2.com/archives/20160620-apple-file-system-apfs-demo.html)
- [新・OS X ハッキング!
175 Apple File System(APFS)を試す](http://news.mynavi.jp/column/osxhack/175/)