One
Apple 檔案系統(APFS)的 Firmlink 黑魔法

本文亦提供以下語言版本: English


Apple 自 macOS Catalina 起引入了一個新的檔案階層系統,用以區隔同一個 Apple 檔案系統(APFS)容器(Container)內的系統以及使用者資料:Macintosh HD 作為系統檔案使用並以唯讀形式掛載;Macintosh HD - Data 作為使用者互動檔案(例如:應用程式、偏好設定檔案)使用並以可讀寫形式掛載。容器中亦包含了其他像是 Recovery 這種用於系統還原(RecoveryOS)使用的磁區(Volume):

$ diskutil list
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *1.0 TB     disk0
   1:             Apple_APFS_ISC Container disk2         524.3 MB   disk0s1
   2:                 Apple_APFS Container disk3         994.7 GB   disk0s2
   3:        Apple_APFS_Recovery Container disk1         5.4 GB     disk0s3

/dev/disk3 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +994.7 GB   disk3
                                 Physical Store disk0s2
   1:                APFS Volume Macintosh HD - Data     410.4 GB   disk3s1
   2:                APFS Volume Macintosh HD            8.9 GB     disk3s3
   3:              APFS Snapshot com.apple.os.update-... 8.9 GB     disk3s3s1
   4:                APFS Volume Preboot                 4.7 GB     disk3s4
   5:                APFS Volume Recovery                769.9 MB   disk3s5
   6:                APFS Volume VM                      2.1 GB     disk3s6

每個磁區的角色(Role)於它們的 Superblock 架構(apfs_superblock_t)中指派,相關定義可在 Apple 官方的 APFS 參考資料中找到:

#define APFS_VOL_ROLE_NONE      0x0000
#define APFS_VOL_ROLE_SYSTEM    0x0001
#define APFS_VOL_ROLE_USER      0x0002
#define APFS_VOL_ROLE_RECOVERY  0x0004
#define APFS_VOL_ROLE_VM        0x0008
#define APFS_VOL_ROLE_PREBOOT   0x0010
#define APFS_VOL_ROLE_INSTALLER 0x0020
#define APFS_VOL_ROLE_DATA      0x0040
#define APFS_VOL_ROLE_BASEBAND  0x0080

雖然當時對於 macOS 來說這是全新的概念,但這項措施其實已經用於 iOS 一段時間。當系統啟動時,Macintosh HDAPFS_VOL_ROLE_SYSTEM)和 Macintosh HD - DataAPFS_VOL_ROLE_DATA)兩個磁區會顯示為單一邏輯磁區,且兩者的內容將透過 Firmlink 合併為一。

Firmlink 類似於 Symbolic Link 但允許雙向移動且僅適用於目錄。這有效的使 macOS 能夠整併 Macintosh HDMacintosh HD - Data 的內容,例如:位於唯讀磁區 Macintosh HD 的目錄 /Users 實際上為 Macintosh HD - Data 這個可讀寫磁區上 /Shared 目錄的 Firmlink。此項設計同時也讓 Apple’s 系統完整性保護(System Integrity Protection,SIP)能更加完美的銜接,例如:受 SIP 保護的系統應用程式安裝於唯讀目錄 /System/Applications,而使用者應用程式則可被安裝於 /Applications,然而使用者僅會看到兩者內容合併顯示於根目錄下的 /Applications

系統預設的 Firmlink 可於 /usr/share/firmlinks 中查詢:

$ cat /usr/share/firmlinks
/AppleInternal	AppleInternal
/Applications	Applications
/Library	Library
/System/Library/Caches	System/Library/Caches
/System/Library/Assets	System/Library/Assets
/System/Library/PreinstalledAssets	System/Library/PreinstalledAssets
/System/Library/AssetsV2	System/Library/AssetsV2
/System/Library/PreinstalledAssetsV2	System/Library/PreinstalledAssetsV2
/System/Library/CoreServices/CoreTypes.bundle/Contents/Library	System/Library/CoreServices/CoreTypes.bundle/Contents/Library
/System/Library/Speech	System/Library/Speech
/Users	Users
/Volumes	Volumes
/cores	cores
/opt	opt
/private	private
/usr/local	usr/local
/usr/libexec/cups	usr/libexec/cups
/usr/share/snmp	usr/share/snmp

自 Catalina 起要於根目錄下建立目錄,我們必須仰賴合成 Firmlink(Synthetic Firmlink):

  • /etc 目錄下新增一個名為 synthetic.conf 的檔案。
  • 確保該檔案的權限設定如下:
root:讀,寫
wheel:讀
everyone:讀

(通常只要透過指令「sudo touch /etc/synthetic.conf」即可完成上述設定。)

要將目錄 /Users/foo/bar 連結至 /baz,我們可以新增一個新的條目至剛剛建立的檔案:

# create a firmlink named "baz" at / which points to "Users/foo/bar"
baz Users/foo/bar

你也可以檢視 man synthetic.conf 來查詢所有可用的選項。請注意,兩個路徑中間必須使用一個「tab」做分隔,使用空白符號將不會產生作用。同時路徑的開頭不包含反斜線(/)。重新啟動系統後剛剛的變更即會生效。