Automating Generation of the mfsroot.gz
NOTE: This post is old. I no longer generate mfsroots in this fashion and my methods continue to evolve, but my latest approach to modifying mfsroots is documented in the Installing FreeBSD via Cobbler post.
Automating Generation of the mfsroot.gz
Like the crunchgen(1) script, this is a starting point only that makes many assumptions. In fact, while writing this I have come up with plans to combine them.
I store an uncompressed mfsroot.gz file in subversion. It is stored in a manner that includes the files in stand/. You may or may not be aware that this is where the boot_crunch file exists. It also happens to be the location of all the hardlinks that point back to the boot_crunch inode. Subversion stores these as if they were all binaries. Below you will see that I programmatically restore the hardlinks.
A side effect of this management structure is that generating the same mfsroot.gz multiple times results in differing file sizes each time despite using the same content. I’m not 100% sure, but believe that this is due to the file metadata being different each time the content is exported from the subversion repo.
NOTE: The URL in the script below is a dummy URL. The real URL is internally hosted with no connectivity to the Internet.
#! /bin/sh # # mkmfsroot.sh: Generate a FreeBSD mfsroot file. This script does not # function on hosts that are not running FreeBSD. # # Author: Rick # Date: 21 August 2012 # Modif: ##### ## Variables ##### SELF="${0##*/}"; NULL="/dev/null"; TMP="/tmp"; PATH="${PATH}"; ZERO="/dev/zero"; MFSROOTDIR="${1}"; SVNREPO="https://dummy.subversion.com/ats/ad/freebsd-prov/mfsroot/${MFSROOTDIR}"; REPODIR="/data/freebsd-prov/mfsroot/${MFSROOTDIR}"; MFSROOTSTAND="${REPODIR}/stand"; SVN="/usr/local/bin/svn"; STANDFILES="doconfig.sh install.cfg boot_crunch"; MFSROOT="/tmp/mfsroot"; MFSROOTMNT="/tmp/mfsrootmnt"; ##### ## Functions ##### # Clean what this script has done CLEANUP() { [ "${MDCFG}" = "True" ] && mdconfig -d -u ${DEVNUM}; if [ -d ${MFSROOTMNT} ]; then cd && umount ${MFSROOTMNT} > ${NULL} 2>&1; rm -rf ${MFSROOTMNT}; fi [ -e ${MFSROOT} ] && rm ${MFSROOT}; [ -e ${REPODIR} ] && rm -rf ${REPODIR}; } # We are erroring and exiting the script ERR_EXIT() { echo "${1}" 1>&2; CLEANUP; exit 1; } ##### ## Main ##### # Force two command line arguments [ -z ${1} ] && ERR_EXIT "Specify an mfsroot!"; # Verify we're on a FreeBSD host HOSTOS=`uname -s`; [ ${HOSTOS} != "FreeBSD" ] && ERR_EXIT "Build host is not FreeBSD! Exiting!"; # Ensure we're using the latest repo version [ -d ${REPODIR} ] && rm -rf ${REPODIR}; ${SVN} export ${SVNREPO} ${REPODIR}; # Does the mfsroot dir we asked for exist? [ ! -d ${REPODIR} ] && ERR_EXIT "${1} does not exist! Exiting!"; # Remove .svn if it exists [ -d ${REPODIR}/.svn ] && rm -rf ${REPODIR}/.svn # Fix stand/ # # What do we do here and why? # # Since the contents of the mfsroot are stored in subversion, when the content # is checked out or exported, each of the files in stand/ are stored as # individual files when they are actually supposed to be hardlinks back to # boot_crunch. Therefore, the code below checks out the content, removes the # files in stand/, and exports boot_crunch and a couple other scripts. It # finishes by regenerating all the hardlinks back to boot_crunch. # # We could resolve this in any number of ways, but as a stop gap measure for # now we will leave it this way and fix it in the future. if [ -d ${MFSROOTSTAND} ]; then rm ${MFSROOTSTAND}/*; for ea in ${STANDFILES}; do ${SVN} export ${SVNREPO}/stand/${ea} ${MFSROOTSTAND}; done cd ${MFSROOTSTAND}; for i in $(./boot_crunch 2>&1 | grep -v usage); do if [ "$i" != "boot_crunch" ]; then rm -f ./"$i"; ln ./boot_crunch "$i"; fi done fi # Calculate the size of the mfsroot, prepare the file and populate it MFSROOTSIZE=$(du -sk ${REPODIR} | awk '{ print $1 }'); [ -z ${MFSROOTSIZE} ] && ERR_EXIT "Unable to determine mfsroot size! Exiting!"; dd if=${ZERO} of=${MFSROOT} bs=1024 count=$((${MFSROOTSIZE} + 1024)); [ $? -ge 1 ] && ERR_EXIT "Unable to create ${MFSROOT}! Exiting!"; MFSROOTDEV=$(mdconfig -f ${MFSROOT}); [ -z ${MFSROOTDEV} ] && ERR_EXIT "Unable to mount ${MFSROOT}! Exiting!"; DEVNUM=$(echo ${MFSROOTDEV} | awk '{ print substr($0,length($0)-0,length($0)) }' ); MDCFG="True"; newfs /dev/${MFSROOTDEV}; [ ! -d ${MFSROOTMNT} ] && mkdir ${MFSROOTMNT}; mount /dev/${MFSROOTDEV} ${MFSROOTMNT} && \ (cd ${REPODIR}; tar -cf - .) | (cd ${MFSROOTMNT}; tar -xf -); # Now the new mfsroot has been populated, it's time to save it and clean up umount ${MFSROOTMNT} && mdconfig -d -u ${DEVNUM} && gzip ${MFSROOT}; [ $? -ne 0 ] && ERR_EXIT "Error unmounting ${MFSROOT}! Exiting!"; MDCFG="False"; CLEANUP; ##### ## EOF #####