RFC-0170:从更新软件包中移除二进制映像

RFC-0170:从更新软件包中移除二进制映像
状态已接受
区域
  • 软件交付
说明

在 OTA 期间重新排序映像写入,以节省空间。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-05-12
审核日期(年-月-日)2022-06-22

摘要

为了回收系统上的空间,我们必须拆分更新软件包。在至少一个空间受限的产品上,我们将节省约 14MiB。这是一项非同小可的更改,需要一个过渡版本。根据 RFC 103,所有 过渡版本都需要自己的 RFC。此 RFC 详细介绍了新的更新软件包格式。

设计初衷

无线下载 (OTA) 更新是一种在运行的设备上升级 Fuchsia 版本的机制。如果有可用更新,系统更新程序将提取更新软件包。提取软件包意味着软件包的内容将写入 BlobFS 并受到垃圾回收的保护。更新软件包包含映像(例如恢复映像和 Zircon 启动映像),这些映像还在 Zircon 分区上保留了空间,以及要下载以完成更新的其他软件包的列表。

目前,Fuchsia 设备必须存储每个映像的两个副本:

  1. 一个副本位于目标分区(例如 ZIRCON_A)中,设备在运行时使用该副本。
  2. 一个副本位于 blobfs 中,因为映像作为更新软件包内的 blob 交付给设备。

通过保护映像免受垃圾回收的影响,更新可保证在中断时向前推进。向前推进是更新系统必须保证的一项极其重要的属性。但是,在空间受限的产品上,将映像写入磁盘分区和 BlobFS 都是次优的预算。

映像写入是 倒数第二步 在 OTA 过程之前 切换哪个分区是活动分区并重新启动到新的 系统映像。在下一次更新之前,内核、固件和恢复映像将受到保护,免受垃圾回收和删除的影响。通过在 OTA 期间重新排序映像写入,我们可以在 OTA 中下载大多数软件包之前从 BlobFS 中回收映像,并回收空间预算以供其他软件包使用。更改 SWD 设计以移除映像的重复副本有可能节省大量空间,这在某些 Fuchsia 设备上非常重要。

为了在 OTA 期间回收二进制映像,同时仍保证向前推进,我们需要更改更新软件包的格式。

利益相关方

教员: hjfreyer@google.com

审核人

  • 软件交付:wittrock@google.com、jsankey@google.com
  • MOS:gtsai@google.com
  • 安全性:ampearce@google.com
  • 产品组装:awolter@google.com
  • 发布:billstevenson@google.com

设计

目前,更新软件包也是一个软件包,其中还包含在提取更新软件包时提取并写入 blobfs 的映像。

我们建议将映像从更新软件包中提取出来,并将每个映像放入其自己的 软件包中。

这与我们当前的 OTA 流程和软件包格式完全一致,但确实需要更改更新软件包格式。

为了引用这些新软件包,我们将在更新软件包中添加一个名为 images.json 的文件,其中包含描述映像软件包的元数据。该文件的示例为:


{
  "version": "1",
  "contents": {
    "partitions": [
      {
        "type": "zbi",
        "slot": "fuchsia",
        "size": 1,
        "hash": "0a",
        "url": "fuchsia-pkg://fuchsia.com/fuchsia-zbi/0?hash={merkle_hash}#path/to/fuchsia.zbi"
      },
      {
        "type": "vbmeta",
        "slot": "fuchsia",
        "size": 2,
        "hash": "0b",
        "url": "fuchsia-pkg://fuchsia.com/fuchsia-vbmeta/0?hash={merkle_hash}#path/to/fuchsia.vbmeta"
      },
      {
        "type": "zbi",
        "slot": "recovery",
        "size": 3,
        "hash": "0c",
        "url": "fuchsia-pkg://fuchsia.com/recovery-zbi/0?hash={merkle_hash}#path/to/recovery.zbi"
      },
      {
        "type": "vbmeta",
        "slot": "recovery",
        "size": 4,
        "hash": "0d",
        "url": "fuchsia-pkg://fuchsia.com/recovery-vbmeta/0?hash={merkle_hash}#path/to/recovery.vbmeta"
      }
    ],
    "firmware": [
      {
        "type": "",
        "size": 5,
        "hash": "0e",
        "url": "fuchsia-pkg://fuchsia.com/update-images-firmware/0?hash={merkle_hash}#path/to/firmware"
      },
      {
        "type": "bl2",
        "size": 6,
        "hash": "0e",
        "url": "fuchsia-pkg://fuchsia.com/update-images-firmware/0?hash={merkle_hash}#path/to/firmware"
      }
    ]
  }
}

version 属性定义了应如何解读 contents 属性。 使用此 RFC 定义的格式时,Version 必须始终为“1”,但现在引入 version 属性可以简化将来可能需要的其他更改。此模式已在 SWD 堆栈的清单中的其他位置使用,并且与 serde 很好地集成。

系统更新程序将解析清单,以确定是否需要提取映像(根据相应哈希的文件是否已位于相应槽中)。对于每个已更改的映像,系统都会提取该映像,将其写入其分区,然后从 BlobFS 中回收该映像。如果 images.json 中没有映像,则我们不会覆盖 zircon 分区上的内容。

映像的大小和哈希值包含在内,用于验证检查。哈希值是以十六进制表示的映像文件的 SHA256 哈希值。由于分区在不同设备上是可变的,因此我们还需要知道映像的大小以进行比较。网址具有 Merkle 哈希值。Merkle 哈希值的计算较为复杂,因此选择 SHA256 哈希值是为了进行更快的比较。

我们建议的 OTA 流程如下:

  1. 下载更新软件包
  2. 解析包含更新映像软件包引用的新元数据文件
  3. 对于该文件中列出的每个映像,如果该映像与非活动分区上指定 Zircon 分区中的映像相同,则继续。元数据文件包含映像的哈希值和大小(因为映像大小不等于分区大小),我们可以快速与非活动分区上映像的哈希值进行比较。否则:
    1. 提取包含映像的软件包,该软件包会将映像写入 BlobFS 并处理完整性检查。将软件包添加到保留索引。
    2. 写入分区。
    3. 从 BlobFS 中回收(通过从保留索引中移除软件包)以回收空间。
  4. 继续下载更新软件包中指定的其余软件包,并完成 OTA。

更改更新软件包的结构可以解决空间限制问题。写入 BlobFS,然后进行垃圾回收,使我们能够利用当前存储架构提供的已全面的安全保证。

实现

为了对更新软件包进行此更改,我们必须进行三阶段发布:首先处理当前更新软件包格式和新更新软件包格式的超集,然后发布仅生成新格式的版本,最后发布停止处理旧版更新软件包的逻辑的版本。

在第一阶段,系统更新程序将经过修改,以成功解析原始更新软件包格式和此 RFC 中提出的修改后的格式。 MOS 仍将使用原始格式生成更新软件包。包含此工作的版本将被标记为过渡版本,确保所有 Fuchsia 设备在收到使用新格式的更新软件包之前,都会收到能够解析新格式的系统更新程序。

在第二阶段,MOS 将开始使用此 RFC 提出的新格式生成更新软件包。

在第三阶段,当我们确信没有设备需要回滚到使用原始更新软件包格式的版本时,系统更新程序将经过修改,以移除对原始更新软件包格式的支持。

如果我们不分阶段发布,只能解读当前版本更新软件包的设备在收到更新后的更新软件包时将被刷机。我们需要将第一阶段版本标记为“过渡”build,以确保所有设备都通过该 build。

更新软件包的用户需要了解分阶段发布。已知用户包括 Scrutiny、MOS、产品组装和软件交付。

性能

预计不会有重大变化。

我们需要获取映像的哈希值并进行比较。在最佳情况下,哈希值匹配,我们无需花费时间提取或写入它们。在最坏的情况下,所有映像都发生更改,我们仍然需要下载和写入相同数量的字节。

安全注意事项

Scrutiny(我们的 build 时安全分析工具)会分析更新软件包以从中提取 ZBI。我们需要更新 Scrutiny 测试,以反映更新软件包中 ZBI 的新位置。

映像的完整性检查不会更改。我们将继续使用相同的方法提取更新软件包,并且更新软件包包含映像软件包的哈希值,当设备重启到新系统时,所有其他安全属性都由经过验证的启动强制执行。

隐私保护注意事项

此 RFC 不会对映像的创建或内容进行任何更改,只会更改映像的交付顺序,因此不会影响隐私。

测试

我们已经针对更新软件包和系统更新程序进行了单元测试和端到端集成测试。我们需要扩展这些测试,以涵盖从当前版本的更新软件包到第一个过渡版本的中间版本。对于第二个版本,我们需要进行测试,以同时处理中间版本的更新软件包和新版本的更新软件包。工作完成后,我们将移除中间测试,并测试使用旧格式的更新软件包进行降级 OTA 始终会失败。

文档

如果此更改获得批准,我们将需要更新更新软件包 文档OTA 文档

缺点、替代方案、未知因素

替代方案是不将映像写入 BlobFS 的设计。

简单的方法是将映像直接铺设到其分区,从 blobfs 中回收更新软件包,最后下载属于保留索引的新软件包 blob。此替代方案易于实现,避免了重复写入,并且不需要过渡版本。但是,我们将不再保证向前推进。如果更新中断,设备可能会完全无法更新。

还有一种替代方案,即我们在更新软件包中保留映像,但将更新软件包视为比现在更特殊:我们可以完全避免将映像保存到 blobfs。此设计将无需对更新软件包进行格式更改,但需要对系统更新程序逻辑进行广泛更改,并将更新软件包的处理与“正常”软件包的处理分开。我们认为,建议的设计只是重构了更新软件包,而不是引入特殊的处理逻辑。

在先技术

更新软件包的设计之前已在 fuchsia.dev 上记录。