blob: 68673a770060953a57b2b8c0fb276f1c3fde14f5 (
plain)
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
|
''
usage() { echo "Usage: $0 <subvolume> <target> [<up-to>]" >&2; exit 1; }
[[ $# -ge 2 && $# -le 3 && ( $# -ne 3 || $(("$3" + 0)) = "$3" ) ]] || usage
[[ $UID -eq 0 ]] || { echo "$0: requires root privileges" >&2; exit 1; }
SOURCE="$1"
TARGET="$2"
[ $# -ge 3 ] && UP_TO="$3" || UP_TO=
get_snapshots() {
FILTER="\$1 > 0"
[[ -n "$UP_TO" ]] && FILTER="\$1 > 0 && \$1 <= $UP_TO"
find "$1" -follow -maxdepth 1 -printf '%f\n' \
| sed 's@/$@@g' | awk "$FILTER { print }" | sort -n
}
clone() {
if [[ -n "$2" ]]; then
echo "Cloning $1 (incremental from $2)..." >&2
PARENT_ARG="-p$SOURCE/$2/snapshot"
else
echo "Cloning $1 (non-incremental)..." >&2
PARENT_ARG="--"
fi
FROM="$SOURCE/$1"
INTO="$TARGET/$1"
[[ -d "$INTO" ]] || mkdir "$INTO"
btrfs send "$PARENT_ARG" "$FROM/snapshot" | pv | btrfs receive "$INTO"
cp -a "$FROM/info.xml" "$INTO/"
}
cleanup() {
echo "Removing $1..." >&2
AT="$TARGET/$1"
[[ -d "$AT/snapshot" ]] && btrfs subvolume delete "$AT/snapshot"
rm -f "$AT/info.xml"
rmdir "$AT"
}
while true; do
ls -- "$SOURCE/" "$TARGET/" >/dev/null
mapfile -t SNAPSHOTS < <(get_snapshots "$SOURCE")
[[ ''${#SNAPSHOTS} -eq 0 ]] && { echo "$0: nothing to do" >&2; exit; }
mapfile -t EXISTENT < <(get_snapshots "$TARGET")
RESET=0
PARENT=
ALL_ARE_BAD=0
for SNAPSHOT in "''${SNAPSHOTS[@]}"; do
if [[ ! -e "$SOURCE/$SNAPSHOT" ]]; then
echo "Notice: resetting listings due to time lapse" >&2
RESET=1
break
elif [[ ! -s "$SOURCE/$SNAPSHOT/info.xml" ]]; then
echo "Warning: skipping source $SNAPSHOT due to invalid info.xml" >&2
continue
fi
CLONE=0
if [[ ! -e "$TARGET/$SNAPSHOT" ]]; then
CLONE=1
elif [[ $ALL_ARE_BAD -eq 1 || ! -f "$TARGET/$SNAPSHOT/info.xml" ]]; then
echo -n "Warning: bad copy $SNAPSHOT (" >&2
[[ $ALL_ARE_BAD -eq 1 ]] && echo -n "bad chain" >&2 || echo -n "incomplete" >&2
echo "), recreating..." >&2
cleanup "$SNAPSHOT"
CLONE=1
ALL_ARE_BAD=1
fi
if [[ $CLONE -eq 1 ]]; then
clone "$SNAPSHOT" "$PARENT"
elif [[ -z "$PARENT" ]]; then
echo "Initial snapshot present, all copies will be incremental" >&2
fi
chmod 750 "$TARGET/$SNAPSHOT"
PARENT="$SNAPSHOT"
done
[[ $RESET -eq 1 ]] && continue
echo "Copies updated, cleaning up..." >&2
for SNAPSHOT in "''${EXISTENT[@]}"; do
FOUND=""
for KEEP in "''${SNAPSHOTS[@]}"; do
if [ "$KEEP" = "$SNAPSHOT" ]; then
FOUND=1
break
fi
done
[ -n "$FOUND" ] || cleanup "$SNAPSHOT"
done
break
done
echo "Done" >&2
''
|