‹ back home

Growing my root partition to the left

2024-05-27 #alpine #btrfs #how-to

When I initially set up my laptop, I created a 16GB swap partition on it. The large size is so that I can suspend to disk (aka: hibernate) onto it.

Resuming from hibernation from this partition doesn’t entirely work. Alpine’s initfs can’t re-use the same passphrase to unlock more than one partition. Well, it can, but I’d be prompted to type the same twice password every time the system boots. LVM was also an option, but that seems like one layer of complexity too many for such a simple setup.

I eventually set up hibernation to use a swapfile inside the main partition. But the original swap partition is still there, idle and unused.

Having run out of space yesterday trying to compile the main/rust package, I decided that it’s time to recover this idle space1.

Overview

My disk layout is as follows:

# fdisk -l
Found valid GPT with protective MBR; using GPT

Disk /dev/nvme0n1: 500118192 sectors, 2534M
Logical sector size: 512
Disk identifier (GUID): XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Partition table holds up to 128 entries
First usable sector is 2048, last usable sector is 500118158

Number  Start (sector)    End (sector)  Size Name
     1            2048         1230847  600M
     2         1230848        34785279 16.0G
     3        34785280       500117503  221G

I put the swap first, because that saved me doing the math (a single subtraction) required to put it at the end. Removing the swap partition is easy, but growing the main system partition to the left is a bit more work.

Deleting the swap partition

I delete the swap partition with:

sfdisk --delete /dev/nvme0n1 2

It is effectively gone:

# fdisk -l /dev/nvme0n1
Found valid GPT with protective MBR; using GPT

Disk /dev/nvme0n1: 500118192 sectors, 2534M
Logical sector size: 512
Disk identifier (GUID): XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Partition table holds up to 128 entries
First usable sector is 2048, last usable sector is 500118158

Number  Start (sector)    End (sector)  Size Name
     1            2048         1230847  600M
     3        34785280       500117503  221G

It low-key bothers me that now partitions are 1 and 3 with no 2. Hopefully I can fix this later.

Backup

This whole operation can potentially destroy all data on my disk. Skipping a backup would tempt the fates to actually make this whole operation fail.

I make a backup of the whole disk by just dumping an image of it into an external drive:

  1. Reboot.
  2. Choose to boot from an Alpine bootable USB drive.
  3. Log in as root.
  4. Plug in USB drive.
  5. modprobe ext4 && mount /dev/sdb1 /mnt
  6. dd if=/dev/nvme0n1 of=/mnt/dump_2024-05-27.img bs=512

Moving the partition to the left

This isn’t as trivial because the partition that I’m moving is my main partition and it needs to be unmounted before an operation like this. I’ll use a gparted bootable USB drive for this.

After downloading gparted, I flash it onto a USB drive using:

dd if=gparted-live-1.6.0-3-amd64.iso of=/dev/sda bs=512

I reboot the laptop again and boot into this USB drive. The touchpad seems to work for moving around the cursor, but not for clicking. The easiest solution is to just grab a wired USB mouse, which works right away.

I right click on the main partition, and choose “Resize/Move”. I type 999999 on the “New size” field and it automatically sets it to occupy all available space.

I expected this operation to take a long time since every single byte in this partition needs to be moved leftwards. It took 44 minutes.

Once gparted has done its job, I proceed to grow the filesystem inside the partition. First, I unlock the encrypted partition with:

cryptsetup luksOpen /dev/nvme0n1p3 root

The btrfs tool operates on a mounted filesystem, so I mount my filesystem and then resize it.

mount -t btrfs /dev/mapper/root /mnt
btrfs filesystem resize max /mnt/

It is instantaneous. Nice.

I can not unmount the filesystem:

umount /mnt

Fixing the partition number

My disk now has partitions 1 and 3. I want to rename the latter to 2.

This is one by deleting the partition from the partition table and creating a new one with the exact same size. This operation doesn’t touch the actual partition itself at all, only its definition in the partition table.

Update

Jackson points out that sfdisk has a --reorder flag which would have made this operation a lot less error prone. This whole section could have been sfdisk --reorder /dev/nvme0n1.

First, I print the current details for the partition, since I’ll need to replicate those:

# fdisk -l
Found valid GPT with protective MBR; using GPT

Disk /dev/nvme0n1: 500118192 sectors, 2534M
Logical sector size: 512
Disk identifier (GUID): XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Partition table holds up to 128 entries
First usable sector is 2048, last usable sector is 500118158

Number  Start (sector)    End (sector)  Size Name
     1            2048         1230847  600M
     3         1230848       500117503  237G

I then delete and recreate the partition as number 2:

sfdisk --delete /dev/nvme0n1 3
echo '1230848' | sfdisk --append /dev/nvme0n1

I only specify the start sector for the new partition, and the default uses all available space (which matches the previously existing one). Because only partition 1 exist, the new partition is assigned number 2. The final result look as expected:

# fdisk -l
Found valid GPT with protective MBR; using GPT

Disk /dev/nvme0n1: 500118192 sectors, 2534M
Logical sector size: 512
Disk identifier (GUID): XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Partition table holds up to 128 entries
First usable sector is 2048, last usable sector is 500118158

Number  Start (sector)    End (sector)  Size Name
     1            2048         1230847  600M
     2         1230848       500117503  237G

I reboot the system, provide my passphrase and… it works! I have effectively reclaimed those 16GiB.


  1. I also reclaimed 24GiB by running apk cache clean, which deleted from my local cache packages that no longer exist upstream. ↩︎

Have comments or want to discuss this topic?
Send an email to my public inbox: ~whynothugo/public-inbox@lists.sr.ht.
Or feel free to reply privately by email: hugo@whynothugo.nl.

— § —