Skip to content

Latest commit

 

History

History
490 lines (336 loc) · 29.8 KB

File metadata and controls

490 lines (336 loc) · 29.8 KB

Converting ext4 to btrfs Filesystem & Related Matters

If you're interested in trying the btrfs filesystem (as an alternative to ext4) on your Raspberry Pi, you've come to the right some place. This procedure assumes your system is running the trixie release of RPi OS. If you're running another OS, you're welcome to try this procedure, but please keep in mind that it was written for RPi OS 'trixie' running on RPi hardware. FWIW, the "target" system hardware here is an RPi Zero 2W, but the conversion procedure should work for any system.

Let's get to it... As currently written this procedure uses two (2) RPis. However, it's also "do-able" on a single system. I have done this btrfs conversion by creating new partitions on a 2nd SD card, and using rsync to copy from the original ext4 source SD. An alternative to that approach is to use the btrfs-convert utility (p/o the btrfs-progs package) on the un-mounted ext4 source partition. I've elected not to include that alternative here, but if you're interested, let me know; I'll clean it up and post it here.

You will need a 2nd SD card for this procedure; you will also need two (2) USB-SD adapters. We will refer to this 2nd SD card as SD2. I used a SanDisk 64GB SDXC microSD card for SD2. Please note that this procedure will not modify your current trixie system (other than to install two software packages via apt), nor will it modify your original SD card - SD1. In this way, you have a "fallback" - your original system remains "as-is" on SD1 in case something goes wrong, or you simply decide after a short trial that btrfs is not for you.

btrfs Conversion Procedure for Raspberry Pis

Again, I used two RPis here for my convenience. I refer to these two RPis as the TARGET RPi, and the SUPPORT RPi; the TARGET RPi is the RPi that will have its root filesystem converted to btrfs.

  1. On the TARGET RPi, use apt to update, upgrade & install two packages, and add btrfs support to your initramfs:

    sudo apt update
    sudo apt -y full-upgrade		                # optional
    sudo apt install btrfs-progs initramfs-tools
      # open editor & add the word `btrfs` to /etc/initramfs-tools/modules, or: 
    sudo echo "btrfs" >> /etc/initramfs-tools/modules
      # apply the change:
    sudo update-initramfs -u 
      # issue 'halt' to TARGET RPi, remove SD card (SD1) & insert SD1 into USB-SD adapter
    sudo halt
    
  2. On the SUPPORT RPi: Plug SD1 and SD2 into USB ports; verify using lsblk --fs.

    lsblk --fs
    NAME        FSTYPE FSVER LABEL  UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
    sda
    ├─sda1      vfat   FAT32 bootfs 1C94-4EC3
    └─sda2      ext4   1.0   rootfs f0abac56-08be-42e2-8726-9baa083e8685
    sdb
    └─sdb1      exfat  1.0   64GB-SD 6FDB-2FBD
    nvme0n1
    ├─nvme0n1p1 vfat   FAT32 bootfs 91FE-7499                             434.5M    15% /boot/firmware
    └─nvme0n1p2 ext4   1.0   rootfs 56f80fa2-e005-4cca-86e6-19da1069914d  428.6G     1% /
    
  3. On the SUPPORT RPi: use fdisk to obtain needed information on /dev/sda (SD1).

      # get information on SD1 from fdisk:
    sudo fdisk -l /dev/sda
    Disk /dev/sda: 59.48 GiB, 63864569856 bytes, 124735488 sectors
    Disk model: STORAGE DEVICE
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0x26576298
    
    Device     Boot   Start       End   Sectors  Size Id Type
    /dev/sda1         16384   1064959   1048576  512M  c W95 FAT32 (LBA)
    /dev/sda2       1064960 124735487 123670528   59G 83 Linux
    
  4. On the SUPPORT RPi: use fdisk to create three (3) partitions on /dev/sdb (SD2):

    sudo fdisk /dev/sdb		# "blank" Command inputs are actually 'Enter' to accept default
    ...
    Command (m for help): o
    Created a new DOS (MBR) disklabel with disk identifier 0x6a99c805.
    The device contains 'gpt' signature and it will be removed by a write command. See fdisk(8) man page and --wipe option for more details.
    
    Command (m for help): p
    
    Disk /dev/sda: 59.48 GiB, 63864569856 bytes, 124735488 sectors
    Disk model: STORAGE DEVICE
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0x6a99c805
    
    Command (m for help): n
    Partition type
    p   primary (0 primary, 0 extended, 4 free)
    e   extended (container for logical partitions)
    Select (default p): 
    Partition number (1-4, default 1):
    First sector (2048-124735487, default 2048):
    Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-124735487, default 124735487): +512M
    
    Created a new partition 1 of type 'Linux' and of size 512 MiB.
    Partition #1 contains a vfat signature.
    
    Do you want to remove the signature? [Y]es/[N]o: y
    
    The signature will be removed by a write command.
    
    Command (m for help): n
    Partition type
    p   primary (1 primary, 0 extended, 3 free)
    e   extended (container for logical partitions)
    Select (default p): 
    Partition number (2-4, default 2):
    First sector (1050624-124735487, default 1050624):
    Last sector, +/-sectors or +/-size{K,M,G,T,P} (1050624-124735487, default 124735487): +50G
    
    Created a new partition 2 of type 'Linux' and of size 50 GiB.
    
    Command (m for help): n
    Partition type
    p   primary (2 primary, 0 extended, 2 free)
    e   extended (container for logical partitions)
    Select (default p): 
    Partition number (3,4, default 3):
    First sector (105908224-124735487, default 105908224):
    Last sector, +/-sectors or +/-size{K,M,G,T,P} (105908224-124735487, default 124735487): +6G
    
    Created a new partition 3 of type 'Linux' and of size 6 GiB.
    
    Command (m for help): p
    Disk /dev/sda: 59.48 GiB, 63864569856 bytes, 124735488 sectors
    Disk model: STORAGE DEVICE
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0x6a99c805
    
    Device     Boot     Start       End   Sectors  Size Id Type
    /dev/sda1            2048   1050623   1048576  512M 83 Linux
    /dev/sda2         1050624 105908223 104857600   50G 83 Linux
    /dev/sda3       105908224 118491135  12582912    6G 83 Linux
    
    Filesystem/RAID signature on partition 1 will be wiped.
    
    Command (m for help): t
    Partition number (1-3, default 3): 1
    Hex code or alias (type L to list all): c
    
    Changed type of partition 'Linux' to 'W95 FAT32 (LBA)'.
    
    Command (m for help): p		# this is what yours should look like
    Disk /dev/sda: 59.48 GiB, 63864569856 bytes, 124735488 sectors
    Disk model: STORAGE DEVICE
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0x6a99c805
    
    Device     Boot     Start       End   Sectors  Size Id Type
    /dev/sdb1            2048   1050623   1048576  512M  c W95 FAT32 (LBA)
    /dev/sdb2         1050624 105908223 104857600   50G 83 Linux
    /dev/sdb3       105908224 118491135  12582912    6G 83 Linux
    
    Filesystem/RAID signature on partition 1 will be wiped.
    
    Command (m for help): v
    No errors detected.
    Remaining 6244352 unallocated 512-byte sectors.
    
    Command (m for help): w
    The partition table has been altered.
    Calling ioctl() to re-read partition table.
    Syncing disks.
    
  5. On the SUPPORT RPi: copy from SD1 to SD2, and format SD2:

      # we take care of the `/boot/firmware` partition first (`dev/sdb1`);
      # copy the boot partition from SD1 (/dev/sda1) to SD2 (/dev/sdb1): 
    
    sudo dd if=/dev/sda1 of=/dev/sdb1 bs=512 
    
    1048576+0 records in
    1048576+0 records out
    536870912 bytes (537 MB, 512 MiB) copied, 29.9509 s, 17.9 MB/s 
    
      # we now format /dev/sdb2 and /dev/sdb3 as btrfs 
    
    sudo mkfs.btrfs -L rootfs /dev/sdb2 
    
    btrfs-progs v6.14
    See https://btrfs.readthedocs.io for more information.
    
    NOTE: several default settings have changed in version 5.15, please make sure
    this does not affect your deployments:
    - DUP for metadata (-m dup)
    - enabled no-holes (-O no-holes)
    - enabled free-space-tree (-R free-space-tree)
    
    Label:              rootfs
    UUID:               6112e919-f036-4da2-91c7-7bfaedd2c145
    Node size:          16384
    Sector size:        4096	(CPU page size: 16384)
    Filesystem size:    50.00GiB
    Block group profiles:
    Data:             single            8.00MiB
    Metadata:         DUP             256.00MiB
    System:           DUP               8.00MiB
    SSD detected:       no
    Zoned device:       no
    Features:           extref, skinny-metadata, no-holes, free-space-tree
    Checksum:           crc32c
    Number of devices:  1
    Devices:
    ID        SIZE  PATH
    1    50.00GiB  /dev/sdb2 
    
      # we now mount /dev/sda1 (SD1) and /dev/sdb1 (SD2) so that we can copy the contents 
      # of /dev/sda1 to /dev/sdb1 via rsync: 
    
    sudo mkdir -p /mnt/SD1 /mnt/SD2
    sudo mount /dev/sda2 /mnt/SD1 && sudo mount /dev/sdb2 /mnt/SD2
    sudo rsync -HAXav /mnt/SD1/ /mnt/SD2/ > rsync-log.txt 2>&1 
    sudo umount /mnt/SD1   # remove SD1, label it and set it aside for future use
    
      # peruse rsync-log.txt to verify a lof of files were copied!  :) 
      # format /dev/sdb3 as btrfs to use as an "extra space" to house snapshots, etc. 
    
    sudo mkfs.btrfs -L BTRFS1 /dev/sdb3
    
      # verify w/ lsblk --fs
    
    lsblk --fs
    NAME        FSTYPE FSVER LABEL  UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
    sda
    ├─sda1      vfat   FAT32 bootfs C73C-AF74
    └─sda2      btrfs        rootfs b90c7819-cd45-4006-9d7b-7407f27085f0
    sdb
    ├─sdb1      vfat   FAT32 bootfs C73C-AF74
    ├─sdb2      btrfs        rootfs 6112e919-f036-4da2-91c7-7bfaedd2c145
    └─sdb3      btrfs        BTRFS1 ed7c6c3f-4ae7-4113-ae54-0b5891e03fbe
    nvme0n1
    ├─nvme0n1p1 vfat   FAT32 bootfs 91FE-7499                             434.5M    15% /boot/firmware
    └─nvme0n1p2 ext4   1.0   rootfs 56f80fa2-e005-4cca-86e6-19da1069914d  428.6G     1% /
    
  6. On the SUPPORT RPi: Minor edits to make to make SD2 bootable:

      # /mnt/sdb2 should still be mounted at /mnt/SD2, so make required changes there first:
      
    sudo nano /mnt/SD2/etc/fstab		# or use your preferred editor to make these changes:
    
      # FROM: 
      
    PARTUUID="whatever-1"  /boot/firmware  vfat    defaults          0       2
    PARTUUID="whatever-2"  /               ext4    defaults,noatime  0       1
    
      # TO: 
      
    LABEL=bootfs          /boot/firmware  vfat    defaults          0       2
    LABEL=rootfs          /               btrfs   defaults,noatime  0       0
    # PARTUUID="whatever-1"  /boot/firmware  vfat    defaults          0       2
    # PARTUUID="whatever-2"  /               ext4    defaults,noatime  0       1
    
      # now mount /dev/sdb1 for another edit to cmdline.txt :
      
    sudo mount /dev/sdb1 /mnt/SD1
    sudo nano /mnt/SD1/cmdline.txt
    
      # FROM:
    
    console=serial0,115200 console=tty1 root=PARTUUID=whatever1 rootfstype=ext4 fsck.repair=yes rootwait cfg80211.ieee80211_regdom=US
    
      #TO:
    
    console=serial0,115200 console=tty1 root=LABEL=rootfs rootfstype=btrfs fsck.repair=no rootwait cfg80211.ieee80211_regdom=US
    
    # C'est terminé!! You should be able to remove SD2 from the SUPPORT RPi, 
    # insert it into the TARGET RPi and boot from it.
    

    C'est terminé! ... You should now be able to remove SD2 from the SUPPORT RPi, insert it into the TARGET RPi and boot from it. And a quick comment on the above changes: Note that I used LABELs instead of PARTUUIDs. That's just a personal preference; you may use PARTUUID (or something else in ls -l /dev/disk, or via the blkid command). If I had hundreds of RPi, constantly swapping SD cards, I suppose I might find PARTUUIDs useful... for my present purposes, LABELs mostly work fine. :)

Summary:

Hopefully, you now have a working (boot-able) btrfs on your RPi. And now that you're here, you may be wondering, "OK... what next?" Well, that's a good question, but unfortunately I don't have a good answer at present - other than, "use it as it is - instead of ext4" ! So far, I've not found switching to btrfs a big change at all... everything I did under ext4 I am still able to do under btrfs. However - I am currently exploring some alternatives... I hope to have something more intelligent to say soon, but as of now - not much. I'll also opine that the vast majority of documentation on btrfs, and how to use it (e.g. for "snapshots") to be inscrutable gobbledygook. Put another way, in one of the many pieces I've read recently, the author simply stated it this way, "Taking snapshots with btrfs is easy; managing those snapshots is not".

Notes:

  1. Re Step 6 above, the edit to /etc/fstab: The sixth field in a /etc/fstab line is known as 'fs_passno', and is used to determine the order that fsck is run. As there is no benefit to running fsck on a btrfs partition, 'fs_passno' is changed from 1 to 0. And yes, this is redundant with cmdline.txt... Why? You can ask "The Raspberries" - I don't really know.
  2. Re Step 6 above, the fsck.repair edit to cmdline.txt: fsck.repair is set "=no" because fsck is not needed for btrfs.
  3. Re Step 6; use of LABELs vs PARTUUIDs vs UUIDs, etc, etc. This may be useful to some: The folder /dev/disk contains nine (9) sub-folders dedicated to the various methods for referring to a device that is a "disk". If you want to use something other than LABEL, you can find it here.
  4. Re Step 1; the modification of initramfs: Some accounts on the Internet state that changes made to initramfs are ephemeral; lasting only until the next kernel upgrade. IOW, when the kernel is upgraded (e.g. in apt), any changes made to initramfs must be re-applied to remain effective under the new/upgraded kernel. In researching this, I found a post in the RPi GitHub Issues that seems to address the question, but like some other posts from this knob, it is inscrutable. IOW this "answer" was unclear, and we may have to wait for a kernel upgrade to learn the answer. If you know the answer - please share! In the meantime, this may help: lsinitramfs /boot/initrd.img-$(uname -r) | grep btrfs
  5. The ability of btrfs to make "snapshots" of the file system is obviously a major attraction for those that like to experiment with their RPi systems. I am still learning the configuration process, but I hear of an app named snapper that is said by some to be quite good. I'll post a follow-up to this recipe once I've "found my footing".

References:

If you want to capitalize on the potential advantages of btrfs, there is quite a lot to learn! This list of references is deliberately brief, but hopefully helpful toward an understanding. I've also included a couple of "assessment articles" on btrfs ICYI. More detailed references may follow.

  1. The Btrfs filesystem: An introduction: A series of articles from LWN (Linux Weekly News), published in 2013-2014. Note that sequels to this article are listed at the end of the article.

  2. Working with Btrfs – General Concepts: A series of articles from Fedora Magazine, published in 2022-2023.

  3. Debian's btrfs Wiki: There are several good btrfs wikis; this one was selected for obvious reasons :) Check the wiki's change log for the update history.

  4. An assessment: Examining btrfs, Linux's perpetually half-finished filesystem, from ars technica, dtd Sep, 2021

  5. An assessment: Is Btrfs still unstable?, from Darwin's Data, dtd Oct 2023