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 是儲存空間團隊的穩固基礎,工程團隊也投入越來越多心力來強化 Fxfs。Blobfs 和 FVM 是在 Fuchsia 早期建立,並以特定用途為考量而建構:

  • Blobfs 的設計宗旨是提供經過驗證、不可變更且可定址的儲存空間。
  • FVM 的設計目的是讓 Blobfs 和 Minfs (一般可變動的檔案系統) 共用單一區塊裝置,並在分割區成長時從裝置動態分配。

這些系統在 Fuchsia 演進過程中扮演重要角色,但現在已成為儲存空間團隊目標的限制因素,也就是效能、可更新性和可維護性。

提升效能

我們已完成許多簡單的 Fuchsia 儲存空間堆疊效能改善作業,現在需要對檔案系統進行更複雜的變更,才能進一步提升效能。

舉例來說,Blobfs 的寫入效能受到限制,因為 Blobfs 是單一執行緒實作項目。在檔案系統中新增多執行緒支援並非易事,需要大幅變更。

Blobfs 的格式會導致寫入放大率偏高,因此我們無法改善效能。如要寫入檔案,必須更新儲存在各種位置的數個中繼資料結構,導致寫入放大現象。此外,Blobfs 的日誌是以區塊為基礎 (相較於 Fxfs 等邏輯日誌),在許多情況下效率較低。

我們維護的每個檔案系統都必須重複這些工作,這會大幅增加各種檔案系統的成本。能夠專注於單一檔案系統 (Fxfs) 是一大優勢,也是本提案的主要動機。

Fxfs 的效能優勢已透過 Fxblob 原型證明。在 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 中的精簡 Merkle 樹狀結構。這項功能於 2021 年實作,可節省空間,但由於推出這項功能相當複雜,因此從未全面發布。C++ Blobfs 仍支援緊湊和舊版版面配置。

相較之下,Fxfs 的磁碟格式會定期變更,但不會造成太多麻煩。撰寫這份 RFC 時 (2023 年 7 月),Fxfs 為 31 版,自上次重大變更 (2022 年 6 月 14 日建立的 21 版) 後,已歷經 10 次格式變更。(請注意,即使是這項重大變更,也可以採用回溯相容的方式進行,但基於實務考量,我們選擇不這麼做)。

可維護性

Fxfs 經過充分測試,可為工程師提供高效的作業環境。以 Rust 實作,涵蓋廣泛的單元、整合和模糊測試,並妥善封裝更細微或困難的問題 (例如圖層檔案合併、記錄、交易和鎖定等)。此外,Fxfs 的層級檔案一經建立就無法變更,且中繼資料更新會附加至記憶體內層級檔案 (和日誌),而不是零星寫入磁碟,因此處理並行作業的問題簡單許多。

相較之下,FVM 有大量技術債,缺乏健全的測試涵蓋範圍,且格式封裝不良 (Fuchsia 中有大量軟體會解讀 FVM 格式),因此很難變更。

如「效能提升」一文所述,我們維護的每個檔案系統實作項目都必須複製大部分的改善項目,這會增加改善項目的成本。這也適用於維護費用。減少維護的軟體,就能減少工程師的工作量。

請注意,由於現有產品依賴 FVM 和 Blobfs,因此我們在一段時間內無法完全淘汰這兩者。不過,我們預期 FVM 和 Blobfs 在現有產品上會相當穩定,維護成本可忽略不計。

利害關係人

協助人員:

abarth@google.com

審查者:

已諮詢:

公共平台:

設計完成後,我們首先向相關利害關係人 (軟體交付團隊很早就參與其中,而安全/核心團隊則在初步原型更為完善後才加入) 說明。

需求條件

  • Fxblob「必須」提供與 Blobfs 語意完全相符的檔案系統 API。也就是:
    • Fxblob 必須允許建立靜態檔案 (寫入一次,讀取多次)。
    • Fxblob 必須具有可定址內容的檔案名稱 (即檔案名稱必須是內容的 Merkle 根)。
    • 為方便攜帶及遷移,應使用與 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 相同的 Merkle 樹狀結構演算法 (檔案名稱是根雜湊) 進行內容定址。安裝 Blob 時會產生 Merkle 樹狀結構,而 Fxfs 會確保 Merkle 根目錄與檔案名稱相符,然後再保存檔案。每當從磁碟讀取檔案的某個區段時,Fxfs 會根據檔案的 Merkle 樹狀結構驗證內容,確保完整性。

Fshost 變更

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

如果 Fshost (透過靜態設定) 設定為預期使用 Fxblob 格式的系統,就會將 /blob 目錄連線至 Fxfs 執行個體中的 blob 磁碟區,並將 /data 目錄連線至 Fxfs 執行個體中的資料目錄。Fshost 也會繼續支援 FVM+Blobfs 的舊版設定。

系統組裝和建構工具的變更

系統組裝是 Fuchsia 建構的最後一個步驟,其中構件會組裝成一組映像檔和資訊清單,構成 Fuchsia 系統映像檔。

系統組裝的其中一項輸出內容是 FVM+Blobfs 映像檔,其中包含系統中的「基本」Blob 集。我們會調整系統組件,以便根據建構時間設定,發出包含這些 Blob 的 Fxfs 格式映像檔。

我們也會以「稀疏」格式發出 Fxfs 圖片,這樣傳輸到裝置時會更有效率。使用的稀疏格式為 Android 稀疏格式,fastboot 已支援此格式,因此是自然而然的選擇。

裝置啟動 (鋪路和刷機) 異動

雖然裝置啟動的鋪路程序已正式淘汰 (RFC-0075:淘汰以 zedboot 為基礎的鋪路程序),但仍廣泛使用,因此我們選擇在鋪路工作流程中新增 Fxfs 映像檔支援。

刷機是現代裝置的啟動程序,採用 fastboot 通訊協定。相較於鋪路 (會解讀收到的 FVM 映像檔),這個通訊協定相對簡單,因此這項變更相當容易。(Fastboot 不會解讀收到的原始映像檔,因此刷入 FVM 與 Fxfs 映像檔沒有差異;相反地,分區映像檔安裝工具會解讀 FVM 映像檔,因此必須新增自訂邏輯,才能處理分區映像檔安裝工具的 Fxfs 映像檔)。

上述稀疏映像檔格式可用於鋪路和刷機,以減少傳輸至裝置的位元組數,並減少記憶體用量。

套件安裝和 OTA 路徑變更

與 blobfs 不同,Fxblob 不支援 fuchsia.io 寫入路徑 (這會造成嚴重的效能問題)。fxblob 支援新的寫入 API,可為資料平面使用共用的 VMO (而非 FIDL),因此減少寫入 Blob 所需的 FIDL 往返次數。

新版 API 分為兩個通訊協定:fuchsia.fxfs.BlobCreator 和 fuchsia.fxfs.BlobWriter。BlobCreator 是 pkg_cache 用來建立新 Blob 的可探索通訊協定 (如果 Blob 尚不存在),而 BlobWriter 則是私有通訊協定,用於將 Blob 資料串流至 Fxfs 的機制。

共用 VMO 會整理為環狀緩衝區,方便我們將寫入要求排入管道。Pkg_cache 會填滿環狀緩衝區的一部分,然後使用 BlobWriter.BytesReady FIDL 方法通知 Fxfs 有些位元組可用。Fxfs 會等待這些通知、從環形緩衝區載入部分資料、計算該資料的 Merkle 雜湊,然後將資料串流至磁碟。

當最後一個 BytesReady 要求傳送至 Fxfs 時,Fxfs 會封鎖該要求,直到 Merkle 樹狀結構完全計算完畢,並根據收到的內容完成驗證為止;如果雜湊和內容不符,Fxfs 會以 ZX_ERR_IO_DATA_INTEGRITY 錯誤訊息回報這項要求失敗。最終呼叫完成後,Fxfs 會確保檔案可見 (即 fuchsia.io.Directory.ReadDirents)。

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

流程如下所示:

Blob 安裝路徑

Merkle 樹狀結構驗證、解壓縮和 VMEX 處理方式異動

Blobfs 目前具備 VMEX 能力,負責在需要時建立可執行的 VMO,以及在讀取時解壓縮 Blob 內容。由於 VMEX 允許執行任意程式碼,blobfs 也會依賴沙箱式解壓縮器,在讀取時解壓縮 Blob 內容。(Blob 內容是由外部提供,因此可視為不受信任的資料)。這個額外的躍點和與解壓縮相關的內容切換,會造成顯著的效能成本。

我們希望透過 Fxblob 最佳化分頁路徑的效能,這需要 (包括) 將解壓縮作業移至 Fxfs 程序本身。不過,將更多功能累積到高度信任的元件中,可能會引發安全疑慮。我們認為移除 Fxfs 的信任關係,即可解決這些疑慮。

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

  1. 根據 Merkle 樹狀結構驗證檔案內容,
  2. 使用 VMEX 資源鑄造可執行頁面。

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

我們也需要提供 VMEX 資源給 /bootstrap/base_resolver,這個元件會從基本集提供套件。這與 pkg-cache 類似 (事實上,大部分的商業邏輯都使用相同的程式庫)。

為確保 Fxfs 完全不受信任,我們也需要將 Merkle 驗證移至其他位置 (雖然 Fxfs 仍會儲存及提供 Merkle 樹狀結構內容)。你可以採取以下幾種做法:

  1. 建立獨立的高信任度元件,只用於驗證資料。
  2. 將這項功能移至核心,核心已是高度信任的環境。

請注意,pkg-cache 開啟 BLOB 時,必須直接與信任的驗證者通訊,或直接查詢這個信任的驗證者,才能比較驗證者使用的根雜湊與特定 BLOB 的預期雜湊。這樣一來,即使遭入侵的 Fxfs 完全變更資料和 Merkle 樹狀結構 (因此驗證器會將遭竄改的資料視為正確),pkg-cache 仍能偵測到竄改行為並拒絕檔案內容。

選擇在何處進行 Merkle 驗證,主要取決於安全性與效能之間的取捨,值得深入探討。因此,這項問題將在獨立的 RFC 中解決,目前驗證會在 Fxfs 中進行。

請注意,Fuchsia 日後還需要支援另外兩項功能,這兩項功能也依賴 Merkle 樹狀結構:Fs-verity 和 dm-verity,這兩項功能分別用於確保檔案和映像檔的完整性,部分 Linux 應用程式會使用這兩項功能。Fuchsia 打算支援這兩者,這是 RFC-0082:在 Fuchsia 上執行未修改的 Linux 程式的一部分。設計程序外 Merkle 驗證時,也需要將這些用途納入考量。

實作

我們採取的方法是在測試和開發環境中實作功能原型並啟用這些原型,以便「浸潤」,目前為止成效良好。我們打算在日後的工作和未完成的功能中繼續採用這項做法 (請參閱「安全性」一節,瞭解與 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 會將中繼資料更新儲存為一連串的變更,而非為索引節點、範圍等項目進行固定分配,因此 Fxfs 的儲存空間用量比 Blobfs 更複雜。不過,移除 FVM (需要一些磁碟空間來儲存自己的中繼資料,並在分配的配量中產生內部分割) 後,我們也能回收一些儲存空間,而且 Fxfs 具有更大的彈性,日後可變更格式來提升儲存空間效能 (例如最佳化層級檔案格式)。撰寫本 RFC 時,我們的原型實作使用的磁碟空間量,與 Blobfs 儲存同等數量的 Blob 相當。

人體工學

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

回溯相容性

我們將繼續支援舊版設定 (FVM + Blobfs) 中的系統,這類系統支援已發布的產品。在相關產品停產前,我們將繼續支援舊版設定 (以及適當的測試基礎架構)。

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

目前沒有遷移路徑的規劃,裝置會使用 Fxblob 或舊版設定。

安全性考量

Blobfs 是 Fuchsia 中非常值得信賴的元件,在 Fuchsia 的驗證執行中扮演重要角色,因為它會驗證系統上幾乎所有其他元件的軟體完整性。(儲存在啟動檔案系統中的低階元件會透過不同方式驗證)。此外,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 中增加使用量)。我們預計會經過長時間的浸泡測試,才會在任何使用者裝置上採用這個架構。

此外,我們也為相關功能新增了常見的單元測試、整合測試和端對端測試。我們也導入了自動化基準,追蹤從 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 相比,有足夠的特定限制,因此我們決定自訂介面值得付出努力。我們獲得了令人滿意的成效,證明這項努力是值得的。

既有技術和參考資料

可以說,這項提案讓 Fuchsia 更接近大多數作業系統,這些作業系統使用一般用途檔案系統來達成更多用途,並透過其他機制提供內容可定址性和驗證等專門功能。雖然我們會為這項提案在 Fxfs 中新增一些特殊功能,但大部分工作會在 Fxfs 的外層進行,核心檔案系統不會變動。

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