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

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

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

问题
Gerrit 更改
  • 678884
作者
审核人
提交日期(年-月-日)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 属性定义了应如何解释内容属性。 使用此 RFC 定义的格式时,版本必须始终为“1”,但现在引入版本属性可以简化将来可能需要的其他更改。此模式已在 SWD 堆栈清单中的其他位置使用,并与 Serde 完美集成。

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

图片的大小和哈希值包含在验证检查中。该哈希值是图片文件的 SHA256 哈希值,以十六进制表示。由于分区因设备而异,因此我们还需要知道图像的大小以便进行比较。网址包含 Merkle 哈希。Merkle 哈希的计算比较复杂,因此我们选择 SHA256 哈希来加快比较速度。

我们提议的 OTA 流程如下:

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

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

实现

要对更新软件包进行此项更改,我们必须发布三个阶段:第一个版本用于处理当前更新软件包格式和新更新软件包格式的超集,第二个版本用于仅生成新格式,而最终版本则用于停止处理旧版本更新软件包的逻辑。

在第一阶段,system_updater 会被修改以成功解析原始更新软件包格式和此 RFC 中建议的修改后的格式。MOS 仍会使用原始格式生成更新包。包含这项工作的版本将被标记为踏板,以确保所有 Fuchsia 设备在收到使用新格式的更新软件包之前,会收到能够解析新格式的 system_updater。

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

在第三个阶段,一旦我们确信设备不需要回滚到使用原始更新软件包格式的版本,系统就会修改 system_updater 以取消对原始更新软件包格式的支持。

如果未预演发布阶段,只能解读当前版本更新软件包的设备在收到更新更新包后会发生故障。我们需要将第一阶段版本标记为“踏板”build,以确保所有设备都能顺利通过该 build。

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

性能

预计不会有显著变化。

我们需要获取图片的哈希值并进行比较。理想情况下,哈希一致,我们不需要花时间提取或编写它们。在最糟糕的情况下,如果所有图片都发生变化,我们仍然需要下载并写入相同数量的字节。

安全注意事项

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

映像的完整性检查不会发生变化。我们将继续使用相同的方法来获取更新软件包,该更新软件包包含映像软件包的哈希,所有其他安全属性都将在设备重新启动到新系统时通过启动时验证来强制执行。

隐私保护注意事项

此 RFC 不对映像的创建或内容进行任何更改,只引入了映像的传送顺序,因此不会影响隐私权。

测试

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

文档

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

缺点、替代方案、未知情况

替代方案是根本不将图像写入 BlobFS 的设计。

简单的方法是将映像直接铺到其分区,从 blobfs 进行垃圾回收,最后下载属于保留索引的新软件包 blob。这种替代方案易于实现,可以避免重复写入,并且不需要跳跃版本。不过,我们无法保证能继续推进后续进度。如果更新中断,设备可能根本无法更新。

还有一种替代方案是,我们会将映像保存在更新软件包中,但会将更新软件包视为比现在更加特殊:我们可以完全避免将图片保存到 blobfs。这种设计将无需更改更新软件包的格式,但需要对系统更新程序逻辑进行大量更改,并将更新软件包的处理与“普通”软件包的处理分离开来。我们认为提议的设计只是重构更新软件包,而不是引入特殊的处理逻辑。

前文

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