# Debian软件包内部管理

deepin 采用了 Debian 的软件包管理机制，所以在 deepin 中，软件包的管理和 Debian 中的软件包管理是一样的。本节将介绍 Debian 中的软件包内部管理机制。

## 档案库元数据

每个发行版的元数据文件都保存在 deepin 镜像站的 “dist/codename” 下面，例如 “[https://community-packages.deepin.com/beige/](https://community-packages.deepin.com/beige/)”。档案库的结构可以通过网络浏览器来浏览。其中有 6 种关键的元数据。

| 文件                    | 位置                  | 内容                              |
|:---------------------:|:-------------------:|:-------------------------------:|
| Release               | 发行版的顶层              | 档案库描述和完整性信息                     |
| Release.gpg           | 发行版的顶层              | "Release" 文件的签名文件，使用档案库密钥签名     |
| Contents-architecture | 发行版的顶层              | 列出在相关架构中所有软件包的全部文件              |
| Release               | 每个发行版/区域/架构组合的顶部    | 归档描述使用 apt_preferences( 5 ) 的规则 |
| Packages              | 每个发行版/区域/二进制架构组合的顶部 | 连接 debian/control 获得二进制包        |
| Sources               | 每个 发行版/区域/源代码 组合的顶部 | 连接 debian/control 获取源代码包        |

为了减少网络流量，在最近的档案库中，这些元数据存储为压缩了的差分文件。

## 顶层“Release”文件及真实性

::: tip
顶层“Release”文件用于签署 secure APT 系统下的归档文件。
:::
每个 deepin 档案库的网址都有一个这样的 “Release” 文件，例如 “[https://community-packages.deepin.com/beige/dists/beige/Release](https://community-packages.deepin.com/beige/dists/beige/Release)”，内容如下

```
Origin: Linux Deepin
Label: Deepin
Codename: beige
Version: 2022
Date: Tue, 07 Feb 2023 13:04:48 UTC
Architectures: i386 amd64 arm64
Components: main commercial community
Description: Deepin Beige packages.
MD5Sum:
 07c4f01af3bb28cdac46fc2e8c005832 9105156 main/binary-i386/Packages
 b10224cbd1db535edbd82fbf1a04097d 2338130 main/binary-i386/Packages.gz
 90315149a67fcc23f9e16191360c6222 120 main/binary-i386/Release
 d41d8cd98f00b204e9800998ecf8427e 0 main/debian-installer/binary-i386/Packages
 7029066c27ac6f5ef18d660d5741979a 20 main/debian-installer/binary-i386/Packages.gz
 d388804b4ff6fb4e8b09278a2187b1e9 26055803 main/binary-amd64/Packages
 510345c94769b5e010887ca34c3785c0 6820724 main/binary-amd64/Packages.gz
 07a506716d5940e121ef109c33607ba4 121 main/binary-amd64/Release

```

::: warning 注意
在 第 2.1.4 节 “Debian 档案库基础” 里，你能够发现我使用"suite" 和 "codename"的逻辑。“发行版”被用来同时谈及"suite" 和 "codename".所有由档案库提供的归档 "area" 名，会被列在 "Components" 下.
:::

顶层文件 "Release" 的完整性，是由叫 secure apt 的加密架构来验证.

- 加密签名文件 "Release.gpg" 是由顶层授权文件 "Release" 和加密的 Debian 档案库公钥创建。
- 公开的 Debian 档案库公钥能够被植入“/etc/apt/trusted.gpg”；
  - 这样的操作可以由安装最新的 base-files 软件包的方式完成，或者
  - 手动使用 gpg 或者 apt-key 工具 获取发布在 ftp-master.debian.org 上最新的档案库公钥。
- secure APT 系统验证下载的顶层文件 "Release" 的完整性。加密验证过程用到了"Release.gpg"文件和在"/etc/apt/trusted.gpg"里的 Debian 档案库公钥.

所有 "Packages" 和 "Sources" 文件的完整性是由在顶层 "Release" 文件里的 MD5sum 值来验证。所有软件包文件的完整性由 "Packages" 和 "Sources" 文件里的 MD5sum 值来验证.参见 debsums(1) 和 第 2.4.2 节 “验证安装的软件包文件”.

因加密签名验证比计算 MD5sum 值消耗更多的 CPU，使用 MD5sum 值来验证每一个软件包，使用加密签名来验证顶层的 "Release" 文件，这种方式提供[较好安全性的同时，也有比较好的性能](http://www.infodrom.org/~joey/Writing/Linux-Journal/secure-apt/) (参见 第 10.3 节 “数据安全基础”).

## 档案库层的“Release”文件

::: tip
档案库层的“Release”文件将用作 apt_preferences(5) 的规则。
:::
归档层次的 "Release" 文件，其全部归档位置在 "/etc/apt/sources.list"中的 "deb"行中指定，如以下的 "[https://community-packages.deepin.com/beige/dists/beige/main/binary-amd64/Release](https://community-packages.deepin.com/beige/dists/beige/main/binary-amd64/Release)"
或 "[https://community-packages.deepin.com/beige/dists/beige/main/binary-arm64/Release](https://community-packages.deepin.com/beige/dists/beige/main/binary-arm64/Release)"。

```
Version: 2022
Component: main
Origin: Linux Deepin
Label: Deepin
Architecture: amd64
Description: Deepin Beige packages.
```

对于部分档案库,比如说 experimental 和 bullseye-backports, 它们包含的软件包不会被自动安装，这是因为有额外的行，例如在 "http://deb.debian.org/debian/dists/experimental/main/binary-amd64/Release" 里面有如下额外的一行。

```
Archive: experimental
Origin: Debian
Label: Debian
NotAutomatic: yes
Component: main
Architecture: amd64
```

请注意，普通的档案库没有 "NotAutomatic: yes", 默认的 Pin-Priority 值是 500, 而对于有 "NotAutomatic: yes"的特殊档案库, 默认的 Pin-Priority 值是 1 (参见 apt_preferences(5) 和 第 2.7.3 节 “调整候选版本”).

## 获取用于软件包的元数据

当使用 APT 工具时，如 aptitude, apt-get, synaptic, apt-file, auto-apt，我们需要更新包含 Debian 档案库信息元数据的本地拷贝。这些本地拷贝的文件名称，和在 "/etc/apt/sources.list" 文件里面的 distribution, area, architecture 相应名称一致。 (参见 第 2.1.4 节 “Debian 档案库基础”).

- "/var/lib/apt/lists/deb.debian.org_debian_dists_distribution_Release"
- "/var/lib/apt/lists/deb.debian.org_debian_dists_distribution_Release.gpg"
- "/var/lib/apt/lists/deb.debian.org_debian_dists_distribution_area_binary-architecture_Packages"
- "/var/lib/apt/lists/deb.debian.org_debian_dists_distribution_area_source_Sources"
- "/var/cache/apt/apt-file/deb.debian.org_debian_dists_distribution_Contents-architecture.gz" (apt-file)

前 4 种类型的文件是所有相关的 APT 命令共享的，并且可以通过 “apt-get update” 或 “aptitude update” 在命令行中进行更新。如果在 “/etc/apt/sources.list” 中有相应的 “deb” 行，则 “软件包” 元数据会进行更新。如果在 “/etc/apt/sources.list” 中有相应的 “deb-src” 行，则 “源代码” 元数据会进行更新。

"Packages" 和 "Sources" 的元数据文件包含有“Filename:”字段，指向二进制和源代码包文件的位置。目前，这些软件包都统一放在"pool/"目录树下，这样可以改善跨版本发布的传输。

“软件包”元数据的本地副本可以使用 aptitude 来进行交互式的搜索。专门的搜索命令 grep-dctrl(1) 可以搜索“软件包”和“源代码”元数据的本地副本。

"Contents-architecture"元数据的本地拷贝，能够被"apt-file update"更新，它的位置和其它 4 个不同。参见 apt-file(1). (auto-apt 的 "Contents-architecture.gz"文件的本地拷贝默认也使用不同的位置。)

## APT 的软件包状态

除了远程获取元数据，lenny 之后的 APT 工具还会将它在本地产生的安装状态信息保存在 “/var/lib/apt/extended_states” 中，APT 会使用它们来追踪自动安装的所有软件包。

## aptitude 的软件包状态

除了远程获取元数据，aptitude 命令还会将它在本地产生的安装状态信息保存在 “/var/lib/aptitude/pkgstates” 中，这些信息只能被 aptitude 使用。

## 获取的软件包的本地副本

所有通过 APT 机制远程获取的软件包都被保存在 “/var/cache/apt/archives” 中，直到它们被清除。

aptitude 的这个缓存文件清理策略，能够在"Options" → "Preferences"下设置，也可以通过它的菜单，"Actions"下的"Clean package cache" 或 "Clean obsolete files" 来执行强制清理。

## deepin 软件包文件名称

deepin 软件包名称和Debian 软件包名称类似，但是有一些不同。

| 软件包类型                                | 名称结构                                                            |
|:------------------------------------:|:---------------------------------------------------------------:|
| 二进制软件包（亦称 deb）                       | \<package-name\>_\<upstream-version-deepin.revision\>_\<architecture\>.deb  |
| 用于 debian-installer 的二进制软件包（亦称 udeb） | \<package-name\>_<upstream-version-deepin.revision\>_\<architecture\>.udeb |
| 源代码软件包（上游源代码）                        | \<package-name\>_\<upstream-version-deepin.revision\>.orig.tar.gz       |
| 1.0 (native)               | \<package-name\>_\<upstream-version-deepin.revision\>.diff.gz           |
| 3.0(quilt)            | \<package-name\>_\<upstream-version-deepin.revision\>.debian.tar.gz     |
| 源代码软件包（说明）                           | \<package-name\>_\<upstream-version-deepin.revision\>.dsc               |

其中，"package-name" 是软件包的名称，"upstream-version" 是软件包的上游版本，"deepin.revision" 是 deepin 打包版本，"architecture" 是软件包的架构。

deepin.revision 的格式为：

1. 假设上游项目打包版本号为`x.y.z` ，deepin打包版本则为 `x.y.z-${ver1}deepin${ver2}`

   - ver1：`ver1`为0时表示 deepin自行打包的上游软件，`ver1`不为0时表示来自上游的quilt软件包自带的-ver版本
   - ver2：表示来自deepin社区的patch数量，依次递增，可为空

2. 来自deepin community自行打包的上游软件 以`0deepin`开头标识，若该项目添加了来自deepin的patch则以`deepin1` 标识，依次累加, 版本号形式`x.y.z-0deepin1` , 若上游已经添加-2这类版本号，版本号则为`x.y.z-2deepin1`
3. 若需要集成native软件包到deepin，则应改为quilt格式 遵循条例2
4. CI自动构建版本号 `x.y.z-${ver1}deepin${ver2}+u001+rb1`，001为距离上一次修改changelog的commit次数，rb1为rebuild次数，依次累加

## dpkg 命令

dpkg(1) 是 Debian 软件包管理中最底层的工具。它非常强大，必须小心使用。

当安装名为 “package_name” 的软件包时，dpkg 会按照下列的顺序处理它。

1. 解包 deb 文件（等同于 “ar -x”）
2. 使用 debconf(1) 执行 “package_name.preinst”
3. 将软件包安装到系统中（等同于 “tar -x”）
4. 使用 debconf(1) 执行 “package_name.postinst”

debconf 系统提供带有 I18N 和 L10N （第 8 章 国际化和本地化）支持的标准化用户交互。

dpkg 创建的重要文件：

| 文件                                        | 内容说明                                          |
|:-----------------------------------------:|:---------------------------------------------:|
| /var/lib/dpkg/info/package_name.conffiles | 列出配置文件。（使用者可修改的）                              |
| /var/lib/dpkg/info/package_name.list      | 列出软件包安装的所有文件和目录                               |
| /var/lib/dpkg/info/package_name.md5sums   | 列出软件包安装的文件的 MD5 哈希值                           |
| /var/lib/dpkg/info/package_name.preinst   | 软件包安装之前运行的软件包脚本                               |
| /var/lib/dpkg/info/package_name.postinst  | 软件包安装之后运行的软件包脚本                               |
| /var/lib/dpkg/info/package_name.prerm     | 软件包移除之前运行的软件包脚本                               |
| /var/lib/dpkg/info/package_name.postrm    | 软件包移除之后运行的软件包脚本                               |
| /var/lib/dpkg/info/package_name.config    | 用于 debconf 系统的软件包脚本                           |
| /var/lib/dpkg/alternatives/package_name   | update-alternatives 命令使用的替代信息                 |
| /var/lib/dpkg/available                   | 所有软件包的可用性信息                                   |
| /var/lib/dpkg/diversions                  | dpkg(1) 使用的文件移动信息，由 dpkg-divert(8) 设置         |
| /var/lib/dpkg/statoverride                | dpkg(1) 使用的文件状态改变信息，由 dpkg-statoverride(8) 设置 |
| /var/lib/dpkg/status                      | 所有软件包的状态信息                                    |
| /var/lib/dpkg/status-old                  | “var/lib/dpkg/status” 文件的第一代备份                |
| /var/backups/dpkg.status*                 | 第二代备份，以及“var/lib/dpkg/status”文件更旧的备份          |

## 2.5.10. update-alternatives 命令

Debian 系统使用 update-alternatives(1) 让用户可以不受干扰地安装多种重叠的程序。例如，如果同时安装了 vim 和 nvi 软件包，你可以使 vi 命令选择运行 vim。

```bash
$ ls -l $(type -p vi)
lrwxrwxrwx 1 root root 20 2007-03-24 19:05 /usr/bin/vi -> /etc/alternatives/vi
$ sudo update-alternatives --display vi
...
$ sudo update-alternatives --config vi
  Selection    Command
 ----------------------------------------------
      1        /usr/bin/vim
*+    2        /usr/bin/nvi

Enter to keep the default[*], or type selection number: 1
```

Debian 选择系统在 "`/etc/alternatives/`" 目录里通过符号链接来维持它的选择。选择进程使用"`/var/lib/dpkg/alternatives/`"目录里面的相应文件。

## 2.5.11. dpkg-statoverride 命令

当安装一个软件包时，由 dpkg-statoverride(8) 命令提供的 状态修改，是告诉dpkg(1) 对 文件 使用不同的属主或权限的一个方法。如果使用了 "--update" 选项，并且文件存在，则该文件会被立即设置为新的属主和模式。

::: warning 小心
系统管理员使用 chmod 或 chown 命令直接修改某个软件包文件的属主或权限，在下次软件包升级时，将会被重置。
:::

::: tip 注意
本人在此使用了文件 一词，但事实上也可用于dpkg 所处理的任何文件系统对象，包括目录，设备等
:::

## 2.5.12. dpkg-divert 命令

dpkg-divert(8) 命令提供的文件转移功能，是强制 dpkg(1) 不将文件安装到其默认位置，而是安装到被转移 的位置。dpkg-divert 专用于软件包维护脚本。不建议系统管理员随意使用它。

:::danger
dpkg-divert 除非你知道你在做什么，否则不要使用它。因为有些软件包可能会依赖于它们的文件位置，如果你随意转移文件，可能会导致软件包无法正常工作。
:::
