Skip to content

Using dattobd as a Backup System

Homer edited this page Oct 21, 2020 · 25 revisions

Introduction

These are step-by-step instructions for implementing the dattobd driver and tools as a simple backup system. The base system used is Gentoo, but this could be adapted to work on any GNU/Linux distro.


Note

I've attempted to make these steps as programmatic as possible, however please review them to ensure they actually work on your specific system, as each is different. You may need to adapt them to your circumstances.


Table of Contents


Compiling

mkdir ~/tmp
cd ~/tmp
equery l dev-vcs/git || emerge -av dev-vcs/git
git clone https://github.com/datto/dattobd
cd dattobd
cf="$(grep "CFLAGS" Makefile |\
   cut -d" " -f4-) $(grep "^CFLAGS" /etc/portage/make.conf |\
   cut -d'"' -f2)"
sed -i "/CCFLAGS =/c\export CCFLAGS = $cf" Makefile
sed -i "s/usr\/local/usr/" Makefile
make all
strip --strip-debug src/dattobd.ko
strip --strip-unneeded app/dbdctl
strip --strip-unneeded utils/update-img
strip --strip-unneeded lib/libdattobd.so.0


Installing

make install
mkdir -p /lib/modules/$(uname -r)/misc
cp src/dattobd.ko /lib/modules/$(uname -r)/misc
depmod -a
bzip2 -9 doc/*.8
cp doc/*.bz2 /usr/share/man/man8


Custom initramfs

mkdir -p ~/tmp/initramfs
cd ~/tmp/initramfs
myinitramfs=$(cat /boot/grub/grub.conf |\
   grep initrd |\
   grep "$(uname -r)" |\
   cut -d'/' -f2)
mycompressor=$(file "/boot/$myinitramfs" |\
   grep -o ":.*compressed" |\
   cut -d' ' -f2)
$mycompressor -cd "/boot/${myinitramfs}" | cpio -i -d -H newc --no-absolute-filenames
mkdir -p ./lib/modules/$(uname -r)/misc
cp ~/tmp/dattobd/src/dattobd.ko ./lib/modules/$(uname -r)/misc
depmod -b . $(uname -r)
echo "dattobd" > ./etc/modules/dattobd
cp ~/tmp/dattobd/lib/libdattobd.so.0 ./usr/lib
cd ./usr/lib
ln -s libdattobd.so.0 libdattobd.so
cd ../..
cp ~/tmp/dattobd/app/dbdctl ./usr/bin
myroot=$(mount | grep "on / " | cut -d' ' -f1)
sed -i '/mopts="\$.mopts.,\$.REAL_ROOTFLAGS."/a \ \ \ \ good_msg "Starting Datto CBT"\n\ \ \ \ modprobe dattobd\n\ \ \ \ dbdctl reload-snapshot $myroot /.datto1 0' ./etc/initrd.d/00-rootdev.sh
find . -print0 | cpio --null -ov --format=newc | $mycompressor -9 > /boot/${myinitramfs}


$EDITOR /etc/init.d/datto_sync

#!/sbin/openrc-run
# $Id$

imagefile="/mnt/backup/system.dd"

depend() {
 use logger
 need localmount
}

start() {
 einfo "Starting Datto CBT"
 update-img /dev/datto0 /.datto0 "$imagefile"
 rm -f /.datto0
 dbdctl transition-to-incremental 0
}

stop() {

 dattomode=$(cat /proc/datto-info | \
       grep state | \
    sed 's/^.*: //')

 if [ $dattomode -eq 3 ]
  then
    einfo "Datto CBT is already in snapshot mode"
    exit 0
 fi

 newcow=0
 oldcow=$(cat /proc/datto-info | \
  grep cow_file | \
  grep -o -e "datto." | \
  sed 's/datto//')
 if [ $oldcow -eq 0 ]
  then
  newcow=1
 fi

 if [ $newcow -eq 0 ]
  then
    dbdctl transition-to-snapshot /.datto${newcow} 0
    update-img /dev/datto0 /.datto${oldcow} "$imagefile"
    rm -f /.datto${oldcow}
    dbdctl transition-to-incremental 0
    dbdctl transition-to-snapshot /.datto${oldcow} 0
  else
    dbdctl transition-to-snapshot /.datto${newcow} 0
 fi
 einfo "Datto CBT is now in snapshot mode"
}


chmod +x /etc/init.d/datto_sync
rc-update add datto_sync default


Preparing the system for first use

equery l sys-apps/bar || emerge -av sys-apps/bar
myroot=$(mount | grep "on / " | cut -d' ' -f1)
imagesize=$(df -h |\
   grep $myroot |\
   sed "s~^$myroot *~~" |\
   cut -d' ' -f1 |\
   tr '[:upper:]' '[:lower:]')
imagefile="/mnt/backup/system.dd"
dbdctl setup-snapshot $myroot /.datto0 0
dd if=/dev/datto0 ibs=4k | bar -s ${imagesize} | dd of=$imagefile obs=4k
dbdctl transition-to-incremental 0
dbdctl transition-to-snapshot /.datto1 0
rc-service datto_sync start


Notes

That's it.

From that point on, the system will automatically do an incremental backup of the root device every time you reboot or shut down.

Be aware that both the initramfs and system initscript expect the dattobd device to be in snapshot mode, and the current COW to be named "/.datto1" (specifically numbered "1"), or else it won't work, you will lose your snapshot and have to start again with a new full backup.

However, the initscript is designed to ensure that this is set correctly during shutdown, regardless of the current state of the driver or the current numbering of the COW (as long as it is currently in active incremental or snapshot state at the time, which it should be unless you've manually intervened).

If you want to perform manual incremental backups during each session (perhaps you rarely reboot), then you can use the following script:

$EDITOR /usr/local/sbin/datto_incremental_backup


#!/bin/sh

source_uuid="c0de1f3d-ecf8-4326-9417-907fd081475b"
mountdir="/mnt/backup"
imagefile="/mnt/backup/system.dd"

# Test source
if [ ! -b "/dev/disk/by-uuid/${source_uuid}" ]
 then
   echo "Error source device \"$source_uuid\" does not exist"
   exit 1
fi

# Test destination
mount | grep -o "$mountdir" >/dev/null 2>&1
if [ $? -ne 0 ]
 then
   mount "$mountdir" >/dev/null 2>&1
   if [ $? -ne 0 ]
    then
   echo "Error: backup storage \"$mountdir\" is not mounted"
   exit 1
   fi
elif [ ! -e "$imagefile" ]
 then
   echo "Error: backup image file \"$imagefile\" does not exist"
   exit 1
fi

file "$imagefile" | \
   sed -e 's/.*UUID=//' -e 's/,.*//' | \
   grep "$source_uuid" >/dev/null 2>&1
if [ $? -ne 0 ]
 then
   echo "Error: \"$imagefile\" is not a backup of \"$source_uuid\""
   exit 1
fi

# Test CBT
if [ ! -e /proc/datto-info ]
 then
   echo "Error: Datto CBT is not running"
   exit 1
elif [ $(($(cat /proc/datto-info | grep state | cut -d':' -f2)-0)) -ne 2 ]
 then
   echo "Error: Datto CBT is not tracking changes"
   exit 1
fi

newcow=0
oldcow=$(cat /proc/datto-info | \
    grep cow_file | \
 grep -o -e "datto." | \
 sed 's/datto//')
if [ $oldcow -eq 0 ]
 then
   newcow=1
fi

dbdctl transition-to-snapshot /.datto${newcow} 0
update-img /dev/datto0 /.datto${oldcow} "$imagefile"
rm -f /.datto${oldcow}
dbdctl transition-to-incremental 0


Then ensure that "source_uuid" is set correctly for the root device:

mydev=$(mount | grep "on / " | cut -d' ' -f1 | cut -d'/' -f3)
myuuid=$(ls -l /dev/disk/by-uuid | grep $mydev | cut -d' ' -f9)
sed -i "s/^source_uuid.*/source_uuid=\"$myuuid\"/" /usr/local/sbin/datto_incremental_backup
chmod +x /usr/local/sbin/datto_incremental_backup


You can now manually run datto_incremental_backup whenever you want to backup, or even add it to cron for periodic automatic backups.