| 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 在現有產品上應已足夠穩定,維護成本可忽略不計。
利害關係人
協助人員:
審查者:
- 安全性 (joshlf@google.com)
- 儲存空間 (csuter@google.com)
- 軟體推送 (galbanum@google.com)
- 核心 (cpu@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 磁碟區,並將 /資料目錄連線至 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)。
我們建立了一個用戶端程式庫,用於封裝環形緩衝區管理作業。
流程如下所示:

Merkle 樹狀結構驗證、解壓縮和 VMEX 處理方式異動
Blobfs 目前持有 VMEX 能力,負責在需要時建立可執行的 VMO,以及在讀取時解壓縮 Blob 內容。由於 VMEX 允許執行任意程式碼,blobfs 也會依賴沙箱解壓縮器,在讀取時解壓縮 Blob 內容。(Blob 內容是由外部提供,因此可視為不受信任的資料)。這個額外的躍點和與解壓縮相關聯的內容切換,會造成顯著的效能成本。
我們希望透過 Fxblob 最佳化分頁路徑的效能,這需要 (包括其他事項) 將解壓縮作業移至 Fxfs 程序本身。不過,將更多功能累積到高度信任的元件中,可能會引發安全疑慮。我們認為可以透過移除 Fxfs 的信任機制,解決這些疑慮。
為達成這個目標,需要將兩項信任的作業移出 Fxfs:
- 根據 Merkle 樹狀結構驗證檔案內容,
- 使用 VMEX 資源鑄造可執行頁面。
為避免 Fxfs 必須保留 VMEX 能力,pkg-cache 會接管這項責任。由於 pkg-cache 必須已受信任,才能將 Blob 名稱對應至雜湊,因此這不會對指派給該元件的信任範圍造成重大影響,且可避免將 Fxfs 視為高信任度元件。
我們也需要提供 VMEX 資源給 /bootstrap/base_resolver,這個元件會從基本組合提供套件。這與 pkg-cache 類似 (事實上,大部分的業務邏輯都使用相同的程式庫)。
為確保 Fxfs 完全不受信任,我們也需要將 Merkle 驗證移至其他位置 (雖然 Fxfs 仍會儲存及提供 Merkle 樹狀結構內容)。你可以採取以下幾種做法:
- 建立獨立的高信任度元件,只用於驗證資料。
- 將這項功能移至核心,核心已是高度信任的環境。
請注意,開啟 Blob 時,pkg-cache 必須直接與信任的驗證者通訊,或直接查詢這個信任的驗證者,才能比較驗證者使用的根雜湊與特定 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 會將中繼資料更新儲存為一組層級檔案中的一系列變更,而非為 inode、範圍等項目分配固定空間,因此 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 結合,分別提供映像檔和特定檔案的內容驗證。