A while ago, I'd had to resize the root partition of my FreeBSD boxes from 128MB to 512MB, as the size of the kernel modules (complete with symbols), new kernel and old kernel together, exceeded the 128MB available. With FreeBSD 8.0 now released, I read that even the 512MB was not enough now, and 1GB was required for root.
Rather than repeat the manual process I'd used before, I wrote a
shell script to automate the back-up, repartition and restore
process. This worked well on crimson and chrome (both hosted on the
same machine, so I could boot from one and work on the other's
disk), but required some more attention when I had to repartition
topaz, the Toshiba laptop. Here, I used a USB CD drive to boot the
FreeBSD LiveFS CD, to allow me to wreak havoc on the hard drive.
However, it seemed that there are some hardware problems with the CD
drive, such that I was getting CD read errors during the kernel
probing of the disk drives. The kernel still booted OK, but when I
entered fixit mode and tried a simple ls
command, the shell
complained about ld-elf.so
, the error message being strewn
with garbage characters. Trying different CDs didn't help, even
when the CD was written on a different burner.
In the end, I decided to try and use the /rescue
binaries,
which are statically linked. This is possible by prepending the
PATH
environment variable with the location of the
/rescue
directory:
# PATH=/mnt2/rescue:$PATH # export PATH
The /rescue
binaries seemed to work, so I was able to
proceed with mounting a remote disk to hold the backup data:
# ifconfig fxp0 inet 192.168.0.12 up # mkdir /tank # mount -t nfs 192.168.0.10:/tank /tank
There was one remaining problem with the script; it used
grep
to identify if the slice being processed had
partitions already mounted. grep
does not exist in
/rescue
. Instead I used expr
, since this also has
the capability to perform regexp matching in strings. Once that
change was made, I was able to let the resizing run its automated
course.
Here's the script. It is available for download.
#!/bin/sh # # NAME # resize-parts.sh - Script to automate resizing of FreeBSD partitions # within a slice # # SYNOPSIS # sh resize-parts.sh [-b] [-l] [-r] [-m mnt] [-d dir] [-f label] target # # Switches: # -b backup partition data # -l write new slice label for partitions (via bsdlabel) # -r restore partition data (presumably from a previous invocation # of resize-part.sh -b -l) # -d dir set location for backup tar files # -m mnt set mount point for slices during backup and restore. # Default is /mnt # -f label use pre-defined bsdlabel input file. If -f is not specified, # bsdlabel will be invoked in edit mode (-e) to allow the # interactive editing of the partition table. # # target is one of chrome, crimson or topaz. The script defines # the root slice and the expected mapping between partition and # mount point. # # If -b, -l and -r are omitted, all are assumed. # # DESCRIPTION # Automates backup, re-labelling, with different sizes, and # restoring of FreeBSD partitions. Assumes knowledge of bsdlabel # syntax, and that a suitably sized directory is available for # backup on /tank. # # If a pre-defined bsdlabel file is provided via the -f switch, the # script assumes that interactive control is not required. No # prompts are issued before critical actions (i.e. writing new label # and formatting partitions/restoring data). # # Backup directory is assumed to be /tank/${target}, unless changed # with the -d switch. # # # MPW 20100130 SCRIPT=${0##*/} backup=0;label=0;restore=0 chkmount=1 BACKUPROOT=/tank TEMPMNT="/mnt" LABELFILE="" # function to prompt user to continue continuewith() { echo -n "$SCRIPT: $1 OK to continue? (y/n): " read cont if [ $cont != "y" ]; then echo "${SCRIPT}: Terminated." exit 1 fi } # exit script with message die() { echo "${SCRIPT}: $1" exit 1 } while [ $# -gt 1 ]; do case $1 in -b) backup=1 ;; -l) label=1 ;; -r) restore=1 ;; -d) BACKUPROOT=$2 shift ;; -f) LABELFILE=$2 shift ;; -m) TEMPMNT=$2 shift ;; *) die "${SCRIPT}: unrecognised switch: $1. \ Usage: sh resize-parts.sh [-b] [-l] [-r] [-m mnt] [-d dir] \ [-f label] target" ;; esac; shift done if [ ${backup} -eq 0 -a ${label} -eq 0 -a ${restore} -eq 0 ]; then backup=1 label=1 restore=1 fi TARGET=$1 case ${TARGET} in chrome) SLICE="ad3s1" PARTS="a=root d=var e=tmp f=usr g=home" ;; crimson) SLICE="ad0s1" PARTS="a=root d=tmp e=var f=usr g=home h=rep" ;; topaz) SLICE="ad0s1" PARTS="a=root d=usr e=tmp f=var g=home h=rep" ;; *) die "illegal (or no) target specified." ;; esac BACKUPDIR="${BACKUPROOT}/${TARGET}" if [ ! -d ${BACKUPDIR} ]; then mkdir -p ${BACKUPDIR} if [ $? -ne 0 ]; then die "unable to create backup directory ${BACKUPDIR}." fi fi # if label file provided, check it is readable if [ "$LABELFILE" != "" -a ! -r ${LABELFILE} ]; then die "unable to read label file." fi # check no part of slice is mounted # use expr as it is available in /rescue (grep is not) mnts=$(expr "/`mount`" : ".*${SLICE}") if [ ${mnts} -ne 0 ]; then die "one or more partitions of ${SLICE} are mounted." fi # backup partition data if [ ${backup} -eq 1 ]; then for part in $PARTS do partid=${part%=*} mntpt=${part#*=} mount -t ufs /dev/${SLICE}${partid} ${TEMPMNT} if [ $? -ne 0 ]; then die " unable to mount ${SLICE}${partid} on ${mntpt}." fi echo "${SCRIPT}: backing up ${SLICE}${partid} to ${BACKUPDIR}/${mntpt}.tar.gz..." tar czf ${BACKUPDIR}/${mntpt}.tar.gz -C ${TEMPMNT} . if [ $? -ne 0 ]; then die "error backing up data." fi umount ${TEMPMNT} done fi echo "${SCRIPT}: backup directory:" ls -l ${BACKUPDIR}/*.tar.gz if [ ${label} -eq 1 ]; then if [ "${LABELFILE}" = "" ]; then # interactive; check ok to continue? continuewith "invoking bsdlabel (better know vi!)." # modify partition sizes as required (interactive) bsdlabel -e ${SLICE} else echo "${SCRIPT}: partitioning ${SLICE} with contents of ${LABELFILE}" bsdlabel -R ${SLICE} ${LABELFILE} fi if [ $? -ne 0 ]; then die "bsdlabel failed." fi fi if [ ${restore} -eq 1 ]; then if [ "${LABELFILE}" = "" -a ${label} -eq 1 ]; then # ok to continue? only asked if bsdlabel step has been run interactively continuewith "formatting partitions and restoring." fi # format new partitions and restore data for part in ${PARTS} do partid=${part%=*} mntpt=${part#*=} echo "${SCRIPT}: formatting ${SLICE}${partid}..." newfs -L ${TARGET}${mntpt} /dev/${SLICE}${partid} if [ $? -ne 0 ]; then die "unable to format the filesystem: ${SLICE}${partid}." fi mount -t ufs /dev/${SLICE}${partid} ${TEMPMNT} if [ $? -ne 0 ]; then die "unable to mount new formatted filesystem: ${SLICE}${partid}." fi echo "${SCRIPT}: restoring ${BACKUPDIR}/${mntpt} to ${SLICE}${partid}..." tar xzpf ${BACKUPDIR}/${mntpt}.tar.gz -C ${TEMPMNT} if [ $? -ne 0 ]; then umount ${TEMPMNT} die "unable to restore ${mntpt} files to ${SLICE}${partid}." fi umount ${TEMPMNT} done fi
Here's an example of a bsdlabel file. This is the version for topaz:
# /dev/ad0s1: 8 partitions: # size offset fstype [fsize bsize bps/cpg] a: 1G 16 4.2BSD 2048 16384 8 # root b: 512M * swap # swap (natch) c: 156301425 0 unused 0 0 # "raw" part, don't edit d: 9G * 4.2BSD 2048 16384 28552 # usr e: 512M * 4.2BSD 2048 16384 8 # tmp f: 1G * 4.2BSD 2048 16384 28552 # var g: 20G * 4.2BSD 2048 16384 28552 # home h: * * 4.2BSD 2048 16384 28552 # rep
The asterisk in the size column of the last row means that rep gets the remaining disk space in that slice (partition).