Added support for handling compressed files
[zfs-ubuntu/.git] / zfs-replicate
1 #!/bin/bash
2
3 # Author: Carl Baldwin & Alan Pippin
4 # Description: This script replicates a given zfs filesystem to a given zfs pool.
5 #              This script will keep all snapshots in sync, removing the ones
6 #              that have been deleted since the last replicate was performed.
7 #              This script will only send the new, or missing, snapshots since
8 #              the last replicate was performed.
9
10 # In test mode (test=1) commands are echoed, not executed
11 test=0
12
13 [ $test == 0 ] && exec >> /var/log/zfs/zfs-replicate.log 2>&1
14
15 # Usage: zfs-backup [filesystem] [destination_pool]
16 # This script has a limitation with children under a given filesystem.
17 # You must initially backup the parent filesystems first using this script
18 # before backing up any of the children filesystems.
19
20 fs=$1
21 fs=${fs%/}
22 fsname=${1#*/}
23 srcpool=${1%%/*}
24 srcfs="${srcpool}/$fsname"
25 srcfs=${srcfs%/}
26 dstpool=$2
27 dstfs="${dstpool}/$srcfs"
28 dstfs=${dstfs%/}
29 nodstsnaps=0
30 common=""
31
32 if [ $test == 1 ]; then
33   echo "fs: $fs"
34   echo "fsname: $fsname"
35   echo "srcpool: $srcpool"
36   echo "srcfs: $srcfs"
37   echo "dstpool: $dstpool"
38   echo "dstfs: $dstfs"
39 fi
40
41 if ! zpool list -H "$srcpool" >/dev/null 2>&1; then
42   echo >&2 "-E- The source pool, '$srcpool' doesn't seem to exist."
43   exit 1
44 fi
45
46 if ! zpool list -H "$dstpool" >/dev/null 2>&1; then
47   echo >&2 "-E- The destination pool, '$dstpool' doesn't seem to exist."
48   exit 1
49 fi
50
51 if ! zfs list -rH -t snapshot "$dstfs" 2>&1 | grep "$dstfs@" > /dev/null 2>&1; then
52   echo >&2 "-W- No snapshots detected on the destination drive for this filesystem"
53   nodstsnaps=1
54 fi
55
56 if [ $nodstsnaps == 0 ]; then
57   zfs list -rH -t snapshot $srcfs | grep "$srcfs@" | awk '{print $1}' > /tmp/source-list
58   zfs list -rH -t snapshot $dstfs | grep "$dstfs@" | sed "s,$dstpool/,," | awk '{print $1}' > /tmp/destination-list
59   diff -u /tmp/source-list /tmp/destination-list | grep -v '^+++' | awk '/^\+/ {print}' | sed "s,^\+,$dstpool/," > /tmp/obsolete-snapshots
60   rm -f /tmp/source-list /tmp/destination-list
61
62   echo >&2 "Removing obsolete backups from the destination pool" 
63   for snapshot in $(cat /tmp/obsolete-snapshots); do
64     echo >&2 "Removing '$snapshot' from destination."
65     [ $test == 0 ] && zfs destroy "$snapshot"
66   done
67
68   echo >&2 "Rolling back to the most recent snapshot on the destination." 
69   [ $test == 0 ] && zfs rollback $(zfs list -rH -t snapshot $dstfs | grep "$dstfs@" | awk '{snap=$1} END {print snap}')
70
71   echo >&2 "Calculating the most recent common snapshot between the two filesystems." 
72   if zfs list -H "$dstfs" > /dev/null 2>&1; then
73     for snap in $(zfs list -rH -t snapshot "$dstfs" | grep "$dstfs@" |
74                     sed 's,.*@,,' | awk '{print$1}'); do
75       if zfs list -rH -t snapshot "$fs" | grep "$fs@" | sed 's,.*@,,' | awk '{print$1}' | grep "^${snap}$" >/dev/null 2>&1; then
76         common=$snap
77       fi  
78     done
79   fi
80 fi
81
82 base=$common
83 foundcommon=false
84 if [ -z "$common" ]; then
85   foundcommon=true
86 fi
87
88 for snap in $(zfs list -rH -t snapshot "$fs" | grep "$fs@" |
89                 sed 's,.*@,,' | awk '{print$1}'); do
90   if [ "$snap" = "$common" ]; then
91     foundcommon=true
92     continue
93   fi
94
95   if $foundcommon; then
96     if [ -z "$base" ]; then
97       echo >&2 "Sending '$fs@$snap'"
98       [ $test == 0 ] && zfs set readonly=on "$dstpool"
99       [ $test == 0 ] && zfs set atime=off "$dstpool"
100       [ $test == 0 ] && zfs set sharenfs=off "$dstpool"
101       [ $test == 0 ] && zfs set mountpoint=legacy "$dstpool"
102       [ $test == 0 ] && zfs send "$fs@$snap" | zfs recv -v "$dstfs"
103     else
104       echo >&2 "Sending '$fs@$base' -> '$fs@$snap'"
105       [ $test == 0 ] && zfs send -i "$fs@$base" "$fs@$snap" | zfs recv -v "$dstfs"
106     fi
107     base=$snap
108   fi
109 done
110
111 true