Chroot v2.0 (btrfs inside)

Btrfs_logo

Intro

Just before starting, I’m going to remind you how to do a chroot on a classic file system (ext4, xft, fat, etc) and then, we’ll see how to do it on our brand new standard : Btrfs.

Contrary to the manipulation to change the root password, that we will also use to repair a corrupted fstab after changing the hard drive ^_^’, we can use the chroot to mount a Linux in our Fedora in order to perform operations (updates, file recovery, debugging, etc).

A few explanations:

The chroot is a function allowing to change temporarily the root location which allows a partitioning of a service or a user in the arborescence.

When you chroot another OS, you have to mount in this chroot the special folders allowing the OS to talk with the kernel (for reminder, the kernel is Linux and Linux is just the kernel, and there, the trolls are going to unleash).

These directories are :

  • /dev for the devices.
  • /proc which contains the information on the system (kernel and process).
  • /sys which contains the information between the system and the hardware.

Old solution (when I was younger so much younger than todaayyyyy)

In the following example, the / partition to mount is sdx which we will mount in /mnt but you can mount it on another folder than /mnt like /chroot.

mount /dev/sdx /mnt
mount --bind /dev /mnt/dev #the --bind option makes the contents accessible in both locations.
mount -t proc /proc /mnt/proc #with -t we specify the proc type
mount -t sysfs /sys /mnt/sys #with -t we specify the sysfs type
chroot /mnt #change the root location 

To exit the chroot, the shell command is exit.

When finished, we unmount all folders:

exit
umount /mnt/dev
umount /mnt/proc
umount /mnt/sys
umount /mnt

the case of lvm

In the case of lvm, the partitions are not available directly and must be mapped:

fdisk -l /dev/vda2
    Disk /dev/vda2: 19 GiB, 20400046080 bytes, 39843840 sectors
    [...]
    I/O size (minimum/optimal): 512 bytes / 512 bytes

mount /dev/vda2 /mnt/
    mount: /mnt: unknown filesystem type 'LVM2_member'.

We will use the lvm tools to locate our partitions

pvscan
    PV /dev/vda2   VG cl              lvm2 [<19.00 GiB / 0    free]
    Total: 1 [<19.00 GiB] / in use: 1 [<19.00 GiB] / in no VG: 0 [0]

vgscan 
    Found volume group "cl" using metadata type lvm2

lvscan
    ACTIVE            '/dev/cl/root' [10.00 GiB] inherit
    ACTIVE            '/dev/cl/swap' [2.00 GiB] inherit
    ACTIVE            '/dev/cl/home' [1.00 GiB] inherit
    ACTIVE            '/dev/cl/var' [<6.00 GiB] inherit

So we see where the logical volumes are mapped and we can mount these partitions with the previous method

mount /dev/cl/root /mnt/
mount /dev/cl/home /mnt/home/
mount --bind /dev /mnt/dev
mount -t proc /proc /mnt/proc
mount -t sysfs /sys /mnt/sys
chroot /mnt

New solution (The Btrfs case)

Fdisk tells us that there are only two partitions on the physical media

Disk /dev/vda: 20 GiB, 21474836480 bytes, 41943040 sectors
 […]
 Device Boot Start End Sectors Size Id Type
 /dev/vda1 * 2048 2099199 2097152 1G 83 Linux
 /dev/vda2 2099200 41943039 39843840 19G 83 Linux

So let’s have a look at the fstab of the target OS:

UUID=3de441bd-59fc-4a12-8343-8392faab5ac7 /     btrfs subvol=root,compress=zstd:1 0 0
UUID=71dc4f0f-9562-40d6-830b-bea065d4f246 /boot ext4 defaults 1 2
UUID=3de441bd-59fc-4a12-8343-8392faab5ac7 /home btrfs subvol=home,compress=zstd:1 0 0 

Looking at the UUIDs, we see two partitions. One ext4 and one btrfs containing two mount points (the subvolumes) / and /home.

Let’s have a look at what is in the btrfs partition (/dev/vda2 here)

mount /dev/vda2 /mnt/

ls /mnt/
     home root

ls /mnt/root/
     bin dev home lib64 media opt root sbin sys usr
     boot etc lib lost+found mnt proc run srv tmp var

ls /mnt/home/
     user

umount /mnt #remember we are cleaning behind us ;-)

So we can see that in the mounted partition there are folders that contain our diverse partitions (the subvolumes).

or far much nicer:

mount /dev/vda2 /mnt/

btrfs subvolume list /mnt
     ID 256 gen 178 top level 5 path home
     ID 258 gen 200 top level 5 path root
     ID 262 gen 160 top level 258 path root/var/lib/machines

umount /mnt #remember we are cleaning behind us ;-)

Now that we have seen the contents of our partition, we will mount the system by adding the mount options btrfs and subvolume subvol=SubVolumeName :

mount /dev/vda2 /mnt/ -t btrfs -o subvol=root

ls /mnt/
    bin dev home lib64 media opt root sbin sys usr
    boot etc lib lost+found mnt proc run srv tmp var

ls /mnt/home/
#<it's empty yet>

mount /dev/vda2 /mnt/home -t btrfs -o subvol=home

ls /mnt/home/
    user

mount /dev/vda1 /boot

mount --bind /dev /mnt/dev

mount -t proc /proc /mnt/proc

mount -t sysfs /sys /mnt/sys

chroot /mnt

and when the job is done we exit and unmount :

exit
umount /mnt/boot
umount /mnt/sys
umount /mnt/proc
umount /mnt/sys
umount /mnt 

To show that this technique is not distro exclusive, I performed the same method from a Fedora security live and a Parrot OS live which is based on a Debian (see the terminal at the bottom right of the screenshot) to run a dnf update. Mind if you use a different shell between your host/client, for example zsh <-> bash (kali linux <-> Fedora), you have to make a small adjustment.

I hope this will be useful to someone in a debugging session.

Have fun \o/