Autonomes Säulenblog

My blog about programming, computer science, and other stuff.

Posts tagged ZFS

Jul 26

Updating FreeBSD with ZFS Root File System

This weekend I wanted to update my FreeBSD box and saw that the ZFS tools fundamentally changed. The UPDATING file has a corresponding note:

20090520:
        Update ZFS to version 13. ZFS users will need to re-build
        and install both kernel and world simultaneously in order 
        for the ZFS tools to work. Existing pools will continue to work
        without upgrade. If a pool is upgraded it will no longer be
        usable by older kernel revs. ZFS send / recv between 
        pool version 6 and pool version 13 is not supported.

Installing kernel and world simultaneously is scary. If something breaks you are left with a broken system, no way to roll back, and uncertain repair possibilities. Sure, you should have backups, but restoring a whole system is still something that takes a lot of time.

But is this not exactly one of ZFS’ strength? ZFS provides easy snapshots and clones. When I search for “FreeBSD ZFS update” I stumbled of this Mail: Solaris live upgrade like FreeBSD ZFS-rootfs update, which gave me the idea to snapshot my root and usr file system. This should give me a reasonably easy rollback path.

Disclaimer

Here is what I did (with some minor tweaks). It might or might not work for you. You might loose all your data following my steps. I cannot be held reliable for that. I also probably made a few mistakes while writing this up. Back up your system!

Prerequistes

I have my FreeBSD with encrypted ZFS set-up like I described in my blog post FreeBSD: Encrypted ZFS Root with Geli. It should also work with a different encryption mechanism or without encryption.

Creating the Snapshots

Let us assume that the ZFS pool is called tank and that the root file system is under tank/root and the usr file system is under tank/usr.

First, let us create a snapshot of the usr file system and of the root file system:

$ sudo zfs snapshot tank/usr@pre_update
$ sudo zfs snapshot tank/root@pre_update

A snapshot is not writable. So let us create clones:

$ sudo zfs clone tank/usr@pre_update tank/usr_pre_update
$ sudo zfs clone tank/root@pre_update tank/root_pre_update

We need two versions of the root file system. The normal one (tank/usr) where usr points to the normal tank/usr and the pre update one (tank/root_pre_update) that points to the pre update snapshot tank/usr_pre_update. We will use a symlink for this.

Unfortunately, you cannot change the mountpoint of your usr file system while the system is running. So let us first finalize and boot into the pre update set-up to see that it is working.

$ echo Temporary mounting the pre update root
$ sudo mdkir -p /pre_update/root
$ sudo zfs set mountpoint=/pre_update/root tank/root_pre_update
$ echo Setting the mountpoint of the pre update usr file system
$ sudo zfs set mountpoint=/usr_pre_update tank/usr_pre_update
$ echo Symlinking usr to the pre update usr file system
$ cd /pre_update/root
$ sudo rmdir usr
$ sudo ln -s usr_pre_update usr
$ echo Setting the pre update root mountpoint to legacy
$ sudo zfs set mountpoint=legacy tank/root_pre_update

We need a similar set-up in the normal root file system where the symlink points to usr_post_update. In order to set the mountpoint of tank/usr to /usr_post/update we need to reboot into single user mode with ZFS disabled. We go to the boot loader prompt (normally by choosing 6 in the boot menu).

# unset vfs.root.mountfrom
# disable-module zfs
# set boot_single
# boot

Once we are in single user mode, we can import zpool and set the mountpoint.

# zpool import -d /boot/zfs
# zpool import -f -R /tank tank
# cd /tank/root
# rmdir usr
# ln -s usr_post_update usr
# zfs set mountpoint=/usr_post_update tank/usr_post_update

Now we reboot the system into the pre update environment. We go to the boot loader prompt and change the root file system to the pre update root file system.

# set vfs.root.mountfrom=zfs:tank/root_pre_update
# boot

If everything goes well, the machine boots up into the cloned environment. We can verify this by looking that /usr points to /usr_pre_update.

Let us reboot again into the normal environment (by doing nothing special during boot-up). Now /usr should point to /usr_post_update. With this system we can build and install the world and the kernel as mentioned in the Handbook - Rebuilding “world”, without the reboot into single user mode (which will not work, anyway).

Rollback

If anything goes wrong during the update, we can boot our pre update environment by going to the boot loader prompt, selecting the pre update root file system and booting the old kernel.

# set vfs.root.mountfrom=zfs:tank/root_pre_update
# boot kernel.old

Here we can copy over the backup kernel to correct destination and roll back to the snapshot with the comfort of the system in a fully functional state that we know.

Should the update work out, we can destroy the pre update environment in order to safe disk space. Or we can leave it around to mitigate problems of the update we are not aware of just yet.

Resources


Nov 9

FreeBSD: Encrpyted ZFS Root with Geli

ZFS is supposed to support encryption, but it does not yet on FreeBSD. In a previous post I wrote about setting up ZFS on FreeBSD where the root file system uses UFS and the rest goes to logical ZFS volumes.

This time I use geli to encrypt a disk partition and use ZFS for the root file system. I encountered a few problems which I’d like to document here.

The FreeBSD Handbook section on encrypted disks show how to set up encrypted disks with geom or geli. However, you cannot use the geli rc-scripts to ask for the passphrase and attach the partition, because we need the partition ready for ZFS which is started by the loader. It needs to be started by the loader, because it shall provide the root file system.

Fortunately, geli can auto-detect encrypted partitions when it is started by the loader. I think it is not possible to use a key-file in this case. At least I could not see how.

I will now show how I set up a new FreeBSD installation with ZFS as the root file system. Following this instruction can and will destroy your data. It can also destroy your data you will put on the newly installed system in the future. I can take no liability.

Firstly, start installing FreeBSD 7.x. Partition the hard disk to have twro partitions. I used 3GB for the first partition which consists of two slices, the first is 1GB for the initial root file system (512MB should be sufficient) and 2GB for swap. The second partition will contain the encrpyted ZFS pool. Continue to install a minimal system.

After the installation is finished initialise the encrypted partition (assuming we want to encrypt ad0s2) and attach it:

# geli init -s 4096 -b /dev/ad0s2
# geli attach /dev/ad0s2

The -b flag will cause geli to ask for the passphrase at start-up.

Now the encrypted partition is ready for ZFS, but I encountered a problem (a bug?). During start-up the the passphrase was not accepted. The problem is that not all charactes I typed where recognized. We will take care of this later (we need a new kernel). For now we use a work-around. Add the following line to /boot/loader.conf

kern.geom.eli.visible_passphrase="1"

ATTENTION: This will show your passphrase when you type it during start-up. This way you will get feed-back what characters got lost.

Create the ZFS pool on the encrypted partition:

# zpool create tank /dev/ad0s2.eli

Edit /etc/rc.conf to enable ZFS:

zfs_enable="YES"

Reboot the system to see if everything works as expected. You should be asked for your passphrase. You will see the passphrase while typing it. After logging in as root you should be able to see that your encrypted partition (ad0s2.eli) is in the ZFS pool by using the following command:

# zpool status

Now you can follow the rest of the instructions for How to install FreeBSD 7.0 under ZFS.

After that is done you probably want to get rid of the passphrase work-around. If you did not have any problems typing in the passphrase, you can simply remove the setting that makes it visible in the /boot/loader.conf Now that we have the space for it, download the FreeBSD sources and create your own kernel configuration with the device dcons removed, which seems to cause the trouble (at least for me). I don’t know why. I just found a discussion where someone mentioned that this might be the problem.


Jul 13

ZFS on FreeBSD

This posting is a small wrap-up of how I set-up ZFS on my FreeBSD 7 installation. I am currently in the process of installing FreeBSD 7 on a Compaq 6710b. A complete report about this will follow in a future posting, but this posting is not about the hardware side.

Sun’s file system ZFS has a lot of cool features. If you understand german, you can listen to Chaosradio Express episode 49 to learn more about it.

DISCLAIMER: Disk related things always put your data at risk. So keep back-ups of your data. Following anything here might destroy your hard disk (or the data on it). I cannot take any responsibility for that. I only write up what worked for me.

I like to use disk encryption. ZFS in theory supports encryption, but it is not really available for FreeBSD, yet. So I set up GEOM Based Disk Encryption as described in the disk encryption chapter of the FreeBSD handbook. Using geli should work just as fine.

Instead of creating a file system with newfs (step 5) I set up the zfs pool using the geom device.

# zpool create tank ad4s1c.bde

Note that step 6 and 7 are not applicable in this set-up.

I used some tips from the FreeBSD ZFS quick start guide, like enabling compression on the ports tree, except the distfiles directory. However, I choose to have a plain old UFS root partition and normal swap partition. Fortunately, you can use one ZFS partition and still have different subdirectories in your root that use this ZFS partition without using symlinks.

# zfs create tank/usr
# zfs create tank/usr/ports
# zfs set compression=gzip tank/usr/ports
# zfs create tank/usr/ports/distfiles
# zfs set compression=off tank/usr/ports/distfiles
# zfs create tank/tmp
# zfs create tank/var

Once everything has been copied to the right place, I set the mountpoints.

# zfs set mountpoint=/usr tank/usr
# zfs set mountpoint=/var tank/var
# zfs set mountpoint=/tmp tank/usr

To have everything set-up at boot time, I added the following to /etc/rc.conf:

gbde_autoattach_all="YES"
gbde_devices="ad4s1c"
gbde_lockdir="/etc/gbde"
zfs_enable="YES"

Furthermore, I made sure that ad4s1c is not mentioned anymore in the fstab (neither ad4s1c.bde). It was still there because I created the partition with the installer.

Note that gbde is handled before ZFS during start-up. Hence, there is no problem having ZFS in an encrypted container like this. The same holds when you use geli.

I wanted to copy some data from a Linux machine (the former installation) to a FreeBSD machine (the new installation) using an external USB disk. You can use ZFS with fuse on linux. So, in principal, that should have worked something like this:

--- On the source machine:
# zpool create ext sdb1
# cp -r * /ext/
# zpool export ext
--- On the target machine
# zpool import ext
# cp -r /ext/* .

Unfortunately, I got a message that the pool was created with a newer ZFS version. So I tried to attach the disk on my iBook where Linux is installed, but while tryin to install fuse ZFS I had to realize that only amd64, x86, and sparc64 are supported by ZFS on fuse.

So far this set-up works just fine. I will see how useful thing like snapshots are.