The scenario: You want to change your disk layout, which involves shrinking the partition with the root file system.
You know that you can grow a file system on the fly. At least: most filesystem types
But shrinking a file system invariably means umounting it first.
And that is a problem: You cannot umount the root file system because it is in use. And you need the root file system while the system is running. And you need a (somewhat) running system to shrink the root file system.
It seems like a chicken-and-egg problem.
The Boot Sequence
The boot sequence for a Linux system is a little complicated as it has to (literally) start from scratch. So there are multiple levels of bootstrapping.
For example, in a legacy system with BIOS boot, it looks like this:
- The BIOS reads the boot sector executes it. That's it. It's all the BIOS knows to do.
This bootstrap program must be small because it has to fit in a single sector. For a long time, the size of a sector has been 512 bytes.
-
The boot sector contains the GNU Grub Bootloader
-
The Grub Bootlader loads other files (typically from
/boot/grub) to get Grub itself up and running. -
Grub presents a simple menu to the user, allowing the user to select an operating system. In may setups, this menu is hidden, and Grub is configured to just load the first item in the menu.
-
Grub loads the linux kernel into memory
-
Grub loads the
initrd(Initial RAM Disk) into memory -
Grub hands over control to the kernel, pointing it to the initrd.
-
As part of the Linux initialisation, various kernel modules are loaded from the
initrd- mostly device drivers and whatever is needed to actually mount the root file system later on. -
The code in the
initrdmounts the real root file system, and "pivots" to this. -
The kernel starts the
initprocess - nowadays: this issystemd. -
systemdstarts whatever system services are installed (e.g. other file systems, network interfaces etc) One of these system services is usually a loging prompt. -
The user can (finally) log in
The point here is that a lot of stuff happens before the root file
system is mounted. The bit we are interested in is the initrd:
This is under our control. And this is what allows us to "do stuff"
before the root file system is mounted.
Exactly how we control the initrd depends on your Linux distribution - the below is correct for the Debian distribution - and probably works in derivatives such as Ubuntu.
Adding Stuff to the initrd
On Debian we can add bits to the initrd (or initramfs) by dropping
files into the directories under /etc/initramfs-tools/ and running
update-initramfs.
First: we need to make sure that the tools for doing the resizing is
actually available in the initrd by adding a "hook" to the initramfs
generation - we do that by adding this file as
/etc/initramfs-tools/hooks/resize-root-fs:
#!/bin/sh
set -e
PREREQS=""
prereqs() { echo "$PREREQS"; }
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
copy_exec /sbin/e2fsck
copy_exec /sbin/resize2fs
exit 0
Then we need the actual script which will run at boot time by adding
this as /etc/initramfs-tools/scripts/local-premount/resize-root-fs:
(adjust the values of ROOT_DEV and SIZE to suit your requirements)
#!/bin/sh
#
set -e
PREREQS=""
prereqs() { echo "$PREREQS"; }
case "$1" in
prereqs)
prereqs
exit 0
;;
esac
# Device from which the root file system is mounted
ROOT_DEV=/dev/mapper/mylaptop--vg-root
# Desired size of the root file system
SIZE=500G
echo Resizing Root file system...
/sbin/e2fsck -yf ${ROOT_DEV}
/sbin/resize2fs -p ${ROOT_DEV} ${SIZE}
/sbin/e2fsck -yf ${ROOT_DEV}
Finally: We can generate a new initrd with:
update-initramfs -u
and reboot. The root file system will then be shrunk on the next reboot.
TidyUp
With the above in the initrd, the root file system will be shrunk on every reboot. Which is probably not what you want.
Once the root file system has been shrunk, you can undo the above changes with:
rm -f /etc/initramfs-tools/scripts/local-premount/resize-root-fs
rm -f /etc/initramfs-tools/hooks/resize-root-fs
update-initramfs -u
which will generate an initrd which is back to normal.
Final words
If you know you need to shrink the root file system to e.g. 500GB, there is a little trick to avoid trouble:
Resize the filesystem to something smaller than 500GB (but still big enough to contain the files) - e.g. 450GB
This will give you a margin of error when you later on resize the partition on which the root file system resides. Why? Because Gigabytes aren't necessarilty Gigabytes. Or at least: Gigabytes are easily confused with Gibibytes.
Some tools work with 1GB = 10^9 bytes (= 1,000,000,000 bytes), others work on the basis of 1GB = 2^30 bytes (= 1,073,741,824 bytes). That is a 7% difference! And accidentally chopping off 7% at the end of a working file system can spell disaster...