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