为 LUKS 加密的 Btrfs 添加新的硬盘

本文会介绍如何为已经使用 LUKS 加密的 Btrfs 空间添加一个新的硬盘。

在早些时间,我的系统提示我系统的根目录空间不足。当时我并没有在意,但是难过的是在第二天我打开电脑,输入登录密码时竟然登录失败了,而且没有任何提示。

排查过程

因为我使用了 systemd-homed 并且开启了加密,我当时以为是滚动更新或者关机异常导致脏数据1 /home 挂掉了。

我当时悲痛欲绝😢,不过好在我机敏异常,想到去登录 root因为 root 用户没有使用 homed,或许 root 用户也无法使用。。 但是绝望的是我禁用了 root 登录。我紧接着想到使用 LiveOS 来启用 root。更绝望的是我之前的 LiveOS U 盘因为临时传输文件让我格式化了!谁能懂我的痛苦啊。不过好在我手头刚好有另一台电脑,我下载了镜像进入了 LiveOS 启用了 root 用户。

之后我尝试使用 root 登录,果然登录进去了。因为怀疑是滚动更新导致挂掉,所以我使用 Timeshift 回滚Timeshift 救我狗命!, 可惜并没有效果。之后我尝试使用 homectl authenticate xxx 来进入,结果提示空间不足无法挂载。原谅我当时没有截图或者记录下来。 这是一个相当好的提示,这意味着我的 /home 目录也许没有损坏。

我立刻删了点 Timeshift 的快照,然后将家目录的空间降到 240G之前 350G。。之后就可以正常登录了。

解决方案选择

既然直到了问题的原因就是没有空间了,多给弄点空间就好了。正好我的电脑里还有一块闲置硬盘 512 G没错,我电脑里面两块硬盘,一块是之前的电脑上拔下来的。好奇的人可以看看之前的记录闲鱼卖货经历。另一块是买电脑时给的。

那么问题就来到了如何添加。已知我目前的架构是 LUKS 全盘加密根目录参见:全盘加密教程, 在加密块上使用 btrfs 作为文件系统。而我的家目录是使用 sytemd-homed 管理的。

我想到的几种方案是

  1. 利用 LVM 直接将两块硬盘混合起来,变成一个虚拟池。然而我开始安装的时候就没有使用 LVM,这条路基本就堵死了。
  2. 直接使用 btrfs 的 btrfs derive add 来直接实时给存储池添加空间。btrfs 提供一部分 LVM 的功能。
  3. 将额外的硬盘直接挂载给 /home 目录。

我开始的考虑是第三种方案。我当时想到的好处是下次重装系统可以保留 /home 分区。但是我转念一想,我每次重装系统几乎都是「干净」重装,也就是完全从 0 开始。没必要单独分区。而且我日常也有随缘备份的习惯,也不至于丢掉重要文件。

这让我选择了方案 2。说实话,我当时并不了解如何操作这个流程,我在网上找了好多资料。基本上能看的教程和问题解答都看了,始终没有我想要的内容。这就是我写这篇文章的动机。在这里特别感谢 archlinux-cn-offtopicarchlinux-cn 群中友好群友对我的帮助。这几乎是我近些年在网上体验最好的社区关系了。也推荐使用 arch 的小伙伴加入这两个群,里面的人都非常友善。

添加新硬盘

考虑好添加方案后就开始实操。

先展示一下我当时的硬盘分区。其中 loop0sytemd-homed 提供的本地回环挂载的加密家目录。nvme0n1 是要添加的新硬盘。

NAME          TYPE    SIZE PTTYPE FSTYPE      MOUNTPOINTS
loop0         loop    300G        crypto_LUKS
└─home-xxx   crypt    300G        btrfs       /home/xxx
zram0         disk      4G        swap        [SWAP]
nvme0n1       disk  476.9G gpt
├─nvme0n1p1   part      1G gpt    vfat        /efi
└─nvme0n1p2   part  475.9G gpt    crypto_LUKS
  └─linuxroot crypt 475.9G        btrfs       /home
                                              /root
                                              /var/log
                                              /var/cache
                                              /tmp
                                              /usr-local
                                              /var/lib/docker
                                              /var/tmp
                                              /var/lib/libvirt
                                              /var/lib/containers
                                              /srv
                                              /
nvme1n1       disk  476.9G        crypto_LUKS

分区

既然目前我们的硬盘已经加密了,我们当然希望加入进来的新盘也要加密。依照这个想法,我们先将新硬盘格式化。

wipefs -af /dev/nvme1n1

并写入分区表。

partprobe /dev/nvme1n1

之后提供一个占据全盘的分区。

sgdisk -n 0:0:0 -t 0:8309 -c 0:luks /dev/nvme1n1

加密

之后对 /nvme0n1p1 进行加密。

cryptsetup luksFormat /dev/nvme1n1p1

较为重要的点来了。虽然我们现在对硬盘进行了加密,并假设我们已经正确将两块硬盘混合到一起了。这就万事大吉了么?很可惜的是,并不是,因为系统无法自动将 /nvme0n1p1 进行挂载,而 btrfs 找不到这个硬盘就会报错并导致无法开机。

所以我们需要让引导知道这个硬盘在哪里,并且需要解密。

我们使用 blkid 来获取分区 UUID。

blkid
/dev/mapper/linuxroot: LABEL="linuxroot" UUID="2d160a68-3525-4d3e-a536-6d5ba958b768" UUID_SUB="fc945959-b8c7-41b8-9fe1-aea5642d6275" BLOCK_SIZE="4096" TYPE="btrfs"
/dev/nvme0n1p1: LABEL_FATBOOT="EFI" LABEL="EFI" UUID="169B-94E9" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="esp" PARTUUID="fbfee40e-e0cd-47a4-a2e7-6649abdce0be"
/dev/nvme0n1p2: UUID="149d82cd-e65e-46a3-8182-e60a30a77cba" TYPE="crypto_LUKS" PARTLABEL="luks" PARTUUID="f13bf7af-7b8d-40c7-963f-d3761d36268e"
/dev/mapper/home-xxx: LABEL="xxx" UUID="a007119c-2334-4bd9-a5e8-023e0aa24a81" UUID_SUB="10c4fc22-f75f-4343-ba06-27b010d4f6d7" BLOCK_SIZE="4096" TYPE="btrfs"
/dev/loop0: UUID="d19148f0-419c-4cbe-b5bb-679447f3e227" LABEL="lyle" SUBSYSTEM="systemd-home" TYPE="crypto_LUKS"
/dev/mapper/linuxroot2: LABEL="linuxroot" UUID="2d160a68-3525-4d3e-a536-6d5ba958b768" UUID_SUB="05b643c3-54f7-4e04-b317-586f0928ec51" BLOCK_SIZE="4096" TYPE="btrfs"
/dev/nvme1n1p1: UUID="70b0a7d3-9968-4a63-b244-884ca28f85ad" TYPE="crypto_LUKS" PARTLABEL="luks" PARTUUID="8f33a848-8590-4d41-ac68-3ff541051730"
/dev/zram0: LABEL="zram0" UUID="f574f838-3ff9-40c1-8e09-97742b8757a0" TYPE="swap"

找到对应的 /dev/nvme1n1p1 的 UUID。输入 rd.luks.name=70b0a7d3-9968-4a63-b244-884ca28f85ad=linuxroot2/etc/cmdline.d/root.conf 中添加对应的引导。

rd.luks.name=149d82cd-e65e-46a3-8182-e60a30a77cba=linuxroot rd.luks.name=70b0a7d3-9968-4a63-b244-884ca28f85ad=linuxroot2 root=/dev/mapper/linuxroot rootfstype=btrfs rootflags=subvol=/@ rw

这样,我们就可以正确的引导。不要忘了重建引导。

mkinitcpio -P

接下来你可以选择使用 TPM 来自动解密。你可以参考 配置 TPM 自动解锁 你只需要注意每次都要同时对两个硬盘都操作一遍即可。

还需要打开加密盘才能添加。

cryptsetup open /dev/nvme1n1p1 linuxroot2

btrfs 添加新盘

这一步挺简单的,只需要执行:

btrfs device add /dev/mapper/linuxroot2 /

需要注意的是 btrfs device add [你要添加的盘] [当前主盘挂载点] 中不要写错位置。最好使用 /dev/mapper/linuxroot2 来作为路径,而不使用 /dev/nemve1n1p1。这是因为这个路径很容易变化,例如从 nvme1n1p1 变成 nvme01n1p1,这可能导致错误。而映射地址 /dev/mapper/linuxroot2 是引导中写的地址,不会发生变化。而且需要注意不要对 /dev/nvme1n1p1 创建文件系统,只需要保持原样即可。也就是说不要使用 mkfs.btrfs

添加进去之后你可以检查一下,使用:

btrfs filesystem show /
Label: 'linuxroot'  uuid: 2d160a68-3525-4d3e-a536-6d5ba958b768
 Total devices 2 FS bytes used 392.91GiB
 devid    1 size 475.92GiB used 392.91GiB path /dev/mapper/linuxroot
 devid    2 size 476.92GiB used 000.00GiB path /dev/mapper/linuxroot2

你可以看到已经添加成功,你还可以通过下方命令来检查。

btrfs filesystem usage -T /

至此就完成了添加硬盘,欢呼!

Footnotes

  1. Systemd-homed#Home_directory_in_a_dirty_state