#!/bin/sh # This script generates FreeBSD images for OpenNebula, being heavily inspired # from srht's FreeBSD build image definition. It assumes running on a FreeBSD host. # ZFS installation as documented by the FreeBSD project # https://wiki.freebsd.org/RootOnZFS/GPTZFSBoot set -e set -x # XXX: Handle command-line arguments? RELEASE=13.1-RELEASE ARCH=amd64 IMAGE_PATH_ZFS=freebsd-zfs-$RELEASE-$(date -I).img.qcow2 IMAGE_PATH_UFS=freebsd-ufs-$RELEASE-$(date -I).img.qcow2 IMAGE_SIZE=10G DIST_BASE="https://download.freebsd.org/ftp/releases/$ARCH/$RELEASE" CLOUDSETUP_COMMIT=4ac15b8647d5525048c5faa5fd4b28491905d000 CLOUDSETUP_URL="https://git.sr.ht/~jornane/cloudsetup/archive/$CLOUDSETUP_COMMIT.tar.gz" ZPOOL=zroot ZPOOL_TMP="zinstalling" ZFSTARGET="$(mktemp -d /var/tmp/zfsbuild.XXXXX)" UFSTARGET="$(mktemp -d /var/tmp/ufsbuild.XXXXX)" CLOUDSETUP_WORK="$(mktemp -d /var/tmp/cloudsetup.XXXXX)" if zpool list -Ho name $ZPOOL_TMP 2>/dev/null; then echo "The pool $ZPOOL_TMP is already imported." >&2 exit 1 fi cleanup() { sync ||: umount "$UFSTARGET/dev" ||: umount "$UFSTARGET/tmp" ||: umount "$UFSTARGET/var/tmp" ||: umount "$UFSTARGET" ||: zpool export $ZPOOL_TMP ||: mdconfig -du md0 ||: mdconfig -du md1 ||: rm -rf "$CLOUDSETUP_WORK" ||: rmdir "$ZFSTARGET" ||: rmdir "$UFSTARGET" ||: } trap cleanup EXIT if [ "$(whoami)" != 'root' ]; then echo "This script must be run as root." >&2 exit 1 fi if ! command -v rsync >/dev/null then env ASSUME_ALWAYS_YES=YES pkg install -y rsync fi if ! command -v qemu-img >/dev/null then env ASSUME_ALWAYS_YES=YES pkg install -y qemu-tools fi fetch -qo- "$CLOUDSETUP_URL" | tar -C "$CLOUDSETUP_WORK" --strip-components 1 -xzf- ufsdisk=$(mktemp /var/tmp/ufsdisk.XXXXX) truncate -s 6G $ufsdisk mdconfig -a -t vnode -f $ufsdisk -u md1 gpart create -s gpt /dev/md1 gpart add -t freebsd-boot -l bootfs -b 40 -s 512K md1 gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 md1 gpart add -t freebsd-ufs -l rootfs -b 1M -s 5G md1 newfs -U /dev/md1p2 # Mount allocated image. mount /dev/md1p2 "$UFSTARGET" # Allocate and partition/format disk image. # We use "legacy boot", aka BIOS boot # Preferably, we'd use EFI boot here, check the FreeBSD wiki link in the header # to see how to make that change, but make the EFI partition larger zfsdisk=$(mktemp /var/tmp/zfsdisk.XXXXX) truncate -s 6G $zfsdisk mdconfig -a -t vnode -f $zfsdisk -u md0 gpart create -s gpt /dev/md0 gpart add -t freebsd-boot -l bootfs -b 40 -s 512K md0 gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 md0 gpart add -t freebsd-zfs -l zfs0 -b 1M -s 5G md0 zpool create -O compression=on -o ashift=12 -o altroot="$ZFSTARGET" -m none -t $ZPOOL_TMP $ZPOOL md0p2 zfs create -o mountpoint=none $ZPOOL_TMP/ROOT # We set zstd-19 so our image will become smaller, at the cost of a longer build time. # At the end of the process, we disable zstd-19 again using zfs inherit compression, # but all files already written will remain zstd-19 compressed zfs create -o mountpoint=/ -o canmount=noauto $ZPOOL_TMP/ROOT/default mount -t zfs $ZPOOL_TMP/ROOT/default "$ZFSTARGET" zpool set bootfs=$ZPOOL_TMP/ROOT/default $ZPOOL_TMP zfs create -o mountpoint=/tmp -o exec=on -o setuid=off $ZPOOL_TMP/tmp zfs create -o canmount=off -o mountpoint=/usr $ZPOOL_TMP/usr zfs create $ZPOOL_TMP/usr/home zfs create -o exec=off -o setuid=off $ZPOOL_TMP/usr/src zfs create -o mountpoint=/usr/ports -o setuid=off $ZPOOL_TMP/usr/ports zfs create -o canmount=off -o mountpoint=/var $ZPOOL_TMP/var zfs create -o exec=off -o setuid=off $ZPOOL_TMP/var/audit zfs create -o exec=off -o setuid=off $ZPOOL_TMP/var/crash zfs create -o exec=off -o setuid=off $ZPOOL_TMP/var/log zfs create -o atime=on -o exec=off -o setuid=off $ZPOOL_TMP/var/mail zfs create -o exec=on -o setuid=off $ZPOOL_TMP/var/tmp ln -s /usr/home "$ZFSTARGET/home" chmod 1777 "$ZFSTARGET/var/tmp" chmod 1777 "$ZFSTARGET/tmp" # Mount dev in chroot mkdir -p "$UFSTARGET/dev" mount -t devfs devfs "$UFSTARGET/dev" # Download and extract base system. dist_files="kernel.txz base.txz" dist_dir="/usr/freebsd-dist/$ARCH/$RELEASE" mkdir -p "$dist_dir" "$UFSTARGET" for f in $dist_files do fetch -m -o "$dist_dir/$f" "$DIST_BASE/$f" tar -C "$UFSTARGET" -xJf "$dist_dir/$f" done # Avoid writing temporary files while building mount_nullfs /tmp "$UFSTARGET/tmp" mount_nullfs /var/tmp "$UFSTARGET/var/tmp" # Install the first-boot script that configures the network and ssh key make -C "$CLOUDSETUP_WORK/" PREFIX="$UFSTARGET/usr/local" install # Configure new system. printf '# Device\tMountpoint\tFStype\tOptions\t\tDump\tPass#\n' >"$UFSTARGET/etc/fstab" touch "$UFSTARGET/firstboot" sysrc -f "$UFSTARGET/boot/loader.conf" \ zfs_load=YES \ autoboot_delay=-1 \ sysrc -f "$UFSTARGET/etc/rc.conf" \ ntpd_enable=YES \ sshd_enable=YES \ growfs_enable=YES \ hostname=freebsd \ firstboot_cloudsetup_enable=YES \ # The resolv.conf file is written by firstboot_cloudsetup #cp /etc/resolv.conf "$UFSTARGET/etc/resolv.conf" tzsetup -s -C "$UFSTARGET" UTC cat >>"$UFSTARGET/etc/ssh/sshd_config" <>"$UFSTARGET/etc/fstab" sync ||: zfs inherit compression $ZPOOL_TMP/ROOT/default trap : EXIT cleanup mkdir -p "$ARCH" qemu-img convert -f raw -O qcow2 "$zfsdisk" "$ARCH/$IMAGE_PATH_ZFS" qemu-img convert -f raw -O qcow2 "$ufsdisk" "$ARCH/$IMAGE_PATH_UFS" rm "$zfsdisk" "$ufsdisk" # Filesystem will be enlarged by growfs(7) on next startup qemu-img resize "$ARCH/$IMAGE_PATH_ZFS" "$IMAGE_SIZE" qemu-img resize "$ARCH/$IMAGE_PATH_UFS" "$IMAGE_SIZE"