User Tools

Site Tools


linux:btrfs

BTRFS

Btrfs (B-tree file system) is a filesystem based on technology Copy-On-Write with advanced functionnality:

  • maximum capacity per filesystem 2^64 octets == 16 EB
  • Allow sub-volumes creation
  • Ability to add/remove block devices without interruption
  • Ability to balance the filesystem without interruption
  • RAID 0, RAID 1, RAID 5, RAID 6 and RAID 10
  • Snapshots and file cloning
  • Compression (gzip / LZO)
  • quotas
  • defragmentation online
  • checksum, Online data scrubbing for finding errors and automatically fixing them for files with redundant copies

Optional, dedup is partialy supported using an additionnal package duperemove or dduper

ZFS is a direct concurrent to BTRFS, with more functionality, but as it's not integrated to Linux kernel due to CDDL license which is incompatible with GPL license, it's not recommended to use it in production.

Usage

Create a BTRFS filesystem

Create a filesystem on a patition, disk or logical volume (can be used with LVM)

manu-opensuse:~ # mkfs.btrfs /dev/sdb3
manu-opensuse:~ # cat /etc/fstab
...
/dev/sdb3 /backup btrfs defaults 0 0

manu-opensuse:~ # mkdir /backup
manu-opensuse:~ # mount -t btrfs /dev/sdb3  /backup

List your filesystem:

manu-opensuse:~ # btrfs filesystem show
Label: none  uuid: a3da64e9-f198-4cb2-adcd-01ec0541cba9
        Total devices 1 FS bytes used 11.58GiB
        devid    1 size 40.00GiB used 13.80GiB path /dev/sdb3

First, let's see how much free space we have:

manu-opensuse:/ # btrfs filesystem df -h /backup
Data, single: total=38.97GiB, used=35.73GiB
System, single: total=32.00MiB, used=16.00KiB
Metadata, single: total=1.00GiB, used=764.86MiB
GlobalReserve, single: total=73.78MiB, used=0.00B

Or

# btrfs filesystem usage /backup
Overall:
    Device size:                  20.02GiB
    Device allocated:             13.78GiB
    Device unallocated:            6.24GiB
    Device missing:                  0.00B
    Used:                         10.02GiB
    Free (estimated):              9.63GiB      (min: 9.63GiB)
    Data ratio:                       1.00
    Metadata ratio:                   1.00
    Global reserve:              144.00MiB      (used: 0.00B)

             Data     Metadata  System
Id Path      single   single    single   Unallocated
-- --------- -------- --------- -------- -----------
 1 /dev/sda3 13.00GiB 768.00MiB 32.00MiB     6.24GiB
-- --------- -------- --------- -------- -----------
   Total     13.00GiB 768.00MiB 32.00MiB     6.24GiB
   Used       9.61GiB 421.36MiB 16.00KiB

Increase or reduce the partition size online:

# btrfs filesystem resize -42g /backup

To prevent device from changing name, you can also use the UUID, which is a persistent name

manu-opensuse:/ # lsblk -a -o UUID,NAME,FSUSED,FSTYPE
UUID                                   NAME                    FSUSED FSTYPE
                                       sda                            
F926-FC70                              ├─sda1                    8.3M vfat
a2e132cf-d69c-448c-bc59-9e833bebb95c   ├─sda2                         ext3
a3da64e9-f198-4cb2-adcd-01ec0541cba9   ├─sda3                   36.6G btrfs
4c91d4e3-89f7-4b17-9507-84b17c69d777   ├─sda4                   15.2G xfs
859cf5c6-a1ba-4711-98b2-387b8c2bd860   ├─sda5                         swap
1z8s8i-WQL7-yutq-VKYE-cbBD-p1ZB-3dCuwo └─sda6                         LVM2_member
35bfc2a9-a3c0-4eee-82a1-1f62ca52aad7     ├─libraryvg-vmlv      107.9G ext4

manu-opensuse:/ # cat /etc/fstab
UUID=a3da64e9-f198-4cb2-adcd-01ec0541cba9  /             btrfs  defaults               0  0
UUID=a3da64e9-f198-4cb2-adcd-01ec0541cba9  /var          btrfs  subvol=/@/var          0  0
UUID=07e198ed-18a3-41ed-9e48-bde82ead65fc  /backup       btrfs  defaults,compress      0  1

Convert an ext4 partition to BTRFS

Make a backup before doing this operation (supported on ext2, 3, 4 and reiserfs)

manu-opensuse:~ # btrfs-convert /dev/<device>

Avanced functions

Sub-volumes

A subvolume is a part of filesystem with its own independent file/directory hierarchy. Subvolumes can share file extents. A snapshot is also subvolume, but with a given initial content of the original subvolume.

subvolumes et snapshots

Un subvolume est comparable à un simple répertoire (il peut contenir des fichiers et d’autres répertoires). Lorsque que l’on utilise Btrfs, il existe au moins un subvolume, le subvolume racine.

# btrfs subvolume create /backup/aaa
# btrfs subvolume create /backup/bbb

List available subvolumes on /backup:

# btrfs subvolume list /backup/

Note : A snapshot is a copy-on-write of Btrfs. It shares the same blocks as the filesystem. It doesn't preserve in case of corruption.

Create a snapshot :

# btrfs subvolume snapshot /backup/aaa /backup/bbb/snapshot1

Suppress a subvolume (and those snapshots) :

# btrfs subvolume delete /bbb/bbb/snapshot1

List properties of subvolume :

# btrfs property list -ts /path/to/subvolume

Put a subvolume in RW:

# btrfs property set -ts /path/to/subvolume ro false

Put a subvolume in RO:

# btrfs property set -ts /path/to/subvolume ro true

Operations on subvolume

Pour déplacer un subvolume, il faut créer un snapshot en read-only du subvolume que l’on souhaite déplacer/renommer puis supprimer l’original.

# btrfs sub snap -r /path/to/subvolume /path/to/snapshot
# btrfs subvolume delete /path/to/subvolume

On passera ensuite le volume en RO/RW :

# btrfs property set -ts /path/to/snapshot ro false

Envoyer un subvolume

Pour transférer un subvolume vers un autre serveur, il faut créer un snapshot en RO du subvolume en question.

# btrfs sub snap -r /path/to/subvolume /path/to/snapshot-RO

You can synchronize a BTRFS snapshot with a remote server:

# btrfs send /path/to/snapshot-RO | ssh root@192.0.2.1 "btrfs receive /path/to/remote-snapshot"

Compression

On peut monter avec l’option compress=zlib ou compress=lzo pour activer une compression à la volée des fichiers. Si le volume avant n’avait pas l’option compress on pourra tout recompresser avec btrfs filesystem defragment -czlib ou -clzo. Maintenance

Quota for Subvolumes

The Btrfs root file system subvolumes /var/log, /var/crash and /var/cache can use all of the available disk space during normal operation, and cause a system malfunction. To help avoid this situation, SUSE Linux Enterprise Server now offers Btrfs quota support for subvolumes. If you set up the root file system by using the respective YaST proposal, it is prepared accordingly: quota groups (qgroup) for all subvolumes are already set up. To set a quota for a subvolume in the root file system, proceed as follows:

Enable quota support:

# btrfs quota enable /

Get a list of subvolumes:

# btrfs subvolume list /

Quotas can only be set for existing subvolumes.

Set a quota for one of the subvolumes that was listed in the previous step. A subvolume can either be identified by path (for example /var/tmp) or by 0/SUBVOLUME ID (for example 0/272). The following example sets a quota of 5 GB for /var/tmp.

# btrfs qgroup limit 5G /var/tmp

The size can either be specified in bytes (5000000000), kilobytes (5000000K), megabytes (5000M), or gigabytes (5G). The resulting values in bytes slightly differ, since 1024 Bytes = 1 KiB, 1024 KiB = 1 MiB, etc.

To list the existing quotas, use the following command. The column max_rfer shows the quota in bytes.

# btrfs qgroup show -r /

TipTip: Nullifying a Quota

In case you want to nullify an existing quota, set a quota size of none:

# btrfs qgroup limit none /var/tmp

To disable quota support for a partition and all its subvolumes, use btrfs quota disable:

# btrfs quota disable /

RAID protection

Defrag, relocate chunks with less than 5% of usage:

manu-opensuse:~ # btrfs fi balance start -dusage=5 /
Done, had to relocate 0 out of 19 chunks

Add a new disk and balance the data

manu-opensuse:~ # btrfs device add /dev/sdc /
manu-opensuse:~ # btrfs filesystem balance /
# btrfs device replace

Create a subvolume with compression

manu-opensuse:~ # btrfs subvolume create /mnt/btrfs/home
manu-opensuse:~ # mount -o compress=lzo subvol=home /dev/sdb2 /mnt/btrfs/home

Enabling quota and then printing out the qgroup information:

manu-opensuse:~ # btrfs quota enable /btrfs/
manu-opensuse:~ # btrfs qgroup show  /btrfs/
0/5 4698025984 8192
0/257 52432896 4096
0/263 4405821440 12288
0/264 4698025984 8192

A snapshot is a subvolume (Copy on Write CoW), you can create one in read-only (-r)

manu-opensuse:~ # mkdir /snapshots
manu-opensuse:~ # btrfs subvolume snapshot -r / /snapshots/root.$(date +%Y%m%d-%H%M)
Create a readonly snapshot of '/' in '/snapshots/root.20160919-0954'

Snapshots can also be automated with snapper

manu-opensuse:~ # snapper -c home create-config /mnt/btrfs/home

A config file will be created in /etc/snapper/configs/ and you can start the services

systemctl start snapper-timeline.timer snapper-cleanup.timer
systemctl enable snapper-timeline.timer snapper-cleanup.timer

List snapshots

manu-opensuse:~ # snapper list
Type   | #   | Pre # | Date                     | User | Cleanup | Description           | Userdata     
-------+-----+-------+--------------------------+------+---------+-----------------------+--------------
single | 0   |       |                          | root |         | current               |              
single | 1   |       | Thu Jun 21 12:41:01 2018 | root |         | first root filesystem |              
single | 2   |       | Thu Jun 21 12:53:40 2018 | root | number  | after installation    | important=yes
pre    | 3   |       | Thu Jun 21 12:59:06 2018 | root | number  | zypp(zypper)          | important=yes
post   | 4   | 3     | Thu Jun 21 13:00:21 2018 | root | number  |                       | important=yes
pre    | 172 |       | Wed Jun 27 09:50:23 2018 | root | number  | zypp(packagekitd)     | important=no 
post   | 173 | 172   | Wed Jun 27 09:50:31 2018 | root | number  |                       | important=no 
pre    | 180 |       | Wed Jun 27 11:54:39 2018 | root | number  | zypp(zypper)          | important=no 

Delete snapshot

manu-opensuse:~ # snapper delete 172

You can reduce it using the following command:

manu-opensuse:~ # snapper set-config SPACE_LIMIT=0.2 NUMBER_LIMIT=2-6 NUMBER_LIMIT_IMPORTANT=4

BTRFS corruption

Check data integrity on block and metadata level with repair if needed (using -B in background if needed)

# btrfs scrub start /backup
# btrfs scrub status /backup
UUID:             76fac721-2294-4f89-a1af-620cde7a1980
Scrub started:    Wed Apr 10 12:34:56 2023
Status:           running
Duration:         0:00:05
Time left:        0:00:05
ETA:              Wed Apr 10 12:35:01 2023
Total to scrub:   28.32GiB
Bytes scrubbed:   13.76GiB  (48.59%)
Rate:             2.75GiB/s
Error summary:    no errors found

With some errors found:

Error summary:    csum=72
  Corrected:      2
  Uncorrectable:  72
  Unverified:     0

btrfs check seems to verify and optionally attempt repair of the structure of the filesystem. btrfs scrub verifies (and possibly repairs) the checksums of every data and metadata block

Advanced check on a partition:

# btrfs check -p /dev/sda9

Check if the filesystem is in error, and indicate a progress bar:

# btrfs device stats /dev/sda3
[/dev/sda3].write_io_errs   0
[/dev/sda3].read_io_errs    0
[/dev/sda3].flush_io_errs   0
[/dev/sda3].corruption_errs 0
[/dev/sda3].generation_errs 0

script btrfs_size.sh

manu-opensuse:~ # cat ./btrfs_size.sh
#!/bin/bash
#Author Kyle Agronick <agronick@gmail.com>
#Usage: Invoke this script to get the size of your subvolumes and snapshots
#Make sure to run "sudo btrfs quota enable /" first


LOCATION='/'
if [ $1 ]; then
LOCATION=$1
fi

OUTPUT="" 

COL1=`sudo btrfs subvolume list "$LOCATION"`

if [ $? -ne 0 ]; then
        echo "Failed to the volume data! BTRFS volume is required on the target location!"
        exit 1
fi

COL1=$(echo "$COL1" | cut -d ' ' -f 2,9) # Only taking the ID and the Snapshot name

COL2=`sudo btrfs qgroup show "$LOCATION" --raw 2>&1`
CONTINUE=false
if [[ $COL2 == *"unrecognized option"* ]]; then
    COL2=`sudo btrfs qgroup show "$LOCATION" `
fi    

COL2=$(echo "$COL2" | cut -c 2-)


function convert()
{ 
        OUT=`echo "$i" | awk '{ sum=$1 ; hum[1024^4]="TB";hum[1024^3]="GB";hum[1024^2]="MB";hum[1024]="KB"; for (x=1024^4; x>=1024; x/=1024){ if (sum>=x) { printf "%.2f%s\n",sum/x,hum[x];break } }}'`
        OUTPUT=$(printf "%-9s" $OUT) 
        echo "$OUTPUT"
}

i=0
ECL_TOTAL=0
INDEX=0
LC_ALL=C
for i in $COL2; do  
    if [[ $i == *"groupid"* ]] || [[ $i == *"----"* ]]; then
        continue;
    fi 
    if [[ ! $i =~ ^[A-Za-z-]+$ ]]; then  
      if [[ "$i" == *\/* ]]; then 
        INDEX=0
        ROWID=$(echo "$i" | cut -c 2-)   
        OUTPUT+="
$ROWID  " 
      else
        ((INDEX++))   
        if [ -z `echo $i | tr -d "[:alpha:]"` ]; then
            echo $i" letters\n"
            OUTPUT="$OUTPUT"$(printf "%-9s" $i)
        else
            if [ $INDEX -eq 2 ]; then
                ECL_TOTAL=$(($i + $ECL_TOTAL)) 
            fi
            OUTPUT="$OUTPUT$(convert $i)"
        fi
       fi
    fi
done


# Determine terminal width
if hash tput 2>/dev/null; then
        COLCOUNT=`tput cols`
elif hash stty 2>/dev/null; then
        COLCOUNT=`stty size | cut -d' ' -f2`
else
        COLCOUNT=80 # Default
fi

declare -a COLUMNWIDHTS=(-$(($COLCOUNT-30)) 20 6)

function printRow
{
        DATA=("$@")

        # The offset is calculated to help aligning the next column properly,
        # if the preveious one was too long
        local offset=0
        for ((i=0;i < $#;i++))
        {
                local modifier=""
                local width=${COLUMNWIDHTS[$i]}
                if [ $width -lt 0 ]; then
                        width=$((0-$width)) # Gettings abs value
                        modifier="-." # Left-padded and truncating if too long
                fi
                local pattern="%$modifier*s"
                local column # The current column with padding
                printf -v column $pattern $(($width + $offset)) "${DATA[$i]}"
                printf "$column"
                offset=$(($offset + $width - ${#column}))
        }
        printf "\n"
}

function printHorizontalLine
{
        printf '%*s\n' $COLCOUNT '' | tr ' ' '='
}

# Header start
printHorizontalLine
printRow "Snapshot / Subvolume" "Total Exclusive Data" "ID"
printHorizontalLine
# Header end

IFS=$'\n'

# Table body start
for item in  $COL1; do
    ID=$(echo $item | cut -d' ' -f1)
    name=$(echo $item | cut -d' ' -f2)
    for item2 in $OUTPUT; do
        ID2=$(echo $item2 | grep -o '^[0-9.]\+' )
        if [ "$ID" = "$ID2" ]; then
                        eval ROWDATA=($(echo $name ${item2[@]} | awk -F' ' '{print $1, $3, $2}'))
                        printRow "${ROWDATA[@]}"
                        break;
        fi
    done
done
# Table body end

if [ $ECL_TOTAL -gt "1" ]; then
    printHorizontalLine
    i=$ECL_TOTAL
    printf "%-64s" " "  
    printf "Exclusive Total: $(convert $i) \n"
fi
manu-opensuse:~ # ./btrfs_size.sh /
==========================================================================================================================================================================================================================================================================================
Snapshot / Subvolume                                                                                                                                                                                                                                        Total Exclusive Data    ID
===================================================================================
@                                                                   16.00KB   257
@/var                                                              498.04MB   258
@/usr/local                                                         31.34MB   259
@/tmp                                                                1.98GB   260
@/srv                                                              150.35MB   261
@/root                                                              10.76MB   262
@/opt                                                              871.97MB   263
@/boot/grub2/x86_64-efi                                              3.38MB   264
@/boot/grub2/i386-pc                                                16.00KB   265
@/.snapshots                                                       488.00KB   266
@/.snapshots/1/snapshot                                              7.18GB   267
...
@/.snapshots/173/snapshot                                            7.18GB   459
@/.snapshots/180/snapshot                                            7.18GB   466
@/.snapshots/181/snapshot                                            7.18GB   467
linux/btrfs.txt · Last modified: 2024/11/12 22:52 by manu