firmware-intel-* 装不上、rclone 装不上、点了十几次更新全部失败——这是个值得记下来的排查过程。
先说结论(TL;DR)
1. 飞牛的模块更新器是逐个 dpkg -i 装 deb,它不会像 apt 那样先卸载冲突的旧包;
2. 新的固件包是从 firmware-misc-nonfree 拆分出来的,声明了 Conflicts 却没有 Replaces,于是 dpkg 直接拒绝安装;
3. 更糟的是整批提交 + 失败回滚,一个包失败就连累整批,所以永远卡在原地。
修法:用 apt 装一次,让它自己处理冲突,然后让飞牛自己的更新器接管剩余的那个 rclone。
一、现象:那几个模块,永远更新失败
先说背景。我这台飞牛跑在一台老机器上:Intel Pentium N3700(Braswell),三块 Intel I211 网卡,系统是 1.2.0900,内核 6.18.18.c1168-trim,底层是 Debian 12 bookworm。
很长一段时间里,「系统更新」页面都挂着几个模块提示可更新。我一开始没在意,只是每次顺手点一下——然后每次都提示「更新失败」。重启、换时间点、清缓存,全都一样。失败的模块很固定:
firmware-intel-miscfirmware-intel-graphicsfirmware-nvidia-graphicsfirmware-amd-graphicswireless-regdbrclone
当时我甚至怀疑是不是自己以前手动装过什么包,把系统「弄脏了」。后来证明,只猜对了一半。
二、第一站:把日志翻出来
GUI 上只有一句「更新失败」,没有任何细节。这种时候只能上 SSH。
飞牛的更新器会把 deb 下载到 /var/tmp/trim-update/,然后调用 dpkg 安装;而 dpkg 的错误输出被重定向到了一个专门的日志:/var/log/dpkg.error.log。
打开这个文件,我看到了这样的内容,而且它重复了 93 次:
dpkg: regarding .../firmware-intel-misc_20260110-1_all.deb containing firmware-intel-misc:
firmware-intel-misc conflicts with firmware-misc-nonfree (<< 20230625-3~)
firmware-misc-nonfree (version 20230210-5) is present and installed.
dpkg: error processing archive /var/tmp/trim-update/firmware-intel-misc_20260110-1_all.deb (--install):
conflicting packages - not installing firmware-intel-misc
Errors were encountered while processing:
/var/tmp/trim-update/firmware-intel-misc_20260110-1_all.deb统计了一下,不同包的失败次数:
93 次,说明我每次点的「更新」都走完了下载、安装、失败、回滚这一整套流程,一次没落下。
三、看懂报错:Conflicts、Replaces,和 apt 与 dpkg 的分工
报错本身说得很清楚:新包和已安装的旧包冲突。
Debian 从 trixie 开始,把原来那个大而全的 firmware-misc-nonfree 拆成了几个更细的包:Intel 的、AMD 的、NVIDIA 的。飞牛源里的 20260110-1 就是拆分后的版本。
用 apt-cache show 看这几个包的元数据,问题一目了然:
firmware-intel-misc 20260110-1 Conflicts: firmware-misc-nonfree (<< 20230625-3~)
firmware-intel-graphics 20260110-1 Conflicts: firmware-misc-nonfree (<< 20230625-3~)
firmware-nvidia-graphics 20260110-1 Conflicts: firmware-misc-nonfree (<< 20230625-3~)它们声明了 Conflicts,意思是「我和旧包不能共存」;但没有声明 Replaces,所以 dpkg 不会自动把旧包顶掉。正常做法是两者都声明:既告诉系统「我不能和它共存」,也告诉系统「我可以替代它、请把它移除」。
这就是 apt 和 dpkg 的分工差异:
apt:会先算依赖,把障碍清掉再装
读取依赖关系→发现 Conflicts→先卸载旧包→安装新包→✅ 成功
dpkg -i:只负责解包,不负责解冲突
直接解包→发现 Conflicts→报错退出→❌ 整批失败
而飞牛的更新器用的是后者。从 liveupdate 二进制里能看到它调用 dpkg 的参数:
dpkg --force-overwrite --force-confold --force-confdef --install ...--force-overwrite 只能解决「文件被别的包占用」,对 Conflicts 这种包级冲突完全无效。于是必然失败。
四、为什么「每次都是这几个」,而且怎么都过不去
如果只是这一个包装不上,那其他包应该能成功才对。但我发现 wireless-regdb 和 firmware-amd-graphics 明明自身没问题,也一起失败了。
原因是:这一批是一次性提交的。dpkg 处理一批 deb 时,只要有一个包报错,整批事务就会失败;而 liveupdate 在失败后还会把 /var/lib/dpkg/status 还原成安装前的备份(status.trim.bak),连已经装好的包也一起撤销。
这解释了一个很反直觉的现象:你点多少次更新都没用,因为每次都是从同一个起点失败,然后回到同一个起点。
经验:当一个「更新」按钮反复失败时,先别怀疑网络。下载、校验、安装、回滚是四个独立阶段,日志里一定会留下痕迹。
五、rclone 是另一个独立的故事
把固件的问题理清后,我发现列表里还剩一个 rclone。它的情况完全不同。
飞牛自己带了一个定制版 rclone(/usr/trim/bin/rclone,版本 v3.67.8),并且把它通过软链接暴露成 /usr/bin/rclone。查一下这个文件的归属:
$ dpkg -S /usr/bin/rclone
trim: /usr/bin/rclone也就是说,这个路径已经属于 trim 这个系统包了。但 liveupdate 的待安装清单里还有一条 rclone 3.67.8-DEV,它的 deb 同样要写 /usr/bin/rclone——两个包抢同一个文件。
这种冲突 dpkg 只能靠 --force-overwrite 强行覆盖,属于「带伤通过」:这次装上了,等 trim 下次升级又会把它盖回去,来回拉扯。
顺带一提,我在日志里还看到自己以前装过的 rclone 1.67.1-DEV 包的痕迹——这大概就是当初「我是不是把系统弄脏了」这个怀疑的来源。确实有一部分是我留下的历史包袱,但主要矛盾还是更新器本身的处理方式。
六、动手修:四步,加一个容易忽略的细节
第 0 步:先备份,别急着删东西
这一批操作会卸载两个旧包,还会重建 initramfs。稳妥起见,先把 dpkg 状态和整个 /lib/firmware 备份下来:
mkdir -p /root/fix-backup
dpkg --get-selections > /root/fix-backup/dpkg-selections.bak
tar -C / -cf - lib/firmware | zstd -o /root/fix-backup/firmware.tar.zst # 约 316MB
zstd -t /root/fix-backup/firmware.tar.zst # 校验第 1 步:用 apt 装,让它自己解决冲突
关键就一句话:别跟 dpkg 硬碰,交给 apt。
apt-get install -y firmware-intel-misc firmware-intel-graphics \
firmware-nvidia-graphics firmware-amd-graphics wireless-regdbapt 会先算一遍依赖,然后告诉你它准备怎么做:
The following packages will be REMOVED:
firmware-linux-nonfree firmware-misc-nonfree
The following NEW packages will be installed:
firmware-intel-graphics firmware-intel-misc firmware-nvidia-graphics
The following packages will be upgraded:
firmware-amd-graphics wireless-regdb「先移除、再安装」正是 dpkg 做不到、而 apt 擅长的那一步。
第 2 步:把被连带删掉的固件补回来(这一步很容易漏)
卸载 firmware-misc-nonfree 时,其中不属于新拆分包的文件会一起被删掉——比如 mediatek、cxgb4,以及一部分旧版 i915 固件。我实测 /lib/firmware/i915 从 372 个文件掉到 305 个,mediatek 目录直接清空。
用备份「只补不覆盖」地恢复,既保住已有文件,又把丢失的补回来:
zstd -dc /root/fix-backup/firmware.tar.zst | tar -C / --skip-old-files -xf -
# 恢复后核对
ls /lib/firmware/i915 | wc -l # 372
ls /lib/firmware/mediatek | wc -l # 44第 3 步:rclone 交给飞牛自己的更新器
rclone 是飞牛 CDN 上的包,还带签名和加密,不适合自己下载安装。直接调用更新器的安装模式(-u):
/usr/trim/bin/liveupdate -u这一次它输出的是:
trim-update[122982]: updating 'rclone'...
trim-update[122982]: 'rclone' update successfully.只有一个 dpkg: warning: trying to overwrite '/usr/bin/rclone', which is also in package trim 的警告——用 force 覆盖过去了,功能不受影响。
第 4 步:收尾
dpkg --configure -a # 清掉中途被打断的配置状态
dpkg --audit # 无输出即为健康
apt-get check # 无报错七、验证:这次是真的好了
$ /usr/trim/bin/liveupdate
{"branchId":7,"result":"succ"} # 待更新模块列表已为空
$ apt-cache policy firmware-intel-misc rclone
Installed: 20260110-1 / 3.67.8-DEV
Candidate: 20260110-1 / 3.67.8-DEV # 已是最新
$ dpkg --audit # 无输出
$ rclone version
rclone v3.67.8
$ dmesg | grep -i 'failed to load' # 无固件加载失败更有意思的是后续:冲突解除之后,之前一直被卡住的其他组件(Intel NPU / OpenCL 驱动、mdadm、fuse3 等)也顺顺利利装上了。可升级包数量从 360 掉到 309。
可见这个「小冲突」实际堵住的是一条很长的更新链。
八、事后复盘的几条经验
更新失败先看日志。 飞牛的 dpkg 错误在
/var/log/dpkg.error.log,apt 的操作历史在/var/log/apt/history.log,比 GUI 的一句「失败」有用一万倍。遇到 Conflicts,请交给 apt。 dpkg 是「执行者」,apt 是「解题者」。手动装包时,能用 apt 就不要裸 dpkg。
「整批提交 + 失败回滚」是个危险的组合。 它会让一个包的冲突变成整个更新系统的死锁。
别手动装和系统包抢路径的软件(比如 rclone 这类)。系统集成度高的组件,交给官方更新器更稳妥。
apt 里那 300 多个可升级包不要全量升。 飞牛用
apt-mark hold锁住了大部分 Debian 基础包,这是有意的设计,不是疏漏。动固件之前一定备份
/lib/firmware。 拆分包升级丢文件这件事,不备份根本发现不了。
九、写在最后
这篇记录没有什么高深的技术,核心就一句:同样的报错看 93 次,不如认真看一次。
我把过程整理出来,一方面给自己留档,另一方面也希望给遇到同样问题的朋友省两个小时。如果你也在飞牛上看到那几个 firmware-* 模块一直更新失败,可以先 grep firmware-intel /var/log/dpkg.error.log 看是不是同一个问题。
风险提示:以上命令都需要 root 权限,且会卸载/安装系统包、重建 initramfs。操作前请务必备份,并确认自己能接受「万一失败要重装系统」的后果。