FreeBSD partition resizing required
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).