#!/bin/sh # Run as root [ "$(id -u)" -eq 0 ] || exec sudo "${0}" "${@}" cd "$(dirname "${0}")" [ "${1}" = "-n" ] && NO_SYNC=true meh() { printf " \033[1;32m*\033[0m %s\n" "${*}"; } omg() { printf " \033[1;33m*\033[0m %s\n" "${*}"; } wtf() { printf " \033[1;31m*\033[0m %s\n" "${*}"; kill $$; exit 1; } sync="rsync://paka/freebsd-ports" sync_opts="--archive --compress --delete --hard-links --sparse --stats --verbose" zpool="amani" ports=${zpool}/ports upstream=${zpool}/srcs/freebsd/ports/upstream prepare=${zpool}/srcs/freebsd/ports/prepare today="$(date +%Y-%m-%d)" upstream_fs="$(zfs get -H -o value mountpoint "${upstream}")" # Check if target ports tree actually exists; otherwise, assume it's safe to replace if zfs list "${ports}" >&- 2>&- then ports_exists="YES" ports_origin="$(zfs get -H -o value origin "${ports}")" ports_origin="${ports_origin%%@*}" ports_fs="$(zfs get -H -o value mountpoint "${ports}")" # Make sure the ports tree is a descendent of the ports upstream [ "${ports_origin}" = "${upstream}" ] || wtf "Target ${ports} is not a child of ${upstream} (is ${ports_origin})" fi ports_fs="${ports_fs:-/usr/ports}" if [ -z "${NO_SYNC}" ] then # Compute the next available snapshot last_snapshot="$(zfs list -rHt snapshot -o name "${upstream}" | grep "@${today}_" | sort | tail -n 1 | sed -e 's/^.*_\([0-9]\{2\}\)$/\1/')" if [ "${last_snapshot}" ] then snapshot="$(printf "${today}_%02u" "$(( ${last_snapshot} + 1 ))")" echo "${today}_${last_snapshot} -> ${snapshot}" else snapshot="${today}_00" echo "None -> ${snapshot}" fi else # Don't bother creating a new snapshot if the tree will not be synchronized last_snapshot="$(zfs list -rHt snapshot -o name "${upstream}" | sort | tail -n 1)" snapshot="${last_snapshot##*@}" echo "Using existing snapshot ${snapshot}" fi # Bail out if anything is / [ -z "${ports_fs}" ] && wtf "ports_fs is / ?!" [ -z "${upstream_fs}" ] && wtf "upstream_fs is / ?!" [ -z "${ports_fs}" -o -z "${upstream_fs}" ] && wtf "Writing to /? Are you nuts?" if [ -z "${NO_SYNC}" ] then meh "Update" rsync ${sync_opts} "${sync}/" "${upstream_fs}/" || wtf "update failed" meh "Snapshot" zfs snapshot "${upstream}@${snapshot}" || wtf "snapshot failed" fi meh "Clone" zfs clone "${upstream}@${snapshot}" "${prepare}" || wtf "clone failed" zfs set atime=off "${prepare}" || wtf "atime-off failed" # Resolve filesystem mountpoints, now that everything should exist prepare_fs="$(zfs get -H -o value mountpoint "${prepare}")" meh "Overlay" ( cd ports; find * | cpio -p "${prepare_fs}" ) || wtf "overlay failed" meh "Patch" for patch in patch/*.patch do meh "... ${patch}" ( cd "${prepare_fs}"; patch -p0 ) < "${patch}" || wtf "patch failed" done meh "Install" zfs set readonly=on "${prepare}" || wtf "readonly failed" # Half of this is conditional on the old ports tree's existence [ -z "${ports_exists}" ] || zfs rename "${ports}" "${ports}-bak" || wtf "backup rename failed" zfs rename "${prepare}" "${ports}" || wtf "rename prepare -> ports failed" [ -z "${ports_exists}" ] || zfs inherit mountpoint "${ports}-bak" || wtf "inherit failed" zfs set mountpoint="${ports_fs}" "${ports}" || wtf "mountpoint failed" [ -z "${ports_exists}" ] || zfs destroy "${ports}-bak" || wtf "destroy failed" meh "All done"