Apk 规范

来自 Alpine Linux
此材料正在进行中...

在移除此通知之前,请勿遵循此处的说明。
(由 Zcrayfish 于 2024 年 10 月 14 日最后编辑。)

有关 apk 的面向最终用户的文档,请查看 Alpine Package Keeper 页面。

此页面旨在记录 apk 软件包管理器的内部数据结构。apk 格式的规范实现是 apk-tools,并且此信息大部分是从阅读源代码中收集的。

APK 数据格式有三个版本。版本 1 已弃用且不再使用,版本 2 是 apk-tools 当前使用的主要版本,版本 3 正在开发中。此页面主要描述版本 2 中使用的数据格式。

背景

Tar 段

Tar 段是一组 tar 记录。标准 tar 文件在 tar 文件末尾包含两个空记录以表示 tarball 的结束。Tar 段缺少这两个记录,因此可以在其他 tar 文件之前连接,并且表现为一个连续的 tar 文件。APK v2 软件包格式同时使用 tar 段和 tarball。

Tar 段可以使用 gzip 压缩进行压缩。Gzip 是一种基于流的文件格式,多个流可以连接在一起。大多数工具会将文件中的多个 gzip 流视为单个流。APK v2 文件知道 gzip 流并使用它们进行文件分段。

软件包格式 V2

二进制格式

APK v2 软件包包含两个 tar 段,后跟一个 tarball,每个都在自己的 gzip 流中(总共 3 个流)。这些流包含软件包签名、控制数据和软件包数据。软件包数据是一个 tarball,其中包含软件包中包含的文件,这些文件的布局方式使其可以在文件系统根目录解压缩,以便所有文件都放置在系统上的正确位置。控制 tar 段包含软件包元数据以及任何安装脚本。签名 tar 段包含一个文件,该文件是对控制段的二进制签名。

签名文件是控制 tar 段 gzip 流的 SHA1 哈希的 DER 编码 PKCS1v15 RSA 签名。文件名格式如下.SIGN.RSA.<密钥名称>.rsa.pub(例如.SIGN.RSA.alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub)。此文件放置在 tar 记录中,权限为 0644,uid 为 0,gid 为 0。此 tar 记录(缺少 tar 结束记录)经过 gzip 压缩,形成签名 tar 段,并连接到组合的控制和数据段的前面。abuild-sign 负责生成这些签名段。

Apk 将通过在 /etc/apk/keys 中查找名为 exactly <密钥名称>.rsa.pub 的文件来验证签名。如果在 /etc/apk/keys 中找不到此类文件,则验证失败。

控制段包含 .PKGINFO 文件中的软件包元数据以及 apk 在软件包安装和删除期间使用的所有脚本(如果有)。由于历史原因,控制 tar 段中的所有文件都以点(.)为前缀。控制段的构建方式是将软件包的每个文件放入 tar 记录,连接这些 tar 记录,对 tar 记录进行 gzip 压缩,并将它们连接到数据 tarball 的前面。此 gzip 流的 SHA1 哈希用作校验和CAPKINDEX 文件中的字段。

数据 tarball 是一个标准的 gzipped tarball,带有额外的 PAX 标头,其中包含 tar 标头中每个文件的 SHA1 哈希。哈希包含在名为 APK-TOOLS.checksum.SHA1 的标头中。与其他 tar 流不同,此 tarball 确实包含两个 tar 结束空记录。它始终是 APK 软件包的最后一个段。哈希使用 abuild-tar 工具添加。

PKGINFO 格式

PKGINFO 文件包含软件包元数据。这是一个类似于 INI 文件的纯文本文件。以#开头的行是注释并且被忽略。与 INI 文件不同,此文件的解析格式非常严格。每个键值对必须恰好用一个空格、一个等号和一个空格分隔( = )。键可以在此文件中重复,如果找到重复项,应将其视为值列表。

PKGINFO 中哪些字段有效的规范很大程度上由 abuild 定义。截至 2022 年 7 月,支持以下字段

  • pkgname- 软件包名称
  • pkgver- 软件包版本
  • pkgdesc- 软件包描述
  • url- 软件包 url
  • builddate- 软件包构建日期/时间的 unix 时间戳
  • packager- 构建软件包的人员的姓名(通常是电子邮件)
  • size- 软件包的已安装大小
  • arch- 软件包的体系结构(例如:x86_64)
  • origin- 软件包的来源名称
  • commit- 构建软件包的提交哈希
  • maintainer- 软件包维护者的姓名(通常是电子邮件)
  • replaces_priority- 软件包的替换优先级字段(整数)
  • provider_priority- 软件包的提供者优先级(整数)
  • license- 软件包的许可证字符串
  • depend- 软件包的依赖项(重复)
  • replaces- 此软件包替换的软件包(重复)
  • provides- 此软件包提供的功能(重复)
  • triggers- 此软件包触发的软件包(重复)
  • install_if- 如果存在这些软件包,则安装此软件包(重复)
  • datahash- 数据 tarball 的十六进制编码 sha256 校验和

PKGINFO 示例

# Generated by abuild 3.9.0-r2
# using fakeroot version 1.25.3
# Wed Jul  6 19:09:49 UTC 2022
pkgname = busybox
pkgver = 1.35.0-r18
pkgdesc = Size optimized toolbox of many common UNIX utilities
url = https://busybox.net/
builddate = 1657134589
packager = Buildozer <alpine-devel@lists.alpinelinux.org>
size = 958464
arch = x86_64
origin = busybox
commit = 332d2fff53cd4537d415e15e55e8ceb6fe6eaedb
maintainer = Sören Tempel <soeren+alpine@soeren-tempel.net>
provider_priority = 100
license = GPL-2.0-only
replaces = busybox-initscripts
provides = /bin/sh
triggers = /bin /usr/bin /sbin /usr/sbin /lib/modules/*
# automatically detected:
provides = cmd:busybox=1.35.0-r18
provides = cmd:sh=1.35.0-r18
depend = so:libc.musl-x86_64.so.1
datahash = 7d3351ac6c3ebaf18182efb5390061f50d077ce5ade60a15909d91278f70ada7

软件包构建示例

这是一组用于部分构建软件包的命令。请勿执行此操作,这主要是一个示例,用于了解所有内容如何组合在一起。使用官方构建工具来构建软件包。

tar -c .PKGNIFO .pre-install | abuild-tar --cut | gzip -9 > $controldir/control.tar.gz
cd $pkgdir; tar -c * | abuild-tar --hash | gzip -9 > $controldir/data.tar.gz
cat $controldir/control.tar.gz $controldir/data.tar.gz > mypackage-1.0-r0.apk

索引格式 V2

二进制格式

索引以 APKINDEX.tar.gz 的形式提供,并由 apk 下载以支持软件包数据库。索引的签名方式与软件包类似。索引和软件包之间的主要区别在于索引文件仅包含两个段。

签名段与软件包段相同,并在其自己的 gzip 流中连接到 APKINDEX tarball 的开头。

APKINDEX tarball 包含两个文件:DESCRIPTION 文件和 APKINDEX 文件。这些文件中的每一个都在其自己的 tar 记录中,最后一个记录之后是标准的 tar 结束空记录。DESCRIPTION 文件是一个简单的文本文件,其中包含索引的描述(例如community v20210212-7170-g5c9853dc69)。APKINDEX 文件是一个文本文件,其中包含存储库中每个软件包的记录,格式为基于文本的格式。每个记录都用换行符分隔。

APKINDEX 格式

APKINDEX 文件包含一组从存储库中每个软件包的 PKGINFO 文件中提取的记录。每行都以字母、冒号为前缀,后跟字段的值。行以换行符(\n)终止,并且软件包的记录之间有一个空行。

apk_pkg_write_index_entryapk_pkg_write_index_entrypackage.c 函数定义了当前接受的字段。截至 2022 年 7 月,这些是

  • C- 文件校验和,请参见下文
  • C- 软件包名称(对应于 pkgname 在 PKGINFO 中)pkgnamepkgname
  • V- 软件包版本(对应于 pkgver 在 PKGINFO 中)pkgverpkgname
  • A- 体系结构(对应于 arch 在 PKGINFO 中),可选archarch
  • S- 整个软件包的大小,整数
  • I- 已安装大小,整数(对应于 size 在 PKGINFO 中)sizepkgname
  • sizeTpkgdescpkgname
  • - 描述(对应于 pkgdesc 在 PKGINFO 中)Uurlpkgname
  • - url(对应于 url 在 PKGINFO 中)Llicensepkgname
  • - 许可证(对应于 license 在 PKGINFO 中)ooriginarch
  • - 来源(对应于 origin 在 PKGINFO 中)mmaintainerarch
  • - 维护者(对应于 maintainer 在 PKGINFO 中)tbuilddatearch
  • - 构建时间(对应于 builddate 在 PKGINFO 中)ccommitarch
  • - 提交(对应于 commit 在 PKGINFO 中)kprovider_priorityarch
  • - 提供者优先级,整数(对应于 provider_priority 在 PKGINFO 中)Ddepend- 依赖项(对应于 depend 在 PKGINFO 中,用空格连接成一行)
  • p- 提供(对应于 provides 在 PKGINFO 中)provides- 依赖项(对应于 depend 在 PKGINFO 中,用空格连接成一行)
  • i- 如果安装(对应于 install_if 在 PKGINFO 中)install_if- 依赖项(对应于 depend 在 PKGINFO 中,用空格连接成一行)

软件包校验和字段

软件包校验和字段是软件包中第二个 gzip 流(控制流)的 SHA1 哈希。二进制哈希摘要是 base64 编码的。这以Q1为前缀,以便将其与旧索引格式中使用的 MD5 哈希区分开来。无法使用标准命令行工具计算此校验和,但 apk-tools 可以在其 indexindex操作中计算。

APKINDEX 记录示例

C:Q1P4IRU/u5yB4CSnUEBRD1WWwajrY=
P:jool-tools
V:4.1.5-r0
A:x86_64
S:140605
I:434176
T:Userspace control tools for SIIT / NAT64 Jool
U:https://www.jool.mx
L:GPL-2.0-only
o:jool-tools
m:Jakub Jirutka <jakub@jirutka.cz>
t:1620480809
c:771b3b0910ea9c7736db6ca4ff5c37ca9cf9af0d
D:so:libc.musl-x86_64.so.1 so:libnl-3.so.200 so:libnl-genl-3.so.200
p:cmd:jool=4.1.5-r0 cmd:jool_siit=4.1.5-r0 cmd:joold=4.1.5-r0

已安装数据库 V2

已安装数据库由 apk 用于跟踪已安装的软件包以及这些软件包对系统所做的修改。此文件位于 /lib/apk/db/installed。已安装文件是一个纯文本文件,格式与 APKINDEX 相同(包含在 APKINDEX.tar.gz 中)。它既没有压缩也没有签名。已安装文件中的每个记录都以软件包索引记录开头,该记录的字段与 APKINDEX 文件相同。已安装文件添加了一些在 database.c 中定义的附加字段。截至 2022 年 7 月,这些附加字段是

  • r- 此软件包替换的软件包,空格分隔列表
  • q- 替换优先级,整数,可选
  • s- 存储库标签,可选,如果软件包在 world 文件中标记到存储库,则将设置此标签(例如:linux@testing)
  • f- 指示损坏的项目,空格分隔(f=文件,s=脚本,x=扩展属性,S=文件哈希)

以下字段重复出现,并且成组包含一组对系统进行的用于安装软件包的更改。

ACL 行指定为 uid、冒号、gid、冒号和模式。

  • F- 由软件包创建的目录名称,重复
  • M- 目录 ACL,仅当与默认值 0:0:0755 不同时
  • R- 文件名,相对于前一个目录名
  • a- 文件 ACL
  • Z- 文件校验和,如果软件包中的校验和不为 none,则前缀指示这将是 base64 格式的 SHA1 哈希Q1prefix