RFC-0225:Fxblob:將 blob 儲存在 Fxfs

RFC-0225:Fxblob:在 Fxfs 中儲存 blob
狀態已接受
區域
  • 儲存空間
說明

從 Fxfs 提供 Blob,從系統中移除 Blobfs 和 FVM。

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2023-07-25
審查日期 (年-月-日)2023-09-05

摘要

這項提案旨在以 Fuchsia 的下一代檔案系統 Fxfs 取代 Fuchsia 的磁碟區管理員 (FVM) 和 Blob 儲存檔案系統 (Blobfs)。系統 blob (包括程式庫、二進位檔和靜態設定) 會儲存在 Fxfs 磁碟區中,且具有與 Blobfs 相同的屬性 (可用內容位址、經過驗證且不可變更)。

提振精神

Fxfs 是 Fuchsia 的新一代檔案系統,旨在提供效能、可更新性和豐富的功能組合。這項服務已證明是儲存空間團隊可運用的穩固基礎,且工程團隊的努力重點也逐漸轉向提升 Fxfs。Blobfs 和 FVM 是在 Fuchsia 初期建立,並以特定用途為目標:

  • Blobfs 的設計目的是提供經過驗證、不可變動的內容可尋址儲存空間。
  • FVM 的設計目的是讓 Blobfs 和 Minfs (一般可變動的檔案系統) 共用單一區塊裝置,並隨著分區數量增加,動態分配裝置。

隨著 Fuchsia 的演進,這些系統扮演了重要角色,但卻成為 Storage 團隊目標的限制因素:效能、可更新性和可維護性。

提升效能

我們已完成許多改善 Fuchsia 儲存體堆疊效能的簡單工作,現在要改善效能,就必須對檔案系統進行更複雜的變更。

舉例來說,Blobfs 是單執行緒實作,因此寫入效能受到限制。在檔案系統中加入多執行緒支援功能並非易事,需要進行重大變更。

Blobfs 格式會因寫入放大效應而影響我們改善效能的努力。寫入檔案時,必須更新儲存在多個位置的幾個中繼資料結構,這會導致寫入作業大幅增加。此外,Blobfs 的記錄檔是區塊式記錄檔 (相較於 Fxfs 等邏輯記錄檔),在許多情況下效率較低。

這些工作必須在我們維護的每個檔案系統中重複執行,因此使用多種檔案系統的成本會大幅增加。能夠將精力集中在單一檔案系統 (Fxfs) 上,是本提案的主要動機。

我們已透過 Fxblob 原型驗證 Fxfs 的效能優勢。在 Intel NUC 上,Fxblob 是:

  • 在 Blob 中分頁的速度提升 55% 至 80%
  • 寫入單一 Blob 的速度提升 17%
  • 與 Blobfs 相比,同時寫入多個 Blob 的速度快上 130%。

我們也正在進行其他效能提升作業。

可更新性

Fxfs 的其中一個主要設計原則是可更新性:我們設計 Fxfs 時,考量到儲存格式會隨時間變化,詳情請參閱 RFC-0136:Fxfs

FVM 和 Blobfs 的設計並未考量到這點,因此在實務上,如果不分支格式和實作項目,就無法對 FVM 或 Blobfs 進行任何重大變更。

如果需要新增功能,或是需要針對儲存磁碟資料的方式進行效能最佳化,就會很難使用 FVM 和 Blobfs。以下列舉幾個例子:

  • RFC-0005:Blobfs 快照,建議對 FVM 格式進行變更,以改善 Fuchsia 在軟體更新方面的抗災能力。由於變更 FVM 格式風險太高且技術上複雜,因此我們撤回這項 RFC。
  • Blobfs 中的 Compact Merkle Trees,這是 2021 年實作的節省空間功能,但由於推出這項功能的複雜性,從未完全發布。C++ Blobfs 仍支援密集版和舊版版面配置。

相較之下,Fxfs 的磁碟格式會定期變更,但不會造成太多干擾。在撰寫本 RFC 時 (2023 年 7 月),Fxfs 為 31 版,自上次重大變更 (2022-06-14 建立的 21 版) 以來,已經歷 10 次格式變更。(請注意,即使這個重大變更可以以回溯相容的方式進行,但基於實際考量,我們選擇不這麼做)。

可維護性

Fxfs 已證實為工程師工作時的測試完善且高效能環境。這項功能已在 Rust 中實作,具有廣泛的單元、整合和模糊測試涵蓋率,並且能妥善封裝更細微或困難的問題 (例如層級檔案合併、記錄、交易和鎖定等)。此外,由於 Fxfs 的層級檔案一旦建立就無法變更,且中繼資料更新會附加至記憶體層級檔案 (和記錄),而非分段寫入磁碟,因此處理並行處理的難度也大幅降低。

相較之下,FVM 有大量技術債務、缺乏完善的測試涵蓋率,且格式封裝不佳 (Fuchsia 中的大量軟體會解讀 FVM 格式),因此系統難以變更。

效能改善一文所述,大部分的改善項目都必須在我們維護的每個檔案系統實作中複製,這會使改善成本成倍增加。這也適用於維護費用。維護的軟體越少,工程師的投入時間就越少。

請注意,由於現有產品需要使用 FVM 和 Blobfs,因此我們無法在一段時間內完全淘汰這兩項技術。不過,我們認為 FVM 和 Blobfs 在現有產品上運作相當穩定,維護成本可忽略不計。

相關人員

協助人員:

abarth@google.com

審查者:

諮詢:

社會化:

我們先將設計內容提供給相關利害關係人 (軟體提交團隊在初期就參與其中,而安全性/核心團隊則是在初始原型設計更完整呈現後才加入)。

需求條件

  • Fxblob 必須提供與 Blobfs 語意完全相符的檔案系統 API。也就是:
    • Fxblob 必須允許建立靜態檔案 (寫入一次,讀取多次)。
    • Fxblob 必須有可透過內容尋址的檔案名稱 (也就是檔案名稱必須是其內容的梅克爾根目錄)。
    • 為方便移植和遷移,應使用 Blobfs 中的相同 Merkle 樹演算法 (也就是說,Blobfs 中的檔案名稱應與 Fxblob 中的檔案名稱相符)。
    • Fxblob 必須先確保檔案內容與其 Merkle 根值相符,才能允許用戶端讀取這些檔案。
    • Fxblob 必須向 pkg-cache 呈現平面目錄結構,其中包含系統上的每個 blob。
    • Fxblob 必須支援可執行檔案和分頁。
  • Fxblob 必須支援 RFC-0207 中指定的「傳送 blob」線路格式。
  • 在所有維度中,Fxblob 的效能應與 Blobfs 相當或更佳。
    • 在延遲和處理量方面,Fxblob 的讀取和寫入效能應優於 Blobfs。
    • Fxblob 的磁碟用量和記憶體用量應與 Blobfs 相當。
  • Fxblob 必須具備精確的記帳機制,以便根據系統中已知的其他 Blob,預先決定儲存空間預算,並在可用空間中建立合理狹窄的邊界。
  • Fxblob 應盡量縮小信任程式碼集 (TCB) 的擴充空間,也就是必須正確執行 Fuchsia 驗證執行作業的安全性屬性。Fxfs 不應需要信任,且信任的作業應委派至其他位置。

設計

從架構的角度來看,目標是從圖 1 移至圖 2:

舊版架構

圖 1:Fuchsia 裝置目前的儲存空間架構。

新架構

圖 2:Fuchsia 裝置的 Fxblob 儲存空間架構。

Fxfs 中的「blob」磁碟區將是模擬 blobfs 屬性的特殊磁碟區,包括內容位址可尋性、不可變性等。對於系統的上層,這項變更是透明的,但系統的許多低層部分都需要變更:

  • 系統組合和建構工具
  • 裝置引導軟體和工具 (鋪設和閃爍)
  • 復原軟體
  • Fshost,
  • Fxfs,以支援新的 blob 區隔的特殊屬性
  • Pkg-cache 和 pkg-resolver,以便使用一組新的 API 讀取及寫入 Blob。

Fxfs 的異動

Fxfs 已準備就緒,可配合此架構,因為它原生支援磁碟區,且格式經過設計,可提供彈性,並可隨時間變更。

原型實作已完成 (請參閱 //src/storage/fxfs/platform/src/fuchsia/fxblob),實作滿足 Blob 儲存空間屬性的特殊磁碟機。磁碟區只有一個根目錄,無法使用 fuchsia.io 通訊協定建立檔案或目錄。相反地,磁碟區會提供特殊的 BlobWriter 通訊協定,pkg-cache 會使用這項通訊協定,將新的 Blob 安裝到磁碟區。

這個磁碟區中的檔案可使用 blobfs 使用的相同梅克爾樹狀結構演算法進行內容位址設定 (檔案名稱是根雜湊)。安裝 Blob 時會產生 Merkle 樹狀結構,而 Fxfs 會確保 Merkle 根目錄與檔案名稱相符,再將檔案儲存下來。每次從磁碟讀取檔案的某個部分時,Fxfs 都會根據檔案的梅克爾樹驗證內容,提供完整性。

Fhost 的變更

Fshost 是低階元件,可偵測區塊裝置並掛載其內含的檔案系統,為系統的其他部分提供 /blob 和 /data 的連線。

當 Fshost 經過設定 (透過靜態設定) 以便預期 Fxblob 格式的系統時,它會將 /blob 目錄連結至 Fxfs 執行個體中的 blob 磁碟區,並將 /資料目錄連結至 Fxfs 執行個體中的資料磁碟區。Fshost 也將繼續支援 FVM+Blobfs 的舊版設定。

系統組合和建構工具的異動

系統組合是 Fuchsia 建構作業的最後一個步驟,其中會將成果物件組合成一系列圖像和資訊清單,這些圖像和資訊清單則組成 Fuchsia 系統映像檔。

系統組合的其中一個輸出項目是 FVM+Blobfs 映像檔,其中包含系統中的「base」blob 集合。我們會調整系統組合,讓系統能夠根據建構期間的設定,產生包含這些 Blob 的 Fxfs 格式映像檔。

我們也會以「稀疏」格式產生 Fxfs 映像檔,以便更有效率地傳輸至裝置。使用的稀疏格式是 Android 稀疏格式,此格式已受 fastboot 支援,因此是自然的選擇。

裝置引導程序 (鋪設和閃燈) 的異動

雖然鋪路程序已正式淘汰,不再用於裝置引導程序 (RFC-0075:淘汰以 zedboot 為基礎的鋪路程序),但仍有廣泛的使用需求,因此我們決定在鋪路工作流程中新增 Fxfs 映像檔支援功能。

刷新是現代裝置引導程序工作流程,需要使用 fastboot 通訊協定。與鋪設 (可解讀所收到的 FVM 映像檔) 相比,這個通訊協定相對簡單,因此這項變更相當簡單。(Fastboot 不會解讀收到的原始映像檔,因此刷新 FVM 和 Fxfs 映像檔並無差異;相反地,鋪設會解讀 FVM 映像檔,因此必須新增自訂邏輯,才能處理鋪設器的 Fxfs 映像檔)。

上述的稀疏圖片格式可用於鋪設和閃燈,以減少傳輸至裝置的位元組數,並降低記憶體占用空間。

套件安裝和 OTA 路徑的變更

與 blobfs 不同,Fxblob 不支援 fuchsia.io 寫入路徑 (該路徑有重大效能問題)。相反地,fxblob 支援新的寫入 API,可在資料層面 (而非 FIDL) 使用共用 VMO,進而減少寫入 blob 所需的 FIDL 往返次數總數。

新的 API 分為兩個通訊協定:fuchsia.fxfs.BlobCreator 和 fuchsia.fxfs.BlobWriter。BlobCreator 是可探索的通訊協定,pkg_cache 會使用這項通訊協定建立新的 Blob (如果尚未建立),而 BlobWriter 是私人通訊協定,可用來協助將 Blob 資料串流至 Fxfs。

共用 VMO 會以環形緩衝區的形式進行整理,讓我們可以管道化寫入要求。Pkg_cache 會填滿環形緩衝區的某個部分,然後使用 BlobWriter.BytesReady FIDL 方法通知 Fxfs 某些位元組的可用性。Fxfs 會等待這些通知,從環形緩衝區載入部分資料,計算該資料的梅克爾雜湊,並將資料串流至磁碟。

當最終的 BytesReady 要求傳送至 Fxfs 時,Fxfs 會阻擋該要求,直到 Merkle 樹完全計算完成,並根據收到的內容驗證;如果雜湊和內容不相符,Fxfs 會以 ZX_ERR_IO_DATA_INTEGRITY 拒絕這項要求。Fxfs 會確保在最終呼叫完成後,檔案可供 fuchsia.io.Directory.ReadDirents 查看。

我們建立了用戶端程式庫,用於封裝環形緩衝區管理。

流程如下所示:

Blob 安裝路徑

Merkle 樹狀結構驗證、解壓縮和 VMEX 處理作業的異動

Blobfs 目前擁有 VMEX 能力,並負責在必要時鑄造可執行的 VMOs,以及在讀取時解壓縮 Blob 內容。由於 VMEX 允許任意程式碼執行,blobfs 也會在讀取時使用沙箱解壓縮器解壓縮 Blob 內容。(Blob 內容是由外部提供,因此可視為不受信任的資料)。與解壓縮作業相關的這個額外跳躍和內容切換會造成顯著的效能成本。

我們希望透過 Fxblob 最佳化分頁路徑的效能,這需要將解壓縮作業移至 Fxfs 程序本身 (以及其他事項)。不過,將更多功能累積至高度信任的元件中,可能會出現安全疑慮。我們認為,只要移除 Fxfs 的信任,就能解決這些疑慮。

為了達成這個目標,我們需要將兩個信任的作業移出 Fxfs:

  1. 根據梅克爾樹驗證檔案內容
  2. 使用 VMEX 資源鑄造可執行的網頁。

為避免 Fxfs 需要保留 VMEX 能力,pkg-cache 會接管這項責任。由於 pkg-cache 必須信任從 blob 名稱對應至雜湊,因此這不會對指派給該元件的信任範圍造成實質影響,也不需要將 Fxfs 視為高信任度元件。

我們也需要為 /bootstrap/base_resolver 提供 VMEX 資源;這個元件會從基本組合中提供套件。它與 pkg-cache 類似 (事實上,它會使用相同的程式庫來處理大部分的業務邏輯)。

為了讓 Fxfs 完全不受信任,我們也需要將梅克爾驗證移至其他位置 (雖然 Fxfs 仍會儲存及提供梅克爾樹狀圖內容)。以下提供幾種做法:

  1. 建立單獨的高度信任元件,只驗證資料。
  2. 將這項功能移至已獲得高度信任的核心。

請注意,pkg-cache 在開啟 blob 時,需要直接與信任的驗證器通訊,或是需要能夠直接查詢這個信任的驗證器,以便將驗證器使用的根雜湊與指定 blob 的預期雜湊進行比較。這樣一來,即使遭到入侵的 Fxfs 完全變更資料和梅克爾樹狀圖 (因此驗證者會將遭竄改的資料視為正確),pkg-cache 仍可偵測竄改行為並拒絕檔案內容。

選擇執行 Merkle 驗證的時間點,歸根結底是安全性與效能之間的取捨,因此值得深入探討。因此,這項問題將在另一個 RFC 中解決,目前驗證作業會在 Fxfs 的程序中進行。

請注意,Fuchsia 日後需要支援的其他兩項功能也仰賴梅克爾樹:Fs-verity 和 dm-verity,分別用於某些 Linux 應用程式的檔案和圖片完整性。RFC-0082:在 Fuchsia 上執行未經修改的 Linux 程式 中,Fuchsia 打算支援這兩種方式。設計外部 Merkle 驗證機制時,也必須考量這些用途。

實作

我們採用了實作功能原型的方法,並在測試和開發環境中啟用這些原型,以便進行「浸潤」測試,目前成效不錯。我們打算繼續為日後的工作和未完成的功能採用相同做法 (如要瞭解與梅克爾驗證相關的工作,請參閱「安全性」一節)。

功能會在建構期間啟用的功能標記後方受控。舉例來說,pkg-cache 會偵測是否有新的 Fxblob 讀取和寫入 API,並在可用時優先使用這些 API,否則會改用一般 fuchsia.io 機制。

成效

原型設計基準測試的初步結果顯示,讀取和寫入效能有所改善。在 Intel NUC 上,Fxblob 是:

  • 在 Blob 中分頁的速度提升 55% 至 80%
  • 寫入單一 Blob 的速度提升 17%
  • 與 Blobfs 相比,同時寫入多個 Blob 的速度快上 130%。

效能的其他重要面向包括記憶體用量和儲存空間用量。這些面向正在分析中,我們會解決 Blobfs 和 Fxblob 之間的任何重大差距。一般來說,Rust 元件通常會耗用更多記憶體,但 Blobfs 中大部分的記憶體用量都是因為快取頁面,而 Fxblob 的成本也相同。

Fxfs 的儲存空間用量比 Blobfs 複雜,因為 Fxfs 會將中繼資料更新內容儲存為一系列的變更,而非為 inode、extent 等提供固定的配置。不過,我們也能透過移除 FVM (需要一些磁碟空間來儲存自身的中繼資料,並在已配置的切片中引發內部碎片),回收一些儲存空間,而且 Fxfs 日後可更靈活地變更格式,以改善儲存空間效能 (例如最佳化層級檔案格式)。在撰寫本 RFC 時,我們的原型實作項目使用相當的磁碟空間,以 Blobfs 的形式儲存相等數量的 Blob。

人體工學

這些變更對絕大多數 Fuchsia 元件來說都是公開透明的。目前只有 pkg-cache 會直接與 Blobfs 互動。

回溯相容性

我們將繼續支援舊版設定 (FVM + Blobfs) 中的系統,以便支援已發布的產品。我們會持續支援舊版設定 (以及適當的測試基礎架構),直到相關產品停產為止。

日後所有 Fuchsia 產品都會採用這項新設定,包括大多數開發人員。

沒有遷移路徑的規劃;裝置將使用 Fxblob 或舊版設定。

安全性考量

Blobfs 是 Fuchsia 中高度信任的元件,在 Fuchsia 的驗證執行中扮演關鍵角色,因為它會驗證系統上幾乎所有其他元件的軟體完整性。(儲存在啟動檔案系統中的低階元件會透過其他方式進行驗證)。此外,blobfs 能夠建立可執行頁面,且由於其他系統元件信任 blobfs 提供的內容,因此 blobfs 可以將任意程式碼插入任何元件的位址空間,讓 blobfs 模擬任何依賴 Blobfs 可執行程式碼的元件功能。

我們採取的其中一種策略,是將特定作業置於沙箱中,以降低 Blobfs 中的這些風險。舉例來說,由於解壓縮作業有解析不受信任輸入內容的風險,因此已移至個別程序。如果沙箱解壓縮器遭到入侵,最糟的情況是服務遭到拒絕,因為 Blobfs 會直接拒絕解壓縮器傳回的任何無效資料。我們認為在 Fxfs 的讀取路徑上,這項策略並非必要,因為本機攻擊的風險較低,但我們會嘗試在寫入路徑上實作類似的沙箱策略,以防範遠端攻擊。

雖然從安全性角度來看,Fxfs 比 Blobfs 有某些優勢 (例如,它是以 Rust 這類記憶體安全的語言實作),但它仍比 C++ Blobfs 複雜許多,而複雜度越高,風險就越高,因此需要採取更多措施來減輕風險。

我們建議的緩解措施已在上述設計一節中說明。

安全團隊指出,以共用記憶體為基礎的 IPC 機制 (即用於 blob 寫入的環緩衝區) 需要額外小心處理,確保讀取器和寫入器在面對不合作的對等端時,能有足夠的健全性,尤其是在共用記憶體已對應的情況下。我們會謹慎設計這些協定,並視需要諮詢安全團隊。

妥協 Fxfs 的結果

由於此提案會擴大 Fxfs 的攻擊面,因此值得記錄攻擊者若能入侵 Fxfs 會做些什麼 (以及無法做些什麼)。

在這個提案中,Fxfs 不會提供 Vmex 資源,而且雖然 Fxblob 會為系統中大多數套件的二進位檔和程式庫提供 (可執行) 內容,但由於內容會由另一個元件 (核心或某些使用者空間元件) 獨立驗證,因此 Fxfs 無法偽造可執行頁面,也無法讓其他處理序執行任意可執行資料。

因此,威脅模型涵蓋 Fxfs 可直接存取的內容:

  • Fxfs 可任意讀取及寫入資料檔案內容。這可能會導致使用者資料的機密資訊外洩,或可能會利用其他元件中的錯誤,這些元件不會驗證儲存的資料。
  • Fxfs 可以刪除 blob 內容,這會導致其他元件遭到拒絕服務。
  • Fxfs 可拒絕 Blob 寫入作業,進而防止系統更新。
  • Fxfs 可與區塊裝置驅動程式互動,並可能會利用其中的漏洞,取得 Fxfs 所沒有的任何驅動程式功能。

隱私權注意事項

不適用。

測試

我們已實作此提案的原型,可啟動 Fuchsia 並執行所有常規系統功能。這個原型會在 CQ 中啟用建構工具,執行一般系統測試套件,確保原型具有實際的用途和涵蓋範圍。

我們最終會將這項功能擴展至更多情境 (例如在特定產品上啟用,這會導致開發人員自然使用這項功能,並在 CQ/CI 中使用更多)。我們會在任何使用者裝置上使用這個架構前,先進行大量測試。

我們也為相關功能新增了單元測試、整合測試和 e2e 測試的常見測試金字塔。我們也實作了自動基準測試,以便追蹤從 Fxblob 讀取和寫入的效能,並與 blobfs 的效能進行比較。

說明文件

我們需要更新部分儲存空間文件,以便實施這項提案。例如,應更新「檔案系統架構」說明文件。

缺點、替代方案和未知事項

缺點:裝置可能無法使用

在舊版架構中,minfs 中的錯誤只會影響 minfs,因此即使資料分區重設,系統仍會處於可運作狀態,因為系統的重要部分 (二進位檔、程式庫、設定等) 都儲存在 blobfs 中。

將 blob 和資料合併至單一檔案系統,意味著 Fxfs 中的錯誤可能會導致裝置無法使用,因此這裡沒有相同的安全網。

我們認為 Fxfs 已通過廣泛測試並自 2021 年起投入使用,因此足夠健全,可做為 Fuchsia 基礎架構的重要組件。檔案系統損毀的機率不高。

如果確實發生損毀情形,最佳做法與其他作業系統的做法類似:提供可用來將裝置還原為可運作狀態的復原路徑。從復原模式執行 OTA 的功能 (fxrev.dev/563864) 是解決這個問題的一種方法,作者也支持這項做法 (因為這可讓我們修復更多問題的裝置)。

請注意,這並非 Fuchsia 特有的風險,大多數其他作業系統都有單一全域檔案系統,用於儲存所有資料 (無論是否必要)。

替代方案:依賴 zx::streams 加快 blob 寫入速度

為 Fxblob 建立的自訂寫入 API 與 zx::streams 有幾分相似之處,後者會使用 VMO 做為共用資料緩衝區,並使用頁面錯誤做為控制機制。不過,資料流的設計目的是為了支援更廣泛的用途 (一般檔案 I/O),因此會產生更多負擔,包括內容大小追蹤、頁面錯誤開銷、較少的 I/O 批次處理 (因為錯誤是頁面精細度)。專用 API 可讓您更輕鬆地實現更高效能的應用程式。

Blob 寫入效能相當重要 (因為這是系統 OTA 和安裝大型套件的關鍵瓶頸),且與一般檔案 I/O 相比,Blob 有足夠的特定限制,因此我們認為自訂介面值得投入心力。我們成效卓越的成果證明瞭這項努力。

既有技術與參考資料

可以說,這項提案讓 Fuchsia 更接近大多數作業系統,這些系統會使用通用檔案系統來執行更多用途,並提供專門功能,例如使用其他機制提供內容位址和驗證功能。雖然我們會為此提案在 Fxfs 中加入一些特殊功能,但這項工作的大部分內容都會在 Fxfs 的外層進行,核心檔案系統則不會有所變動。

舉例來說,在 Linux 上,您可以將通用檔案系統與 dm-verity 或 fs-verity 結合,分別提供映像檔和特定檔案的內容驗證。