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

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

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

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

摘要

为了回收系统上的空间,我们必须拆分更新软件包。在至少一款受空间限制的产品中,我们将节省约 14 MiB。这项更改非常重要,需要发布过渡版本。根据 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

Reviewers:

  • 软件交付团队: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"
      }
    ]
  }
}

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

system-updater 将解析清单,以确定是否需要提取映像(基于具有相应哈希的文件是否已位于相应槽位)。对于每个发生更改的映像,系统都会提取该映像,将其写入其分区,然后从 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 不会对图片的创建或内容做出任何更改,只会更改图片的传送顺序,因此不会影响隐私。

测试

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

文档

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

缺点、替代方案、未知

替代方案是完全不将图片写入 BlobFS 的设计。

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

我们还可以采用另一种方法,即将映像保留在更新软件包中,但将更新软件包视为比现在更特殊的软件包:我们可以完全避免将映像保存到 blobfs。这种设计可以免去对更新软件包进行格式更改的需要,但需要对系统更新程序逻辑进行大量更改,并使更新软件包的处理与“正常”软件包的处理分道而行。我们认为,所提议的设计只是重构了更新软件包,而不是引入了特殊的处理逻辑。

在先技术

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