文件级备份Arch Linux

0x01: 现状

一块硬盘上同时装有Windows 10和Arch Linux。数日前,Windows在一次更新中崩溃并无法恢复,我计划不再保留其于硬盘,而使用虚拟机运行Windows,以便在这不稳定的操作系统再次崩溃时利用虚拟机的快照(snapshot)功能迅速将其恢复至可用状态。

由此地,我希望将Windows所使用的数个分区全部删除,并将空余之空间供Arch Linux的rootfs用。然而,我不能直接使用 resize2fs 完成这个操作,原因如下:

  1. 该文件系统位于一个LVM逻辑卷(LV)上,且该卷组(VG)所包含的(唯一一个)物理卷(PV)是一个被LUKS加密的分区。
  2. 上述的被加密分区在整个硬盘靠后的位置。(这是先安装Windows 10,后安装Arch Linux造成的)

前者或许能通过[扩大LUKS卷->扩大LUKS容器->扩大LVM PV->扩大LVM LV->扩大ext4]来完成,但后者直接令扩大LUKS卷不可行。

Arch Linux仅包含了三个卷:

Block device    FS     Mount
/dev/nvme0n1p6  FAT32  /boot
/dev/nvme0n1p7  LUKS 
|_vg0           LVM VG
  |_arch-rootfs ext4   /
  |_swap        swap   (swap)

0x02: 方案

鉴于此,我决定将Arch Linux的rootfs备份,抹掉该硬盘,重新分区(包括LUKS和LVM),再将数据恢复。

0x03: 操作

首先准备另一块硬盘以存储备份,如有固态硬盘则更好。

准备写入archiso的U盘,并以该U盘启动系统。登入后,给用于存储备份的硬盘(下简称:备份盘)分区,划分一个ext4分区即可。将这个分区挂载至 /mnt/backup :

archiso # parted /dev/sda
(parted) mktable gpt
(parted) mkpart primary 0 100%
(parted) quit
archiso # mkfs.ext4 /dev/sda1

archiso # mkdir /mnt/backup
archiso # mount /dev/sda1 /mnt/backup

(可选) 在Block-device层级备份,这是为了确保万无一失——如发生意外,可用此过程产生的备份恢复。需要确保备份盘有足够的容量。

archiso # dd if=/dev/nvme0n1 of=/mnt/backup/nvme0n1_image.bin bs=256M conv=sync,noerror status=progress

挂载Arch Linux的rootfs至 /mnt/arch :

archiso # cryptsetup luksOpen /dev/nvme0n1p7 dm0
archiso # pvscan
archiso # lvscan
archiso # mkdir /mnt/arch
archiso # mount /dev/vg0/arch-rootfs /mnt/arch

备份Arch Linux的rootfs:

archiso # cd /mnt/arch
archiso # tar -zcpvf /mnt/backup/arch-rootfs.tar.gz --one-file-system ./

事实上,对于熵较高的文件(如ELF,经压缩的媒体文件等)使用 gzip 并不能显著地减少其体积,但却耗费大量时间。故可不使用 gzip,将第二行命令换做:

archiso # tar -cpvf /mnt/backup/arch-rootfs.tar --one-file-system ./

解除挂载并抹掉系统盘,重新建立分区:

archiso # cd /
archiso # umount /mnt/arch
archiso # cryptsetup close dm0

archiso # parted /dev/nvme0n1
(parted) mktable gpt
(parted) mkpart primary 0 1024M
(parted) mkpart primary 1024M 100%
(parted) quit

archiso # mkfs.fat -F32 /dev/nvme0n1p1
archiso # cryptsetup luksFormat /dev/nvme0n1p2
archiso # cryptsetup luksOpen /dev/nvme0n1p2 dm0

archiso # lvmdiskscan
archiso # pvcreate /dev/mapper/dm0
archiso # pvscan
archiso # vgcreate vg0 /dev/mapper/dm0
archiso # vgscan
archiso # lvcreate -L 225G vg0 -n arch-rootfs
archiso # lvcreate -l 100%FREE vg0 -n swap
archiso # lvscan

archiso # mkfs.ext4 /dev/mapper/vg0-arch-rootfs
archiso # mkswap /dev/mapper/vg0-swap

挂载分区并为 /dev , /run , /sys/proc 建立符号链接:

archiso # mkdir -p /mnt/arch/boot
archiso # mount /dev/mapper/vg0-arch-rootfs /mnt/arch
archiso # mount /dev/nvme0n1p1 /mnt/arch/boot
archiso # swapon /dev/mapper/vg0-swap

archiso # ln -s /dev /mnt/arch/dev
archiso # ln -s /run /mnt/arch/run
archiso # ln -s /sys /mnt/arch/sys
archiso # ln -s /proc /mnt/arch/proc

由备份恢复系统, chroot 到恢复后的系统,重新生成内核的initramfs并安装bootloader(我的系统使用的是grub):

archiso # cd /mnt/arch
archiso # tar -zxpvf /mnt/backup/arch-rootfs_image.tar.gz 
//如果你在建立镜像时未使用gzip...
archiso # tar -xpvf /mnt/backup/arch-rootfs_image.tar
archiso # cd ..

archiso # arch-chroot /mnt/arch
arch # pacman -S linux grub efibootmgr os-prober
arch # mkinitcpio -p linux
arch # grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=grub

通过 lsblk -o +UUID 找到新的三个卷(挂载与 /, /bootswap)的UUID,并根据此修改 /etc/fstab ;找到LUKS卷 dm0 的UUID,并修改 /etc/default/grub 所包含的如下行:

GRUB_CMDLINE_LINUX="cryptdevice=UUID={uuid_of_dm0}:dm0"

重新生成grub配置文件:

grub-mkconfig -o /boot/grub/grub.cfg

解除挂载所有文件系统,关机,拔下U盘并重启:

arch # exit
archiso # cd /
archiso # umount -R /mnt/arch
archiso # umount /mnt/backup
archiso # swapoff -a
archiso # cryptsetup close dm0
archiso # poweroff

可能还需要在BIOS中添加grub的EFI文件(位于 {nvme0n1p1}/efi/boot/bootx64.efi).