Difference between revisions of "Btrfs - Tips and tricks"

From ArchWiki
Jump to: navigation, search
(GRUB2)
(Snapshots)
Line 124: Line 124:
 
=== Booting into snapshots ===
 
=== Booting into snapshots ===
  
In order to boot into a subvolume the 'rootflags=subvol' option has to be used on the kernel line. The 'subvol=' mount options in {{ic|/etc/[[fstab]]}} of the snapshot to boot into also have to be specified correctly (note: if using the example script above, {{ic|/etc/[[fstab]]}} is set automatically).  
+
In order to boot into a subvolume the 'rootflags=subvol' option has to be used on the kernel line. The 'subvol=' mount options in {{ic|/etc/[[fstab]]}} of the snapshot to boot into also have to be specified correctly (note: if using the example [[Btrfs - Tips and tricks#Snapshot script|script above]], {{ic|/etc/[[fstab]]}} is set automatically).  
  
 
==== [[GRUB]] ====
 
==== [[GRUB]] ====

Revision as of 22:38, 30 July 2013

Go back to Btrfs.

Snapshots

Automatic snapshots on each boot

It is possible to automatically save the then current state of a btrfs subvolume during system boot. Two files are needed to accomplish this, a script which snapshots btrfs subvolumes and a systemd unit file to run the script during the boot process.

Requirements

The root filesystem must have been installed into its own dedicated btrfs subvolume and the btrfs-root where all subvolumes reside has to be accessible.

  • Example fstab /etc/fstab:
# ...
UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX /               btrfs subvol=__current/ROOT,defaults 0 0
UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX /run/btrfs-root btrfs compress=lzo,defaults 0 0
UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX /opt            btrfs subvol=__current/opt,defaults 0 0
# ...

Snapshot script

An example script to snapshot the btrfs subvolumes mounted under '/'. The script will automatically detect the names of all subvolumes mounted under '/'.

What the script does, is to delete all snapshots from the state, the system was in at last boot, make new snapshots and alter the /etc/fstab file of the snapshotted root subvolume to allow it to be booted without manual configuration.

The script requires 3 parameters:

  1. The path of the btrfs filesystem root. e. g. /run/btrfs-root,
  2. The name of the btrfs root volume as specified in /etc/fstab. E. g. __current/ROOT,
  3. The path where the newly created snapshots will reside without its 1st parameter portion. E. g. __snapshot/__state_at_last_successful_boot (if the actual path is '/run/btrfs-root/__snapshot/__state_at_last_successful_boot')


CAUTION: This script will delete all snapshots of the same name as the regular subvolume names in the path its third parameter is pointing to. Be careful that the 3rd parameter is NOT pointing at the place where your subvolumes reside in --- In this example, '/run/btrfs-root/__current' as a 3rd parameter would be incorrect and possibly lead to data loss.


  • Example script /usr/local/bin/snapshot_current_system_state.sh:
#!/bin/sh

# example call: /usr/local/bin/snapshot_current_system_state.sh '/run/btrfs-root' '__current/ROOT' '__snapshot/__state_at_last_successful_boot' 

if [ $# -ne 3 ]
then
  /usr/bin/echo -e "This script requires three parameters:\n1st parameter: The path of the btrfs filesystem root. e. g. /run/btrfs-root\n2nd parameter: The name of the btrfs root volume as specified in /etc/[[fstab]]. E. g. __current/ROOT\n3rd parameter: The path where the newly created snapshots will reside without its 1st parameter portion. E. g. __snapshot/__state_at_last_successful_boot\nCAUTION: This script will delete all snapshots of the same name as the regular volume names in the path parameter 3 is pointing to."
  exit 0
fi

btrfs_root="${1}" # example: '/run/btrfs-root'
path_to_root_volume="${2}" # example: '__current/ROOT'
path_to_snapshots="${3}" # example: '__snapshot/__state_at_last_successful_boot'

# take no snapshots when booted into a snapshot
if [ -e '/SNAPSHOT-TIMESTAMP' ]
then
  exit 0
fi

# anti recursive snapshots
for subvolume in $(/usr/bin/btrfs subvolume list '/' | /usr/bin/awk '{print $NF}')
do
  path_to_snapshot="${btrfs_root}/${path_to_snapshots}/${subvolume}"

  if [ -d "${path_to_snapshot}" ]
  then
    /usr/bin/btrfs subvolume delete "${path_to_snapshot}"
  fi  
done

subvolumes="$(/usr/bin/btrfs subvolume list '/' | /usr/bin/awk '{print $NF}')"
for subvolume in $subvolumes
do
  snapshot_directory="${btrfs_root}/${path_to_snapshots}/$(/usr/bin/dirname ${subvolume})"

  if [ ! -d "${snapshot_directory}" ]
  then
    /usr/bin/mkdir -p "${snapshot_directory}" 
  fi  

  /usr/bin/btrfs subvolume snapshot "${btrfs_root}/${subvolume}" "${btrfs_root}/${path_to_snapshots}/${subvolume}" 

  if [ "${subvolume}" = "${path_to_root_volume}" ]
  then
    timestamp="$(/usr/bin/date +%d.%m.%Y-%H:%M:%S)"

    /usr/bin/echo -e "Arch Linux --- state at last successful boot (nonpersistent) [${timestamp}]\n" > "${btrfs_root}/${path_to_snapshots}/${path_to_root_volume}/etc/issue"

    /usr/bin/echo "${timestamp}" > "${btrfs_root}/${path_to_snapshots}/${path_to_root_volume}/SNAPSHOT-TIMESTAMP"

    sed_path_to_snapshots="$(/usr/bin/echo ${path_to_snapshots} | /usr/bin/sed --posix --regexp-extended 's/\//\\\//g')"

    for subvolumeX in $(echo $subvolumes | /usr/bin/sed --posix --regexp-extended 's/\//\\\//g')
    do
      /usr/bin/sed --posix --regexp-extended "s/subvol=${subvolumeX}/subvol=${sed_path_to_snapshots}\/${subvolumeX}/g" --in-place "${btrfs_root}/${path_to_snapshots}/${path_to_root_volume}/etc/[[fstab]]"
    done
  fi
done

/usr/bin/sync

systemd unit file

Following systemd unit file will run the script every time the system manages to successfully boot into multi-user.target:

  • Example unit file /etc/systemd/system/snapshot_current_system_state_upon_boot.service:
[Unit]
Description=Takes a snapshot of each btrfs-subvolume mounted under / after multi-user.target has been reached.
After=multi-user.target

[Service]
Type=oneshot
ExecStart=/bin/sh /usr/local/bin/snapshot_current_system_state.sh '/run/btrfs-root' '__current/ROOT' '__snapshot/__state_at_last_successful_boot'

[Install]
WantedBy=multi-user.target

The unit file has to be symlinked by systemd:

systemctl enable snapshot_current_system_state_upon_boot.service

Booting into snapshots

In order to boot into a subvolume the 'rootflags=subvol' option has to be used on the kernel line. The 'subvol=' mount options in /etc/fstab of the snapshot to boot into also have to be specified correctly (note: if using the example script above, /etc/fstab is set automatically).

GRUB

  • Example menue entry /etc/grub.d/40_custom:
# ....
menuentry "Arch Linux --- state at last successfull boot (nonpersistent)" --unrestricted {
  linux /vmlinuz-linux root=/dev/disk/by-uuid/<UUID of btrfs-root> rootflags=subvol=__snapshot/__state_at_last_successful_boot/__current/ROOT init=/usr/lib/systemd/systemd ro quiet
  initrd /initramfs-linux.img
}
# ...

/etc/grub.d/40_custom has to be executable:

chmod 700 /etc/grub.d/40_custom

/boot/grub/grub.cfg has to be recreated:

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