OpenBSD edition. A script that will produce a copy of an original disk, will run installboot(8) on it and replace the disk UID in /etc/fstab with the clone’s UID. The purpose is to produce a copy of the current system disk ready to replace it by simply changing the boot device or replacing the original in case of failure.
Intended for live disks, an alternative to cloning a disk in OpenBSD using dd.
EXECUTING IT WITHOUT UNDERSTANDING WHAT IT DOES WILL LIKELY RESULT IN DATA LOSS, HAIR LOSS, LOSS OF FLOSS AND POSSIBLE OTHER KINDS OF LOSS.
There are two failsafes built in, firstly it won’t do anything unless run with ‘yarly’ as the first parameter, second it will ask for confirmation warning that all data on the destination disk will be lost, unless run with ‘cron’ as the second parameter.
It’s not ready to run as-is, variables will need to be properly set before executing.
It will erase MBR and all partitioning data on DST_DISK and will copy the scheme from SRC_DISK over. My disks are the same size, DST_DISK can’t be of lower size than SRC_DISK, obviously, if it’s bigger some space will be left unused. After that it will run newfs on DST_DISK on all partitions of type 4.2BSD, everything else, like swap, will be left untouched. Each such partition will be mounted to DST_MOUNT and the data from the corresponding partition on SRC_DISK will be dumped over. If the current partition is the root partition and after dumping/restoring there’s /boot on it, the script will run installboot(8). If there’s an /etc/fstab file, it will replace all instances of SRC_DISK’s duid in it with DST_DISK’s. This should leave the disc ready to be booted with a working system on it.
Most commands the script runs are redirected to STDOUT, changing this variable to /dev/null will make the commands basically silent, but stderr is not redirected, which is probably a good thing.
It is entirely possible that something will go amiss with non-standard partitioning schemes, it does make a few assumptions, like /etc being part of / for example, or that the partitions to be backed up are already mounted.
#!/bin/sh
# makes a few assumptions, standard mount points
# will run installboot to write the boot sector
# will modify /etc/fstab on root partition,
# replacing source disk UID with DST_DISK
# CHANGE THESE VARIABLES
SRC_DISK="sd0" # disk to be cloned
DST_DISK="sd2" # disk to clone *to*
DST_MOUNT="/mnt/clonebak" # where the partition written to will be mounted
# make it /dev/stdout to see details, /dev/null to hide them
STDOUT="/dev/stdout"
# don't run unless 'yarly' is passed as parameter
if [[ $1 != 'yarly' ]] ; then
echo 'orly?'
exit
fi
# if not run with 'cron' as the second parameter, ask for confirmation
if [[ $2 != 'cron' ]] ; then
print -n "This will destroy everything on *$DST_DISK*. Are you sure? (yes/no) "
read sure
if [[ $sure != 'yes' ]] ; then
exit
fi
fi
# create mountpoint for clone
echo "creating $DST_MOUNT..."
mkdir -p $DST_MOUNT > $STDOUT
if [[ $? != 0 ]] ; then
echo " $DST_MOUNT creation failed, aborting"
exit
fi
# can dump only if partition is mounted, any partition that isn't will be
# skipped if there's no mounted source partition, no reason to do anything
mount | grep /dev/$SRC_DISK > $STDOUT
if [[ $? != 0 ]] ; then
echo "no partitions on $SRC_DISK seem to be mounted, aborting"
exit
fi
# make sure destination disk isn't mounted anywhere,
# or script will fail unpredictably
mount | grep /dev/$DST_DISK > $STDOUT
if [[ $? != 1 ]] ; then
echo "looks like $DST_DISK is mounted, or there was an error, aborting"
exit
fi
# make sure the mount point is free
mount | grep $DST_MOUNT
if [[ $? != 1 ]] ; then
echo "$DST_MOUNT is mounted or there was an error, aborting"
exit
fi
# real work starts here
# copy partitioning scheme to clone
echo "reinitializing $DST_DISK..."
fdisk -iy $DST_DISK > $STDOUT
disklabel -d $DST_DISK > $STDOUT
echo "partitioning..."
disklabel $SRC_DISK > - | (disklabel -R $DST_DISK -)
# get a list of all partitions on SRC_DISK and iterate through them
PARTITIONS=`disklabel $SRC_DISK | grep ^\ \ [a-p] | sed s/\:.*// | sed s/\ \ //`
for i in $PARTITIONS; do
PARTINFO=`disklabel $SRC_DISK | grep \ \ $i\:`
if [[ $PARTINFO != *4.2BSD* ]] ; then
continue
fi
MOUNTDIR=`disklabel $SRC_DISK | grep \ \ $i\: | sed s/^.*#\ //`
# before being able to dump a partition we need to check if it's
# mounted and where. If it isn't, skip it, don't try to mount it,
# there might be a reason why it's down
echo "checking $SRC_DISK$i mount point..."
mount | grep \/dev\/$SRC_DISK$i > $STDOUT
if [[ $? != 0 ]] ; then
# not mounted, don't dump
echo " $SRC_DISK$i isn't mounted, won't be backed up"
continue
fi
SRC_MOUNT=`mount | grep \/dev\/$SRC_DISK$i | sed s/^.*$SRC_DISC$i\ on\ // | sed s/\ type\ .*//`
echo " ..found $SRC_MOUNT"
# install a new file system and mount it
echo "formatting $DST_DISK$i..."
newfs $DST_DISK$i > $STDOUT
echo "mounting /dev/$DST_DISK$i to $DST_MOUNT..."
mount /dev/$DST_DISK$i $DST_MOUNT > $STDOUT
if [[ $? != 0 ]] ; then
echo " mount failed, skipping $DST_DISK$i"
continue
fi
# finally, the backup
echo "dumping $SRC_MOUNT onto $DST_MOUNT..."
/sbin/dump -0au -f - $SRC_MOUNT | ( cd $DST_MOUNT ; /sbin/restore -rf - )
# if we just dumped the root partition
# let's install boot sector
if [[ $SRC_MOUNT = "/" && -f $DST_MOUNT/boot ]] ; then
echo "writing boot on $DST_DISK..."
/usr/mdec/installboot $DST_MOUNT/boot /usr/mdec/biosboot $DST_DISK > $STDOUT
fi
# and change duid in fstab
if [[ $SRC_MOUNT = "/" && -f $DST_MOUNT/etc/fstab ]] ; then
echo "replacing duid in $DST_MOUNT/etc/fstab..."
# make a copy of fstab, run sed on the copy with output to original
cp $DST_MOUNT/etc/fstab $DST_MOUNT/etc/fstab.bak
SRC_DUID=`disklabel $SRC_DISK | grep ^duid: | sed s/duid\:\ //`
DST_DUID=`disklabel $DST_DISK | grep ^duid: | sed s/duid\:\ //`
sed s/$SRC_DUID/$DST_DUID/ $DST_MOUNT/etc/fstab.bak > $DST_MOUNT/etc/fstab
fi
umount $DST_MOUNT > $STDOUT
if [[ $? != 0 ]] ; then
echo "there was an error unmounting $DST_MOUNT, aborting"
exit
fi
done