I wanted to share some notes I made while I was seeking to understand the subvolume structure of Leap 42.2 btrfs root filesystem and how I might go about copying it. In the notes below I’ve included sequences of commands that might prove useful in creating a root filesystem from scratch - **before using any of them please make sure you fully understand them, and make sure you check that prerequisite commands have worked before applying subsequent commands, if you fail to check for errors you may wind up targeting the wrong filesystem **(I use a sacrificial virtualbox to conduct my experiments with btrfs).
The default Leap 42.2 root filesystem supports rollback by employing btrfs snapshots (openSUSE includes the snapper utility for managing these snapshots). The rootfs is divided into separately mounted btrfs subvolumes based on whether the content of the subvolume needs to be included in snapshots. Only one subvolume is subject to snapshots, it contains the relatively static content of the rootfs, such as /etc, /bin, and /usr. Another twenty or so subvolumes are not subject to snapshots, they hold the more volatile/varying content, such as /tmp and /var/tmp (it’s not practical or useful to snapshot rapidly changing logs, working data, or datastores). There are indefinitely more subvolumes because each snapshot is also a subvolume.
The following command can be used to list the subvolumes participating in a root filesystem when mounted as / (but what about when / isn’t mounted?):
linux-s7mu:~ # btrfs subvolume list /
ID 257 gen 33 top level 5 path @
ID 258 gen 62 top level 257 path @/.snapshots
ID 259 gen 155 top level 258 path @/.snapshots/1/snapshot
ID 260 gen 61 top level 257 path @/boot/grub2/i386-pc
ID 261 gen 16 top level 257 path @/boot/grub2/x86_64-efi
ID 262 gen 43 top level 257 path @/opt
ID 263 gen 47 top level 257 path @/srv
ID 264 gen 154 top level 257 path @/tmp
ID 265 gen 66 top level 257 path @/usr/local
ID 266 gen 155 top level 257 path @/var/cache
ID 267 gen 36 top level 257 path @/var/crash
ID 268 gen 24 top level 257 path @/var/lib/libvirt/images
ID 269 gen 65 top level 257 path @/var/lib/machines
ID 270 gen 25 top level 257 path @/var/lib/mailman
ID 271 gen 27 top level 257 path @/var/lib/mariadb
ID 272 gen 28 top level 257 path @/var/lib/mysql
ID 273 gen 28 top level 257 path @/var/lib/named
ID 274 gen 30 top level 257 path @/var/lib/pgsql
ID 275 gen 155 top level 257 path @/var/log
ID 276 gen 36 top level 257 path @/var/opt
ID 277 gen 155 top level 257 path @/var/spool
ID 278 gen 154 top level 257 path @/var/tmp
ID 283 gen 61 top level 258 path @/.snapshots/2/snapshot
The subvolume called “@” holds the content of the root filesystem that is subject to snapshots. The @ subvolume is also the parent of the additional non-snapshotted subvolumes such as @/tmp and @/var/tmp. (I think “@” is just a name and it could easily be named “rootfs”, but there is a cross-distro convention to use “@”.)
Be aware that the @ subvolume is not the top of the filesystem, it is actually a child of the subvolume with an ID of zero, which is the top/initial/sole subvolume present after filesystem creation. The ID zero subvolume is not normally mounted, but can be explicitly mounted by using the mount option subvolid=0 (when creating or maintaining the overall filesystem).
Also its important to note that the actual subvolume that gets mounted on / at boot is not the one named @, but one of its snapshots subvolumes resident in @/.snapshots. Each btrfs filesystem internally records which subvolume to mount by default, in the case of Leap 42.2, this is set to a snapshot volume, the default value can be queried using issuing the following command:
linux-s7mu:~ # btrfs subvolume get-default /
ID 259 gen 177 top level 258 path @/.snapshots/1/snapshot
Although this is the default snapshot to mount, other snapshots could be selected instead. For example, boot time rollback is easily achieved by choosing from a selection of rootfs snapshots. For a running system, the snapper utility can be used to compare and manage changes across the available snapshots.
At this date Leap 42.2 appears to lack a post-install tool for recreating the subvolume structure of the root filesystem. If you need to replicate an existing root filesystem you will either have to run the installer and create a dummy installation, or manually create the filesystem using btrfs commands. The following commands replicate a typical Leap 42.2 root filesystem volume structure onto the partition /dev/sdb2:
# Create a new btrfs filesystem
mkfs.btrfs /dev/sdb2
# Mount subvolid=0 subvolume
mkdir -p /mnt/tmp_rootsv
mount /dev/sdb2 -o subvolid=0 /mnt/tmp_rootsv
# Create the main snapshotted subvolume of the root filesystem
btrfs subvolume create /mnt/tmp_rootsv/@
# Create the non-snapshotted subvolumes
mkdir -p /mnt/tmp_rootsv/@ && btrfs subvolume create /mnt/tmp_rootsv/@/.snapshots
mkdir -p /mnt/tmp_rootsv/@/boot/grub2 && btrfs subvolume create /mnt/tmp_rootsv/@/boot/grub2/i386-pc
mkdir -p /mnt/tmp_rootsv/@/boot/grub2 && btrfs subvolume create /mnt/tmp_rootsv/@/boot/grub2/x86_64-efi
mkdir -p /mnt/tmp_rootsv/@ && btrfs subvolume create /mnt/tmp_rootsv/@/opt
mkdir -p /mnt/tmp_rootsv/@ && btrfs subvolume create /mnt/tmp_rootsv/@/srv
mkdir -p /mnt/tmp_rootsv/@ && btrfs subvolume create /mnt/tmp_rootsv/@/tmp
mkdir -p /mnt/tmp_rootsv/@/usr && btrfs subvolume create /mnt/tmp_rootsv/@/usr/local
mkdir -p /mnt/tmp_rootsv/@/var && btrfs subvolume create /mnt/tmp_rootsv/@/var/cache
mkdir -p /mnt/tmp_rootsv/@/var && btrfs subvolume create /mnt/tmp_rootsv/@/var/crash
mkdir -p /mnt/tmp_rootsv/@/var/lib/libvirt && btrfs subvolume create /mnt/tmp_rootsv/@/var/lib/libvirt/images
mkdir -p /mnt/tmp_rootsv/@/var/lib && btrfs subvolume create /mnt/tmp_rootsv/@/var/lib/machines
mkdir -p /mnt/tmp_rootsv/@/var/lib && btrfs subvolume create /mnt/tmp_rootsv/@/var/lib/mailman
mkdir -p /mnt/tmp_rootsv/@/var/lib && btrfs subvolume create /mnt/tmp_rootsv/@/var/lib/mariadb
mkdir -p /mnt/tmp_rootsv/@/var/lib && btrfs subvolume create /mnt/tmp_rootsv/@/var/lib/mysql
mkdir -p /mnt/tmp_rootsv/@/var/lib && btrfs subvolume create /mnt/tmp_rootsv/@/var/lib/named
mkdir -p /mnt/tmp_rootsv/@/var/lib && btrfs subvolume create /mnt/tmp_rootsv/@/var/lib/pgsql
mkdir -p /mnt/tmp_rootsv/@/var && btrfs subvolume create /mnt/tmp_rootsv/@/var/log
mkdir -p /mnt/tmp_rootsv/@/var && btrfs subvolume create /mnt/tmp_rootsv/@/var/opt
mkdir -p /mnt/tmp_rootsv/@/var && btrfs subvolume create /mnt/tmp_rootsv/@/var/spool
mkdir -p /mnt/tmp_rootsv/@/var && btrfs subvolume create /mnt/tmp_rootsv/@/var/tmp
# Create a place to keep snapshots and create an initial snapshot
mkdir /mnt/tmp_rootsv/@/.snapshots/1
btrfs subvolume snapshot /mnt/tmp_rootsv/@ /mnt/tmp_rootsv/@/.snapshots/1/snapshot
# Find the ID of the initial snapshot and set it to be the default to be mounted
defaultsv="$(btrfs subvolume list -o /mnt/tmp_rootsv/@/.snapshots/1 | gawk '$NF ~ "1/snapshot$" {print $2}')"
btrfs subvolume set-default $defaultsv /mnt/tmp_rootsv/@/.snapshots/1/snapshot
# Finished - unmount complete filesystem
umount /mnt/tmp_rootsv
Having replicated the subvolume structure you could then use any of the conventional commands such as rsync, tar, or cp to replicate content from one rootfs to another. You might also use btrfs utilities to replicate a snapshot of a running system to a snapshot on a new root filesystem. For example, I could use these commands to snapshot a running system and send the snapshot to a new filesystem in the partition /dev/sdb2:
# Create a snapshot of the source root filesystem
btrfs sub snap -r / //.snapshots/snapforcopy
mkdir /mnt/newfs/
mount /dev/sdb2 /mnt/newfs/
mount /dev/sdb2 -o subvol=@/.snapshots /mnt/newfs/.snapshots
# Create a folder on the destination to hold the snapshot
mkdir /mnt/newfs/.snapshots/initial
# Copy the snapshot to the destination filesystem
btrfs send /.snapshots/snapforcopy | btrfs receive /mnt/newfs/.snapshots/initial
# Rename the received snapshot to something more useful
mv /mnt/newfs/.snapshots/initial/snapforcopy /mnt/newfs/.snapshots/initial/snapshot
# Enable read/write on the copy
btrfs property set -ts /mnt/newfs/.snapshots/initial/snapshot ro false
# Create the snapshots folder (won't have been in the snapshot)
mkdir /mnt/newfs/.snapshots/initial/snapshot/.snapshots
# Find the ID of the snapshot
defaultsv="$(btrfs subvolume list -o '/mnt/newfs'/.snapshots/ | gawk '$NF ~ "initial/snapshot$" {print $2}')"
# Set the snapshot to be the default to be mounted at next mount/boot
btrfs subvolume set-default $defaultsv /mnt/newfs/.snapshots/initial/snapshot
# Finished
umount /mnt/newfs/.snapshots /mnt/newfs
mount /dev/sdb2 /mnt/newfs/
After the above commands have completed, I would still have to figure out what non-snapshot subvolume content is required and then use rsync or similar to copy it over to the new filesystem.
Another thing to note is that the output of du and df no longer present the full picture you should also consult the btrfs utility commands:
btrfs filesystem df
btrfs filesystem du
The totality of the above makes a btrfs root filesystem quite a bit more complex than the old alternatives. The structure of the subvolumes and snapshotting is built on a set of conventions and features that are complex, layered, and not always documented. If you’re going to switch to btrfs you need to become familiar with much of the above and its implications for your recovery and backup procedures.
I’m unsure if standards would have permitted a different approach to the subvolume layout, but I would have preferred to see just two sub-volumes, static and volatile, along with the liberal use of symbolic-links two to conform to the established Linux/UNIX layout.
Personally I’ve decided I won’t benefit much from root filesystem snapshots. The number and complexity of the subvolume layout is too high a price to pay for something I’m not using. If the subvolume layout was just split over two subvolumes, I would definitely use it. In order to continue using my existing backup/recover procedures I’ve found it simpler to stick with ext4.