encryptio.com

This sentence is very meta.

Resizing The Boot Volume of a FreeBSD VMWare Virtual Machine

This page is a draft. It may be incomplete, inaccurate, or written not well. It will definitely change in the future. Comments, corrections, and suggestions welcome.

I had a bit of trouble doing this, so I figured I'd write it up for others that might be having the same problem.

"Simple" Geek's Overview of the FreeBSD Disk Subsystem

Feel free to skip this section if you already know or don't care how the disk subsystem in FreeBSD works. Also, this section is based on my experiences, not my exact source-understanding knowledge.

FreeBSD is extremely flexible in its usage of filesystems - any kind of division of a disk creates new devices in /dev ("specials"), and any of these devices can be used as the filesystem store. These devices are created automatically by the GEOM part of the kernel - it watches for notable changes, and if a division was created, destroyed, or modified, the specials are updated accordingly.

In a typical setup, you'd have /dev/ad0 (or just ad0) as the special for the entire disk, including the headers that declare "normal" (BIOS/DOS-style) partitions. To access a single partition, you'd use ad0s1, ad0s2, etc. You can use fdisk(8) to edit the BIOS partition tables for a disk.

However, the BIOS partition tables have a few serious limitations. The limit of 4 partitions in particular causes issues for Unix-like systems as they often prefer to have many partitions for various sections of the filesystem. This limitation led to the development of BSD labels, editable by the bsdlabel(8) utility. The BSD labels allow up to 8 partitions, one of which is, by convention, reserved for the whole drive. Typically you would put a BSD label inside a BIOS partition (create with bsdlabel -w ad0s1). This BSD label creates the specials ad0s1a, ad0s1b, ad0s1c, etc. The "c" partition is special - it's reserved for referring to the whole disk (or partition) as a whole - that is, ad0s1c is almost equivalent to ad0s1.

In this typical setup, filesystems are placed on the BSD label partitions inside the BIOS partitions. (UFS filesystems are created with newfs(8)).

However, GEOM is very flexible and does not require this division specifically. In fact, you can nest arbitrarily short division trees or arbitrarily long division trees - you could create a UFS filesystem on ad0 and only use it as a single filesystem (note that you will not be able to boot from this drive, because the BIOS partition table gives the information required, but here it does not exist.) You could also use a BSD label off the drive without a BIOS partition table, which would give you an unbootable set of specials ad0a, ad0b, etc. More uselessly, you can nest these and create a BSD label in ad0a, giving you ad0aa, ad0ab, ad0ac, ad0ad, etc.

I'll be sticking with a typical partitioning scheme (Raw → BIOS → BSD → UFS/Swap) for this, but keep these facts in mind - FreeBSD will not complain if you don't go by the typical partitioning scheme, but you may not be able to boot from that drive if you don't keep to it.

Procedure

My virtual disk was set up as a single BIOS partition with two BSD label partitions, a being the root UFS filesystem and b being the swap space.

First, we create a new virtual drive at the size we want it to be and add it as a secondary drive to the virtual machine and boot it up.

Let's see what we've got:

root# ls /dev
... # non-drive devices omitted
ad0
ad0s1
ad0s1a
ad0s1b
ad0s1c
ad1

Okay, so VMWare just gave us a pure blank drive. Let's add a BIOS partition map:

root# fdisk -u /dev/ad1
...asks lots of questions
...accept the defaults until the end ("write to disk y/N")
...to have a single partition the size of the drive
root# ls /dev
...
ad0
ad0s1
ad0s1a
ad0s1b
ad0s1c
ad1
ad1s1

Good so far. Now for the BSD label.

root# bsdlabel -w /dev/ad1s1
root# ls /dev
...
ad0
ad0s1
ad0s1a
ad0s1b
ad0s1c
ad1
ad1s1
ad1s1a
ad1s1c

Not quite what we want... A look in the bsdlabel editor shows this:

root# bsdlabel -e /dev/ad1s1
# opens $EDITOR with this file:

# /dev/ad1s1:
8 partitions:
#        size   offset    fstype   [fsize bsize bps/cpg]
  a: 29359181       16    4.2BSD     2048 16384 28552
  c: 29359197        0    unused        0     0         # "raw" part, don't edit

Using my old label as a guide, I edited the label to give this:

# /dev/ad1s1:
8 partitions:
#        size   offset    fstype   [fsize bsize bps/cpg]
  a: 28834893   524288    4.2BSD     2048 16384 28552
  b:   524288        0      swap
  c: 29359197        0    unused        0     0         # "raw" part, don't edit

Note that the corresponding BSD label partition ids (letters) should match between the old and new drives.

A quick glance at the specials looks right, and we know we have a swap partition that will eventually be the same device name as before.

root# ls /dev
...
ad0
ad0s1
ad0s1a
ad0s1b
ad0s1c
ad1
ad1s1
ad1s1a
ad1s1b
ad1s1c

Now we create a filesystem and copy all the files over. Read the manpages for dump(8) and restore(8) for more info on the command specifics. If you have more than one UFS partition, you'll need to do this multiple times.

Also note that you should stop any major daemons that might be in an inconsistent state on disk (database servers in particular).

root# newfs /dev/ad1s1a
root# mkdir /mnt/new
root# mount /dev/ad1s1a /mnt/new
root# cd /mnt/new
root# dump -L0 -a -P 'restore rfv -' /     # that -L0 is dash ell zero
...wait wait wait...

So all the files are now set up. There's one more job to do before we can boot from the new disk - write the bootloader!

root# bsdlabel -B /dev/ad1s1

Now we shut down the system and fire up a new virtual machine with the new disk as its primary.