-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbtrfs
169 lines (137 loc) · 4.89 KB
/
btrfs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# backupninja handler for incremental backups using btrfs send-receive
# feedback: pavel.cernocky at gmail.com
#
# btrfs handler is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or any later version.
#
# btrfs handler is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA
# function definitions
function eval_config {
debug "eval_config"
# system section
setsection system
# general section
setsection general
getconf tmp /tmp
# source section
setsection source
getconf subvolume; src_subvolume=$subvolume
getconf btrfs btrfs; src_btrfs=$btrfs
getconf type; src_type=$type
if [ "$src_type" == "remote" ]; then
getconf testconnect yes; src_testconnect=$testconnect
getconf ssh ssh; src_ssh=$ssh
getconf host; src_host=$host
getconf port 22; src_port=$port
getconf user; src_user=$user
getconf ssh_options; src_ssh_options=$ssh_options
fi
# dest section
setsection dest
getconf directory; dst_directory=$directory
getconf btrfs btrfs; dst_btrfs=$btrfs
getconf type; dst_type=$type
if [ "$dst_type" == "remote" ]; then
getconf testconnect yes; dst_testconnect=$testconnect
getconf ssh ssh; dst_ssh=$ssh
getconf host; dst_host=$host
getconf port 22; dst_port=$port
getconf user; dst_user=$user
getconf ssh_options; dst_ssh_options=$ssh_options
fi
# config check
if [ "$src_type" != "local" ] && [ "$src_type" != "remote" ]; then
fatal "Source type should be 'local' or 'remote'"
fi
if [ "$dst_type" != "local" ] && [ "$dst_type" != "remote" ]; then
fatal "Dest type should be 'local' or 'remote'"
fi
check_var "src_subvolume"
check_var "dst_directory"
ssh_cmd_base="ssh -T -o PasswordAuthentication=no"
if [ "$src_type" == "remote" ]; then
check_var "src_user"
check_var "src_host"
src_ssh_cmd="$ssh_cmd_base -p $src_port $src_ssh_options $src_user@$src_host"
if [ "$src_testconnect" == "yes" ]; then
test_connect "$src_ssh_cmd" "$src_host" "$src_port" "$src_user"
fi
fi
if [ "$dst_type" == "remote" ]; then
check_var "dst_user"
check_var "dst_host"
dst_ssh_cmd="$ssh_cmd_base -p $dst_port $dst_ssh_options $dst_user@$dst_host"
if [ "$dst_testconnect" == "yes" ]; then
test_connect "$dst_ssh_cmd" "$dst_host" "$dst_port" "$dst_user"
fi
fi
}
function check_var {
[[ -n "${!1}" ]] || { fatal "$1 is not set" ; }
}
function test_connect {
local ssh_cmd="$1"
local host="$2"
local port="$3"
local user="$4"
debug "$ssh_cmd 'echo -n 1'"
result=`$ssh_cmd 'echo -n 1'`
if [ "$result" != "1" ]; then
fatal "Can't connect to $host:$port as $user ($ssh_cmd)."
else
debug "Connected to $host as $user successfully"
fi
}
# the backup procedure
eval_config
start_time=$(date +%s)
src_last_snapshot_filename="$src_subvolume.last-snapshot-name"
src_parent=$(dirname $src_subvolume)
dir=$(basename $src_subvolume)
dst_parent=$dst_directory
act="$dir.$(date -d@$start_time +%F.%H-%M-%S)"
src_act="$src_parent/$act"
dst_act="$dst_parent/$act"
if [ -e "$src_last_snapshot_filename" ]; then
prev=$($src_ssh_cmd cat "$src_last_snapshot_filename")
src_prev="$src_parent/$prev"
dst_prev="$dst_parent/$prev"
fi
debug "src_last_snapshot_filename=$src_last_snapshot_filename"
debug "src_act=$src_act"
debug "src_prev=$src_prev"
debug "dst_act=$dst_act"
debug "dst_prev=$dst_prev"
info "----- $(date -d@$start_time +"%F %T") ----- Starting backup: $src_subvolume > $dst_act"
# create snapshot
$src_ssh_cmd $src_btrfs subvolume snapshot -r "$src_subvolume" "$src_act"
$src_ssh_cmd sync
# backup
pv_err=$(mktemp)
if [ ! -e "$src_prev" ]; then
info "Last snapshot filename does not exist ($src_last_snapshot_filename), doing initial backup"
$src_ssh_cmd $src_btrfs send "$src_act" | pv --wait --timer --rate --bytes --force 2>"$pv_err" | $dst_ssh_cmd $dst_btrfs receive "$dst_parent"
else
$src_ssh_cmd $src_btrfs send -p "$src_prev" "$src_act" | pv --wait --timer --rate --bytes --force 2>"$pv_err" | $dst_ssh_cmd $dst_btrfs receive "$dst_parent"
fi
if [ $? -ne 0 ]; then
fatal "Backup failed"
fi
info $(cat "$pv_err")
rm "$pv_err"
if [ -z "$src_ssh_cmd" ]; then
$src_ssh_cmd echo "$act" > "$src_last_snapshot_filename"
else
$src_ssh_cmd "echo $act > $src_last_snapshot_filename"
fi
end_time=$(date +%s)
elapsed=$(( $end_time - $start_time ))
info "----- $(date -d@$end_time +"%F %T") ----- Finished successfuly, elapsed $(date --utc -d@$elapsed +%T)"