My home mail server complains about a full /var partition. Actually, the partition is not full. However, it does not have enough free space to update the malware database of ClamAV. Therefore, I have to do something about it. Unfortunately I do not have any free space adjacent to /var on the disk. So growfs(8) is not an option on this system.

Warning! Make sure you know what you do before following this procedure on a productive system. This can damage your data. This can make your system unbootable. If you feel unsure or never have done this before I recommend you to practice it on a test system. If do this on a productive system, loose important data and come back here to write comments asking about how to recover you will get only one answer from me: Hear my evil laugh! 😈 You have been warned.

First, I get an USB thumb drive, which is large enough to hold the full backups of all partitions I have to shuffle. As my SSD in the mail server is 16 GB an equal sized USB drive will be large enough for sure. On the USB drive, I create a native OpenBSD partition to hold all the backup files required:

$ doas disklabel -E sd1
> a a
offset: [64]
size: [33554432]
FS type: [4.2BSD]
> q
$ doas newfs /dev/rsd1a

All the tools I need during the resizing operation in single user mode are stored under /sbin. If this folder is on the same file system as the root folder, I am fine. If not I will have to mount the correct partition after booting into single user mode. The tools I need during the operation are disklabel(8), newfs(8), dump(8) and restore(8).

$ which disklabel
/sbin/disklabel
$ which newfs
/sbin/newfs
$ which dump
/sbin/dump
$ which restore
/sbin/restore

Now I store some important information as a text file on the backup drive. The first and most important one is the current disklabel(5):

# doas sh -c "disklabel sd0 > /mnt/disklabel.txt"

If you like me feel more comfortable with seeing partition sizes in bytes than in sectores, an additional text file might help you:

$ df -h > sizes.txt

To be on the safe side I copy the current fstab(5) to the backup drive:

$ doas cp /etc/fstab /mnt/

It is also a good idea to have a working backup set of the whole disk on another storage medium. I store my daily dumps on a file server in the same network.

I will take the required backups of the affected file systems while being in single user mode. This has the advantage, that the affected file systems will be offline. Therefore, no change to the filesystems can happen during the backup operation.

After I have double-checked that everything is ready, I reboot my system into single user mode:

>> OpenBSD/amd64 BOOT 3.33
boot> boot -s
<... all the kernel messages ...>
Enter pathname of shell or RETURN for sh:

First, I mount the backup partition on the USB drive connected to the system:

# mount /dev/sd1a /mnt

For a successful dump(8) a writeable /tmp directory is required. I can mount the /tmp partition to its normal location if its not affected by the operation:

# mount -o rw /tmp

Backups first. So I run a full dump(8) of the /var partition. There is no need to mount it for the dump, it is even better if the partition remains unmounted. dump(8) can still access it using the UUID of the disk and the letter of the partition in the disklabel. First show the fstab(5) from the backup partition to get the correct UUID.letter combination for /var

# cat /mnt/fstab

Then start the dump(8) of the partition:

# dump -0af /mnt/var.0.dump 123456789abcdef0.e
<... dump(8) telling you about it's progress ...>

Up to this point, I have not changed anything on the disk. This means I can stop here without any harm if I have bad feelings about the operation. The next part of the operation is going to change data on the disk; this means my system will be broken until I finish this critical block of commands.

Now I start disklabel(8) with the built-in editor:

# disklabel -E sd0
Label editor (enter '?' for help at any prompt)
> p m
OpenBSD area: 64-31262490; size: 15264.9M; free: 0.1M

# size offset fstype [fsize bsize cpg]  
a: 484.8M 64 4.2BSD 2048 16384 1 # /
b: 889.7M 992992 swap       # none
c: 15272.1M 0 unused      
d: 767.7M 2815072 4.2BSD 2048 16384 1 # /tmp
e: 1132.6M 4387392 4.2BSD 2048 16384 1 # /var
f: 1304.8M 6706912 4.2BSD 2048 16384 1 # /usr
g: 754.9M 9379200 4.2BSD 2048 16384 1 # /usr/X11R6
h: 2857.7M 10925216 4.2BSD 2048 16384 1 # /usr/local
i: 1518.9M 16777728 4.2BSD 2048 16384 12958 # /usr/src
j: 1290.9M 19888448 4.2BSD 2048 16384 12958 # /usr/obj
k: 4262.8M 22532160 4.2BSD 2048 16384 1 # /home

>

This is the actual disklabel with size in bytes of my mailserver. I am going to edit this label in order to increase the size of /var. As there is no more free space on the disk, I will have to destroy two partitions: /usr/src and /usr/obj. No backup of them? Nope, I have no backups of these because I can easily recover /usr/src by a cvs(1) checkout of the OpenBSD source tree. And /usr/obj is only needed to compile the sources of OpenBSD. It is going to be smaller than what the default partitioning scheme of the installer suggests. However, this is not a problem for me because I never to a full system build on this machine; I only rebuild iscsid(8) with some patch I have written.

> d j
> d i
> d e
> a j
offset: [16777728] 4387392
size: [2319520]
FS type: [4.2BSD]
> a i
offset: [16777728]
size: [5754432] 1307m
Rounding size to cylinder (16065 sectors): 2676987
FS type: [4.2BSD]
Rounding size to bsize (32 sectors): 2676960
> a e
offset: [19454688]
size: [3077472]
FS type: [4.2BSD]
> p m
OpenBSD area: 64-31262490; size: 15264.9M; free: 0.1M

# size offset fstype [fsize bsize cpg]
a: 484.8M 64 4.2BSD 2048 16384 1 # /
b: 889.7M 992992 swap # none
c: 15272.1M 0 unused
d: 767.7M 2815072 4.2BSD 2048 16384 1 # /tmp
e: 1502.7M 19454688 4.2BSD 2048 16384 1
f: 1304.8M 6706912 4.2BSD 2048 16384 1 # /usr
g: 754.9M 9379200 4.2BSD 2048 16384 1 # /usr/X11R6
h: 2857.7M 10925216 4.2BSD 2048 16384 1 # /usr/local
i: 1307.1M 16777728 4.2BSD 2048 16384 1
j: 1132.6M 4387392 4.2BSD 2048 16384 1
k: 4262.8M 22532160 4.2BSD 2048 16384 1 # /home

> q
Write new label?: [y] y

The next step is to format the new partitions. This can easily be done by using newfs(8):

# newfs /dev/rsd0e
/dev/rsd0e: 1502.7MB in 3077472 sectors of 512 bytes
8 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
super-block backups (for fsck -b #) at:
32, 414688, 829344, 1244000, 1658656, 2073312, 2487968, 2902624,
# newfs /dev/rsd0i
/dev/rsd0i: 1307.1MB in 2676960 sectors of 512 bytes
7 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
super-block backups (for fsck -b #) at:
32, 414688, 829344, 1244000, 1658656, 2073312, 2487968,
# newfs /dev/rsd0j
/dev/rsd0j: 1132.6MB in 2319520 sectors of 512 bytes
6 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
super-block backups (for fsck -b #) at:
32, 414688, 829344, 1244000, 1658656, 2073312,
#

Now it is time to restore the data of the partitions in the backup. For this I mount the new partition of /var writeable to its location, change to this directory and run restore(8):

# mount -o rw /var
# cd /var
# restore rf /mnt/var.0.dump

If everything looks good I umount(8) the backup partition and remove the USB drive from the system.

# umount /mnt

Now I reboot the system and keep my fingers crossed.